1
0
mirror of https://github.com/cc65/cc65.git synced 2025-03-03 09:32:33 +00:00

Merge pull request #2593 from sidneycadot/sim65-add-peripheral-support-and-docs-and-samples

sim65: add C/assembly support, docs, and samples for the new peripheral functionality.
This commit is contained in:
Sidney Cadot 2025-01-11 08:50:28 +01:00 committed by GitHub
commit 9fd090ae8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 603 additions and 16 deletions

75
asminc/sim65.inc Normal file
View File

@ -0,0 +1,75 @@
; *******************************************************************************
; ** **
; ** sim65.inc : assembler definitions for the sim6502 and sim65c02 targets. **
; ** **
; ** Sidney Cadot, January 2025 **
; ** **
; *******************************************************************************
; The '_peripherals' symbol is defined in the linker configuration
; file to correspond to the first address in the periperal memory
; aparture.
;
; We use it here as a base address for all peripheral addresses.
.import _peripherals
; **************************************************************
; ** **
; ** Define assembler symbols for the "counter" peripheral. **
; ** **
; **************************************************************
peripheral_counter_base := _peripherals + 0
peripheral_counter_latch := peripheral_counter_base + 0
peripheral_counter_select := peripheral_counter_base + 1
peripheral_counter_value := peripheral_counter_base + 2
; Values for the peripheral_counter_select register.
COUNTER_SELECT_CLOCKCYCLE_COUNTER = $00
COUNTER_SELECT_INSTRUCTION_COUNTER = $01
COUNTER_SELECT_IRQ_COUNTER = $02
COUNTER_SELECT_NMI_COUNTER = $03
COUNTER_SELECT_WALLCLOCK_TIME = $80
COUNTER_SELECT_WALLCLOCK_TIME_SPLIT = $81
; ********************************************************************
; ** **
; ** Define assembler symbols for the "sim65 control" peripheral. **
; ** **
; ********************************************************************
peripheral_sim65_base := _peripherals + 10
peripheral_sim65_cpu_mode := peripheral_sim65_base + 0
peripheral_sim65_trace_mode := peripheral_sim65_base + 1
; Values for the peripheral_sim65_cpu_mode register.
SIM65_CPU_MODE_6502 = $00
SIM65_CPU_MODE_65C02 = $01
SIM65_CPU_MODE_6502X = $02
; Bitfield values for the peripheral_sim65_trace_mode field.
SIM65_TRACE_MODE_FIELD_INSTR_COUNTER = $40
SIM65_TRACE_MODE_FIELD_CLOCK_COUNTER = $20
SIM65_TRACE_MODE_FIELD_PC = $10
SIM65_TRACE_MODE_FIELD_INSTR_BYTES = $08
SIM65_TRACE_MODE_FIELD_INSTR_ASSEMBLY = $04
SIM65_TRACE_MODE_FIELD_CPU_REGISTERS = $02
SIM65_TRACE_MODE_FIELD_CC65_SP = $01
; Values for the peripheral_sim65_trace_mode field that fully disable / enable tracing.
SIM65_TRACE_MODE_DISABLE = $00
SIM65_TRACE_MODE_ENABLE_FULL = $7F
; ************************
; ** **
; ** End of sim65.inc **
; ** **
; ************************

View File

