mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-03 22:33:29 +00:00
Merge branch 'master' into AppleIISCSI
This commit is contained in:
commit
dc35ec8fa0
@ -56,233 +56,210 @@ std::string Preinstruction::operand_description(int index, int opcode) const {
|
||||
}
|
||||
}
|
||||
|
||||
std::string Preinstruction::to_string(int opcode) const {
|
||||
bool flip_operands = false;
|
||||
const char *instruction;
|
||||
namespace {
|
||||
|
||||
const char *_to_string(Operation operation, bool is_quick) {
|
||||
switch(operation) {
|
||||
case Operation::Undefined: return "None";
|
||||
case Operation::NOP: instruction = "NOP"; break;
|
||||
case Operation::ABCD: instruction = "ABCD"; break;
|
||||
case Operation::SBCD: instruction = "SBCD"; break;
|
||||
case Operation::NBCD: instruction = "NBCD"; break;
|
||||
case Operation::Undefined: return "None";
|
||||
case Operation::NOP: return "NOP";
|
||||
case Operation::ABCD: return "ABCD";
|
||||
case Operation::SBCD: return "SBCD";
|
||||
case Operation::NBCD: return "NBCD";
|
||||
|
||||
case Operation::ADDb: instruction = "ADD.b"; break;
|
||||
case Operation::ADDw: instruction = "ADD.w"; break;
|
||||
case Operation::ADDl: instruction = "ADD.l"; break;
|
||||
case Operation::ADDb: return "ADD.b";
|
||||
case Operation::ADDw: return "ADD.w";
|
||||
case Operation::ADDl: return "ADD.l";
|
||||
|
||||
case Operation::ADDAw:
|
||||
if(mode<0>() == AddressingMode::Quick) {
|
||||
instruction = "ADD.w";
|
||||
} else {
|
||||
instruction = "ADDA.w";
|
||||
}
|
||||
break;
|
||||
case Operation::ADDAl:
|
||||
if(mode<0>() == AddressingMode::Quick) {
|
||||
instruction = "ADD.l";
|
||||
} else {
|
||||
instruction = "ADDA.l";
|
||||
}
|
||||
break;
|
||||
case Operation::ADDAw: return is_quick ? "ADD.w" : "ADDA.w";
|
||||
case Operation::ADDAl: return is_quick ? "ADD.l" : "ADDA.l";
|
||||
|
||||
case Operation::ADDXb: instruction = "ADDX.b"; break;
|
||||
case Operation::ADDXw: instruction = "ADDX.w"; break;
|
||||
case Operation::ADDXl: instruction = "ADDX.l"; break;
|
||||
case Operation::ADDXb: return "ADDX.b";
|
||||
case Operation::ADDXw: return "ADDX.w";
|
||||
case Operation::ADDXl: return "ADDX.l";
|
||||
|
||||
case Operation::SUBb: instruction = "SUB.b"; break;
|
||||
case Operation::SUBw: instruction = "SUB.w"; break;
|
||||
case Operation::SUBl: instruction = "SUB.l"; break;
|
||||
case Operation::SUBb: return "SUB.b";
|
||||
case Operation::SUBw: return "SUB.w";
|
||||
case Operation::SUBl: return "SUB.l";
|
||||
|
||||
case Operation::SUBAw:
|
||||
if(mode<0>() == AddressingMode::Quick) {
|
||||
instruction = "SUB.w";
|
||||
} else {
|
||||
instruction = "SUBA.w";
|
||||
}
|
||||
break;
|
||||
case Operation::SUBAl:
|
||||
if(mode<0>() == AddressingMode::Quick) {
|
||||
instruction = "SUB.l";
|
||||
} else {
|
||||
instruction = "SUBA.l";
|
||||
}
|
||||
break;
|
||||
case Operation::SUBAw: return is_quick ? "SUB.w" : "SUBA.w";
|
||||
case Operation::SUBAl: return is_quick ? "SUB.l" : "SUBA.l";
|
||||
|
||||
case Operation::SUBXb: instruction = "SUBX.b"; break;
|
||||
case Operation::SUBXw: instruction = "SUBX.w"; break;
|
||||
case Operation::SUBXl: instruction = "SUBX.l"; break;
|
||||
case Operation::SUBXb: return "SUBX.b";
|
||||
case Operation::SUBXw: return "SUBX.w";
|
||||
case Operation::SUBXl: return "SUBX.l";
|
||||
|
||||
case Operation::MOVEb: instruction = "MOVE.b"; break;
|
||||
case Operation::MOVEw: instruction = "MOVE.w"; break;
|
||||
case Operation::MOVEl:
|
||||
if(mode<0>() == AddressingMode::Quick) {
|
||||
instruction = "MOVE.q";
|
||||
} else {
|
||||
instruction = "MOVE.l";
|
||||
}
|
||||
break;
|
||||
case Operation::MOVEb: return "MOVE.b";
|
||||
case Operation::MOVEw: return "MOVE.w";
|
||||
case Operation::MOVEl: return is_quick ? "MOVE.q" : "MOVE.l";
|
||||
|
||||
case Operation::MOVEAw: instruction = "MOVEA.w"; break;
|
||||
case Operation::MOVEAl: instruction = "MOVEA.l"; break;
|
||||
case Operation::MOVEAw: return "MOVEA.w";
|
||||
case Operation::MOVEAl: return "MOVEA.l";
|
||||
|
||||
case Operation::LEA: instruction = "LEA"; break;
|
||||
case Operation::PEA: instruction = "PEA"; break;
|
||||
case Operation::LEA: return "LEA";
|
||||
case Operation::PEA: return "PEA";
|
||||
|
||||
case Operation::MOVEtoSR: instruction = "MOVEtoSR"; break;
|
||||
case Operation::MOVEfromSR: instruction = "MOVEfromSR"; break;
|
||||
case Operation::MOVEtoCCR: instruction = "MOVEtoCCR"; break;
|
||||
case Operation::MOVEtoUSP: instruction = "MOVEtoUSP"; break;
|
||||
case Operation::MOVEfromUSP: instruction = "MOVEfromUSP"; break;
|
||||
case Operation::MOVEtoSR: return "MOVEtoSR";
|
||||
case Operation::MOVEfromSR: return "MOVEfromSR";
|
||||
case Operation::MOVEtoCCR: return "MOVEtoCCR";
|
||||
case Operation::MOVEtoUSP: return "MOVEtoUSP";
|
||||
case Operation::MOVEfromUSP: return "MOVEfromUSP";
|
||||
|
||||
case Operation::ORItoSR: instruction = "ORItoSR"; break;
|
||||
case Operation::ORItoCCR: instruction = "ORItoCCR"; break;
|
||||
case Operation::ANDItoSR: instruction = "ANDItoSR"; break;
|
||||
case Operation::ANDItoCCR: instruction = "ANDItoCCR"; break;
|
||||
case Operation::EORItoSR: instruction = "EORItoSR"; break;
|
||||
case Operation::EORItoCCR: instruction = "EORItoCCR"; break;
|
||||
case Operation::ORItoSR: return "ORItoSR";
|
||||
case Operation::ORItoCCR: return "ORItoCCR";
|
||||
case Operation::ANDItoSR: return "ANDItoSR";
|
||||
case Operation::ANDItoCCR: return "ANDItoCCR";
|
||||
case Operation::EORItoSR: return "EORItoSR";
|
||||
case Operation::EORItoCCR: return "EORItoCCR";
|
||||
|
||||
case Operation::BTST: instruction = "BTST"; break;
|
||||
case Operation::BCLR: instruction = "BCLR"; break;
|
||||
case Operation::BCHG: instruction = "BCHG"; break;
|
||||
case Operation::BSET: instruction = "BSET"; break;
|
||||
case Operation::BTST: return "BTST";
|
||||
case Operation::BCLR: return "BCLR";
|
||||
case Operation::BCHG: return "BCHG";
|
||||
case Operation::BSET: return "BSET";
|
||||
|
||||
case Operation::CMPb: instruction = "CMP.b"; break;
|
||||
case Operation::CMPw: instruction = "CMP.w"; break;
|
||||
case Operation::CMPl: instruction = "CMP.l"; break;
|
||||
case Operation::CMPb: return "CMP.b";
|
||||
case Operation::CMPw: return "CMP.w";
|
||||
case Operation::CMPl: return "CMP.l";
|
||||
|
||||
case Operation::CMPAw: instruction = "CMPA.w"; break;
|
||||
case Operation::CMPAl: instruction = "CMPA.l"; break;
|
||||
case Operation::CMPAw: return "CMPA.w";
|
||||
case Operation::CMPAl: return "CMPA.l";
|
||||
|
||||
case Operation::TSTb: instruction = "TST.b"; break;
|
||||
case Operation::TSTw: instruction = "TST.w"; break;
|
||||
case Operation::TSTl: instruction = "TST.l"; break;
|
||||
case Operation::TSTb: return "TST.b";
|
||||
case Operation::TSTw: return "TST.w";
|
||||
case Operation::TSTl: return "TST.l";
|
||||
|
||||
case Operation::JMP: instruction = "JMP"; break;
|
||||
case Operation::JSR: instruction = "JSR"; break;
|
||||
case Operation::RTS: instruction = "RTS"; break;
|
||||
case Operation::DBcc: instruction = "DBcc"; break;
|
||||
case Operation::Scc: instruction = "Scc"; break;
|
||||
case Operation::JMP: return "JMP";
|
||||
case Operation::JSR: return "JSR";
|
||||
case Operation::RTS: return "RTS";
|
||||
case Operation::DBcc: return "DBcc";
|
||||
case Operation::Scc: return "Scc";
|
||||
|
||||
case Operation::Bccb:
|
||||
case Operation::Bccl:
|
||||
case Operation::Bccw: instruction = "Bcc"; break;
|
||||
case Operation::Bccw: return "Bcc";
|
||||
|
||||
case Operation::BSRb:
|
||||
case Operation::BSRl:
|
||||
case Operation::BSRw: instruction = "BSR"; break;
|
||||
case Operation::BSRw: return "BSR";
|
||||
|
||||
case Operation::CLRb: instruction = "CLR.b"; break;
|
||||
case Operation::CLRw: instruction = "CLR.w"; break;
|
||||
case Operation::CLRl: instruction = "CLR.l"; break;
|
||||
case Operation::CLRb: return "CLR.b";
|
||||
case Operation::CLRw: return "CLR.w";
|
||||
case Operation::CLRl: return "CLR.l";
|
||||
|
||||
case Operation::NEGXb: instruction = "NEGX.b"; break;
|
||||
case Operation::NEGXw: instruction = "NEGX.w"; break;
|
||||
case Operation::NEGXl: instruction = "NEGX.l"; break;
|
||||
case Operation::NEGXb: return "NEGX.b";
|
||||
case Operation::NEGXw: return "NEGX.w";
|
||||
case Operation::NEGXl: return "NEGX.l";
|
||||
|
||||
case Operation::NEGb: instruction = "NEG.b"; break;
|
||||
case Operation::NEGw: instruction = "NEG.w"; break;
|
||||
case Operation::NEGl: instruction = "NEG.l"; break;
|
||||
case Operation::NEGb: return "NEG.b";
|
||||
case Operation::NEGw: return "NEG.w";
|
||||
case Operation::NEGl: return "NEG.l";
|
||||
|
||||
case Operation::ASLb: instruction = "ASL.b"; break;
|
||||
case Operation::ASLw: instruction = "ASL.w"; break;
|
||||
case Operation::ASLl: instruction = "ASL.l"; break;
|
||||
case Operation::ASLm: instruction = "ASL.w"; break;
|
||||
case Operation::ASLb: return "ASL.b";
|
||||
case Operation::ASLw: return "ASL.w";
|
||||
case Operation::ASLl: return "ASL.l";
|
||||
case Operation::ASLm: return "ASL.w";
|
||||
|
||||
case Operation::ASRb: instruction = "ASR.b"; break;
|
||||
case Operation::ASRw: instruction = "ASR.w"; break;
|
||||
case Operation::ASRl: instruction = "ASR.l"; break;
|
||||
case Operation::ASRm: instruction = "ASR.w"; break;
|
||||
case Operation::ASRb: return "ASR.b";
|
||||
case Operation::ASRw: return "ASR.w";
|
||||
case Operation::ASRl: return "ASR.l";
|
||||
case Operation::ASRm: return "ASR.w";
|
||||
|
||||
case Operation::LSLb: instruction = "LSL.b"; break;
|
||||
case Operation::LSLw: instruction = "LSL.w"; break;
|
||||
case Operation::LSLl: instruction = "LSL.l"; break;
|
||||
case Operation::LSLm: instruction = "LSL.w"; break;
|
||||
case Operation::LSLb: return "LSL.b";
|
||||
case Operation::LSLw: return "LSL.w";
|
||||
case Operation::LSLl: return "LSL.l";
|
||||
case Operation::LSLm: return "LSL.w";
|
||||
|
||||
case Operation::LSRb: instruction = "LSR.b"; break;
|
||||
case Operation::LSRw: instruction = "LSR.w"; break;
|
||||
case Operation::LSRl: instruction = "LSR.l"; break;
|
||||
case Operation::LSRm: instruction = "LSR.w"; break;
|
||||
case Operation::LSRb: return "LSR.b";
|
||||
case Operation::LSRw: return "LSR.w";
|
||||
case Operation::LSRl: return "LSR.l";
|
||||
case Operation::LSRm: return "LSR.w";
|
||||
|
||||
case Operation::ROLb: instruction = "ROL.b"; break;
|
||||
case Operation::ROLw: instruction = "ROL.w"; break;
|
||||
case Operation::ROLl: instruction = "ROL.l"; break;
|
||||
case Operation::ROLm: instruction = "ROL.w"; break;
|
||||
case Operation::ROLb: return "ROL.b";
|
||||
case Operation::ROLw: return "ROL.w";
|
||||
case Operation::ROLl: return "ROL.l";
|
||||
case Operation::ROLm: return "ROL.w";
|
||||
|
||||
case Operation::RORb: instruction = "ROR.b"; break;
|
||||
case Operation::RORw: instruction = "ROR.w"; break;
|
||||
case Operation::RORl: instruction = "ROR.l"; break;
|
||||
case Operation::RORm: instruction = "ROR.w"; break;
|
||||
case Operation::RORb: return "ROR.b";
|
||||
case Operation::RORw: return "ROR.w";
|
||||
case Operation::RORl: return "ROR.l";
|
||||
case Operation::RORm: return "ROR.w";
|
||||
|
||||
case Operation::ROXLb: instruction = "ROXL.b"; break;
|
||||
case Operation::ROXLw: instruction = "ROXL.w"; break;
|
||||
case Operation::ROXLl: instruction = "ROXL.l"; break;
|
||||
case Operation::ROXLm: instruction = "ROXL.w"; break;
|
||||
case Operation::ROXLb: return "ROXL.b";
|
||||
case Operation::ROXLw: return "ROXL.w";
|
||||
case Operation::ROXLl: return "ROXL.l";
|
||||
case Operation::ROXLm: return "ROXL.w";
|
||||
|
||||
case Operation::ROXRb: instruction = "ROXR.b"; break;
|
||||
case Operation::ROXRw: instruction = "ROXR.w"; break;
|
||||
case Operation::ROXRl: instruction = "ROXR.l"; break;
|
||||
case Operation::ROXRm: instruction = "ROXR.w"; break;
|
||||
case Operation::ROXRb: return "ROXR.b";
|
||||
case Operation::ROXRw: return "ROXR.w";
|
||||
case Operation::ROXRl: return "ROXR.l";
|
||||
case Operation::ROXRm: return "ROXR.w";
|
||||
|
||||
case Operation::MOVEMtoMl: instruction = "MOVEM.l"; break;
|
||||
case Operation::MOVEMtoMw: instruction = "MOVEM.w"; break;
|
||||
case Operation::MOVEMtoRl:
|
||||
instruction = "MOVEM.l";
|
||||
flip_operands = true;
|
||||
break;
|
||||
case Operation::MOVEMtoRw:
|
||||
instruction = "MOVEM.w";
|
||||
flip_operands = true;
|
||||
break;
|
||||
case Operation::MOVEMtoMl: return "MOVEM.l";
|
||||
case Operation::MOVEMtoMw: return "MOVEM.w";
|
||||
case Operation::MOVEMtoRl: return "MOVEM.l";
|
||||
case Operation::MOVEMtoRw: return "MOVEM.w";
|
||||
|
||||
case Operation::MOVEPl: instruction = "MOVEP.l"; break;
|
||||
case Operation::MOVEPw: instruction = "MOVEP.w"; break;
|
||||
case Operation::MOVEPl: return "MOVEP.l";
|
||||
case Operation::MOVEPw: return "MOVEP.w";
|
||||
|
||||
case Operation::ANDb: instruction = "AND.b"; break;
|
||||
case Operation::ANDw: instruction = "AND.w"; break;
|
||||
case Operation::ANDl: instruction = "AND.l"; break;
|
||||
case Operation::ANDb: return "AND.b";
|
||||
case Operation::ANDw: return "AND.w";
|
||||
case Operation::ANDl: return "AND.l";
|
||||
|
||||
case Operation::EORb: instruction = "EOR.b"; break;
|
||||
case Operation::EORw: instruction = "EOR.w"; break;
|
||||
case Operation::EORl: instruction = "EOR.l"; break;
|
||||
case Operation::EORb: return "EOR.b";
|
||||
case Operation::EORw: return "EOR.w";
|
||||
case Operation::EORl: return "EOR.l";
|
||||
|
||||
case Operation::NOTb: instruction = "NOT.b"; break;
|
||||
case Operation::NOTw: instruction = "NOT.w"; break;
|
||||
case Operation::NOTl: instruction = "NOT.l"; break;
|
||||
case Operation::NOTb: return "NOT.b";
|
||||
case Operation::NOTw: return "NOT.w";
|
||||
case Operation::NOTl: return "NOT.l";
|
||||
|
||||
case Operation::ORb: instruction = "OR.b"; break;
|
||||
case Operation::ORw: instruction = "OR.w"; break;
|
||||
case Operation::ORl: instruction = "OR.l"; break;
|
||||
case Operation::ORb: return "OR.b";
|
||||
case Operation::ORw: return "OR.w";
|
||||
case Operation::ORl: return "OR.l";
|
||||
|
||||
case Operation::MULU: instruction = "MULU"; break;
|
||||
case Operation::MULS: instruction = "MULS"; break;
|
||||
case Operation::DIVU: instruction = "DIVU"; break;
|
||||
case Operation::DIVS: instruction = "DIVS"; break;
|
||||
case Operation::MULU: return "MULU";
|
||||
case Operation::MULS: return "MULS";
|
||||
case Operation::DIVU: return "DIVU";
|
||||
case Operation::DIVS: return "DIVS";
|
||||
|
||||
case Operation::RTE: instruction = "RTE"; break;
|
||||
case Operation::RTR: instruction = "RTR"; break;
|
||||
case Operation::RTE: return "RTE";
|
||||
case Operation::RTR: return "RTR";
|
||||
|
||||
case Operation::TRAP: instruction = "TRAP"; break;
|
||||
case Operation::TRAPV: instruction = "TRAPV"; break;
|
||||
case Operation::CHK: instruction = "CHK"; break;
|
||||
case Operation::TRAP: return "TRAP";
|
||||
case Operation::TRAPV: return "TRAPV";
|
||||
case Operation::CHK: return "CHK";
|
||||
|
||||
case Operation::EXG: instruction = "EXG"; break;
|
||||
case Operation::SWAP: instruction = "SWAP"; break;
|
||||
case Operation::EXG: return "EXG";
|
||||
case Operation::SWAP: return "SWAP";
|
||||
|
||||
case Operation::TAS: instruction = "TAS"; break;
|
||||
case Operation::TAS: return "TAS";
|
||||
|
||||
case Operation::EXTbtow: instruction = "EXT.w"; break;
|
||||
case Operation::EXTwtol: instruction = "EXT.l"; break;
|
||||
case Operation::EXTbtow: return "EXT.w";
|
||||
case Operation::EXTwtol: return "EXT.l";
|
||||
|
||||
case Operation::LINKw: instruction = "LINK"; break;
|
||||
case Operation::UNLINK: instruction = "UNLINK"; break;
|
||||
case Operation::LINKw: return "LINK";
|
||||
case Operation::UNLINK: return "UNLINK";
|
||||
|
||||
case Operation::STOP: instruction = "STOP"; break;
|
||||
case Operation::RESET: instruction = "RESET"; break;
|
||||
case Operation::STOP: return "STOP";
|
||||
case Operation::RESET: return "RESET";
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const char *InstructionSet::M68k::to_string(Operation operation) {
|
||||
return _to_string(operation, false);
|
||||
}
|
||||
|
||||
std::string Preinstruction::to_string(int opcode) const {
|
||||
if(operation == Operation::Undefined) return "None";
|
||||
|
||||
const char *const instruction = _to_string(operation, mode<0>() == AddressingMode::Quick);
|
||||
const bool flip_operands = (operation == Operation::MOVEMtoRl) || (operation == Operation::MOVEMtoRw);
|
||||
|
||||
const std::string operand1 = operand_description(0 ^ int(flip_operands), opcode);
|
||||
const std::string operand2 = operand_description(1 ^ int(flip_operands), opcode);
|
||||
@ -293,3 +270,7 @@ std::string Preinstruction::to_string(int opcode) const {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char *Preinstruction::operation_string() const {
|
||||
return _to_string(operation, mode<0>() == AddressingMode::Quick);
|
||||
}
|
||||
|
@ -104,6 +104,8 @@ enum class Operation: uint8_t {
|
||||
Max = RESET
|
||||
};
|
||||
|
||||
const char *to_string(Operation op);
|
||||
|
||||
template <Model model>
|
||||
constexpr bool requires_supervisor(Operation op) {
|
||||
switch(op) {
|
||||
@ -346,6 +348,12 @@ class Preinstruction {
|
||||
/// is supplied then any quick fields in this instruction will be decoded;
|
||||
/// otherwise they'll be printed as just 'Q'.
|
||||
std::string to_string(int opcode = -1) const;
|
||||
|
||||
/// Produces a slightly-more-idiomatic version of the operation name than
|
||||
/// a direct to_string(instruction.operation) would, given that this decoder
|
||||
/// sometimes aliases operations, disambiguating based on addressing mode
|
||||
/// (e.g. MOVEQ is MOVE.l with the Q addressing mode).
|
||||
const char *operation_string() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -200,6 +200,7 @@ class ConcreteMachine:
|
||||
public:
|
||||
ConcreteMachine(const Analyser::Static::AppleIIgs::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
m65816_(*this),
|
||||
memory_(target.model >= Analyser::Static::AppleIIgs::Target::Model::ROM03),
|
||||
iwm_(CLOCK_RATE / 2),
|
||||
drives35_{
|
||||
{CLOCK_RATE / 2, true},
|
||||
|
@ -26,7 +26,9 @@ class MemoryMap {
|
||||
public:
|
||||
// MARK: - Initial construction and configuration.
|
||||
|
||||
MemoryMap() : auxiliary_switches_(*this), language_card_(*this) {}
|
||||
MemoryMap(bool is_rom03) : auxiliary_switches_(*this), language_card_(*this) {
|
||||
setup_shadow_maps(is_rom03);
|
||||
}
|
||||
|
||||
void set_storage(std::vector<uint8_t> &ram, std::vector<uint8_t> &rom) {
|
||||
// Keep a pointer for later; also note the proper RAM offset.
|
||||
@ -456,9 +458,6 @@ class MemoryMap {
|
||||
// $6000–$a000 Odd banks only, rest of Super High-res
|
||||
// [plus IO and language card space, subject to your definition of shadowing]
|
||||
|
||||
static constexpr int shadow_shift = 10;
|
||||
static constexpr int auxiliary_offset = 0x1'0000 >> shadow_shift;
|
||||
|
||||
enum Inhibit {
|
||||
TextPage1 = 0x01,
|
||||
HighRes1 = 0x02,
|
||||
@ -468,15 +467,25 @@ class MemoryMap {
|
||||
TextPage2 = 0x20,
|
||||
};
|
||||
|
||||
// Clear all shadowing.
|
||||
shadow_pages.reset();
|
||||
|
||||
// Text Page 1, main and auxiliary — $0400–$0800.
|
||||
for(size_t c = 0x0400 >> shadow_shift; c < 0x0800 >> shadow_shift; c++) {
|
||||
shadow_pages[c] = shadow_pages[c+auxiliary_offset] = !(shadow_register_ & Inhibit::TextPage1);
|
||||
{
|
||||
const bool should_shadow_text1 = !(shadow_register_ & Inhibit::TextPage1);
|
||||
if(should_shadow_text1) {
|
||||
shadow_pages |= shadow_text1;
|
||||
}
|
||||
}
|
||||
|
||||
// Text Page 2, main and auxiliary — 0x0800–0x0c00.
|
||||
// TODO: on a ROM03 machine only.
|
||||
for(size_t c = 0x0800 >> shadow_shift; c < 0x0c00 >> shadow_shift; c++) {
|
||||
shadow_pages[c] = shadow_pages[c+auxiliary_offset] = !(shadow_register_ & Inhibit::TextPage2);
|
||||
//
|
||||
// The mask applied will be all 0 for a pre-ROM03 machine.
|
||||
{
|
||||
const bool should_shadow_text2 = !(shadow_register_ & Inhibit::TextPage2);
|
||||
if(should_shadow_text2) {
|
||||
shadow_pages |= shadow_text2;
|
||||
}
|
||||
}
|
||||
|
||||
// Hi-res graphics Page 1, main and auxiliary — $2000–$4000;
|
||||
@ -486,35 +495,55 @@ class MemoryMap {
|
||||
// high-res graphics page 1 inhibit bit alone is definitive.
|
||||
//
|
||||
// Odd test:
|
||||
// (high-res graphics inhibit or auxiliary high res graphics inhibit) _and_ (super high-res inhibit).
|
||||
// (high-res graphics inhibit or auxiliary high res graphics inhibit) _and_
|
||||
// (super high-res inhibit).
|
||||
//
|
||||
for(size_t c = 0x2000 >> shadow_shift; c < 0x4000 >> shadow_shift; c++) {
|
||||
shadow_pages[c] = !(shadow_register_ & Inhibit::HighRes1);
|
||||
shadow_pages[c+auxiliary_offset] = !(
|
||||
{
|
||||
const bool should_shadow_highres1 = !(shadow_register_ & Inhibit::HighRes1);
|
||||
if(should_shadow_highres1) {
|
||||
shadow_pages |= shadow_highres1;
|
||||
}
|
||||
|
||||
const bool should_shadow_aux_highres1 = !(
|
||||
shadow_register_ & (Inhibit::HighRes1 | Inhibit::AuxiliaryHighRes) &&
|
||||
shadow_register_ & Inhibit::SuperHighRes
|
||||
);
|
||||
if(should_shadow_aux_highres1) {
|
||||
shadow_pages |= shadow_highres1_aux;
|
||||
}
|
||||
}
|
||||
|
||||
// Hi-res graphics Page 2, main and auxiliary — $4000–$6000;
|
||||
// also part of the super high-res graphics page.
|
||||
//
|
||||
// Test applied: much like that for page 1.
|
||||
for(size_t c = 0x4000 >> shadow_shift; c < 0x6000 >> shadow_shift; c++) {
|
||||
shadow_pages[c] = !(shadow_register_ & Inhibit::HighRes2);
|
||||
shadow_pages[c+auxiliary_offset] = !(
|
||||
{
|
||||
const bool should_shadow_highres2 = !(shadow_register_ & Inhibit::HighRes2);
|
||||
if(should_shadow_highres2) {
|
||||
shadow_pages |= shadow_highres2;
|
||||
}
|
||||
|
||||
const bool should_shadow_aux_highres2 = !(
|
||||
shadow_register_ & (Inhibit::HighRes2 | Inhibit::AuxiliaryHighRes) &&
|
||||
shadow_register_ & Inhibit::SuperHighRes
|
||||
);
|
||||
if(should_shadow_aux_highres2) {
|
||||
shadow_pages |= shadow_highres2_aux;
|
||||
}
|
||||
}
|
||||
|
||||
// Residue of Super Hi-Res — $6000–$a000 (odd pages only).
|
||||
//
|
||||
// Test applied:
|
||||
// auxiliary high res graphics inhibit and super high-res inhibit
|
||||
for(size_t c = 0x6000 >> shadow_shift; c < 0xa000 >> shadow_shift; c++) {
|
||||
shadow_pages[c+auxiliary_offset] =
|
||||
!(shadow_register_ & Inhibit::SuperHighRes && shadow_register_ & Inhibit::AuxiliaryHighRes);
|
||||
{
|
||||
const bool should_shadow_superhighres = !(
|
||||
shadow_register_ & Inhibit::SuperHighRes &&
|
||||
shadow_register_ & Inhibit::AuxiliaryHighRes
|
||||
);
|
||||
if(should_shadow_superhighres) {
|
||||
shadow_pages |= shadow_superhighres;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,6 +566,45 @@ class MemoryMap {
|
||||
|
||||
#undef assert_is_region
|
||||
|
||||
private:
|
||||
// Various precomputed bitsets describing key regions; std::bitset doesn't support constexpr instantiation
|
||||
// beyond the first 64 bits at the time of writing, alas, so these are generated at runtime.
|
||||
std::bitset<128> shadow_text1;
|
||||
std::bitset<128> shadow_text2;
|
||||
std::bitset<128> shadow_highres1, shadow_highres1_aux;
|
||||
std::bitset<128> shadow_highres2, shadow_highres2_aux;
|
||||
std::bitset<128> shadow_superhighres;
|
||||
|
||||
void setup_shadow_maps(bool is_rom03) {
|
||||
static constexpr int shadow_shift = 10;
|
||||
static constexpr int auxiliary_offset = 0x1'0000 >> shadow_shift;
|
||||
|
||||
for(size_t c = 0x0400 >> shadow_shift; c < 0x0800 >> shadow_shift; c++) {
|
||||
shadow_text1[c] = shadow_text1[c+auxiliary_offset] = true;
|
||||
}
|
||||
|
||||
// Shadowing of text page 2 was added only with the ROM03 machine.
|
||||
if(is_rom03) {
|
||||
for(size_t c = 0x0800 >> shadow_shift; c < 0x0c00 >> shadow_shift; c++) {
|
||||
shadow_text2[c] = shadow_text2[c+auxiliary_offset] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t c = 0x2000 >> shadow_shift; c < 0x4000 >> shadow_shift; c++) {
|
||||
shadow_highres1[c] = true;
|
||||
shadow_highres1_aux[c+auxiliary_offset] = true;
|
||||
}
|
||||
|
||||
for(size_t c = 0x4000 >> shadow_shift; c < 0x6000 >> shadow_shift; c++) {
|
||||
shadow_highres2[c] = true;
|
||||
shadow_highres2_aux[c+auxiliary_offset] = true;
|
||||
}
|
||||
|
||||
for(size_t c = 0x6000 >> shadow_shift; c < 0xa000 >> shadow_shift; c++) {
|
||||
shadow_superhighres[c+auxiliary_offset] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// Memory layout here is done via double indirection; the main loop should:
|
||||
// (i) use the top two bytes of the address to get an index from memory_map_; and
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
|
||||
namespace {
|
||||
@ -56,16 +57,18 @@ struct Transaction {
|
||||
uint32_t address = 0;
|
||||
uint16_t value = 0;
|
||||
bool address_strobe = false;
|
||||
bool same_address = false;
|
||||
bool read = false;
|
||||
int data_strobes = 0;
|
||||
|
||||
bool operator !=(const Transaction &rhs) const {
|
||||
bool operator != (const Transaction &rhs) const {
|
||||
if(timestamp != rhs.timestamp) return true;
|
||||
// if(function_code != rhs.function_code) return true;
|
||||
if(address != rhs.address) return true;
|
||||
if(value != rhs.value) return true;
|
||||
if(address_strobe != rhs.address_strobe) return true;
|
||||
if(data_strobes != rhs.data_strobes) return true;
|
||||
if(same_address != rhs.same_address) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -108,6 +111,7 @@ struct BusHandler {
|
||||
transaction.function_code |= (cycle.operation & Microcycle::IsData) ? 0x1 : 0x2;
|
||||
}
|
||||
transaction.address_strobe = cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress);
|
||||
transaction.same_address = cycle.operation & Microcycle::SameAddress;
|
||||
transaction.data_strobes = cycle.operation & (Microcycle::SelectByte | Microcycle::SelectWord);
|
||||
if(cycle.address) transaction.address = *cycle.address & 0xffff'ff;
|
||||
transaction.timestamp = time;
|
||||
@ -116,7 +120,7 @@ struct BusHandler {
|
||||
time += cycle.length;
|
||||
|
||||
// Do the operation...
|
||||
const uint32_t address = cycle.address ? (*cycle.address & 0xffff'ff) : 0;
|
||||
const uint32_t address = cycle.address ? (*cycle.address & 0xff'ffff) : 0;
|
||||
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
||||
default: break;
|
||||
|
||||
@ -153,14 +157,13 @@ struct BusHandler {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Add the data value if relevant.
|
||||
if(transaction.data_strobes) {
|
||||
transaction.value = cycle.value16();
|
||||
}
|
||||
|
||||
// Push back only if interesting.
|
||||
if(transaction.address_strobe || transaction.data_strobes || transaction.function_code == 7) {
|
||||
if(capture_all_transactions || transaction.address_strobe || transaction.data_strobes || transaction.function_code == 7) {
|
||||
if(transaction_delay) {
|
||||
--transaction_delay;
|
||||
|
||||
@ -178,6 +181,7 @@ struct BusHandler {
|
||||
|
||||
int transaction_delay;
|
||||
int instructions;
|
||||
bool capture_all_transactions = false;
|
||||
|
||||
HalfCycles time;
|
||||
std::vector<Transaction> transactions;
|
||||
@ -238,6 +242,150 @@ template <typename M68000> struct Tester {
|
||||
M68000 processor;
|
||||
};
|
||||
|
||||
void print_state(FILE *target, const CPU::MC68000Mk2::State &state, const std::vector<Transaction> &transactions, bool is_initial) {
|
||||
for(int c = 0; c < 8; c++) {
|
||||
fprintf(target, "\"d%d\": %u, ", c, state.registers.data[c]);
|
||||
}
|
||||
|
||||
for(int c = 0; c < 7; c++) {
|
||||
fprintf(target, "\"a%d\": %u, ", c, state.registers.address[c]);
|
||||
}
|
||||
|
||||
fprintf(target, "\"usp\": %u, ", state.registers.user_stack_pointer);
|
||||
fprintf(target, "\"ssp\": %u, ", state.registers.supervisor_stack_pointer);
|
||||
fprintf(target, "\"sr\": %u, ", state.registers.status);
|
||||
fprintf(target, "\"pc\": %u, ", state.registers.program_counter - 4);
|
||||
|
||||
fprintf(target, "\"prefetch\": [%u, %u], ", state.prefetch[0], state.prefetch[1]);
|
||||
|
||||
fprintf(target, "\"ram\": [");
|
||||
|
||||
// Compute RAM from transactions; if this is the initial state then RAM should
|
||||
// be everything that was subject to a read which had not previously been
|
||||
// subject to a write. Otherwise it can just be everything.
|
||||
std::unordered_map<uint32_t, uint8_t> ram;
|
||||
if(is_initial) {
|
||||
std::unordered_set<uint32_t> written_addresses;
|
||||
|
||||
for(const auto &transaction: transactions) {
|
||||
switch(transaction.data_strobes) {
|
||||
default: continue;
|
||||
case 1:
|
||||
if(transaction.read) {
|
||||
if(ram.find(transaction.address) == ram.end()) {
|
||||
ram[transaction.address] = transaction.value;
|
||||
}
|
||||
} else {
|
||||
written_addresses.insert(transaction.address);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if(transaction.read) {
|
||||
if(ram.find(transaction.address) == ram.end()) {
|
||||
ram[transaction.address] = uint8_t(transaction.value >> 8);
|
||||
}
|
||||
if(ram.find(transaction.address+1) == ram.end()) {
|
||||
ram[transaction.address+1] = uint8_t(transaction.value);
|
||||
}
|
||||
} else {
|
||||
written_addresses.insert(transaction.address);
|
||||
written_addresses.insert(transaction.address + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(const auto &transaction: transactions) {
|
||||
switch(transaction.data_strobes) {
|
||||
default: continue;
|
||||
case 1:
|
||||
ram[transaction.address] = transaction.value;
|
||||
break;
|
||||
case 2:
|
||||
ram[transaction.address] = uint8_t(transaction.value >> 8);
|
||||
ram[transaction.address+1] = uint8_t(transaction.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_first = true;
|
||||
for(const auto &pair: ram) {
|
||||
if(!is_first) fprintf(target, ", ");
|
||||
is_first = false;
|
||||
fprintf(target, "[%d, %d]", pair.first, pair.second);
|
||||
}
|
||||
fprintf(target, "]");
|
||||
}
|
||||
|
||||
void print_transactions(FILE *target, const std::vector<Transaction> &transactions, HalfCycles end) {
|
||||
auto iterator = transactions.begin();
|
||||
bool is_first = true;
|
||||
do {
|
||||
if(!is_first) fprintf(target, ", ");
|
||||
is_first = false;
|
||||
fprintf(target, "[");
|
||||
|
||||
auto next = iterator + 1;
|
||||
|
||||
// Attempt to pair off transactions to reproduct YACHT notation.
|
||||
bool is_access = true;
|
||||
if(!iterator->address_strobe && !iterator->data_strobes) {
|
||||
fprintf(target, "\"n\", ");
|
||||
is_access = false;
|
||||
} else {
|
||||
assert(!iterator->data_strobes);
|
||||
|
||||
// Check how many transactions this address persists for;
|
||||
// that'll allow a TAS to be recognised here.
|
||||
while(next->same_address && next != transactions.end()) {
|
||||
++next;
|
||||
}
|
||||
--next;
|
||||
|
||||
if(next == iterator + 1) {
|
||||
if(next->read) {
|
||||
fprintf(target, "\"r\", ");
|
||||
} else {
|
||||
fprintf(target, "\"w\", ");
|
||||
}
|
||||
} else {
|
||||
fprintf(target, "\"t\", ");
|
||||
}
|
||||
|
||||
// Include next in the calculation of time below.
|
||||
++next;
|
||||
}
|
||||
HalfCycles length;
|
||||
if(next == transactions.end()) {
|
||||
length = end - iterator->timestamp;
|
||||
} else {
|
||||
length = next->timestamp - iterator->timestamp;
|
||||
}
|
||||
fprintf(target, "%d", length.as<int>() >> 1);
|
||||
|
||||
if(is_access) {
|
||||
// Undo the 'move to one after' step that allowed next to be included
|
||||
// in this transaction's cycle count.
|
||||
--next;
|
||||
|
||||
fprintf(target, ", %d, ", iterator->function_code);
|
||||
fprintf(target, "%d, ", iterator->address & 0xff'ffff);
|
||||
|
||||
switch(next->data_strobes) {
|
||||
default: assert(false);
|
||||
case 1: fprintf(target, "\".b\", %d", next->value & 0xff); break;
|
||||
case 2: fprintf(target, "\".w\", %d", next->value); break;
|
||||
}
|
||||
|
||||
++next;
|
||||
}
|
||||
|
||||
fprintf(target, "]");
|
||||
iterator = next;
|
||||
} while(iterator != transactions.end());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@interface M68000OldVsNewTests : XCTestCase
|
||||
@ -245,6 +393,120 @@ template <typename M68000> struct Tester {
|
||||
|
||||
@implementation M68000OldVsNewTests
|
||||
|
||||
//- (void)testGenerate {
|
||||
- (void)generate {
|
||||
srand(68000);
|
||||
InstructionSet::M68k::Predecoder<InstructionSet::M68k::Model::M68000> decoder;
|
||||
RandomStore random_store;
|
||||
auto tester = std::make_unique<Tester<NewProcessor>>(random_store, 0x02);
|
||||
tester->bus_handler.capture_all_transactions = true;
|
||||
|
||||
// Bucket opcodes by operation.
|
||||
std::unordered_map<const char *, std::vector<uint16_t>> opcodesByOperation;
|
||||
for(int c = 0; c < 65536; c++) {
|
||||
// Test only defined opcodes that aren't STOP (which will never teminate).
|
||||
const auto instruction = decoder.decode(uint16_t(c));
|
||||
if(
|
||||
instruction.operation == InstructionSet::M68k::Operation::Undefined ||
|
||||
instruction.operation == InstructionSet::M68k::Operation::STOP
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto operation = instruction.operation_string();
|
||||
opcodesByOperation[operation].push_back(c);
|
||||
}
|
||||
|
||||
// Find somewhere to write to.
|
||||
NSString *const tempDir = NSTemporaryDirectory();
|
||||
NSLog(@"Outputting to %@", tempDir);
|
||||
|
||||
// Aim to get at least 1,000,000 tests total.
|
||||
const auto testsPerOperation = int((1'000'000 + (opcodesByOperation.size() - 1)) / opcodesByOperation.size());
|
||||
|
||||
// Generate by operation.
|
||||
NSLog(@"Generating %d tests each for %lu operations", testsPerOperation, opcodesByOperation.size());
|
||||
for(const auto &pair: opcodesByOperation) {
|
||||
NSLog(@"Generating %s", pair.first);
|
||||
NSString *const targetName = [NSString stringWithFormat:@"%@%s.json", tempDir, pair.first];
|
||||
FILE *const target = fopen(targetName.UTF8String, "wt");
|
||||
|
||||
const bool force_addresses_even = decoder.decode(pair.second[0]).operation == InstructionSet::M68k::Operation::UNLINK;
|
||||
bool is_first_test = true;
|
||||
fprintf(target, "[");
|
||||
|
||||
// Test each for the selected number of iterations.
|
||||
for(int test = 0; test < testsPerOperation; test++) {
|
||||
if(!is_first_test) fprintf(target, ",\n");
|
||||
is_first_test = false;
|
||||
|
||||
// Establish with certainty the initial memory state.
|
||||
random_store.clear();
|
||||
|
||||
const auto opcodeIndex = int(rand() * pair.second.size() / RAND_MAX);
|
||||
const uint16_t opcode = pair.second[opcodeIndex];
|
||||
tester->reset_with_opcode(opcode);
|
||||
|
||||
// Generate a random initial register state.
|
||||
auto initialState = tester->processor.get_state();
|
||||
|
||||
// Require address pointers to be even 99% of the time, or always for UNLINK.
|
||||
const bool addresses_are_even = (rand() >= int(float(RAND_MAX) * 0.99f)) || force_addresses_even;
|
||||
for(int c = 0; c < 8; c++) {
|
||||
initialState.registers.data[c] = rand() ^ (rand() << 1);
|
||||
if(c != 7) {
|
||||
initialState.registers.address[c] = rand() ^ (rand() << 1);
|
||||
if(addresses_are_even) initialState.registers.address[c] &= ~1;
|
||||
}
|
||||
}
|
||||
|
||||
// Pick a random status such that:
|
||||
//
|
||||
// (i) supervisor mode is active 99% of the time;
|
||||
// (ii) trace is inactive; and
|
||||
// (iii) interrupt level is 7.
|
||||
const bool is_supervisor = rand() >= int(float(RAND_MAX) * 0.99f);
|
||||
initialState.registers.status = (rand() | (int(is_supervisor) << 13) | (7 << 8)) & ~(1 << 15);
|
||||
initialState.registers.user_stack_pointer = rand() << 1;
|
||||
initialState.registers.supervisor_stack_pointer = rand() << 1;
|
||||
|
||||
// Set state.
|
||||
tester->processor.set_state(initialState);
|
||||
|
||||
// Run for zero instructions to grab the real initial state (i.e. valid prefetch, ssp, etc).
|
||||
// Then make sure no transactions or time carry over into the actual instruction.
|
||||
tester->run_instructions(0);
|
||||
auto populatedInitialState = tester->processor.get_state();
|
||||
tester->bus_handler.transactions.clear();
|
||||
tester->bus_handler.time = HalfCycles(0);
|
||||
|
||||
// Run for another instruction to do the actual work.
|
||||
tester->run_instructions(1);
|
||||
|
||||
const auto finalState = tester->processor.get_state();
|
||||
|
||||
// Output initial state.
|
||||
fprintf(target, "{ \"name\": \"%04x [%s] %d\", ", opcode, decoder.decode(opcode).to_string().c_str(), test + 1);
|
||||
fprintf(target, "\"initial\": {");
|
||||
print_state(target, populatedInitialState, tester->bus_handler.transactions, true);
|
||||
|
||||
// Output final state.
|
||||
fprintf(target, "}, \"final\": {");
|
||||
print_state(target, finalState, tester->bus_handler.transactions, false);
|
||||
|
||||
// Output total length and bus activity.
|
||||
fprintf(target, "}, \"length\": %d, ", tester->bus_handler.time.as<int>() >> 1);
|
||||
|
||||
fprintf(target, "\"transactions\": [");
|
||||
print_transactions(target, tester->bus_handler.transactions, tester->bus_handler.time);
|
||||
fprintf(target, "]}");
|
||||
}
|
||||
|
||||
fprintf(target, "\n]\n");
|
||||
fclose(target);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testOldVsNew {
|
||||
RandomStore random_store;
|
||||
auto oldTester = std::make_unique<Tester<OldProcessor>>(random_store, 0x01);
|
||||
@ -294,7 +556,7 @@ template <typename M68000> struct Tester {
|
||||
int testsRun = 0;
|
||||
std::set<InstructionSet::M68k::Operation> failing_operations;
|
||||
for(int c = 0; c < 65536; c++) {
|
||||
// printf("%04x\n", c);
|
||||
printf("%04x\n", c);
|
||||
|
||||
// Test only defined opcodes that aren't STOP (which will never teminate).
|
||||
const auto instruction = decoder.decode(uint16_t(c));
|
||||
|
@ -11,7 +11,10 @@
|
||||
#include "../../../Machines/Apple/AppleIIgs/MemoryMap.hpp"
|
||||
|
||||
namespace {
|
||||
using MemoryMap = Apple::IIgs::MemoryMap;
|
||||
// Test the ROM03-era mapping hardware.
|
||||
struct MemoryMap: public Apple::IIgs::MemoryMap {
|
||||
MemoryMap() : Apple::IIgs::MemoryMap(true) {}
|
||||
};
|
||||
}
|
||||
|
||||
@interface IIgsMemoryMapTests : XCTestCase
|
||||
|
@ -334,6 +334,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
|
||||
case OperationConstructDirect:
|
||||
data_address_ = (registers_.direct + instruction_buffer_.value) & 0xffff;
|
||||
data_address_increment_mask_ = 0x00'ff'ff;
|
||||
|
||||
if(!(registers_.direct&0xff)) {
|
||||
// If the low byte is 0 and this is emulation mode, incrementing
|
||||
// is restricted to the low byte.
|
||||
@ -345,6 +346,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
|
||||
case OperationConstructDirectLong:
|
||||
data_address_ = (registers_.direct + instruction_buffer_.value) & 0xffff;
|
||||
data_address_increment_mask_ = 0x00'ff'ff;
|
||||
|
||||
if(!(registers_.direct&0xff)) {
|
||||
++next_op_;
|
||||
}
|
||||
@ -380,31 +382,41 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
|
||||
data_buffer_.clear();
|
||||
continue;
|
||||
|
||||
// TODO: confirm incorrect_data_address_ below.
|
||||
|
||||
case OperationConstructDirectX:
|
||||
data_address_ = (
|
||||
(registers_.direct & registers_.e_masks[0]) +
|
||||
((instruction_buffer_.value + registers_.direct + registers_.x.full) & registers_.e_masks[1])
|
||||
) & 0xffff;
|
||||
// There are no direct, x instructions that access a two-byte value when
|
||||
// in emulation mode, so this can assume native mode.
|
||||
data_address_increment_mask_ = 0x00'ff'ff;
|
||||
|
||||
incorrect_data_address_ = (registers_.direct & 0xff00) + (data_address_ & 0x00ff);
|
||||
// If the low byte of the direct register is 0, use the current e mask
|
||||
// potentially to keep the high byte of the direct register unmodified.
|
||||
//
|
||||
// Also skip the next program step, which would be a redundant fetch
|
||||
// from the program counter.
|
||||
//
|
||||
// Otherwise: retain a 16-bit address.
|
||||
|
||||
data_address_ = instruction_buffer_.value + registers_.direct + registers_.x.full;
|
||||
if(!(registers_.direct&0xff)) {
|
||||
data_address_ =
|
||||
(registers_.direct & registers_.e_masks[0]) |
|
||||
(data_address_ & registers_.e_masks[1]);
|
||||
++next_op_;
|
||||
} else {
|
||||
data_address_ &= 0xffff;
|
||||
}
|
||||
continue;
|
||||
|
||||
case OperationConstructDirectY:
|
||||
data_address_ = (
|
||||
(registers_.direct & registers_.e_masks[0]) +
|
||||
((instruction_buffer_.value + registers_.direct + registers_.y.full) & registers_.e_masks[1])
|
||||
) & 0xffff;
|
||||
// Cf. comments above in DirectX.
|
||||
data_address_increment_mask_ = 0x00'ff'ff;
|
||||
|
||||
incorrect_data_address_ = (registers_.direct & 0xff00) + (data_address_ & 0x00ff);
|
||||
data_address_ = instruction_buffer_.value + registers_.direct + registers_.y.full;
|
||||
if(!(registers_.direct&0xff)) {
|
||||
data_address_ =
|
||||
(registers_.direct & registers_.e_masks[0]) |
|
||||
(data_address_ & registers_.e_masks[1]);
|
||||
++next_op_;
|
||||
} else {
|
||||
data_address_ &= 0xffff;
|
||||
}
|
||||
continue;
|
||||
|
||||
|
@ -281,13 +281,36 @@ struct ProcessorStorage {
|
||||
|
||||
// Flags aplenty.
|
||||
MOS6502Esque::LazyFlags flags;
|
||||
uint8_t mx_flags[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit.
|
||||
uint16_t m_masks[2] = {0xff00, 0x00ff}; // [0] = src mask (i.e. that which is unaffected by an operation); [1] = dst mask (i.e. 0xffff ^ src mask).
|
||||
uint16_t x_mask = 0x00ff; // A mask representing the current size of the index registers. Equivalent to m_masks[1].
|
||||
uint16_t e_masks[2] = {0xff00, 0x00ff}; // Akin to m_masks, but set as per emulation mode.
|
||||
int m_shift = 0; // How far to shift memory/A to align its sign bit with that of the flags register. i.e. 8 for 16-bit mode, 0 for 8-bit mode.
|
||||
int x_shift = 0; // m_shift equivalent for X and Y.
|
||||
bool emulation_flag = true; // The emulation flag; true = in emulation mode.
|
||||
|
||||
// [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit.
|
||||
uint8_t mx_flags[2] = {1, 1};
|
||||
|
||||
// Used to determine which parts of a register are currently in use, as a function
|
||||
// of the M flag.
|
||||
//
|
||||
// [0] = src mask (i.e. that which is unaffected by an operation);
|
||||
// [1] = dst mask (i.e. 0xffff ^ src mask).
|
||||
//
|
||||
// e.g. a LDA from the value Q would prima facie leave A as equal to:
|
||||
// (A & m_masks[0]) | (Q & m_masks[1]);
|
||||
uint16_t m_masks[2] = {0xff00, 0x00ff};
|
||||
|
||||
// A mask representing the current size of the index registers.
|
||||
// Equivalent in meaning to m_masks[1] but representative of the X flag.
|
||||
uint16_t x_mask = 0x00ff;
|
||||
|
||||
// Akin to m_masks, but a function of emulation mode; used primarily for address calculation.
|
||||
uint16_t e_masks[2] = {0xff00, 0x00ff};
|
||||
|
||||
// How far to shift memory/A to align its sign bit with that of the flags register.
|
||||
// i.e. 8 for 16-bit mode, 0 for 8-bit mode.
|
||||
int m_shift = 0;
|
||||
|
||||
// m_shift equivalent for X and Y.
|
||||
int x_shift = 0;
|
||||
|
||||
// The emulation flag; true = in emulation mode.
|
||||
bool emulation_flag = true;
|
||||
|
||||
// The offset for direct addressing (i.e. outside of emulation mode).
|
||||
uint16_t direct = 0;
|
||||
|
@ -354,6 +354,7 @@ class BusHandler {
|
||||
};
|
||||
|
||||
struct State {
|
||||
uint16_t prefetch[2];
|
||||
InstructionSet::M68k::RegisterSet registers;
|
||||
};
|
||||
|
||||
|
@ -341,7 +341,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
// Sets up the next data access size and read flags.
|
||||
#define SetupDataAccess(read_flag, select_flag) \
|
||||
access_announce.operation = Microcycle::NewAddress | Microcycle::IsData | (read_flag); \
|
||||
access.operation = access_announce.operation | (select_flag);
|
||||
access.operation = Microcycle::SameAddress | Microcycle::IsData | (read_flag) | (select_flag);
|
||||
|
||||
// Sets the address source for the next data access.
|
||||
#define SetDataAddress(addr) \
|
||||
@ -3025,6 +3025,9 @@ CPU::MC68000Mk2::State Processor<BusHandler, dtack_is_implicit, permit_overrun,
|
||||
state.registers.user_stack_pointer = stack_pointers_[0].l;
|
||||
state.registers.supervisor_stack_pointer = stack_pointers_[1].l;
|
||||
|
||||
state.prefetch[0] = prefetch_.high.w;
|
||||
state.prefetch[1] = prefetch_.low.w;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -3048,6 +3051,10 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
// Ensure the local is-supervisor flag is updated.
|
||||
did_update_status();
|
||||
|
||||
// Populate the prefetch.
|
||||
prefetch_.high.w = state.prefetch[0];
|
||||
prefetch_.low.w = state.prefetch[1];
|
||||
}
|
||||
|
||||
template <class BusHandler, bool dtack_is_implicit, bool permit_overrun, bool signal_will_perform>
|
||||
|
Loading…
x
Reference in New Issue
Block a user