diff --git a/Emulator/Components/CPU/CPU.h b/Emulator/Components/CPU/CPU.h index 786be00b8..8322da1d7 100755 --- a/Emulator/Components/CPU/CPU.h +++ b/Emulator/Components/CPU/CPU.h @@ -119,6 +119,7 @@ class CPU final : public Peddle, public Inspectable { << flags << next + << pendingRead << reg.pc << reg.pc0 diff --git a/Emulator/Components/CPU/Peddle/Peddle.h b/Emulator/Components/CPU/Peddle/Peddle.h index 084f803f7..2aa58198b 100644 --- a/Emulator/Components/CPU/Peddle/Peddle.h +++ b/Emulator/Components/CPU/Peddle/Peddle.h @@ -82,7 +82,10 @@ class Peddle : public SubComponent { // The next microinstruction to be executed MicroInstruction next; + // Pending read operation (used in PEDDLE_ASYNC_READS mode) + Async::ReadTarget pendingRead {}; + // // Registers // @@ -380,6 +383,9 @@ class Peddle : public SubComponent { virtual u8 readDasm(u16 addr) const { return 0; } virtual u16 readResetVector(); + // Feeds the result of an asynchronous read operation into the CPU + void concludeRead(u8 value); + // // Perforing atomic CPU operations diff --git a/Emulator/Components/CPU/Peddle/PeddleConfig.h b/Emulator/Components/CPU/Peddle/PeddleConfig.h index a6cdb8b34..b61239f94 100644 --- a/Emulator/Components/CPU/Peddle/PeddleConfig.h +++ b/Emulator/Components/CPU/Peddle/PeddleConfig.h @@ -51,5 +51,20 @@ * * Enable to simplify usage, disable to gain speed. */ - #define PEDDLE_SIMPLE_MEMORY_API true + +/* Asynchronous read operations + * + * By default, when Peddle reads a value from memory, it invokes the read + * function and immediately transfers the returned value to the proper location, + * e.g., the instruction register or the data latch. In cases where the + * surrounding environment cannot provide the correct value when the read + * function is called, asynchronous reads can be enabled. In this case, Peddle + * still calls the read function to inform the environment about the access but + * discards the returned value. It is then the responsibility of the environment + * to pass in the correct value by calling the concludeRead() function before + * the next CPU cycle begins. The Atari 2600 emulator Tiara utilizes + * asynchronous read mode, as the values read from certain chip registers can + * only be decided after the CPU has completed its current cycle. + */ +#define PEDDLE_ASYNC_READS false diff --git a/Emulator/Components/CPU/Peddle/PeddleExec_cpp.h b/Emulator/Components/CPU/Peddle/PeddleExec_cpp.h index 8e84a3f2c..4742bd35a 100644 --- a/Emulator/Components/CPU/Peddle/PeddleExec_cpp.h +++ b/Emulator/Components/CPU/Peddle/PeddleExec_cpp.h @@ -16,17 +16,38 @@ // Read -void Peddle::latchIR(u8 value) { reg.ir = value; } -void Peddle::latchADL(u8 value) { reg.adl = value; } -void Peddle::latchADH(u8 value) { reg.adh = value; } -void Peddle::latchIDL(u8 value) { reg.idl = value; } -void Peddle::latchD(u8 value) { reg.d = value; } -void Peddle::latchPCL(u8 value) { reg.pc = (u16)((reg.pc & 0xff00) | (value)); } -void Peddle::latchPCH(u8 value) { reg.pc = (u16)((reg.pc & 0x00ff) | (value) << 8); } -void Peddle::latchP(u8 value) { setPWithoutB(value); } -void Peddle::latchA(u8 value) { loadA(value); } - -/* +#if PEDDLE_ASYNC_READS == true + +#define LATCH_INSTR(x) pendingRead = Async::IR; (void)x; +#define LATCH_ADL(x) pendingRead = Async::ADL; (void)x; +#define LATCH_ADH(x) pendingRead = Async::ADH; (void)x; +#define LATCH_IDL(x) pendingRead = Async::IDL; (void)x; +#define LATCH_D(x) pendingRead = Async::D; (void)x; +#define LATCH_PCL(x) pendingRead = Async::PCL; (void)x; +#define LATCH_PCH(x) pendingRead = Async::PCH; (void)x; +#define LATCH_P(x) pendingRead = Async::P; (void)x; +#define LATCH_A(x) pendingRead = Async::A; (void)x; + +void +Peddle::concludeRead(u8 x) +{ + switch (pendingRead) { + + case Async::IR: reg.ir = x; break; + case Async::ADL: reg.adl = x; break; + case Async::ADH: reg.adh = x; break; + case Async::IDL: reg.idl = x; break; + case Async::D: reg.d = x; break; + case Async::PCL: reg.pc = (u16)((reg.pc & 0xff00) | (x)); break; + case Async::PCH: reg.pc = (u16)((reg.pc & 0x00ff) | (x) << 8); break; + case Async::P: setPWithoutB(x); break; + case Async::A: loadA(x); break; + default: break; + } +} + +#else + #define LATCH_INSTR(x) reg.ir = (x) #define LATCH_ADL(x) reg.adl = (x) #define LATCH_ADH(x) reg.adh = (x) @@ -36,44 +57,36 @@ void Peddle::latchA(u8 value) { loadA(value); } #define LATCH_PCH(x) reg.pc = (u16)((reg.pc & 0x00ff) | (x) << 8) #define LATCH_P(x) setPWithoutB(x) #define LATCH_A(x) loadA(x) -*/ -#define LATCH_INSTR(x) latchIR(x) -#define LATCH_ADL(x) latchADL(x) -#define LATCH_ADH(x) latchADH(x) -#define LATCH_IDL(x) latchIDL(x) -#define LATCH_D(x) latchD(x) -#define LATCH_PCL(x) latchPCL(x) -#define LATCH_PCH(x) latchPCH(x) -#define LATCH_P(x) latchP(x) -#define LATCH_A(x) latchA(x) + +#endif #define FETCH_IR \ -if (likely(!rdyLine)) LATCH_INSTR(read(reg.pc++)); else return; +if (likely(!rdyLine)) { LATCH_INSTR(read(reg.pc++)); } else return; #define FETCH_ADDR_LO \ -if (likely(!rdyLine)) LATCH_ADL(reg.adl = read(reg.pc++)); else return; +if (likely(!rdyLine)) { LATCH_ADL(read(reg.pc++)); } else return; #define FETCH_ADDR_HI \ -if (likely(!rdyLine)) LATCH_ADH(reg.adh = read(reg.pc++)); else return; +if (likely(!rdyLine)) { LATCH_ADH(read(reg.pc++)); } else return; #define FETCH_POINTER_ADDR \ -if (likely(!rdyLine)) LATCH_IDL(read(reg.pc++)); else return; +if (likely(!rdyLine)) { LATCH_IDL(read(reg.pc++)); } else return; #define FETCH_ADDR_LO_INDIRECT \ -if (likely(!rdyLine)) LATCH_ADL(read((u16)reg.idl++)); else return; +if (likely(!rdyLine)) { LATCH_ADL(read((u16)reg.idl++)); } else return; #define FETCH_ADDR_HI_INDIRECT \ -if (likely(!rdyLine)) LATCH_ADH(read((u16)reg.idl++)); else return; +if (likely(!rdyLine)) { LATCH_ADH(read((u16)reg.idl++)); } else return; #define IDLE_FETCH \ if (likely(!rdyLine)) readIdle(reg.pc); else return; #define READ_RELATIVE \ -if (likely(!rdyLine)) LATCH_D(read(reg.pc)); else return; +if (likely(!rdyLine)) { LATCH_D(read(reg.pc)); } else return; #define READ_IMMEDIATE \ -if (likely(!rdyLine)) LATCH_D(read(reg.pc++)); else return; +if (likely(!rdyLine)) { LATCH_D(read(reg.pc++)); } else return; #define READ_FROM(x) \ -if (likely(!rdyLine)) LATCH_D(read(x)); else return; +if (likely(!rdyLine)) { LATCH_D(read(x)); } else return; #define READ_FROM_ADDRESS \ -if (likely(!rdyLine)) LATCH_D(read(HI_LO(reg.adh, reg.adl))); else return; +if (likely(!rdyLine)) { LATCH_D(read(HI_LO(reg.adh, reg.adl))); } else return; #define READ_FROM_ZERO_PAGE \ -if (likely(!rdyLine)) LATCH_D(readZeroPage(reg.adl)); else return; +if (likely(!rdyLine)) { LATCH_D(readZeroPage(reg.adl)); } else return; #define READ_FROM_ADDRESS_INDIRECT \ -if (likely(!rdyLine)) LATCH_D(readZeroPage(reg.dl)); else return; +if (likely(!rdyLine)) { LATCH_D(readZeroPage(reg.dl)); } else return; #define IDLE_READ_IMPLIED \ if (likely(!rdyLine)) readIdle(reg.pc); else return; diff --git a/Emulator/Components/CPU/Peddle/PeddleTypes.h b/Emulator/Components/CPU/Peddle/PeddleTypes.h index 83a6e846d..02e4f19ce 100644 --- a/Emulator/Components/CPU/Peddle/PeddleTypes.h +++ b/Emulator/Components/CPU/Peddle/PeddleTypes.h @@ -373,6 +373,12 @@ enum_long(MICRO_INSTRUCTION) { }; typedef MICRO_INSTRUCTION MicroInstruction; +namespace Async { + +enum ReadTarget { IDLE, IR, ADL, ADH, IDL, D, PCL, PCH, P, A }; + +} + // // Structures