From 5239d3a11b11619d6fb65f2691cea7ac92c136f7 Mon Sep 17 00:00:00 2001 From: sidney Date: Thu, 19 Dec 2024 03:48:15 +0100 Subject: [PATCH] Polishing the peripherals (and counter) interface. --- src/sim65/6502.c | 8 +-- src/sim65/error.c | 2 +- src/sim65/memory.c | 4 +- src/sim65/peripherals.c | 123 +++++++++++++++++++--------------------- src/sim65/peripherals.h | 82 +++++++++++++++++---------- 5 files changed, 117 insertions(+), 102 deletions(-) diff --git a/src/sim65/6502.c b/src/sim65/6502.c index f48ba8fee..7e993a59c 100644 --- a/src/sim65/6502.c +++ b/src/sim65/6502.c @@ -4108,7 +4108,7 @@ unsigned ExecuteInsn (void) if (HaveNMIRequest) { HaveNMIRequest = 0; - PRegs.counter_nmi_events += 1; + Peripherals.Counter.nmi_events += 1; PUSH (PCH); PUSH (PCL); @@ -4124,7 +4124,7 @@ unsigned ExecuteInsn (void) } else if (HaveIRQRequest && GET_IF () == 0) { HaveIRQRequest = 0; - PRegs.counter_irq_events += 1; + Peripherals.Counter.irq_events += 1; PUSH (PCH); PUSH (PCL); @@ -4146,11 +4146,11 @@ unsigned ExecuteInsn (void) Handlers[CPU][OPC] (); /* Increment the instruction counter by one.NMIs and IRQs are counted separately. */ - PRegs.counter_instructions += 1; + Peripherals.Counter.cpu_instructions += 1; } /* Increment the 64-bit clock cycle counter with the cycle count for the instruction that we just executed */ - PRegs.counter_clock_cycles += Cycles; + Peripherals.Counter.clock_cycles += Cycles; /* Return the number of clock cycles needed by this insn */ return Cycles; diff --git a/src/sim65/error.c b/src/sim65/error.c index 58c188676..45fb243d8 100644 --- a/src/sim65/error.c +++ b/src/sim65/error.c @@ -118,7 +118,7 @@ void SimExit (int Code) /* Exit the simulation with an exit code */ { if (PrintCycles) { - fprintf (stdout, "%" PRIu64 " cycles\n", PRegs.counter_clock_cycles); + fprintf (stdout, "%" PRIu64 " cycles\n", Peripherals.Counter.clock_cycles); } exit (Code); } diff --git a/src/sim65/memory.c b/src/sim65/memory.c index c4b6bb220..c80bf0f93 100644 --- a/src/sim65/memory.c +++ b/src/sim65/memory.c @@ -62,7 +62,7 @@ void MemWriteByte (uint16_t Addr, uint8_t Val) if ((PERIPHERALS_APERTURE_BASE_ADDRESS <= Addr) && (Addr <= PERIPHERALS_APERTURE_LAST_ADDRESS)) { /* Defer the the memory-mapped peripherals handler for this write. */ - PeripheralWriteByte (Addr - PERIPHERALS_APERTURE_BASE_ADDRESS, Val); + PeripheralsWriteByte (Addr - PERIPHERALS_APERTURE_BASE_ADDRESS, Val); } else { /* Write to the Mem array. */ Mem[Addr] = Val; @@ -86,7 +86,7 @@ uint8_t MemReadByte (uint16_t Addr) if ((PERIPHERALS_APERTURE_BASE_ADDRESS <= Addr) && (Addr <= PERIPHERALS_APERTURE_LAST_ADDRESS)) { /* Defer the the memory-mapped peripherals handler for this read. */ - return PeripheralReadByte (Addr - PERIPHERALS_APERTURE_BASE_ADDRESS); + return PeripheralsReadByte (Addr - PERIPHERALS_APERTURE_BASE_ADDRESS); } else { /* Read from the Mem array. */ return Mem[Addr]; diff --git a/src/sim65/peripherals.c b/src/sim65/peripherals.c index 6ff420a24..cc2eb6528 100644 --- a/src/sim65/peripherals.c +++ b/src/sim65/peripherals.c @@ -40,8 +40,8 @@ -/* The peripheral registers. */ -PeripheralRegs PRegs; +/* The system-wide state of the peripherals */ +Sim65Peripherals Peripherals; @@ -51,46 +51,37 @@ PeripheralRegs PRegs; -static uint64_t get_uint64_wallclock_time(void) -{ - struct timespec ts; - int result = clock_gettime(CLOCK_REALTIME, &ts); - if (result != 0) - { - // On failure, time will be set to the max value. - return 0xffffffffffffffff; - } - - /* Return time since the 1-1-1970 epoch, in nanoseconds. - * Note that this time may be off by an integer number of seconds, as POSIX - * maintaines that all days are 86,400 seconds long, which is not true due to - * leap seconds. - */ - return ts.tv_sec * 1000000000 + ts.tv_nsec; -} - - - -void PeripheralWriteByte (uint8_t Addr, uint8_t Val) -/* Write a byte to a memory location in the peripheral address aperture. */ +void PeripheralsWriteByte (uint8_t Addr, uint8_t Val) +/* Write a byte to a memory location in the peripherals address aperture. */ { switch (Addr) { - case PERIPHERALS_ADDRESS_OFFSET_LATCH: { - /* A write to the "latch" register performs a simultaneous latch of all registers */ + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_LATCH: { + /* A write to the "latch" register performs a simultaneous latch of all registers. */ /* Latch the current wallclock time first. */ - PRegs.latched_wallclock_time = get_uint64_wallclock_time(); + struct timespec ts; + int result = clock_gettime(CLOCK_REALTIME, &ts); + if (result != 0) { + /* Unable to read time. Report max uint64 value for both fields. */ + Peripherals.Counter.latched_wallclock_time = 0xffffffffffffffff; + Peripherals.Counter.latched_wallclock_time_split = 0xffffffffffffffff; + } else { + /* Number of nanoseconds since 1-1-1970. */ + Peripherals.Counter.latched_wallclock_time = 1000000000u * ts.tv_sec + ts.tv_nsec; + /* High word is number of seconds, low word is number of nanoseconds. */ + Peripherals.Counter.latched_wallclock_time_split = (ts.tv_sec << 32) | ts.tv_nsec; + } - /* Now latch all the cycles maintained by the processor. */ - PRegs.latched_counter_clock_cycles = PRegs.counter_clock_cycles; - PRegs.latched_counter_instructions = PRegs.counter_instructions; - PRegs.latched_counter_irq_events = PRegs.counter_irq_events; - PRegs.latched_counter_nmi_events = PRegs.counter_nmi_events; + /* Latch the counters that reflect the state of the processor. */ + Peripherals.Counter.latched_clock_cycles = Peripherals.Counter.clock_cycles; + Peripherals.Counter.latched_cpu_instructions = Peripherals.Counter.cpu_instructions; + Peripherals.Counter.latched_irq_events = Peripherals.Counter.irq_events; + Peripherals.Counter.latched_nmi_events = Peripherals.Counter.nmi_events; break; } - case PERIPHERALS_ADDRESS_OFFSET_SELECT: { + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_SELECT: { /* Set the value of the visibility-selection register. */ - PRegs.visible_latch_register = Val; + Peripherals.Counter.visible_latch_register = Val; break; } default: { @@ -101,33 +92,34 @@ void PeripheralWriteByte (uint8_t Addr, uint8_t Val) -uint8_t PeripheralReadByte (uint8_t Addr) -/* Read a byte from a memory location in the peripheral address aperture. */ +uint8_t PeripheralsReadByte (uint8_t Addr) +/* Read a byte from a memory location in the peripherals address aperture. */ { switch (Addr) { - case PERIPHERALS_ADDRESS_OFFSET_SELECT: { - return PRegs.visible_latch_register; + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_SELECT: { + return Peripherals.Counter.visible_latch_register; } - case PERIPHERALS_ADDRESS_OFFSET_REG64 + 0: - case PERIPHERALS_ADDRESS_OFFSET_REG64 + 1: - case PERIPHERALS_ADDRESS_OFFSET_REG64 + 2: - case PERIPHERALS_ADDRESS_OFFSET_REG64 + 3: - case PERIPHERALS_ADDRESS_OFFSET_REG64 + 4: - case PERIPHERALS_ADDRESS_OFFSET_REG64 + 5: - case PERIPHERALS_ADDRESS_OFFSET_REG64 + 6: - case PERIPHERALS_ADDRESS_OFFSET_REG64 + 7: { + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 0: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 1: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 2: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 3: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 4: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 5: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 6: + case PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE + 7: { /* Read from any of the eight counter bytes. * The first byte is the 64 bit value's LSB, the seventh byte is its MSB. */ - unsigned byte_select = Addr - PERIPHERALS_ADDRESS_OFFSET_REG64; /* 0 .. 7 */ + unsigned byte_select = Addr - PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE; /* 0 .. 7 */ uint64_t value; - switch (PRegs.visible_latch_register) { - case PERIPHERALS_REG64_SELECT_CLOCKCYCLE_COUNTER: value = PRegs.latched_counter_clock_cycles; break; - case PERIPHERALS_REG64_SELECT_INSTRUCTION_COUNTER: value = PRegs.latched_counter_instructions; break; - case PERIPHERALS_REG64_SELECT_IRQ_COUNTER: value = PRegs.latched_counter_irq_events; break; - case PERIPHERALS_REG64_SELECT_NMI_COUNTER: value = PRegs.latched_counter_nmi_events; break; - case PERIPHERALS_REG64_SELECT_WALLCLOCK_TIME: value = PRegs.latched_wallclock_time; break; - default: value = 0; /* Reading from a non-supported register will yield 0. */ + switch (Peripherals.Counter.visible_latch_register) { + case PERIPHERALS_COUNTER_SELECT_CLOCKCYCLE_COUNTER: value = Peripherals.Counter.latched_clock_cycles; break; + case PERIPHERALS_COUNTER_SELECT_INSTRUCTION_COUNTER: value = Peripherals.Counter.latched_cpu_instructions; break; + case PERIPHERALS_COUNTER_SELECT_IRQ_COUNTER: value = Peripherals.Counter.latched_irq_events; break; + case PERIPHERALS_COUNTER_SELECT_NMI_COUNTER: value = Peripherals.Counter.latched_nmi_events; break; + case PERIPHERALS_COUNTER_SELECT_WALLCLOCK_TIME: value = Peripherals.Counter.latched_wallclock_time; break; + case PERIPHERALS_COUNTER_SELECT_WALLCLOCK_TIME_SPLIT: value = Peripherals.Counter.latched_wallclock_time_split; break; + default: value = 0; /* Reading from a non-existent register will yield 0. */ } /* Return the desired byte of the latched counter. 0==LSB, 7==MSB. */ return value >> (byte_select * 8); @@ -144,16 +136,19 @@ uint8_t PeripheralReadByte (uint8_t Addr) void PeripheralsInit (void) /* Initialize the peripheral registers */ { - PRegs.counter_clock_cycles = 0; - PRegs.counter_instructions = 0; - PRegs.counter_irq_events = 0; - PRegs.counter_nmi_events = 0; + /* Initialize the COUNTER peripheral */ - PRegs.latched_counter_clock_cycles = 0; - PRegs.latched_counter_instructions = 0; - PRegs.latched_counter_irq_events = 0; - PRegs.latched_counter_nmi_events = 0; - PRegs.latched_wallclock_time = 0; + Peripherals.Counter.clock_cycles = 0; + Peripherals.Counter.cpu_instructions = 0; + Peripherals.Counter.irq_events = 0; + Peripherals.Counter.nmi_events = 0; - PRegs.visible_latch_register = 0; + Peripherals.Counter.latched_clock_cycles = 0; + Peripherals.Counter.latched_cpu_instructions = 0; + Peripherals.Counter.latched_irq_events = 0; + Peripherals.Counter.latched_nmi_events = 0; + Peripherals.Counter.latched_wallclock_time = 0; + Peripherals.Counter.latched_wallclock_time_split = 0; + + Peripherals.Counter.visible_latch_register = 0; } diff --git a/src/sim65/peripherals.h b/src/sim65/peripherals.h index 76da6e2f8..988fd2460 100644 --- a/src/sim65/peripherals.h +++ b/src/sim65/peripherals.h @@ -35,44 +35,64 @@ #include -#define PERIPHERALS_APERTURE_BASE_ADDRESS 0xffc0 -#define PERIPHERALS_APERTURE_LAST_ADDRESS 0xffc9 +/* The memory range where the memory-mapped peripherals can be accessed. */ -#define PERIPHERALS_ADDRESS_OFFSET_LATCH 0x00 -#define PERIPHERALS_ADDRESS_OFFSET_SELECT 0x01 -#define PERIPHERALS_ADDRESS_OFFSET_REG64 0x02 +#define PERIPHERALS_APERTURE_BASE_ADDRESS 0xffc0 +#define PERIPHERALS_APERTURE_LAST_ADDRESS 0xffc9 -#define PERIPHERALS_LATCH (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_ADDRESS_OFFSET_LATCH) -#define PERIPHERALS_SELECT (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_ADDRESS_OFFSET_SELECT) -#define PERIPHERALS_REG64 (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_ADDRESS_OFFSET_REG64) +/* Declarations for the COUNTER peripheral (currently the only peripheral). */ -#define PERIPHERALS_REG64_SELECT_CLOCKCYCLE_COUNTER 0x00 -#define PERIPHERALS_REG64_SELECT_INSTRUCTION_COUNTER 0x01 -#define PERIPHERALS_REG64_SELECT_IRQ_COUNTER 0x02 -#define PERIPHERALS_REG64_SELECT_NMI_COUNTER 0x03 -#define PERIPHERALS_REG64_SELECT_WALLCLOCK_TIME 0x80 +#define PERIPHERALS_COUNTER_ADDRESS_OFFSET_LATCH 0x00 +#define PERIPHERALS_COUNTER_ADDRESS_OFFSET_SELECT 0x01 +#define PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE 0x02 + +#define PERIPHERALS_COUNTER_LATCH (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_ADDRESS_OFFSET_COUNTER_LATCH) +#define PERIPHERALS_COUNTER_SELECT (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_ADDRESS_OFFSET_COUNTER_SELECT) +#define PERIPHERALS_COUNTER_VALUE (PERIPHERALS_APERTURE_BASE_ADDRESS + PERIPHERALS_ADDRESS_OFFSET_COUNTER) + +#define PERIPHERALS_COUNTER_SELECT_CLOCKCYCLE_COUNTER 0x00 +#define PERIPHERALS_COUNTER_SELECT_INSTRUCTION_COUNTER 0x01 +#define PERIPHERALS_COUNTER_SELECT_IRQ_COUNTER 0x02 +#define PERIPHERALS_COUNTER_SELECT_NMI_COUNTER 0x03 +#define PERIPHERALS_COUNTER_SELECT_WALLCLOCK_TIME 0x80 +#define PERIPHERALS_COUNTER_SELECT_WALLCLOCK_TIME_SPLIT 0x81 typedef struct { - /* the invisible counters that are continuously updated */ - uint64_t counter_clock_cycles; - uint64_t counter_instructions; - uint64_t counter_irq_events; - uint64_t counter_nmi_events; - /* latched counters upon a write to the 'latch' address. - * One of these will be visible (read only) through an each-byte aperture. */ - uint64_t latched_counter_clock_cycles; - uint64_t latched_counter_instructions; - uint64_t latched_counter_irq_events; - uint64_t latched_counter_nmi_events; + /* The invisible counters that keep processor state. */ + uint64_t clock_cycles; + uint64_t cpu_instructions; + uint64_t irq_events; + uint64_t nmi_events; + /* Latched counters upon a write to the PERIPHERALS_COUNTER_LATCH address. + * One of these will be visible (read only) through an eight-byte aperture. + * The purpose of these latched registers is to read 64-bit values one byte + * at a time, without having to worry that their content will change along + * the way. + */ + uint64_t latched_clock_cycles; + uint64_t latched_cpu_instructions; + uint64_t latched_irq_events; + uint64_t latched_nmi_events; uint64_t latched_wallclock_time; - /* Select which of the five latched registers will be visible. - * This is a Read/Write byte-wide register. - * If a non-existent register is selected, the 8-byte aperture will read as zero. + uint64_t latched_wallclock_time_split; + /* Select which of the six latched registers will be visible. + * This is a single byte, read/write register, accessible via address PERIPHERALS_COUNTER_SELECT. + * If a non-existent latch register is selected, the PERIPHERALS_REGS64 value will be zero. */ uint8_t visible_latch_register; -} PeripheralRegs; +} CounterPeripheral; -extern PeripheralRegs PRegs; + + +/* Declare the 'Sim65Peripherals' type and its single instance 'Peripherals'. */ + +typedef struct { + /* State of the peripherals simulated by sim65. + * Currently, there is only one: the COUNTER peripheral. */ + CounterPeripheral Counter; +} Sim65Peripherals; + +extern Sim65Peripherals Peripherals; /*****************************************************************************/ /* Code */ @@ -80,11 +100,11 @@ extern PeripheralRegs PRegs; -void PeripheralWriteByte (uint8_t Addr, uint8_t Val); +void PeripheralsWriteByte (uint8_t Addr, uint8_t Val); /* Write a byte to a memory location in the peripheral address aperture. */ -uint8_t PeripheralReadByte (uint8_t Addr); +uint8_t PeripheralsReadByte (uint8_t Addr); /* Read a byte from a memory location in the peripheral address aperture. */