1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-22 12:33:29 +00:00

Add a through path for Archimedes disk images.

This commit is contained in:
Thomas Harte 2024-03-04 10:13:57 -05:00
parent 95cc34ba23
commit eae92a0cdb
4 changed files with 66 additions and 43 deletions

View File

@ -17,6 +17,7 @@ enum class Machine {
Atari2600, Atari2600,
AtariST, AtariST,
Amiga, Amiga,
Archimedes,
ColecoVision, ColecoVision,
Electron, Electron,
Enterprise, Enterprise,

View File

@ -87,37 +87,45 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::
auto catalogue = std::make_unique<Catalogue>(); auto catalogue = std::make_unique<Catalogue>();
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk); Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
const Storage::Encodings::MFM::Sector *free_space_map_second_half = parser.sector(0, 0, 1); // Grab the second half of the free-space map because it has the boot option in it.
const Storage::Encodings::MFM::Sector *const free_space_map_second_half = parser.sector(0, 0, 1);
if(!free_space_map_second_half) return nullptr; if(!free_space_map_second_half) return nullptr;
const bool has_large_sectors = free_space_map_second_half->samples[0].size() == 1024;
std::vector<uint8_t> root_directory; std::vector<uint8_t> root_directory;
root_directory.reserve(5 * 256); root_directory.reserve((has_large_sectors ? 5 : 8) * 256);
for(uint8_t c = 2; c < 7; c++) {
for(uint8_t c = 2; c < (has_large_sectors ? 4 : 7); c++) {
const Storage::Encodings::MFM::Sector *const sector = parser.sector(0, 0, c); const Storage::Encodings::MFM::Sector *const sector = parser.sector(0, 0, c);
if(!sector) return nullptr; if(!sector) return nullptr;
root_directory.insert(root_directory.end(), sector->samples[0].begin(), sector->samples[0].end()); root_directory.insert(root_directory.end(), sector->samples[0].begin(), sector->samples[0].end());
} }
// Quick sanity checks. // Check for end of directory marker.
if(root_directory[0x4cb]) return nullptr; if(root_directory[has_large_sectors ? 0x7d7 : 0x4cb]) return nullptr;
catalogue->is_hugo = !memcmp(&root_directory[1], "Hugo", 4) && !memcmp(&root_directory[0x4FB], "Hugo", 4);
const bool is_nick = !memcmp(&root_directory[1], "Nick", 4) && !memcmp(&root_directory[0x4FB], "Nick", 4); // Check for both directory identifiers.
catalogue->is_hugo = !memcmp(&root_directory[1], "Hugo", 4) && !memcmp(&root_directory[0x4fb], "Hugo", 4);
const bool is_nick = !memcmp(&root_directory[1], "Nick", 4) && !memcmp(&root_directory[0x7fb], "Nick", 4);
if(!catalogue->is_hugo && !is_nick) { if(!catalogue->is_hugo && !is_nick) {
return nullptr; return nullptr;
} }
if(!has_large_sectors) {
// TODO: I don't know where the boot option rests with large sectors.
switch(free_space_map_second_half->samples[0][0xfd]) { switch(free_space_map_second_half->samples[0][0xfd]) {
default: catalogue->bootOption = Catalogue::BootOption::None; break; default: catalogue->bootOption = Catalogue::BootOption::None; break;
case 1: catalogue->bootOption = Catalogue::BootOption::LoadBOOT; break; case 1: catalogue->bootOption = Catalogue::BootOption::LoadBOOT; break;
case 2: catalogue->bootOption = Catalogue::BootOption::RunBOOT; break; case 2: catalogue->bootOption = Catalogue::BootOption::RunBOOT; break;
case 3: catalogue->bootOption = Catalogue::BootOption::ExecBOOT; break; case 3: catalogue->bootOption = Catalogue::BootOption::ExecBOOT; break;
} }
}
// Parse the root directory, at least. // Parse the root directory, at least.
for(std::size_t file_offset = 0x005; file_offset < 0x4cb; file_offset += 0x1a) { for(std::size_t file_offset = 0x005; file_offset < (has_large_sectors ? 0x7d7 : 0x4cb); file_offset += 0x1a) {
// Obtain the name, which will be at most ten characters long, and will // Obtain the name, which will be at most ten characters long, and will
// be terminated by either a NULL character or a \r. // be terminated by either a NULL character or a \r.
char name[11]; char name[11]{};
std::size_t c = 0; std::size_t c = 0;
for(; c < 10; c++) { for(; c < 10; c++) {
const char next = root_directory[file_offset + c] & 0x7f; const char next = root_directory[file_offset + c] & 0x7f;

View File

@ -60,10 +60,11 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
} }
Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
auto target = std::make_unique<Target>(); auto target8bit = std::make_unique<Target>();
auto targetArchimedes = std::make_unique<Analyser::Static::Target>(Machine::Archimedes);
// Strip out inappropriate cartridges. // Copy appropriate cartridges to the 8-bit target.
target->media.cartridges = AcornCartridgesFrom(media.cartridges); target8bit->media.cartridges = AcornCartridgesFrom(media.cartridges);
// If there are any tapes, attempt to get data from the first. // If there are any tapes, attempt to get data from the first.
if(!media.tapes.empty()) { if(!media.tapes.empty()) {
@ -94,9 +95,9 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
// Inspect first file. If it's protected or doesn't look like BASIC // Inspect first file. If it's protected or doesn't look like BASIC
// then the loading command is *RUN. Otherwise it's CHAIN"". // then the loading command is *RUN. Otherwise it's CHAIN"".
target->loading_command = is_basic ? "CHAIN\"\"\n" : "*RUN\n"; target8bit->loading_command = is_basic ? "CHAIN\"\"\n" : "*RUN\n";
target->media.tapes = media.tapes; target8bit->media.tapes = media.tapes;
} }
} }
@ -112,16 +113,16 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
if(dfs_catalogue || (adfs_catalogue && adfs_catalogue->is_hugo)) { if(dfs_catalogue || (adfs_catalogue && adfs_catalogue->is_hugo)) {
// Accept the disk and determine whether DFS or ADFS ROMs are implied. // Accept the disk and determine whether DFS or ADFS ROMs are implied.
// Use the Pres ADFS if using an ADFS, as it leaves Page at &EOO. // Use the Pres ADFS if using an ADFS, as it leaves Page at &EOO.
target->media.disks = media.disks; target8bit->media.disks = media.disks;
target->has_dfs = bool(dfs_catalogue); target8bit->has_dfs = bool(dfs_catalogue);
target->has_pres_adfs = bool(adfs_catalogue); target8bit->has_pres_adfs = bool(adfs_catalogue);
// Check whether a simple shift+break will do for loading this disk. // Check whether a simple shift+break will do for loading this disk.
Catalogue::BootOption bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption; Catalogue::BootOption bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption;
if(bootOption != Catalogue::BootOption::None) { if(bootOption != Catalogue::BootOption::None) {
target->should_shift_restart = true; target8bit->should_shift_restart = true;
} else { } else {
target->loading_command = "*CAT\n"; target8bit->loading_command = "*CAT\n";
} }
// Check whether adding the AP6 ROM is justified. // Check whether adding the AP6 ROM is justified.
@ -137,39 +138,44 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
"VERIFY", "ZERO" "VERIFY", "ZERO"
}) { }) {
if(std::search(file.data.begin(), file.data.end(), command, command+strlen(command)) != file.data.end()) { if(std::search(file.data.begin(), file.data.end(), command, command+strlen(command)) != file.data.end()) {
target->has_ap6_rom = true; target8bit->has_ap6_rom = true;
target->has_sideways_ram = true; target8bit->has_sideways_ram = true;
} }
} }
} }
} else if(adfs_catalogue) {
targetArchimedes->media.disks = media.disks;
} }
} }
// Enable the Acorn ADFS if a mass-storage device is attached; // Enable the Acorn ADFS if a mass-storage device is attached;
// unlike the Pres ADFS it retains SCSI logic. // unlike the Pres ADFS it retains SCSI logic.
if(!media.mass_storage_devices.empty()) { if(!media.mass_storage_devices.empty()) {
target->has_pres_adfs = false; // To override a floppy selection, if one was made. target8bit->has_pres_adfs = false; // To override a floppy selection, if one was made.
target->has_acorn_adfs = true; target8bit->has_acorn_adfs = true;
// Assume some sort of later-era Acorn work is likely to happen; // Assume some sort of later-era Acorn work is likely to happen;
// so ensure *TYPE, etc are present. // so ensure *TYPE, etc are present.
target->has_ap6_rom = true; target8bit->has_ap6_rom = true;
target->has_sideways_ram = true; target8bit->has_sideways_ram = true;
target->media.mass_storage_devices = media.mass_storage_devices; target8bit->media.mass_storage_devices = media.mass_storage_devices;
// Check for a boot option. // Check for a boot option.
const auto sector = target->media.mass_storage_devices.front()->get_block(1); const auto sector = target8bit->media.mass_storage_devices.front()->get_block(1);
if(sector[0xfd]) { if(sector[0xfd]) {
target->should_shift_restart = true; target8bit->should_shift_restart = true;
} else { } else {
target->loading_command = "*CAT\n"; target8bit->loading_command = "*CAT\n";
} }
} }
TargetList targets; TargetList targets;
if(!target->media.empty()) { if(!target8bit->media.empty()) {
targets.push_back(std::move(target)); targets.push_back(std::move(target8bit));
}
if(!targetArchimedes->media.empty()) {
targets.push_back(std::move(targetArchimedes));
} }
return targets; return targets;
} }

