mirror of
https://github.com/cc65/cc65.git
synced 2025-02-06 12:31:12 +00:00
Merge branch 'cc65:master' into struct-assignment-bug2566
This commit is contained in:
commit
380a946faa
@ -5,7 +5,7 @@ SYMBOLS {
|
||||
MEMORY {
|
||||
ZP: file = "", start = $0000, size = $0100;
|
||||
HEADER: file = %O, start = $0000, size = $000C;
|
||||
MAIN: file = %O, define = yes, start = $0200, size = $FDF0 - __STACKSIZE__;
|
||||
MAIN: file = %O, define = yes, start = $0200, size = $FDC0 - __STACKSIZE__;
|
||||
}
|
||||
SEGMENTS {
|
||||
ZEROPAGE: load = ZP, type = zp;
|
||||
|
@ -5,7 +5,7 @@ SYMBOLS {
|
||||
MEMORY {
|
||||
ZP: file = "", start = $0000, size = $0100;
|
||||
HEADER: file = %O, start = $0000, size = $000C;
|
||||
MAIN: file = %O, define = yes, start = $0200, size = $FDF0 - __STACKSIZE__;
|
||||
MAIN: file = %O, define = yes, start = $0200, size = $FDC0 - __STACKSIZE__;
|
||||
}
|
||||
SEGMENTS {
|
||||
ZEROPAGE: load = ZP, type = zp;
|
||||
|
@ -228,6 +228,105 @@ but if customization is needed <tt/sim6502.cfg/ or <tt/sim65c02.cfg/ might be us
|
||||
|
||||
</itemize>
|
||||
|
||||
<sect>Counter peripheral
|
||||
|
||||
<p>The sim65 simulator supports a memory-mapped counter peripheral that manages
|
||||
a number of 64-bit counters that are continuously updated as the simulator is
|
||||
running. For each counter, it also provides a 64 bit "latching" register.
|
||||
|
||||
<p>The functionality of the counter peripheral is accessible through 3 registers:
|
||||
|
||||
<itemize>
|
||||
<item><tt>PERIPHERALS_COUNTER_LATCH</tt> ($FFC0, write-only)
|
||||
<item><tt>PERIPHERALS_COUNTER_SELECT</tt> ($FFC1, read/write)
|
||||
<item><tt>PERIPHERALS_COUNTER_VALUE</tt> ($FFC2..$FFC9, read-only)
|
||||
</itemize>
|
||||
|
||||
<p>These three registers are used as follows.
|
||||
|
||||
<p>When a program explicitly requests a "counter latch" operation by writing any value
|
||||
to the <tt>PERIPHERALS_COUNTER_LATCH</tt> address ($FFC0), all live registers are simultaneously
|
||||
copied to the latch registers. They will keep their newly latched values until another latch
|
||||
operation is requested.
|
||||
|
||||
<p>The <tt>PERIPHERALS_COUNTER_SELECT</tt> address ($FFC1) register holds an 8-bit value that
|
||||
specifies which 64-bit latch register is currently readable through the <tt>PERIPHERALS_COUNTER_VALUE</tt>
|
||||
address range. Six values are currently defined:
|
||||
|
||||
<itemize>
|
||||
<item>$00: latched clock cycle counter selected.
|
||||
<item>$01: latched CPU instruction counter selected.
|
||||
<item>$02: latched IRQ interrupt counter selected.
|
||||
<item>$03: latched NMI interrupt counter selected.
|
||||
<item>$80: latched wallclock time (nanoseconds) selected.
|
||||
<item>$81: latched wallclock time (split: seconds, nanoseconds) selected.
|
||||
</itemize>
|
||||
|
||||
<p>Values $00 to $03 provide access to the latched (frozen) value of their respective live
|
||||
counters at the time of the last write to <tt>PERIPHERALS_COUNTER_LATCH</tt>.
|
||||
|
||||
<p>When <tt>PERIPHERALS_COUNTER_SELECT</tt> equals $80, the <tt>PERIPHERALS_COUNTER_VALUE</tt>
|
||||
will be a 64-bit value corresponding to the number of nanoseconds elapsed since the Unix epoch
|
||||
(Midnight, Jan 1st, 1970 UTC), at the time of the last latch operation.
|
||||
|
||||
<p>When <tt>PERIPHERALS_COUNTER_SELECT</tt> equals $81, the high 32 bits of <tt>PERIPHERALS_COUNTER_VALUE</tt>
|
||||
will be a 32-bit value corresponding to the number of seconds elapsed since the Unix epoch (Midnight, Jan 1st,
|
||||
1970 UTC), at the time of the last latch operation. The low 32 bits of
|
||||
<tt>PERIPHERALS_COUNTER_VALUE</tt> will hold the nanoseconds since the start of that second.
|
||||
|
||||
<p>The two different wallclock-time latch registers will always refer to precisely the same time instant.
|
||||
For some applications, the single 64-bit value measured in nanoseconds will be more convenient, while
|
||||
for other applications, the split 32/32 bits representation with separate second and nanosecond
|
||||
values will be more convenient.
|
||||
|
||||
<p>Note that the time elapsed since the Unix epoch is an approximation, as the implementation depends on the
|
||||
way POSIX defines time-since-the-epoch. Unfortunately, POSIX incorrectly assumes that all days are precisely
|
||||
86400 seconds long, which is not true in case of leap seconds. The way this inconsistency is resolved is
|
||||
system dependent.
|
||||
|
||||
<p>On reset, <tt>PERIPHERALS_COUNTER_SELECT</tt> is initialized to zero. If the <tt>PERIPHERALS_COUNTER_SELECT</tt>
|
||||
register holds a value other than one of the six values described above, all <tt>PERIPHERALS_COUNTER_VALUE</tt>
|
||||
bytes will read as zero.
|
||||
|
||||
<p>The <tt>PERIPHERALS_COUNTER_VALUE</tt> addresses ($FFC2..$FFC9) are used to read to currently
|
||||
selected 64-bit latch register value. Address $FFC2 holds the least significant byte (LSB),
|
||||
while address $FFC9 holds the most significant byte (MSB).
|
||||
|
||||
<p>On reset, all latch registers are reset to zero. Reading any of the <tt>PERIPHERALS_COUNTER_VALUE</tt>
|
||||
bytes before the first write to <tt>PERIPHERALS_COUNTER_LATCH</tt> will yield zero.
|
||||
|
||||
Example:
|
||||
|
||||
<tscreen><verb>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
volatile uint8_t * CounterLatch = (uint8_t *)0xffc0;
|
||||
volatile uint8_t * CounterSelect = (uint8_t *)0xffc1;
|
||||
volatile uint32_t * CounterValue = (uint32_t *)0xffc2;
|
||||
|
||||
static void print_current_counters(void)
|
||||
{
|
||||
*CounterLatch = 0; /* latch values */
|
||||
|
||||
*CounterSelect = 0x00;
|
||||
printf("clock cycles ............... : %08lx %08lx\n", CounterValue[1], CounterValue[0]);
|
||||
*CounterSelect = 0x01;
|
||||
printf("instructions ............... : %08lx %08lx\n", CounterValue[1], CounterValue[0]);
|
||||
*CounterSelect = 0x80;
|
||||
printf("wallclock time ............. : %08lx %08lx\n", CounterValue[1], CounterValue[0]);
|
||||
*CounterSelect = 0x81;
|
||||
printf("wallclock time, split ...... : %08lx %08lx\n", CounterValue[1], CounterValue[0]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
print_current_counters();
|
||||
print_current_counters();
|
||||
return 0;
|
||||
}
|
||||
</verb></tscreen>
|
||||
|
||||
<sect>Copyright<p>
|
||||
|
||||
|
@ -86,6 +86,7 @@
|
||||
<ClInclude Include="sim65\error.h" />
|
||||
<ClInclude Include="sim65\memory.h" />
|
||||
<ClInclude Include="sim65\paravirt.h" />
|
||||
<ClInclude Include="sim65\peripherals.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="sim65\6502.c" />
|
||||
@ -93,8 +94,9 @@
|
||||
<ClCompile Include="sim65\main.c" />
|
||||
<ClCompile Include="sim65\memory.c" />
|
||||
<ClCompile Include="sim65\paravirt.c" />
|
||||
<ClCompile Include="sim65\peripherals.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include "peripherals.h"
|
||||
#include "error.h"
|
||||
#include "6502.h"
|
||||
#include "paravirt.h"
|
||||
@ -4730,6 +4731,8 @@ unsigned ExecuteInsn (void)
|
||||
if (HaveNMIRequest) {
|
||||
|
||||
HaveNMIRequest = false;
|
||||
Peripherals.Counter.NmiEvents += 1;
|
||||
|
||||
PUSH (PCH);
|
||||
PUSH (PCL);
|
||||
PUSH (Regs.SR & ~BF);
|
||||
@ -4744,6 +4747,8 @@ unsigned ExecuteInsn (void)
|
||||
} else if (HaveIRQRequest && GET_IF () == 0) {
|
||||
|
||||
HaveIRQRequest = false;
|
||||
Peripherals.Counter.IrqEvents += 1;
|
||||
|
||||
PUSH (PCH);
|
||||
PUSH (PCL);
|
||||
PUSH (Regs.SR & ~BF);
|
||||
@ -4762,8 +4767,14 @@ unsigned ExecuteInsn (void)
|
||||
|
||||
/* Execute it */
|
||||
Handlers[CPU][OPC] ();
|
||||
|
||||
/* Increment the instruction counter by one.NMIs and IRQs are counted separately. */
|
||||
Peripherals.Counter.CpuInstructions += 1;
|
||||
}
|
||||
|
||||
/* Increment the 64-bit clock cycle counter with the cycle count for the instruction that we just executed. */
|
||||
Peripherals.Counter.ClockCycles += Cycles;
|
||||
|
||||
/* Return the number of clock cycles needed by this instruction */
|
||||
return Cycles;
|
||||
}
|
||||
|
@ -36,9 +36,10 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#include "peripherals.h"
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -50,9 +51,6 @@
|
||||
/* flag to print cycles at program termination */
|
||||
int PrintCycles = 0;
|
||||
|
||||
/* cycles are counted by main.c */
|
||||
extern unsigned long long TotalCycles;
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -120,7 +118,7 @@ void SimExit (int Code)
|
||||
/* Exit the simulation with an exit code */
|
||||
{
|
||||
if (PrintCycles) {
|
||||
fprintf (stdout, "%llu cycles\n", TotalCycles);
|
||||
fprintf (stdout, "%" PRIu64 " cycles\n", Peripherals.Counter.ClockCycles);
|
||||
}
|
||||
exit (Code);
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "6502.h"
|
||||
#include "error.h"
|
||||
#include "memory.h"
|
||||
#include "peripherals.h"
|
||||
#include "paravirt.h"
|
||||
|
||||
|
||||
@ -60,9 +61,6 @@
|
||||
/* Name of program file */
|
||||
const char* ProgramFile;
|
||||
|
||||
/* count of total cycles executed */
|
||||
unsigned long long TotalCycles = 0;
|
||||
|
||||
/* exit simulator after MaxCycles Cccles */
|
||||
unsigned long long MaxCycles = 0;
|
||||
|
||||
@ -309,6 +307,7 @@ int main (int argc, char* argv[])
|
||||
}
|
||||
|
||||
MemInit ();
|
||||
PeripheralsInit ();
|
||||
|
||||
SPAddr = ReadProgramFile ();
|
||||
ParaVirtInit (I, SPAddr);
|
||||
@ -318,7 +317,6 @@ int main (int argc, char* argv[])
|
||||
RemainCycles = MaxCycles;
|
||||
while (1) {
|
||||
Cycles = ExecuteInsn ();
|
||||
TotalCycles += Cycles;
|
||||
if (MaxCycles) {
|
||||
if (Cycles > RemainCycles) {
|
||||
ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles reached.");
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#include "peripherals.h"
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -59,7 +59,14 @@ uint8_t Mem[0x10000];
|
||||
void MemWriteByte (uint16_t Addr, uint8_t Val)
|
||||
/* Write a byte to a memory location */
|
||||
{
|
||||
Mem[Addr] = Val;
|
||||
if ((PERIPHERALS_APERTURE_BASE_ADDRESS <= Addr) && (Addr <= PERIPHERALS_APERTURE_LAST_ADDRESS))
|
||||
{
|
||||
/* Defer the the memory-mapped peripherals handler for this write. */
|
||||
PeripheralsWriteByte (Addr - PERIPHERALS_APERTURE_BASE_ADDRESS, Val);
|
||||
} else {
|
||||
/* Write to the Mem array. */
|
||||
Mem[Addr] = Val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -76,7 +83,14 @@ void MemWriteWord (uint16_t Addr, uint16_t Val)
|
||||
uint8_t MemReadByte (uint16_t Addr)
|
||||
/* Read a byte from a memory location */
|
||||
{
|
||||
return Mem[Addr];
|
||||
if ((PERIPHERALS_APERTURE_BASE_ADDRESS <= Addr) && (Addr <= PERIPHERALS_APERTURE_LAST_ADDRESS))
|
||||
{
|
||||
/* Defer the the memory-mapped peripherals handler for this read. */
|
||||
return PeripheralsReadByte (Addr - PERIPHERALS_APERTURE_BASE_ADDRESS);
|
||||
} else {
|
||||
/* Read from the Mem array. */
|
||||
return Mem[Addr];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
209
src/sim65/peripherals.c
Normal file
209
src/sim65/peripherals.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* peripherals.c */
|
||||
/* */
|
||||
/* Memory-mapped peripheral subsystem for the 6502 simulator */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 2024-2025, Sidney Cadot */
|
||||
/* */
|
||||
/* */
|
||||
/* This software is provided 'as-is', without any expressed or implied */
|
||||
/* warranty. In no event will the authors be held liable for any damages */
|
||||
/* arising from the use of this software. */
|
||||
/* */
|
||||
/* Permission is granted to anyone to use this software for any purpose, */
|
||||
/* including commercial applications, and to alter it and redistribute it */
|
||||
/* freely, subject to the following restrictions: */
|
||||
/* */
|
||||
/* 1. The origin of this software must not be misrepresented; you must not */
|
||||
/* claim that you wrote the original software. If you use this software */
|
||||
/* in a product, an acknowledgment in the product documentation would be */
|
||||
/* appreciated but is not required. */
|
||||
/* 2. Altered source versions must be plainly marked as such, and must not */
|
||||
/* be misrepresented as being the original software. */
|
||||
/* 3. This notice may not be removed or altered from any source */
|
||||
/* distribution. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#if defined(__MINGW64__)
|
||||
/* For gettimeofday() */
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "peripherals.h"
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* The system-wide state of the peripherals */
|
||||
Sim65Peripherals Peripherals;
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
static bool GetWallclockTime (struct timespec * ts)
|
||||
/* Get the wallclock time with nanosecond resolution. */
|
||||
{
|
||||
/* Note: the 'struct timespec' type is available on all compilers we want to support. */
|
||||
|
||||
bool time_valid;
|
||||
|
||||
#if defined(__MINGW64__)
|
||||
/* When using the MinGW64 compiler, neither timespec_get() nor clock_gettime()
|
||||
* are available; using either of them makes the Linux workflow build fail.
|
||||
* The gettimeofday() function does work, so use that; its microsecond resolution
|
||||
* is fine for most applications.
|
||||
*/
|
||||
struct timeval tv;
|
||||
time_valid = (gettimeofday(&tv, NULL) == 0);
|
||||
if (time_valid) {
|
||||
ts->tv_sec = tv.tv_sec;
|
||||
ts->tv_nsec = tv.tv_usec * 1000;
|
||||
}
|
||||
#elif defined(_MSC_VER)
|
||||
/* Using the Microsoft C++ compiler.
|
||||
* clock_gettime() is not available; use timespec_get() instead.
|
||||
*/
|
||||
time_valid = timespec_get(ts, TIME_UTC) == TIME_UTC;
|
||||
#else
|
||||
/* On all other compilers, assume that clock_gettime() is available.
|
||||
* This is true on Linux and MacOS, at least.
|
||||
*/
|
||||
time_valid = clock_gettime(CLOCK_REALTIME, ts) == 0;
|
||||
#endif
|
||||
|
||||
return time_valid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PeripheralsWriteByte (uint8_t Addr, uint8_t Val)
|
||||
/* Write a byte to a memory location in the peripherals address aperture. */
|
||||
{
|
||||
switch (Addr) {
|
||||
|
||||
/* Handle writes to the Counter peripheral. */
|
||||
|
||||
case PERIPHERALS_COUNTER_ADDRESS_OFFSET_LATCH: {
|
||||
|
||||
/* A write to the "latch" register performs a simultaneous latch of all registers. */
|
||||
|
||||
/* Latch the current wallclock time before doing anything else. */
|
||||
|
||||
struct timespec ts;
|
||||
bool time_valid = GetWallclockTime (&ts);
|
||||
|
||||
if (time_valid) {
|
||||
/* Wallclock time: number of nanoseconds since 1-1-1970. */
|
||||
Peripherals.Counter.LatchedWallclockTime = 1000000000 * (uint64_t)ts.tv_sec + ts.tv_nsec;
|
||||
/* Wallclock time, split: high word is number of seconds since 1-1-1970,
|
||||
* low word is number of nanoseconds since the start of that second. */
|
||||
Peripherals.Counter.LatchedWallclockTimeSplit = (uint64_t)ts.tv_sec << 32 | ts.tv_nsec;
|
||||
} else {
|
||||
/* Unable to get time. Report max uint64 value for both fields. */
|
||||
Peripherals.Counter.LatchedWallclockTime = -1;
|
||||
Peripherals.Counter.LatchedWallclockTimeSplit = -1;
|
||||
}
|
||||
|
||||
/* Latch the counters that reflect the state of the processor. */
|
||||
Peripherals.Counter.LatchedClockCycles = Peripherals.Counter.ClockCycles;
|
||||
Peripherals.Counter.LatchedCpuInstructions = Peripherals.Counter.CpuInstructions;
|
||||
Peripherals.Counter.LatchedIrqEvents = Peripherals.Counter.IrqEvents;
|
||||
Peripherals.Counter.LatchedNmiEvents = Peripherals.Counter.NmiEvents;
|
||||
break;
|
||||
}
|
||||
case PERIPHERALS_COUNTER_ADDRESS_OFFSET_SELECT: {
|
||||
/* Set the value of the visibility-selection register. */
|
||||
Peripherals.Counter.LatchedValueSelected = Val;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Handle writes to unused and read-only peripheral addresses. */
|
||||
|
||||
default: {
|
||||
/* No action. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t PeripheralsReadByte (uint8_t Addr)
|
||||
/* Read a byte from a memory location in the peripherals address aperture. */
|
||||
{
|
||||
switch (Addr) {
|
||||
|
||||
/* Handle reads from the Counter peripheral. */
|
||||
|
||||
case PERIPHERALS_COUNTER_ADDRESS_OFFSET_SELECT: {
|
||||
return Peripherals.Counter.LatchedValueSelected;
|
||||
}
|
||||
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 SelectedByteIndex = Addr - PERIPHERALS_COUNTER_ADDRESS_OFFSET_VALUE; /* 0 .. 7 */
|
||||
uint64_t Value;
|
||||
switch (Peripherals.Counter.LatchedValueSelected) {
|
||||
case PERIPHERALS_COUNTER_SELECT_CLOCKCYCLE_COUNTER: Value = Peripherals.Counter.LatchedClockCycles; break;
|
||||
case PERIPHERALS_COUNTER_SELECT_INSTRUCTION_COUNTER: Value = Peripherals.Counter.LatchedCpuInstructions; break;
|
||||
case PERIPHERALS_COUNTER_SELECT_IRQ_COUNTER: Value = Peripherals.Counter.LatchedIrqEvents; break;
|
||||
case PERIPHERALS_COUNTER_SELECT_NMI_COUNTER: Value = Peripherals.Counter.LatchedNmiEvents; break;
|
||||
case PERIPHERALS_COUNTER_SELECT_WALLCLOCK_TIME: Value = Peripherals.Counter.LatchedWallclockTime; break;
|
||||
case PERIPHERALS_COUNTER_SELECT_WALLCLOCK_TIME_SPLIT: Value = Peripherals.Counter.LatchedWallclockTimeSplit; break;
|
||||
default: Value = 0; /* Reading from a non-existent latch register will yield 0. */
|
||||
}
|
||||
/* Return the desired byte of the latched counter; 0==LSB, 7==MSB. */
|
||||
return (uint8_t)(Value >> (SelectedByteIndex * 8));
|
||||
}
|
||||
|
||||
/* Handle reads from unused peripheral and write-only addresses. */
|
||||
|
||||
default: {
|
||||
/* Return zero value. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PeripheralsInit (void)
|
||||
/* Initialize the peripherals. */
|
||||
{
|
||||
/* Initialize the Counter peripheral */
|
||||
|
||||
Peripherals.Counter.ClockCycles = 0;
|
||||
Peripherals.Counter.CpuInstructions = 0;
|
||||
Peripherals.Counter.IrqEvents = 0;
|
||||
Peripherals.Counter.NmiEvents = 0;
|
||||
|
||||
Peripherals.Counter.LatchedClockCycles = 0;
|
||||
Peripherals.Counter.LatchedCpuInstructions = 0;
|
||||
Peripherals.Counter.LatchedIrqEvents = 0;
|
||||
Peripherals.Counter.LatchedNmiEvents = 0;
|
||||
Peripherals.Counter.LatchedWallclockTime = 0;
|
||||
Peripherals.Counter.LatchedWallclockTimeSplit = 0;
|
||||
|
||||
Peripherals.Counter.LatchedValueSelected = 0;
|
||||
}
|
120
src/sim65/peripherals.h
Normal file
120
src/sim65/peripherals.h
Normal file
@ -0,0 +1,120 @@
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* peripherals.h */
|
||||
/* */
|
||||
/* Memory-mapped peripheral subsystem for the 6502 simulator */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 2024-2025, Sidney Cadot */
|
||||
/* */
|
||||
/* */
|
||||
/* This software is provided 'as-is', without any expressed or implied */
|
||||
/* warranty. In no event will the authors be held liable for any damages */
|
||||
/* arising from the use of this software. */
|
||||
/* */
|
||||
/* Permission is granted to anyone to use this software for any purpose, */
|
||||
/* including commercial applications, and to alter it and redistribute it */
|
||||
/* freely, subject to the following restrictions: */
|
||||
/* */
|
||||
/* 1. The origin of this software must not be misrepresented; you must not */
|
||||
/* claim that you wrote the original software. If you use this software */
|
||||
/* in a product, an acknowledgment in the product documentation would be */
|
||||
/* appreciated but is not required. */
|
||||
/* 2. Altered source versions must be plainly marked as such, and must not */
|
||||
/* be misrepresented as being the original software. */
|
||||
/* 3. This notice may not be removed or altered from any source */
|
||||
/* distribution. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
#ifndef PERIPHERALS_H
|
||||
#define PERIPHERALS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* The memory range where the memory-mapped peripherals can be accessed. */
|
||||
|
||||
#define PERIPHERALS_APERTURE_BASE_ADDRESS 0xffc0
|
||||
#define PERIPHERALS_APERTURE_LAST_ADDRESS 0xffc9
|
||||
|
||||
/* Declarations for the COUNTER peripheral (currently the only peripheral). */
|
||||
|
||||
#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 keep processor state. */
|
||||
uint64_t ClockCycles;
|
||||
uint64_t CpuInstructions;
|
||||
uint64_t IrqEvents;
|
||||
uint64_t NmiEvents;
|
||||
/* The 'latched_...' fields below hold values that are sampled 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 LatchedClockCycles;
|
||||
uint64_t LatchedCpuInstructions;
|
||||
uint64_t LatchedIrqEvents;
|
||||
uint64_t LatchedNmiEvents;
|
||||
uint64_t LatchedWallclockTime;
|
||||
uint64_t LatchedWallclockTimeSplit;
|
||||
/* 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_COUNTER_VALUE will be zero.
|
||||
*/
|
||||
uint8_t LatchedValueSelected;
|
||||
} CounterPeripheral;
|
||||
|
||||
|
||||
|
||||
/* Declare the 'Sim65Peripherals' type and its single instance 'Peripherals'. */
|
||||
|
||||
typedef struct {
|
||||
/* State of the peripherals available in sim65.
|
||||
* Currently, there is only one peripheral: the Counter. */
|
||||
CounterPeripheral Counter;
|
||||
} Sim65Peripherals;
|
||||
|
||||
extern Sim65Peripherals Peripherals;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
void PeripheralsWriteByte (uint8_t Addr, uint8_t Val);
|
||||
/* Write a byte to a memory location in the peripheral address aperture. */
|
||||
|
||||
|
||||
uint8_t PeripheralsReadByte (uint8_t Addr);
|
||||
/* Read a byte from a memory location in the peripheral address aperture. */
|
||||
|
||||
|
||||
void PeripheralsInit (void);
|
||||
/* Initialize the peripherals. */
|
||||
|
||||
|
||||
|
||||
/* End of peripherals.h */
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user