mirror of
				https://github.com/TomHarte/CLK.git
				synced 2025-10-27 08:16:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			138 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			138 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //
 | |
| //  StaticAnalyser.cpp
 | |
| //  Clock Signal
 | |
| //
 | |
| //  Created by Thomas Harte on 03/05/2018.
 | |
| //  Copyright 2018 Thomas Harte. All rights reserved.
 | |
| //
 | |
| 
 | |
| #include "StaticAnalyser.hpp"
 | |
| 
 | |
| #include "../AppleII/Target.hpp"
 | |
| #include "../AppleIIgs/Target.hpp"
 | |
| #include "../Oric/Target.hpp"
 | |
| #include "../Disassembler/6502.hpp"
 | |
| #include "../Disassembler/AddressMapper.hpp"
 | |
| 
 | |
| #include "../../../Storage/Disk/Track/TrackSerialiser.hpp"
 | |
| #include "../../../Storage/Disk/Encodings/AppleGCR/SegmentParser.hpp"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| Analyser::Static::Target *AppleIITarget(const Storage::Encodings::AppleGCR::Sector *sector_zero) {
 | |
| 	using Target = Analyser::Static::AppleII::Target;
 | |
| 	auto *const target = new Target;
 | |
| 
 | |
| 	if(sector_zero && sector_zero->encoding == Storage::Encodings::AppleGCR::Sector::Encoding::FiveAndThree) {
 | |
| 		target->disk_controller = Target::DiskController::ThirteenSector;
 | |
| 	} else {
 | |
| 		target->disk_controller = Target::DiskController::SixteenSector;
 | |
| 	}
 | |
| 
 | |
| 	return target;
 | |
| }
 | |
| 
 | |
| Analyser::Static::Target *AppleIIgsTarget() {
 | |
| 	return new Analyser::Static::AppleIIgs::Target();
 | |
| }
 | |
| 
 | |
| Analyser::Static::Target *OricTarget(const Storage::Encodings::AppleGCR::Sector *) {
 | |
| 	using Target = Analyser::Static::Oric::Target;
 | |
| 	auto *const target = new Target;
 | |
| 	target->rom = Target::ROM::Pravetz;
 | |
| 	target->disk_interface = Target::DiskInterface::Pravetz;
 | |
| 	target->loading_command = "CALL 800\n";
 | |
| 	return target;
 | |
| }
 | |
| 
 | |
| }
 | |
| 
 | |
| Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
 | |
| 	// This analyser can comprehend disks only.
 | |
| 	if(media.disks.empty()) return {};
 | |
| 
 | |
| 	auto &disk = media.disks.front();
 | |
| 	TargetList targets;
 | |
| 
 | |
| 	// If the disk image is too large for a 5.25" disk, map this to the IIgs.
 | |
| 	if(disk->get_maximum_head_position() > Storage::Disk::HeadPosition(40)) {
 | |
| 		targets.push_back(std::unique_ptr<Analyser::Static::Target>(AppleIIgsTarget()));
 | |
| 		targets.back()->media = media;
 | |
| 		return targets;
 | |
| 	}
 | |
| 
 | |
| 	// Grab track 0, sector 0: the boot sector.
 | |
| 	const auto track_zero = disk->get_track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0)));
 | |
| 	const auto sector_map = Storage::Encodings::AppleGCR::sectors_from_segment(
 | |
| 		Storage::Disk::track_serialisation(*track_zero, Storage::Time(1, 50000)));
 | |
| 
 | |
| 	const Storage::Encodings::AppleGCR::Sector *sector_zero = nullptr;
 | |
| 	for(const auto &pair: sector_map) {
 | |
| 		if(!pair.second.address.sector) {
 | |
| 			sector_zero = &pair.second;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// If there's no boot sector then if there are also no sectors at all,
 | |
| 	// decline to nominate a machine. Otherwise go with an Apple as the default.
 | |
| 	if(!sector_zero) {
 | |
| 		if(sector_map.empty()) {
 | |
| 			return targets;
 | |
| 		} else {
 | |
| 			targets.push_back(std::unique_ptr<Analyser::Static::Target>(AppleIITarget(nullptr)));
 | |
| 			targets.back()->media = media;
 | |
| 			return targets;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// If the boot sector looks like it's intended for the Oric, create an Oric.
 | |
| 	// Otherwise go with the Apple II.
 | |
| 
 | |
| 	const auto disassembly = Analyser::Static::MOS6502::Disassemble(sector_zero->data, Analyser::Static::Disassembler::OffsetMapper(0xb800), {0xb800});
 | |
| 
 | |
| 	bool did_read_shift_register = false;
 | |
| 	bool is_oric = false;
 | |
| 
 | |
| 	// Look for a tight BPL loop reading the Oric's shift register address of 0x31c. The Apple II just has RAM there,
 | |
| 	// so the probability of such a loop is infinitesimal.
 | |
| 	for(const auto &instruction: disassembly.instructions_by_address) {
 | |
| 		// Is this a read of the shift register?
 | |
| 		if(
 | |
| 			(
 | |
| 				(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDA) ||
 | |
| 				(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDX) ||
 | |
| 				(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDY)
 | |
| 			) &&
 | |
| 			instruction.second.addressing_mode == Analyser::Static::MOS6502::Instruction::Absolute &&
 | |
| 			instruction.second.address == 0x031c) {
 | |
| 			did_read_shift_register = true;
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		if(did_read_shift_register) {
 | |
| 			if(
 | |
| 				instruction.second.operation == Analyser::Static::MOS6502::Instruction::BPL &&
 | |
| 				instruction.second.address == 0xfb) {
 | |
| 				is_oric = true;
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			did_read_shift_register = false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Check also for calls into the 0x3xx page above 0x320, as that's where the Oric's boot ROM is.
 | |
| 	for(const auto address: disassembly.outward_calls) {
 | |
| 		is_oric |= address >= 0x320 && address < 0x400;
 | |
| 	}
 | |
| 
 | |
| 	if(is_oric) {
 | |
| 		targets.push_back(std::unique_ptr<Analyser::Static::Target>(OricTarget(sector_zero)));
 | |
| 	} else {
 | |
| 		targets.push_back(std::unique_ptr<Analyser::Static::Target>(AppleIITarget(sector_zero)));
 | |
| 	}
 | |
| 	targets.back()->media = media;
 | |
| 	return targets;
 | |
| }
 |