View File

@ -144,6 +144,8 @@ struct ActivityObserver: public Activity::Observer {
ROM::Request missing_roms; ROM::Request missing_roms;
_machine = Machine::MachineForTargets(_analyser.targets, CSROMFetcher(&missing_roms), error); _machine = Machine::MachineForTargets(_analyser.targets, CSROMFetcher(&missing_roms), error);
if(!_machine) { if(!_machine) {
switch(error) {
case Machine::Error::MissingROM: {
const std::wstring description = missing_roms.description(0, L'•'); const std::wstring description = missing_roms.description(0, L'•');
static_assert(sizeof(wchar_t) == 4, "This code assumes wchar_t is UTF32"); static_assert(sizeof(wchar_t) == 4, "This code assumes wchar_t is UTF32");
NSString *nativeString = [[NSString alloc] NSString *nativeString = [[NSString alloc]
@ -151,6 +153,12 @@ struct ActivityObserver: public Activity::Observer {
length:description.size()*sizeof(wchar_t) length:description.size()*sizeof(wchar_t)
encoding:NSUTF32LittleEndianStringEncoding]; encoding:NSUTF32LittleEndianStringEncoding];
[missingROMs appendString:nativeString]; [missingROMs appendString:nativeString];
} break;
default:
NSLog(@"Unhandled machine creation error %d", error);
break;
}
return nil; return nil;
} }
updater.performer.machine = _machine.get(); updater.performer.machine = _machine.get();