mirror of
				https://github.com/TomHarte/CLK.git
				synced 2025-10-31 05:16:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			620 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			620 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //
 | |
| //  Z80.cpp
 | |
| //  Clock Signal
 | |
| //
 | |
| //  Created by Thomas Harte on 30/12/2017.
 | |
| //  Copyright 2017 Thomas Harte. All rights reserved.
 | |
| //
 | |
| 
 | |
| #include "Z80.hpp"
 | |
| 
 | |
| #include "Kernel.hpp"
 | |
| 
 | |
| using namespace Analyser::Static::Z80;
 | |
| namespace  {
 | |
| 
 | |
| using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly<Disassembly, uint16_t>;
 | |
| 
 | |
| class Accessor {
 | |
| 	public:
 | |
| 		Accessor(const std::vector<uint8_t> &memory, const std::function<std::size_t(uint16_t)> &address_mapper, uint16_t address) :
 | |
| 			memory_(memory), address_mapper_(address_mapper), address_(address) {}
 | |
| 
 | |
| 		uint8_t byte() {
 | |
| 			std::size_t mapped_address = address_mapper_(address_);
 | |
| 			address_++;
 | |
| 			if(mapped_address >= memory_.size()) {
 | |
| 				overrun_ = true;
 | |
| 				return 0xff;
 | |
| 			}
 | |
| 			return memory_[mapped_address];
 | |
| 		}
 | |
| 
 | |
| 		uint16_t word() {
 | |
| 			uint8_t low = byte();
 | |
| 			uint8_t high = byte();
 | |
| 			return static_cast<uint16_t>(low | (high << 8));
 | |
| 		}
 | |
| 
 | |
| 		bool overrun() {
 | |
| 			return overrun_;
 | |
| 		}
 | |
| 
 | |
| 		bool at_end() {
 | |
| 			std::size_t mapped_address = address_mapper_(address_);
 | |
| 			return mapped_address >= memory_.size();
 | |
| 		}
 | |
| 
 | |
| 		uint16_t address() {
 | |
| 			return address_;
 | |
| 		}
 | |
| 
 | |
| 	private:
 | |
| 		const std::vector<uint8_t> &memory_;
 | |
| 		const std::function<std::size_t(uint16_t)> &address_mapper_;
 | |
| 		uint16_t address_;
 | |
| 		bool overrun_ = false;
 | |
| };
 | |
| 
 | |
| #define x(v) (v >> 6)
 | |
| #define y(v) ((v >> 3) & 7)
 | |
| #define q(v) ((v >> 3) & 1)
 | |
| #define p(v) ((v >> 4) & 3)
 | |
| #define z(v) (v & 7)
 | |
| 
 | |
| Instruction::Condition condition_table[] = {
 | |
| 	Instruction::Condition::NZ,		Instruction::Condition::Z,
 | |
| 	Instruction::Condition::NC,		Instruction::Condition::C,
 | |
| 	Instruction::Condition::PO,		Instruction::Condition::PE,
 | |
| 	Instruction::Condition::P,		Instruction::Condition::M
 | |
| };
 | |
| 
 | |
| Instruction::Location register_pair_table[] = {
 | |
| 	Instruction::Location::BC,
 | |
| 	Instruction::Location::DE,
 | |
| 	Instruction::Location::HL,
 | |
| 	Instruction::Location::SP
 | |
| };
 | |
| 
 | |
| Instruction::Location register_pair_table2[] = {
 | |
| 	Instruction::Location::BC,
 | |
| 	Instruction::Location::DE,
 | |
| 	Instruction::Location::HL,
 | |
| 	Instruction::Location::AF
 | |
| };
 | |
| 
 | |
| Instruction::Location RegisterTableEntry(int offset, Accessor &accessor, Instruction &instruction, bool needs_indirect_offset) {
 | |
| 	Instruction::Location register_table[] = {
 | |
| 		Instruction::Location::B,	Instruction::Location::C,
 | |
| 		Instruction::Location::D,	Instruction::Location::E,
 | |
| 		Instruction::Location::H,	Instruction::Location::L,
 | |
| 		Instruction::Location::HL_Indirect,
 | |
| 		Instruction::Location::A
 | |
| 	};
 | |
| 
 | |
| 	Instruction::Location location = register_table[offset];
 | |
| 	if(location == Instruction::Location::HL_Indirect && needs_indirect_offset) {
 | |
| 		instruction.offset = accessor.byte() - 128;
 | |
| 	}
 | |
| 
 | |
| 	return location;
 | |
| }
 | |
| 
 | |
| Instruction::Operation alu_table[] = {
 | |
| 	Instruction::Operation::ADD,
 | |
| 	Instruction::Operation::ADC,
 | |
| 	Instruction::Operation::SUB,
 | |
| 	Instruction::Operation::SBC,
 | |
| 	Instruction::Operation::AND,
 | |
| 	Instruction::Operation::XOR,
 | |
| 	Instruction::Operation::OR,
 | |
| 	Instruction::Operation::CP
 | |
| };
 | |
| 
 | |
| Instruction::Operation rotation_table[] = {
 | |
| 	Instruction::Operation::RLC,
 | |
| 	Instruction::Operation::RRC,
 | |
| 	Instruction::Operation::RL,
 | |
| 	Instruction::Operation::RR,
 | |
| 	Instruction::Operation::SLA,
 | |
| 	Instruction::Operation::SRA,
 | |
| 	Instruction::Operation::SLL,
 | |
| 	Instruction::Operation::SRL
 | |
| };
 | |
| 
 | |
| Instruction::Operation block_table[][4] = {
 | |
| 	{Instruction::Operation::LDI, Instruction::Operation::CPI, Instruction::Operation::INI, Instruction::Operation::OUTI},
 | |
| 	{Instruction::Operation::LDD, Instruction::Operation::CPD, Instruction::Operation::IND, Instruction::Operation::OUTD},
 | |
| 	{Instruction::Operation::LDIR, Instruction::Operation::CPIR, Instruction::Operation::INIR, Instruction::Operation::OTIR},
 | |
| 	{Instruction::Operation::LDDR, Instruction::Operation::CPDR, Instruction::Operation::INDR, Instruction::Operation::OTDR},
 | |
| };
 | |
| 
 | |
| void DisassembleCBPage(Accessor &accessor, Instruction &instruction, bool needs_indirect_offset) {
 | |
| 	const uint8_t operation = accessor.byte();
 | |
| 
 | |
| 	if(!x(operation)) {
 | |
| 		instruction.operation = rotation_table[y(operation)];
 | |
| 		instruction.source = instruction.destination = RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset);
 | |
| 	} else {
 | |
| 		instruction.destination = RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset);
 | |
| 		instruction.source = Instruction::Location::Operand;
 | |
| 		instruction.operand = y(operation);
 | |
| 
 | |
| 		switch(x(operation)) {
 | |
| 			case 1:	instruction.operation = Instruction::Operation::BIT;	break;
 | |
| 			case 2:	instruction.operation = Instruction::Operation::RES;	break;
 | |
| 			case 3:	instruction.operation = Instruction::Operation::SET;	break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_indirect_offset) {
 | |
| 	const uint8_t operation = accessor.byte();
 | |
| 
 | |
| 	switch(x(operation)) {
 | |
| 		default:
 | |
| 			instruction.operation = Instruction::Operation::Invalid;
 | |
| 		break;
 | |
| 		case 2:
 | |
| 			if(z(operation) < 4 && y(operation) >= 4) {
 | |
| 				instruction.operation = block_table[y(operation)-4][z(operation)];
 | |
| 			} else {
 | |
| 				instruction.operation = Instruction::Operation::Invalid;
 | |
| 			}
 | |
| 		break;
 | |
| 		case 3:
 | |
| 			switch(z(operation)) {
 | |
| 				case 0:
 | |
| 					instruction.operation = Instruction::Operation::IN;
 | |
| 					instruction.source = Instruction::Location::BC_Indirect;
 | |
| 					if(y(operation) == 6) {
 | |
| 						instruction.destination = Instruction::Location::None;
 | |
| 					} else {
 | |
| 						instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
 | |
| 					}
 | |
| 				break;
 | |
| 				case 1:
 | |
| 					instruction.operation = Instruction::Operation::OUT;
 | |
| 					instruction.destination = Instruction::Location::BC_Indirect;
 | |
| 					if(y(operation) == 6) {
 | |
| 						instruction.source = Instruction::Location::None;
 | |
| 					} else {
 | |
| 						instruction.source = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
 | |
| 					}
 | |
| 				break;
 | |
| 				case 2:
 | |
| 					instruction.operation = (y(operation)&1) ? Instruction::Operation::ADC : Instruction::Operation::SBC;
 | |
| 					instruction.destination = Instruction::Location::HL;
 | |
| 					instruction.source = register_pair_table[y(operation) >> 1];
 | |
| 				break;
 | |
| 				case 3:
 | |
| 					instruction.operation = Instruction::Operation::LD;
 | |
| 					if(q(operation)) {
 | |
| 						instruction.destination = RegisterTableEntry(p(operation), accessor, instruction, needs_indirect_offset);
 | |
| 						instruction.source = Instruction::Location::Operand_Indirect;
 | |
| 					} else {
 | |
| 						instruction.destination = Instruction::Location::Operand_Indirect;
 | |
| 						instruction.source = RegisterTableEntry(p(operation), accessor, instruction, needs_indirect_offset);
 | |
| 					}
 | |
| 					instruction.operand = accessor.word();
 | |
| 				break;
 | |
| 				case 4:
 | |
| 					instruction.operation = Instruction::Operation::NEG;
 | |
| 				break;
 | |
| 				case 5:
 | |
| 					instruction.operation = (y(operation) == 1) ? Instruction::Operation::RETI : Instruction::Operation::RETN;
 | |
| 				break;
 | |
| 				case 6:
 | |
| 					instruction.operation = Instruction::Operation::IM;
 | |
| 					instruction.source = Instruction::Location::Operand;
 | |
| 					switch(y(operation)&3) {
 | |
| 						case 0:	instruction.operand = 0;	break;
 | |
| 						case 1:	instruction.operand = 0;	break;
 | |
| 						case 2:	instruction.operand = 1;	break;
 | |
| 						case 3:	instruction.operand = 2;	break;
 | |
| 					}
 | |
| 				break;
 | |
| 				case 7:
 | |
| 					switch(y(operation)) {
 | |
| 						case 0:
 | |
| 							instruction.operation = Instruction::Operation::LD;
 | |
| 							instruction.destination = Instruction::Location::I;
 | |
| 							instruction.source = Instruction::Location::A;
 | |
| 						break;
 | |
| 						case 1:
 | |
| 							instruction.operation = Instruction::Operation::LD;
 | |
| 							instruction.destination = Instruction::Location::R;
 | |
| 							instruction.source = Instruction::Location::A;
 | |
| 						break;
 | |
| 						case 2:
 | |
| 							instruction.operation = Instruction::Operation::LD;
 | |
| 							instruction.destination = Instruction::Location::A;
 | |
| 							instruction.source = Instruction::Location::I;
 | |
| 						break;
 | |
| 						case 3:
 | |
| 							instruction.operation = Instruction::Operation::LD;
 | |
| 							instruction.destination = Instruction::Location::A;
 | |
| 							instruction.source = Instruction::Location::R;
 | |
| 						break;
 | |
| 						case 4:		instruction.operation = Instruction::Operation::RRD;	break;
 | |
| 						case 5:		instruction.operation = Instruction::Operation::RLD;	break;
 | |
| 						default:	instruction.operation = Instruction::Operation::NOP;	break;
 | |
| 					}
 | |
| 				break;
 | |
| 			}
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void DisassembleMainPage(Accessor &accessor, Instruction &instruction) {
 | |
| 	bool needs_indirect_offset = false;
 | |
| 	enum HLSubstitution {
 | |
| 		None, IX, IY
 | |
| 	} hl_substitution = None;
 | |
| 
 | |
| 	while(true) {
 | |
| 		uint8_t operation = accessor.byte();
 | |
| 
 | |
| 		switch(x(operation)) {
 | |
| 			case 0:
 | |
| 				switch(z(operation)) {
 | |
| 					case 0:
 | |
| 						switch(y(operation)) {
 | |
| 							case 0: instruction.operation = Instruction::Operation::NOP;		break;
 | |
| 							case 1: instruction.operation = Instruction::Operation::EXAFAFd;	break;
 | |
| 							case 2:
 | |
| 								instruction.operation = Instruction::Operation::DJNZ;
 | |
| 								instruction.operand = accessor.byte() - 128;
 | |
| 							break;
 | |
| 							default:
 | |
| 								instruction.operation = Instruction::Operation::JR;
 | |
| 								instruction.operand = accessor.byte() - 128;
 | |
| 								if(y(operation) >= 4) instruction.condition = condition_table[y(operation) - 4];
 | |
| 							break;
 | |
| 						}
 | |
| 					break;
 | |
| 					case 1:
 | |
| 						if(y(operation)&1) {
 | |
| 							instruction.operation = Instruction::Operation::ADD;
 | |
| 							instruction.destination = Instruction::Location::HL;
 | |
| 							instruction.source = register_pair_table[y(operation) >> 1];
 | |
| 						} else {
 | |
| 							instruction.operation = Instruction::Operation::LD;
 | |
| 							instruction.destination = register_pair_table[y(operation) >> 1];
 | |
| 							instruction.source = Instruction::Location::Operand;
 | |
| 							instruction.operand = accessor.word();
 | |
| 						}
 | |
| 					break;
 | |
| 					case 2:
 | |
| 						switch(y(operation)) {
 | |
| 							case 0:
 | |
| 								instruction.operation = Instruction::Operation::LD;
 | |
| 								instruction.destination = Instruction::Location::BC_Indirect;
 | |
| 								instruction.source = Instruction::Location::A;
 | |
| 							break;
 | |
| 							case 1:
 | |
| 								instruction.operation = Instruction::Operation::LD;
 | |
| 								instruction.destination = Instruction::Location::A;
 | |
| 								instruction.source = Instruction::Location::BC_Indirect;
 | |
| 							break;
 | |
| 							case 2:
 | |
| 								instruction.operation = Instruction::Operation::LD;
 | |
| 								instruction.destination = Instruction::Location::DE_Indirect;
 | |
| 								instruction.source = Instruction::Location::A;
 | |
| 							break;
 | |
| 							case 3:
 | |
| 								instruction.operation = Instruction::Operation::LD;
 | |
| 								instruction.destination = Instruction::Location::A;
 | |
| 								instruction.source = Instruction::Location::DE_Indirect;
 | |
| 							break;
 | |
| 							case 4:
 | |
| 								instruction.operation = Instruction::Operation::LD;
 | |
| 								instruction.destination = Instruction::Location::Operand_Indirect;
 | |
| 								instruction.source = Instruction::Location::HL;
 | |
| 							break;
 | |
| 							case 5:
 | |
| 								instruction.operation = Instruction::Operation::LD;
 | |
| 								instruction.destination = Instruction::Location::HL;
 | |
| 								instruction.source = Instruction::Location::Operand_Indirect;
 | |
| 							break;
 | |
| 							case 6:
 | |
| 								instruction.operation = Instruction::Operation::LD;
 | |
| 								instruction.destination = Instruction::Location::Operand_Indirect;
 | |
| 								instruction.source = Instruction::Location::A;
 | |
| 							break;
 | |
| 							case 7:
 | |
| 								instruction.operation = Instruction::Operation::LD;
 | |
| 								instruction.destination = Instruction::Location::A;
 | |
| 								instruction.source = Instruction::Location::Operand_Indirect;
 | |
| 							break;
 | |
| 						}
 | |
| 
 | |
| 						if(y(operation) > 3) {
 | |
| 							instruction.operand = accessor.word();
 | |
| 						}
 | |
| 					break;
 | |
| 					case 3:
 | |
| 						if(y(operation)&1) {
 | |
| 							instruction.operation = Instruction::Operation::DEC;
 | |
| 						} else {
 | |
| 							instruction.operation = Instruction::Operation::INC;
 | |
| 						}
 | |
| 						instruction.source = instruction.destination = register_pair_table[y(operation) >> 1];
 | |
| 					break;
 | |
| 					case 4:
 | |
| 						instruction.operation = Instruction::Operation::INC;
 | |
| 						instruction.source = instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
 | |
| 					break;
 | |
| 					case 5:
 | |
| 						instruction.operation = Instruction::Operation::DEC;
 | |
| 						instruction.source = instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
 | |
| 					break;
 | |
| 					case 6:
 | |
| 						instruction.operation = Instruction::Operation::LD;
 | |
| 						instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
 | |
| 						instruction.source = Instruction::Location::Operand;
 | |
| 						instruction.operand = accessor.byte();
 | |
| 					break;
 | |
| 					case 7:
 | |
| 						switch(y(operation)) {
 | |
| 							case 0:	instruction.operation = Instruction::Operation::RLCA;	break;
 | |
| 							case 1:	instruction.operation = Instruction::Operation::RRCA;	break;
 | |
| 							case 2:	instruction.operation = Instruction::Operation::RLA;	break;
 | |
| 							case 3:	instruction.operation = Instruction::Operation::RRA;	break;
 | |
| 							case 4:	instruction.operation = Instruction::Operation::DAA;	break;
 | |
| 							case 5:	instruction.operation = Instruction::Operation::CPL;	break;
 | |
| 							case 6:	instruction.operation = Instruction::Operation::SCF;	break;
 | |
| 							case 7:	instruction.operation = Instruction::Operation::CCF;	break;
 | |
| 						}
 | |
| 					break;
 | |
| 				}
 | |
| 			break;
 | |
| 			case 1:
 | |
| 				if(y(operation) == 6 && z(operation) == 6) {
 | |
| 					instruction.operation = Instruction::Operation::HALT;
 | |
| 				} else {
 | |
| 					instruction.operation = Instruction::Operation::LD;
 | |
| 					instruction.source = RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset);
 | |
| 					instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
 | |
| 				}
 | |
| 			break;
 | |
| 			case 2:
 | |
| 				instruction.operation = alu_table[y(operation)];
 | |
| 				instruction.source = RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset);
 | |
| 				instruction.destination = Instruction::Location::A;
 | |
| 			break;
 | |
| 			case 3:
 | |
| 				switch(z(operation)) {
 | |
| 					case 0:
 | |
| 						instruction.operation = Instruction::Operation::RET;
 | |
| 						instruction.condition = condition_table[y(operation)];
 | |
| 					break;
 | |
| 					case 1:
 | |
| 						switch(y(operation)) {
 | |
| 							default:
 | |
| 								instruction.operation = Instruction::Operation::POP;
 | |
| 								instruction.source = register_pair_table2[y(operation) >> 1];
 | |
| 							break;
 | |
| 							case 1:
 | |
| 								instruction.operation = Instruction::Operation::RET;
 | |
| 							break;
 | |
| 							case 3:
 | |
| 								instruction.operation = Instruction::Operation::EXX;
 | |
| 							break;
 | |
| 							case 5:
 | |
| 								instruction.operation = Instruction::Operation::JP;
 | |
| 								instruction.source = Instruction::Location::HL;
 | |
| 							break;
 | |
| 							case 7:
 | |
| 								instruction.operation = Instruction::Operation::LD;
 | |
| 								instruction.destination = Instruction::Location::SP;
 | |
| 								instruction.source = Instruction::Location::HL;
 | |
| 							break;
 | |
| 						}
 | |
| 					break;
 | |
| 					case 2:
 | |
| 						instruction.operation = Instruction::Operation::JP;
 | |
| 						instruction.condition = condition_table[y(operation)];
 | |
| 						instruction.operand = accessor.word();
 | |
| 					break;
 | |
| 					case 3:
 | |
| 						switch(y(operation)) {
 | |
| 							case 0:
 | |
| 								instruction.operation = Instruction::Operation::JP;
 | |
| 								instruction.source = Instruction::Location::Operand;
 | |
| 								instruction.operand = accessor.word();
 | |
| 							break;
 | |
| 							case 1:
 | |
| 								DisassembleCBPage(accessor, instruction, needs_indirect_offset);
 | |
| 							break;
 | |
| 							case 2:
 | |
| 								instruction.operation = Instruction::Operation::OUT;
 | |
| 								instruction.source = Instruction::Location::A;
 | |
| 								instruction.destination = Instruction::Location::Operand_Indirect;
 | |
| 								instruction.operand = accessor.byte();
 | |
| 							break;
 | |
| 							case 3:
 | |
| 								instruction.operation = Instruction::Operation::IN;
 | |
| 								instruction.destination = Instruction::Location::A;
 | |
| 								instruction.source = Instruction::Location::Operand_Indirect;
 | |
| 								instruction.operand = accessor.byte();
 | |
| 							break;
 | |
| 							case 4:
 | |
| 								instruction.operation = Instruction::Operation::EX;
 | |
| 								instruction.destination = Instruction::Location::SP_Indirect;
 | |
| 								instruction.source = Instruction::Location::HL;
 | |
| 							break;
 | |
| 							case 5:
 | |
| 								instruction.operation = Instruction::Operation::EX;
 | |
| 								instruction.destination = Instruction::Location::DE;
 | |
| 								instruction.source = Instruction::Location::HL;
 | |
| 							break;
 | |
| 							case 6:
 | |
| 								instruction.operation = Instruction::Operation::DI;
 | |
| 							break;
 | |
| 							case 7:
 | |
| 								instruction.operation = Instruction::Operation::EI;
 | |
| 							break;
 | |
| 						}
 | |
| 					break;
 | |
| 					case 4:
 | |
| 						instruction.operation = Instruction::Operation::CALL;
 | |
| 						instruction.source = Instruction::Location::Operand_Indirect;
 | |
| 						instruction.operand = accessor.word();
 | |
| 						instruction.condition = condition_table[y(operation)];
 | |
| 					break;
 | |
| 					case 5:
 | |
| 						switch(y(operation)) {
 | |
| 							default:
 | |
| 								instruction.operation = Instruction::Operation::PUSH;
 | |
| 								instruction.source = register_pair_table2[y(operation) >> 1];
 | |
| 							break;
 | |
| 							case 1:
 | |
| 								instruction.operation = Instruction::Operation::CALL;
 | |
| 								instruction.source = Instruction::Location::Operand;
 | |
| 								instruction.operand = accessor.word();
 | |
| 							break;
 | |
| 							case 3:
 | |
| 								needs_indirect_offset = true;
 | |
| 								hl_substitution = IX;
 | |
| 							continue;	// i.e. repeat loop.
 | |
| 							case 5:
 | |
| 								DisassembleEDPage(accessor, instruction, needs_indirect_offset);
 | |
| 							break;
 | |
| 							case 7:
 | |
| 								needs_indirect_offset = true;
 | |
| 								hl_substitution = IY;
 | |
| 							continue;	// i.e. repeat loop.
 | |
| 						}
 | |
| 					break;
 | |
| 					case 6:
 | |
| 						instruction.operation = alu_table[y(operation)];
 | |
| 						instruction.source = Instruction::Location::Operand;
 | |
| 						instruction.destination = Instruction::Location::A;
 | |
| 						instruction.operand = accessor.byte();
 | |
| 					break;
 | |
| 					case 7:
 | |
| 						instruction.operation = Instruction::Operation::RST;
 | |
| 						instruction.source = Instruction::Location::Operand;
 | |
| 						instruction.operand = y(operation) << 3;
 | |
| 					break;
 | |
| 				}
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		// This while(true) isn't an infinite loop for everything except those paths that opt in
 | |
| 		// via continue.
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	// Perform IX/IY substitution for HL, if applicable.
 | |
| 	if(hl_substitution != None) {
 | |
| 		// EX DE, HL is not affected.
 | |
| 		if(instruction.operation == Instruction::Operation::EX) return;
 | |
| 
 | |
| 		// If an (HL) is involved, switch it for IX+d or IY+d.
 | |
| 		if(	instruction.source == Instruction::Location::HL_Indirect ||
 | |
| 			instruction.destination == Instruction::Location::HL_Indirect) {
 | |
| 
 | |
| 			if(instruction.source == Instruction::Location::HL_Indirect) {
 | |
| 				instruction.source = (hl_substitution == IX) ? Instruction::Location::IX_Indirect_Offset : Instruction::Location::IY_Indirect_Offset;
 | |
| 			}
 | |
| 			if(instruction.destination == Instruction::Location::HL_Indirect) {
 | |
| 				instruction.destination = (hl_substitution == IX) ? Instruction::Location::IX_Indirect_Offset : Instruction::Location::IY_Indirect_Offset;
 | |
| 			}
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		// Otherwise, switch either of H or L for I[X/Y]h and I[X/Y]l.
 | |
| 		if(instruction.source == Instruction::Location::H) {
 | |
| 			instruction.source = (hl_substitution == IX) ? Instruction::Location::IXh : Instruction::Location::IYh;
 | |
| 		}
 | |
| 		if(instruction.source == Instruction::Location::L) {
 | |
| 			instruction.source = (hl_substitution == IX) ? Instruction::Location::IXl : Instruction::Location::IYl;
 | |
| 		}
 | |
| 		if(instruction.destination == Instruction::Location::H) {
 | |
| 			instruction.destination = (hl_substitution == IX) ? Instruction::Location::IXh : Instruction::Location::IYh;
 | |
| 		}
 | |
| 		if(instruction.destination == Instruction::Location::L) {
 | |
| 			instruction.destination = (hl_substitution == IX) ? Instruction::Location::IXl : Instruction::Location::IYl;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| struct Z80Disassembler {
 | |
| 	static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector<uint8_t> &memory, const std::function<std::size_t(uint16_t)> &address_mapper, uint16_t entry_point) {
 | |
| 		disassembly.disassembly.internal_calls.insert(entry_point);
 | |
| 		Accessor accessor(memory, address_mapper, entry_point);
 | |
| 
 | |
| 		while(!accessor.at_end()) {
 | |
| 			Instruction instruction;
 | |
| 			instruction.address = accessor.address();
 | |
| 
 | |
| 			DisassembleMainPage(accessor, instruction);
 | |
| 
 | |
| 			// If any memory access was invalid, end disassembly.
 | |
| 			if(accessor.overrun()) return;
 | |
| 
 | |
| 			// Store the instruction away.
 | |
| 			disassembly.disassembly.instructions_by_address[instruction.address] = instruction;
 | |
| 
 | |
| 			// Update access tables.
 | |
| 			int access_type =
 | |
| 				((instruction.source == Instruction::Location::Operand_Indirect) ? 1 : 0) |
 | |
| 				((instruction.destination == Instruction::Location::Operand_Indirect) ? 2 : 0);
 | |
| 			uint16_t address = static_cast<uint16_t>(instruction.operand);
 | |
| 			bool is_internal = address_mapper(address) < memory.size();
 | |
| 			switch(access_type) {
 | |
| 				default: break;
 | |
| 				case 1:
 | |
| 					if(is_internal) {
 | |
| 						disassembly.disassembly.internal_loads.insert(address);
 | |
| 					} else {
 | |
| 						disassembly.disassembly.external_loads.insert(address);
 | |
| 					}
 | |
| 				break;
 | |
| 				case 2:
 | |
| 					if(is_internal) {
 | |
| 						disassembly.disassembly.internal_stores.insert(address);
 | |
| 					} else {
 | |
| 						disassembly.disassembly.external_stores.insert(address);
 | |
| 					}
 | |
| 				break;
 | |
| 				case 3:
 | |
| 					if(is_internal) {
 | |
| 						disassembly.disassembly.internal_modifies.insert(address);
 | |
| 					} else {
 | |
| 						disassembly.disassembly.internal_modifies.insert(address);
 | |
| 					}
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			// Add any (potentially) newly discovered entry point.
 | |
| 			if(	instruction.operation == Instruction::Operation::JP ||
 | |
| 				instruction.operation == Instruction::Operation::JR ||
 | |
| 				instruction.operation == Instruction::Operation::CALL ||
 | |
| 				instruction.operation == Instruction::Operation::RST) {
 | |
| 				disassembly.remaining_entry_points.push_back(static_cast<uint16_t>(instruction.operand));
 | |
| 			}
 | |
| 
 | |
| 			// This is it if: an unconditional RET, RETI, RETN, JP or JR is found.
 | |
| 			if(instruction.condition != Instruction::Condition::None)	continue;
 | |
| 
 | |
| 			if(instruction.operation == Instruction::Operation::RET)	return;
 | |
| 			if(instruction.operation == Instruction::Operation::RETI)	return;
 | |
| 			if(instruction.operation == Instruction::Operation::RETN)	return;
 | |
| 			if(instruction.operation == Instruction::Operation::JP)		return;
 | |
| 			if(instruction.operation == Instruction::Operation::JR)		return;
 | |
| 		}
 | |
| 	}
 | |
| };
 | |
| 
 | |
| }	// end of anonymous namespace
 | |
| 
 | |
| Disassembly Analyser::Static::Z80::Disassemble(
 | |
| 	const std::vector<uint8_t> &memory,
 | |
| 	const std::function<std::size_t(uint16_t)> &address_mapper,
 | |
| 	std::vector<uint16_t> entry_points) {
 | |
| 	return Analyser::Static::Disassembly::Disassemble<Disassembly, uint16_t, Z80Disassembler>(memory, address_mapper, entry_points);
 | |
| }
 |