Skip to content

Commit

Permalink
Peddle: Added ASYNC_READS mode (not used by VirtualC64)
Browse files Browse the repository at this point in the history
  • Loading branch information
dirkwhoffmann committed Oct 2, 2024
1 parent 5b272ab commit 757f305
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 34 deletions.
1 change: 1 addition & 0 deletions Emulator/Components/CPU/CPU.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class CPU final : public Peddle, public Inspectable<CPUInfo> {

<< flags
<< next
<< pendingRead

<< reg.pc
<< reg.pc0
Expand Down
6 changes: 6 additions & 0 deletions Emulator/Components/CPU/Peddle/Peddle.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down Expand Up @@ -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
Expand Down
17 changes: 16 additions & 1 deletion Emulator/Components/CPU/Peddle/PeddleConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
79 changes: 46 additions & 33 deletions Emulator/Components/CPU/Peddle/PeddleExec_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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<C>(reg.pc++)); else return;
if (likely(!rdyLine)) { LATCH_INSTR(read<C>(reg.pc++)); } else return;
#define FETCH_ADDR_LO \
if (likely(!rdyLine)) LATCH_ADL(reg.adl = read<C>(reg.pc++)); else return;
if (likely(!rdyLine)) { LATCH_ADL(read<C>(reg.pc++)); } else return;
#define FETCH_ADDR_HI \
if (likely(!rdyLine)) LATCH_ADH(reg.adh = read<C>(reg.pc++)); else return;
if (likely(!rdyLine)) { LATCH_ADH(read<C>(reg.pc++)); } else return;
#define FETCH_POINTER_ADDR \
if (likely(!rdyLine)) LATCH_IDL(read<C>(reg.pc++)); else return;
if (likely(!rdyLine)) { LATCH_IDL(read<C>(reg.pc++)); } else return;
#define FETCH_ADDR_LO_INDIRECT \
if (likely(!rdyLine)) LATCH_ADL(read<C>((u16)reg.idl++)); else return;
if (likely(!rdyLine)) { LATCH_ADL(read<C>((u16)reg.idl++)); } else return;
#define FETCH_ADDR_HI_INDIRECT \
if (likely(!rdyLine)) LATCH_ADH(read<C>((u16)reg.idl++)); else return;
if (likely(!rdyLine)) { LATCH_ADH(read<C>((u16)reg.idl++)); } else return;
#define IDLE_FETCH \
if (likely(!rdyLine)) readIdle<C>(reg.pc); else return;

#define READ_RELATIVE \
if (likely(!rdyLine)) LATCH_D(read<C>(reg.pc)); else return;
if (likely(!rdyLine)) { LATCH_D(read<C>(reg.pc)); } else return;
#define READ_IMMEDIATE \
if (likely(!rdyLine)) LATCH_D(read<C>(reg.pc++)); else return;
if (likely(!rdyLine)) { LATCH_D(read<C>(reg.pc++)); } else return;
#define READ_FROM(x) \
if (likely(!rdyLine)) LATCH_D(read<C>(x)); else return;
if (likely(!rdyLine)) { LATCH_D(read<C>(x)); } else return;
#define READ_FROM_ADDRESS \
if (likely(!rdyLine)) LATCH_D(read<C>(HI_LO(reg.adh, reg.adl))); else return;
if (likely(!rdyLine)) { LATCH_D(read<C>(HI_LO(reg.adh, reg.adl))); } else return;
#define READ_FROM_ZERO_PAGE \
if (likely(!rdyLine)) LATCH_D(readZeroPage<C>(reg.adl)); else return;
if (likely(!rdyLine)) { LATCH_D(readZeroPage<C>(reg.adl)); } else return;
#define READ_FROM_ADDRESS_INDIRECT \
if (likely(!rdyLine)) LATCH_D(readZeroPage<C>(reg.dl)); else return;
if (likely(!rdyLine)) { LATCH_D(readZeroPage<C>(reg.dl)); } else return;

#define IDLE_READ_IMPLIED \
if (likely(!rdyLine)) readIdle<C>(reg.pc); else return;
Expand Down
6 changes: 6 additions & 0 deletions Emulator/Components/CPU/Peddle/PeddleTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 757f305

Please sign in to comment.