@ -1,12 +1,15 @@
SYMBOLS {
__EXEHDR__: type = import;
__STACKSIZE__: type = weak, value = $0800; # 2k stack
_peripherals: type = export, value = $FFC0;
}
MEMORY {
ZP: file = "", start = $0000, size = $0100;
HEADER: file = %O, start = $0000, size = $000C;
MAIN: file = %O, define = yes, start = $0200, size = $FDC0 - __STACKSIZE__;
MAIN: file = %O, define = yes, start = $0200, size = $FFC0 - $0200 - __STACKSIZE__;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
EXEHDR: load = HEADER, type = ro;
@ -18,6 +21,7 @@ SEGMENTS {
DATA: load = MAIN, type = rw;
BSS: load = MAIN, type = bss, define = yes;
}
FEATURES {
CONDES: type = constructor,
label = __CONSTRUCTOR_TABLE__,

View File

@ -1,12 +1,15 @@
SYMBOLS {
__EXEHDR__: type = import;
__STACKSIZE__: type = weak, value = $0800; # 2k stack
_peripherals: type = export, value = $FFC0;
}
MEMORY {
ZP: file = "", start = $0000, size = $0100;
HEADER: file = %O, start = $0000, size = $000C;
MAIN: file = %O, define = yes, start = $0200, size = $FDC0 - __STACKSIZE__;
MAIN: file = %O, define = yes, start = $0200, size = $FFC0 - $0200 - __STACKSIZE__;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
EXEHDR: load = HEADER, type = ro;
@ -18,6 +21,7 @@ SEGMENTS {
DATA: load = MAIN, type = rw;
BSS: load = MAIN, type = bss, define = yes;
}
FEATURES {
CONDES: type = constructor,
label = __CONSTRUCTOR_TABLE__,

View File

@ -40,6 +40,8 @@ The simulator is called as follows:
Long options:
--help Help (this text)
--cycles Print amount of executed CPU cycles
--cpu <type> Override CPU type (6502, 65C02, 6502X)
--trace Enable CPU trace
--verbose Increase verbosity
--version Print the simulator version number
</verb></tscreen>
@ -70,6 +72,17 @@ Here is a description of all the command line options:
count.
<tag><tt>--cpu &lt;type&gt;</tt></tag>
Specify the CPU type to use while executing the program. This CPU type
is normally determined from the program file header, but it can be useful
to override it.
<tag><tt>--trace</tt></tag>
Print a single line of information for each instruction or interrupt that
is executed by the CPU to stdout.
<tag><tt>-v, --verbose</tt></tag>
Increase the simulator verbosity.
@ -298,25 +311,23 @@ bytes before the first write to <tt>PERIPHERALS_COUNTER_LATCH</tt> will yield ze
Example:
<tscreen><verb>
#include <stdio.h>
#include <stdint.h>
/* This example uses the peripheral support in sim65.h */
volatile uint8_t * CounterLatch = (uint8_t *)0xffc0;
volatile uint8_t * CounterSelect = (uint8_t *)0xffc1;
volatile uint32_t * CounterValue = (uint32_t *)0xffc2;
#include <stdio.h>
#include <sim65.h>
static void print_current_counters(void)
{
*CounterLatch = 0; /* latch values */
peripherals.counter.latch = 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]);
peripherals.counter.select = COUNTER_SELECT_CLOCKCYCLE_COUNTER;
printf("clock cycles ............... : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]);
peripherals.counter.select = COUNTER_SELECT_INSTRUCTION_COUNTER;
printf("instructions ............... : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]);
peripherals.counter.select = COUNTER_SELECT_WALLCLOCK_TIME;
printf("wallclock time ............. : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]);
peripherals.counter.select = COUNTER_SELECT_WALLCLOCK_TIME_SPLIT;
printf("wallclock time, split ...... : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]);
printf("\n");
}
@ -328,6 +339,102 @@ int main(void)
}
</verb></tscreen>
<sect>SIM65 control peripheral
<p>The sim65 simulator supports a memory-mapped peripheral that allows control
of the simulator behavior itself.
<p>The sim65 control peripheral interface consists of 2 registers:
<itemize>
<item><tt>PERIPHERALS_SIMCONTROL_CPUMODE</tt> ($FFCA, read/write)
<item><tt>PERIPHERALS_SIMCONTROL_TRACEMODE</tt> ($FFCB, read/write)
</itemize>
<p>Address <tt>PERIPHERALS_SIMCONTROL_CPUMODE</tt> allows access to the currently active CPU mode.
<p>Possible values are CPU_6502 (0), CPU_65C02 (1), and CPU_6502X (2). For specialized applications,
it may be useful to switch CPU models at runtime; this is supported by writing 0, 1, or 2 to this address.
Writing any other value will be ignored.
<p>Address <tt>PERIPHERALS_SIMCONTROL_TRACEMODE</tt> allows inspection and control of the currently active
CPU tracing mode.
<p>A value of 0 means tracing is disabled; a value of $7F fully enables tracing. The 7
lower bits of the value actually provide control over which fields are printed; see below
for an explanation of the seven fields.
<p>Having the ability to enable/disable tracing on the fly can be a useful debugging aid. For example,
it can be used to enable tracing for short fragments of code. Consider the following example:
<tscreen><verb>
/* This example uses the TRACE_ON and TRACE_OFF macros defined in sim65.h */
#include <stdio.h>
#include <sim65.h>
unsigned x;
int main(void)
{
TRACE_ON();
x = 0x1234; /* We want to see what happens here. */
TRACE_OFF();
return 0;
}
</verb></tscreen>
<p>This small test program, when compiled with optimizations enabled (-O), produces the output trace below:
<tscreen><verb>
70 232 022E A2 12 ldx #$12 A=7F X=00 Y=04 S=FD Flags=nvdizC SP=FFBC
71 234 0230 A9 34 lda #$34 A=7F X=12 Y=04 S=FD Flags=nvdizC SP=FFBC
72 236 0232 8D C8 02 sta $02C8 A=34 X=12 Y=04 S=FD Flags=nvdizC SP=FFBC
73 240 0235 8E C9 02 stx $02C9 A=34 X=12 Y=04 S=FD Flags=nvdizC SP=FFBC
74 244 0238 A9 00 lda #$00 A=34 X=12 Y=04 S=FD Flags=nvdizC SP=FFBC
75 246 023A 8D CB FF sta $FFCB A=00 X=12 Y=04 S=FD Flags=nvdiZC SP=FFBC
</verb></tscreen>
<p>The example output shows the full trace format, consisting of the following seven fields:
<itemize>
<item>The first field is an instruction counter. We see here that the assignment '<tt>x = 0x1234;</tt>'
starts at the 70th CPU instruction since the start of the simulator, and takes four 6502 instructions.
The two instructions that follow correspond to the execution of the <tt>TRACE_OFF</tt>' macro
that disables tracing.
<item>The second field shows the clock cycles since the start of the program. Here we see that the
first four instructions take 12 clock cycles in total (262 - 250 = 12).
<item>The third field shows the program counter as a four-digit, i.e., the PC register. Its 16-bit
value is displayed as a 4-digit hecadecimal number.
<item>The fourth field shows one to three hexadecimal byte values that make up the instruction.
<item>The fifth field shows the instruction in human-readable assembly language.
<item>The sixth field shows the CPU registers before execution of the instruction. The A, X, Y, and
S registers are each shown as a single byte value. The six status bits of the CPU are shown in
the order NVDIZC (Negative, Overflow, Decimal, Interrupt, Zero, Carry). They are displayed as
a capital letter if the flag is set, or a small letter if the flag is unset.
<item>The seventh and last field shows the software stack pointer SP as used by CC65 programs that
conform to the CC65 conventions.
</itemize>
<p>Writing a specific value to <tt>PERIPHERALS_SIMCONTROL_TRACEMODE</tt> will control which of these
seven fields are displayed. The following values are defined to denote the seven fields:
<itemize>
<item>TRACE_FIELD_INSTR_COUNTER = 0x40
<item>TRACE_FIELD_CLOCK_COUNTER = 0x20
<item>TRACE_FIELD_PC = 0x10
<item>TRACE_FIELD_INSTR_BYTES = 0x08
<item>TRACE_FIELD_INSTR_ASSEMBLY = 0x04
<item>TRACE_FIELD_CPU_REGISTERS = 0x02
<item>TRACE_FIELD_CC65_SP = 0x01
</itemize>
<p>For example, writing the value $16 to <tt>PERIPHERALS_SIMCONTROL_TRACEMODE</tt> will only display
the program counter, instruction assembly, and CPU registers fields.
<sect>Copyright<p>
sim65 (and all cc65 binutils) are (C) Copyright 1998-2000 Ullrich von

136
include/sim65.h Normal file
View File

@ -0,0 +1,136 @@
/*****************************************************************************/
/* */
/* sim65.h */
/* */
/* Definitions for the sim6502 and sim65c02 targets */
/* */
/* */
/* */
/* (C) 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 _SIM65_H
#define _SIM65_H
/* Check that we include this file while compiling to a compatible target. */
#if !defined(__SIM6502__) && !defined(__SIM65C02__)
# error This module may only be used when compiling for the sim6502 or sim65c02 targets!
#endif
#include <stdint.h>
/* The sim65 targets (sim6502, sim65c02) have a peripheral memory aperture located at
* address range 0xFFC0 .. 0xFFDF. Currently, the following peripherals are located
* inside that memory apeture:
*
* $FFC0 .. $FFC9 "counter" peripheral
* $FFCA .. $FFCB "sim65 control" peripheral
* $FFCC .. $FFDF (currently unused)
*
* The "peripherals" structure below corresponds to the register layout of the currently
* defined peripherals in this memory range. Combined with the fact that the sim6502 and
* sim65c02 linker configuration files define the "peripherals" symbol to be fixed at
* address $FFC0, this provides easy-to-use and efficient access to the peripheral registers.
*
* After including "sim65.h", it is possible for a C program to do things like:
*
* {
* peripherals.counter.latch = 0;
* peripherals.sim65.cpu_mode = SIM65_CPU_MODE_6502X;
* peripherals.sim65.trace_mode = SIM65_TRACE_MODE_ENABLE_FULL;
* }
*
* Note that "peripherals" variable is declared volatile. This instructs a C compiler to
* forego optimizations on memory accesses to the variable. However, CC65 currently ignores
* the volatile attribute. Fortunately, it is not smart with respect to optimizing
* memory accesses, so accessing the "peripherals" fields works fine in practice.
*/
extern volatile struct {
struct {
uint8_t latch;
uint8_t select;
union {
uint8_t value [8]; /* Access value as eight separate bytes. */
uint16_t value16 [4]; /* Access value as four 16-bit words. */
uint32_t value32 [2]; /* Access value as two 32-bit long words. */
};
} counter;
struct {
uint8_t cpu_mode;
uint8_t trace_mode;
} sim65;
} peripherals;
/* Values for the peripherals.counter.select field. */
#define COUNTER_SELECT_CLOCKCYCLE_COUNTER 0x00
#define COUNTER_SELECT_INSTRUCTION_COUNTER 0x01
#define COUNTER_SELECT_IRQ_COUNTER 0x02
#define COUNTER_SELECT_NMI_COUNTER 0x03
#define COUNTER_SELECT_WALLCLOCK_TIME 0x80
#define COUNTER_SELECT_WALLCLOCK_TIME_SPLIT 0x81
/* Values for the peripherals.sim65.cpu_mode field. */
#define SIM65_CPU_MODE_6502 0x00
#define SIM65_CPU_MODE_65C02 0x01
#define SIM65_CPU_MODE_6502X 0x02
/* Bitfield values for the peripherals.sim65.trace_mode field. */
#define SIM65_TRACE_MODE_FIELD_INSTR_COUNTER 0x40
#define SIM65_TRACE_MODE_FIELD_CLOCK_COUNTER 0x20
#define SIM65_TRACE_MODE_FIELD_PC 0x10
#define SIM65_TRACE_MODE_FIELD_INSTR_BYTES 0x08
#define SIM65_TRACE_MODE_FIELD_INSTR_ASSEMBLY 0x04
#define SIM65_TRACE_MODE_FIELD_CPU_REGISTERS 0x02
#define SIM65_TRACE_MODE_FIELD_CC65_SP 0x01
/* Values for the peripherals.sim65.trace_mode field that fully disable / enable tracing. */
#define SIM65_TRACE_MODE_DISABLE 0x00
#define SIM65_TRACE_MODE_ENABLE_FULL 0x7F
/* Convenience macros to enable / disable tracing at runtime. */
#define TRACE_ON() do peripherals.sim65.trace_mode = SIM65_TRACE_MODE_ENABLE_FULL; while(0)
#define TRACE_OFF() do peripherals.sim65.trace_mode = SIM65_TRACE_MODE_DISABLE; while(0)
/* Convenience macro to query the CPU mode at runtime. */
#define GET_CPU_MODE() peripherals.sim65.cpu_mode
/* Convenience macro to set the CPU mode at runtime.
*
* Use SIM65_CPU_MODE_6502, SIM65_CPU_MODE_65C02, or SIM65_CPU_MODE_6502 as argument.
*
* Important Note:
*
* When running in a program compiled for the "sim6502" target, it is safe to switch to
* 65C02 or 6502X mode, since the runtime library will only use plain 6502 opcodes, and
* those work the same in 65C02 and 6502X mode.
*
* However, when running in a program compiled for the "sim65c02" target, it is NOT safe
* to switch to 6502 or 6502X mode, since many routines in the runtime library use
* 65C02-specific opcodes, and these will not work as expected when the CPU is switched
* to 6502 or 6502X mode. When such an instruction is encountered, the program will
* exhibit undefined behavior.
*/
#define SET_CPU_MODE(mode) do peripherals.sim65.cpu_mode = mode; while(0)
/* End of sim65.h */
#endif

View File

@ -0,0 +1,104 @@
/*
* Sim65 cpu-mode switching example.
*
* Description
* -----------
*
* We can inspect and manipulate the CPU model that sim65 emulates at runtime.
*
* Sim65 always runs in one of three modes:
*
* - 6502 mode: the 151 documented opcodes are supported; if the processor encounters
* one of the 105 undocumented opcodes, the simulator ends with an
* 'illegal opcode' message.
* - 65C02 mode: the 105 undocumented opcodes now have well-defined behavior. Some
* do useful things, while all others are now defined as NOPs.
* - 6502X mode: the 105 undocumented opcodes don't have documented behavior, but
* they /do/ have behavior on a real 6502. This behavior has been
* figured out, and is deterministic (with minor exceptions).
* In this mode, sim65 mimics the behavior of a real 6502 when
* it encounters an undocumented opcode, rather than terminating.
*
* In the example below, we first switch to 6502X mode and execute a small
* assembly code fragment, then repeat this in 65C02 mode.
*
* The code fragment is designed to distinguish between a 6502 and a 65C02
* processor based on the behavior of the ADC function in decimal mode.
*
* Important Note:
*
* When running in a program compiled for the "sim6502" target, it is safe to switch to
* 65C02 or 6502X mode, since the runtime library will only use plain 6502 opcodes, and
* those work the same in 65C02 and 6502X mode.
*
* However, when running in a program compiled for the "sim65c02" target, it is NOT safe
* to switch to 6502 or 6502X mode, since many routines in the runtime library use
* 65C02-specific opcodes, and these will not work as expected when the CPU is switched
* to 6502 or 6502X mode. When such an instruction is encountered, the program will
* exhibit undefined behavior.
*
* For this reason, this program will only work when compiled for the "sim6502" target.
*
* Running the example
* -------------------
*
* cl65 -t sim6502 -O cpumode_example.c -o cpumode_example.prg
* sim65 cpumode_example.prg
*
*/
#include <stdio.h>
#include <stdbool.h>
#include <sim65.h>
static bool __fastcall__ is_65c02(void)
{
/* This assembly routine loads 0 into AX on a 6502 (also on a 6502 on which decimal
* mode is not implemented), and 1 on a 65C02.
*
* Note: this implementation triggers a "control reaches end of non-void function"
* warning that can be safely ignored. While no return statement is present, the
* return value is correctly loaded into AX by the assembly code.
*/
__asm__("sed");
__asm__("ldx #0");
__asm__("txa");
__asm__("sbc #155");
__asm__("asl");
__asm__("rol");
__asm__("and #1");
__asm__("cld");
}
int main(void)
{
printf("CPU mode at startup ....... : %u\n", GET_CPU_MODE());
printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO");
printf("\n");
printf("Switching to 6502 mode ....\n");
SET_CPU_MODE(SIM65_CPU_MODE_6502);
printf("Current CPU mode .......... : %u\n", GET_CPU_MODE());
printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO");
printf("\n");
printf("Switching to 65C02 mode ...\n");
SET_CPU_MODE(SIM65_CPU_MODE_65C02);
printf("Current CPU mode .......... : %u\n", GET_CPU_MODE());
printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO");
printf("\n");
printf("Switching to 6502X mode ...\n");
SET_CPU_MODE(SIM65_CPU_MODE_6502X);
printf("Current CPU mode .......... : %u\n", GET_CPU_MODE());
printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO");
printf("\n");
printf("Bye!\n");
return 0;
}

View File

@ -0,0 +1,117 @@
/*
* Sim65 timer example.
*
* Description
* -----------
*
* This example tests the clock cycle counter feature of sim65.
*
* The function 'timestamp' obtains the lower 32-bits of the clock cycle counter.
*
* The function 'calc_sum_terms' calculates the sum of a range of integers
* starting at zero. It simply iterates over all terms, which means that its
* runtime is a linear function of its input value.
*
* In the main function, we first derive an 'offset' value by getting two timestamp
* values, with nothing happening in between. Ideally this should yield a 0 clock
* cycle duration, but due to the overhead of calling the 'timestamp' function,
* and the 'timestamp' function itself, the difference between these timestamp
* will be non-zero. We store this value in the 'overhead' variable, and subtract
* this value in later measurements.
*
* Next, we measure the duration of calling the function 'calc_sum_terms' with two
* input values, 0, and 1. The duration includes storing the result in the 'result'
* variable.
*
* Extrapolating from these two measurements, and assuming that the runtime of
* calling 'calc_sum_terms' and storing its result scales linearly with its argument,
* we can predict the duration of a call to 'calc_sum_terms' with a much larger
* argument (max_terms = 10000).
*
* Finally, we actually measure the duration with max_terms = 10000. If the
* duration measured is equal to the predicted value, we exit successfully. If not,
* we exit with failure.
*
* Running the example
* -------------------
*
* cl65 -t sim6502 -O timer_example.c -o timer_example.prg
* sim65 timer_example.prg
*
*/
#include <stdio.h>
#include <sim65.h>
static uint32_t timestamp(void)
{
peripherals.counter.select = COUNTER_SELECT_CLOCKCYCLE_COUNTER;
peripherals.counter.latch = 0;
return peripherals.counter.value32[0];
}
static unsigned long calc_sum_terms(unsigned max_term)
/* A function with a runtime that scales linearly with its argument. */
{
unsigned k;
unsigned long sum = 0;
for (k = 0; k <= max_term; ++k)
{
sum += k;
}
return sum;
}
int main(void)
{
unsigned max_term;
unsigned long result;
uint32_t t1, t2, overhead;
int32_t d0, d1, duration;
int32_t predicted_duration;
/* Calibration measurement of zero clock cycles, to determine the overhead. */
overhead = 0;
t1 = timestamp();
t2 = timestamp() - overhead;
overhead = (t2 - t1);
/* Calculate call duration (including assignment of result) for argument value 0. */
max_term = 0;
t1 = timestamp();
result = calc_sum_terms(max_term);
t2 = timestamp();
d0 = (t2 - t1) - overhead;
printf("max_term = %u -> result = %lu; duration = %ld\n", max_term, result, d0);
/* Calculate call duration (including assignment of result) for argument value 1. */
max_term = 1;
t1 = timestamp();
result = calc_sum_terms(max_term);
t2 = timestamp();
d1 = (t2 - t1) - overhead;
printf("max_term = %u -> result = %lu; duration = %ld\n", max_term, result, d1);
/* Predict runtime for a much bigger argument value, 10000. */
max_term = 10000;
predicted_duration = d0 + max_term * (d1 - d0);
printf("predicted duration for max_term = %u: %ld\n", max_term, predicted_duration);
/* Do the actual measurement for max_term = 10000.
* Note: equality between the prediction and the measurement is only achieved if we compile with -O.
*/
t1 = timestamp();
result = calc_sum_terms(max_term);
t2 = timestamp();
duration = (t2 - t1) - overhead;
printf("max_term = %u -> result = %lu; duration = %ld\n", max_term, result, duration);
return 0;
}

View File

@ -0,0 +1,40 @@
/*
* Sim65 trace functionailty example.
*
* Description
* -----------
*
* The easiest way to use tracing in sim65 is to pass the '--trace' option
* to sim65 while starting a program.
*
* However, it is also possiblke to enable and disable the trace functionality
* at runtime, from within the C code itself. This can be useful to produce
* runtime traces of small code fragments for debugging purposes.
*
* In this example, We use the TRACE_ON and TRACE_OFF macros provided in sim65.h
* to trace what the CPU is doing during a single statement: the assignment of
* a constant to a global variable.
*
* Running the example
* -------------------
*
* cl65 -t sim6502 -O trace_example.c -o trace_example.prg
* sim65 trace_example.prg
*
* Compiling and running the program like this will produce a trace of six 6502 instructions.
* The first four instructions correspond to the 'x = 0x1234' assignment statement.
* The last two instructions (ending in a store to address $FFCB) disable the trace facility.
*
*/
#include <sim65.h>
unsigned x;
int main(void)
{
TRACE_ON();
x = 0x1234;
TRACE_OFF();
return 0;
}