From 0081fe548ce3908dee2624a322d89af8d06ed4bb Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Sun, 7 May 2023 16:26:42 -0400 Subject: [PATCH 1/6] sim64 universal 64-bit cycle count support: MaxCycleCount is accounted by countdown, eliminating the 1-instruction-overhead issue, and removing the need to compare against a growing TotalCycles. Makes main.c responsible for counting total cycles, instead of 6502.c, so the size of MaxCycleCount etc. is fully determined in one location. Makes error.c responsible for PrintCycles instead of paravirt.c, so that it can be treated globally instead of Return value of main() should be SIM65_ERROR because it is unreachable by design. --- src/sim65/6502.c | 18 ------------------ src/sim65/6502.h | 6 ------ src/sim65/error.c | 25 +++++++++++++++++++++++++ src/sim65/error.h | 6 ++++++ src/sim65/main.c | 40 +++++++++++++++++++++------------------- src/sim65/paravirt.c | 6 +----- 6 files changed, 53 insertions(+), 48 deletions(-) diff --git a/src/sim65/6502.c b/src/sim65/6502.c index 6c23b0dfc..9d2c93da8 100644 --- a/src/sim65/6502.c +++ b/src/sim65/6502.c @@ -64,18 +64,12 @@ static CPURegs Regs; /* Cycles for the current insn */ static unsigned Cycles; -/* Total number of CPU cycles exec'd */ -static unsigned long TotalCycles; - /* NMI request active */ static unsigned HaveNMIRequest; /* IRQ request active */ static unsigned HaveIRQRequest; -/* flag to print cycles at program termination */ -int PrintCycles; - /*****************************************************************************/ /* Helper functions and macros */ @@ -3277,18 +3271,6 @@ unsigned ExecuteInsn (void) Handlers[CPU][OPC] (); } - /* Count cycles */ - TotalCycles += Cycles; - /* Return the number of clock cycles needed by this insn */ return Cycles; } - - - -unsigned long GetCycles (void) -/* Return the total number of cycles executed */ -{ - /* Return the total number of cycles */ - return TotalCycles; -} diff --git a/src/sim65/6502.h b/src/sim65/6502.h index f8e894567..39b995793 100644 --- a/src/sim65/6502.h +++ b/src/sim65/6502.h @@ -96,12 +96,6 @@ unsigned ExecuteInsn (void); ** executed instruction. */ -unsigned long GetCycles (void); -/* Return the total number of clock cycles executed */ - -extern int PrintCycles; -/* flag to print cycles at program termination */ - /* End of 6502.h */ diff --git a/src/sim65/error.c b/src/sim65/error.c index 441b07d2a..fc24ca006 100644 --- a/src/sim65/error.c +++ b/src/sim65/error.c @@ -41,6 +41,20 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* flag to print cycles at program termination */ +int PrintCycles = 0; + +/* cycles are counted by main.c */ +extern unsigned long long TotalCycles; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -99,3 +113,14 @@ void Internal (const char* Format, ...) va_end (ap); exit (SIM65_ERROR); } + + + +void SimExit (int Code) +/* Exit the simulation with an exit code */ +{ + if (PrintCycles) { + fprintf (stdout, "%llu cycles\n", TotalCycles); + } + exit (Code); +} diff --git a/src/sim65/error.h b/src/sim65/error.h index ea54fa048..a016881c6 100644 --- a/src/sim65/error.h +++ b/src/sim65/error.h @@ -55,6 +55,9 @@ #define SIM65_ERROR_TIMEOUT 0x7E /* An error result for max CPU instructions exceeded. */ +extern int PrintCycles; +/* flag to print cycles at program termination */ + /*****************************************************************************/ @@ -75,6 +78,9 @@ void ErrorCode (int Code, const char* Format, ...) attribute((noreturn, format(p void Internal (const char* Format, ...) attribute((noreturn, format(printf,1,2))); /* Print an internal error message and die */ +void SimExit (int Code); +/* Exit the simulation with an exit code */ + /* End of error.h */ diff --git a/src/sim65/main.c b/src/sim65/main.c index d92d52ef6..f5ace1909 100644 --- a/src/sim65/main.c +++ b/src/sim65/main.c @@ -36,7 +36,6 @@ #include #include #include -#include /* common */ #include "abend.h" @@ -61,14 +60,14 @@ /* Name of program file */ const char* ProgramFile; -/* exit simulator after MaxCycles Cycles */ -unsigned long MaxCycles; +/* count of total cycles executed */ +unsigned long long TotalCycles = 0; -/* maximum number of cycles that can be tested, -** requires overhead for longest possible instruction, -** which should be 7, using 16 for safety. -*/ -#define MAXCYCLES_LIMIT (ULONG_MAX-16) +/* exit simulator after MaxCycles Cccles */ +unsigned long long MaxCycles = 0; + +/* countdown from MaxCycles */ +unsigned long long RemainCycles; /* Header signature 'sim65' */ static const unsigned char HeaderSignature[] = { @@ -145,11 +144,7 @@ static void OptQuitXIns (const char* Opt attribute ((unused)), const char* Arg attribute ((unused))) /* quit after MaxCycles cycles */ { - MaxCycles = strtoul(Arg, NULL, 0); - /* Guard against overflow. */ - if (MaxCycles >= MAXCYCLES_LIMIT) { - Error("'-x parameter out of range. Max: %lu",MAXCYCLES_LIMIT); - } + MaxCycles = strtoull(Arg, NULL, 0); } static unsigned char ReadProgramFile (void) @@ -247,6 +242,7 @@ int main (int argc, char* argv[]) unsigned I; unsigned char SPAddr; + unsigned int Cycles; /* Initialize the cmdline module */ InitCmdLine (&argc, &argv, "sim65"); @@ -309,18 +305,24 @@ int main (int argc, char* argv[]) MemInit (); SPAddr = ReadProgramFile (); - ParaVirtInit (I, SPAddr); Reset (); + RemainCycles = MaxCycles; while (1) { - ExecuteInsn (); - if (MaxCycles && (GetCycles () >= MaxCycles)) { - ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles reached."); + Cycles = ExecuteInsn (); + TotalCycles += Cycles; + if (MaxCycles) { + if (Cycles > RemainCycles) { + ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles (%llu) reached.", MaxCycles); + } + RemainCycles -= Cycles; } } - /* Return an apropriate exit code */ - return EXIT_SUCCESS; + /* Unreachable. sim65 program must exit through paravirtual PVExit + ** or timeout from MaxCycles producing an error. + */ + return SIM65_ERROR; } diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index 0b16f89e9..af162acfa 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -124,11 +124,7 @@ static unsigned PopParam (unsigned char Incr) static void PVExit (CPURegs* Regs) { Print (stderr, 1, "PVExit ($%02X)\n", Regs->AC); - if (PrintCycles) { - Print (stdout, 0, "%lu cycles\n", GetCycles ()); - } - - exit (Regs->AC); + SimExit (Regs->AC); } From aad64063c97d2b379955f90d006c244c744c559e Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Sun, 7 May 2023 16:33:07 -0400 Subject: [PATCH 2/6] makefiles no longer need comment about sim65 64-bit support --- test/asm/val/Makefile | 1 - test/standard/Makefile | 1 - test/val/Makefile | 1 - 3 files changed, 3 deletions(-) diff --git a/test/asm/val/Makefile b/test/asm/val/Makefile index 09a6b91bc..54b1100ec 100644 --- a/test/asm/val/Makefile +++ b/test/asm/val/Makefile @@ -22,7 +22,6 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -# sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. SIM65FLAGS = -x 4000000000 CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65) diff --git a/test/standard/Makefile b/test/standard/Makefile index 40299c1bf..bf513c84e 100644 --- a/test/standard/Makefile +++ b/test/standard/Makefile @@ -22,7 +22,6 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -# sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. SIM65FLAGS = -x 4000000000 -c CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) diff --git a/test/val/Makefile b/test/val/Makefile index 158967f9e..56d8e5ff9 100644 --- a/test/val/Makefile +++ b/test/val/Makefile @@ -24,7 +24,6 @@ ifdef QUIET NULLERR = 2>$(NULLDEV) endif -# sim65 can support 64-bit cycle counts on some platforms, but not all. This must fit in 32-bit. SIM65FLAGS = -x 4000000000 -c CC65 := $(if $(wildcard ../../bin/cc65*),..$S..$Sbin$Scc65,cc65) From 3419cbd3484427ad732289c5ff7cab41639cf1ca Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Sun, 7 May 2023 16:33:47 -0400 Subject: [PATCH 3/6] sim65 64-bit cycle count tests These take ~10 seconds to run locally --- test/asm/Makefile | 2 +- test/asm/misc/Makefile | 70 +++++++++++++++++++++++++++++++ test/asm/misc/sim65-time-wait.inc | 55 ++++++++++++++++++++++++ test/asm/misc/sim65-timein.s | 17 ++++++++ test/asm/misc/sim65-timeout.s | 17 ++++++++ test/asm/readme.txt | 6 +++ 6 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 test/asm/misc/Makefile create mode 100644 test/asm/misc/sim65-time-wait.inc create mode 100644 test/asm/misc/sim65-timein.s create mode 100644 test/asm/misc/sim65-timeout.s diff --git a/test/asm/Makefile b/test/asm/Makefile index 3481dae78..dea53f6b2 100644 --- a/test/asm/Makefile +++ b/test/asm/Makefile @@ -12,7 +12,7 @@ endif WORKDIR = ../testwrk/asm -SUBDIRS = cpudetect opcodes listing val err +SUBDIRS = cpudetect opcodes listing val err misc .PHONY: all continue mostlyclean clean diff --git a/test/asm/misc/Makefile b/test/asm/misc/Makefile new file mode 100644 index 000000000..5a9d4f3ef --- /dev/null +++ b/test/asm/misc/Makefile @@ -0,0 +1,70 @@ +# Makefile for the remaining asm tests that need special care in one way or another + +ifneq ($(shell echo),) + CMD_EXE = 1 +endif + +ifdef CMD_EXE + S = $(subst /,\,/) + NOT = - # Hack + EXE = .exe + NULLDEV = nul: + MKDIR = mkdir $(subst /,\,$1) + RMDIR = -rmdir /s /q $(subst /,\,$1) +else + S = / + NOT = ! + EXE = + NULLDEV = /dev/null + MKDIR = mkdir -p $1 + RMDIR = $(RM) -r $1 +endif + +ifdef QUIET + .SILENT: + NULLOUT = >$(NULLDEV) + NULLERR = 2>$(NULLDEV) +endif + +SIM65FLAGS = -x 200000000 + +CA65 := $(if $(wildcard ../../../bin/ca65*),..$S..$S..$Sbin$Sca65,ca65) +LD65 := $(if $(wildcard ../../../bin/ld65*),..$S..$S..$Sbin$Sld65,ld65) +SIM65 := $(if $(wildcard ../../../bin/sim65*),..$S..$S..$Sbin$Ssim65,sim65) + +WORKDIR = ..$S..$S..$Stestwrk$Sasm$Smisc + +.PHONY: all clean + +SOURCES := $(wildcard *.s) +TESTS = $(SOURCES:%.s=$(WORKDIR)/%.6502.prg) +TESTS += $(SOURCES:%.s=$(WORKDIR)/%.65c02.prg) + +all: $(TESTS) + +$(WORKDIR): + $(call MKDIR,$(WORKDIR)) + +define PRG_template + +# sim65 ensure 64-bit wait time does not timeout +$(WORKDIR)/sim65-timein.$1.prg: sim65-timein.s | $(WORKDIR) + $(if $(QUIET),echo misc/sim65-timein.$1.prg) + $(CA65) -t sim$1 -o $$(@:.prg=.o) $$< $(NULLERR) + $(LD65) -t sim$1 -o $$@ $$(@:.prg=.o) sim$1.lib $(NULLERR) + $(SIM65) -x 4400000000 -c $$@ $(NULLOUT) $(NULLERR) + +# sim65 ensure 64-bit wait time does timeout +$(WORKDIR)/sim65-timeout.$1.prg: sim65-timeout.s | $(WORKDIR) + $(if $(QUIET),echo misc/sim65-timeout.$1.prg) + $(CA65) -t sim$1 -o $$(@:.prg=.o) $$< $(NULLERR) + $(LD65) -t sim$1 -o $$@ $$(@:.prg=.o) sim$1.lib $(NULLERR) + $(NOT) $(SIM65) -x 4400000000 -c $$@ $(NULLOUT) $(NULLERR) + +endef # PRG_template + +$(eval $(call PRG_template,6502)) +$(eval $(call PRG_template,65c02)) + +clean: + @$(call RMDIR,$(WORKDIR)) diff --git a/test/asm/misc/sim65-time-wait.inc b/test/asm/misc/sim65-time-wait.inc new file mode 100644 index 000000000..bc761ac16 --- /dev/null +++ b/test/asm/misc/sim65-time-wait.inc @@ -0,0 +1,55 @@ +; Shared timer for: +; sim65-timein.s +; sim65-timeout.s + +; wait A * 100,000,000 cycles, plus small amount of overhead +wait100m: + tay + bne :+ + rts ; return quickly if A=0 +: + jsr wait50331648 ; 50331648 + jsr wait25165824 ; 75497472 + jsr wait12582912 ; 88080384 + jsr wait6291456 ; 94371840 + jsr wait3145728 ; 97517568 + jsr wait1572864 ; 99090432 + jsr wait786432 ; 99876864 + jsr wait98304 ; 99975168 + jsr wait24576 ; 99999744 + jsr wait192 ; 99999936 + jsr wait48 ; 99999984 + nop ; 99999986 + nop ; 99999988 + php ; 99999991 + plp ; 99999995 + dey ; 99999997 + bne :- ; 100000000 + rts +; Note that this branch could cross a page if poorly aligned, +; adding an additional 1 cycle per loop. +; This precision is not important for the tests used. + +wait50331648: jsr wait25165824 +wait25165824: jsr wait12582912 +wait12582912: jsr wait6291456 +wait6291456: jsr wait3145728 +wait3145728: jsr wait1572864 +wait1572864: jsr wait786432 +wait786432: jsr wait393216 +wait393216: jsr wait196608 +wait196608: jsr wait98304 +wait98304: jsr wait49152 +wait49152: jsr wait24576 +wait24576: jsr wait12288 +wait12288: jsr wait6144 +wait6144: jsr wait3072 +wait3072: jsr wait1536 +wait1536: jsr wait768 +wait768: jsr wait384 +wait384: jsr wait192 +wait192: jsr wait96 +wait96: jsr wait48 +wait48: jsr wait24 +wait24: jsr wait12 +wait12: rts diff --git a/test/asm/misc/sim65-timein.s b/test/asm/misc/sim65-timein.s new file mode 100644 index 000000000..13365f0a8 --- /dev/null +++ b/test/asm/misc/sim65-timein.s @@ -0,0 +1,17 @@ +; Verifies that sim65 can handle 64-bit timeout counter. +; sim65 sim65-timein.prg -x 4400000000 + +.export _main +.import exit + +_main: + ; wait ~4,300,000,000 cycles + lda #43 + jsr wait100m + ; This is a positive test. + ; If the timeout did not occur, returning 0 reports success. + lda #0 + rts + +; wait100m +.include "sim65-time-wait.inc" diff --git a/test/asm/misc/sim65-timeout.s b/test/asm/misc/sim65-timeout.s new file mode 100644 index 000000000..6f1778dcd --- /dev/null +++ b/test/asm/misc/sim65-timeout.s @@ -0,0 +1,17 @@ +; Verifies that sim65 can handle 64-bit timeout counter. +; sim65 sim65-timeout.prg -x 4400000000 + +.export _main +.import exit + +_main: + ; wait ~4,500,000,000 cycles + lda #45 + jsr wait100m + ; This is a negative test. + ; If the timeout did not occur, returning 0 reports failure. + lda #0 + rts + +; wait100m +.include "sim65-time-wait.inc" diff --git a/test/asm/readme.txt b/test/asm/readme.txt index 49b530d1c..9b716e60c 100644 --- a/test/asm/readme.txt +++ b/test/asm/readme.txt @@ -36,3 +36,9 @@ val: Runtime assembly tests using sim65 that should end with an exit code of 0 if they pass. If they fail the exit code should be either -1, or a number indicating what part of the test failed. + + +misc: +----- + +This is for tests that require special make steps or conditions. From 7f0baff792bd4f5b4937dc1d19811f310ba7b29f Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Sun, 7 May 2023 16:35:05 -0400 Subject: [PATCH 4/6] document how to return from assembly sim65 test --- doc/sim65.sgml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/sim65.sgml b/doc/sim65.sgml index 310de4667..c838cd3b0 100644 --- a/doc/sim65.sgml +++ b/doc/sim65.sgml @@ -126,9 +126,17 @@ a set of built-in paravirtualization functions (Creating a Test in Assembly

Assembly tests may similarly be assembled and linked with - + +Return from The binary file has a 12 byte header: From 2cb457b85f8019e6437907ddd19e3d2c5f2546c6 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Sun, 7 May 2023 16:51:12 -0400 Subject: [PATCH 5/6] sim65 use error codes outside the simulated program's range for non-sim errors --- doc/sim65.sgml | 7 +++++++ src/sim65/error.h | 8 +++++--- src/sim65/paravirt.c | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/doc/sim65.sgml b/doc/sim65.sgml index c838cd3b0..b1e5afbdc 100644 --- a/doc/sim65.sgml +++ b/doc/sim65.sgml @@ -44,6 +44,13 @@ The simulator is called as follows: --version Print the simulator version number +sim65 will exit with the error code of the simulated program, +which is limited to an 8-bit result 0-255. + +An error in sim65, like bad arguments or an internal problem will exit with Command line options in detail

diff --git a/src/sim65/error.h b/src/sim65/error.h index a016881c6..6dbee974c 100644 --- a/src/sim65/error.h +++ b/src/sim65/error.h @@ -49,10 +49,12 @@ -#define SIM65_ERROR 0x7F -/* Does not use EXIT_FAILURE because it may overlap with test results. */ +#define SIM65_ERROR -1 +/* An error result for errors that are not part of the simulated test. +** Note that set simulated test can only return 8-bit errors 0-255. +*/ -#define SIM65_ERROR_TIMEOUT 0x7E +#define SIM65_ERROR_TIMEOUT -2 /* An error result for max CPU instructions exceeded. */ extern int PrintCycles; diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c index af162acfa..2e52d6e7e 100644 --- a/src/sim65/paravirt.c +++ b/src/sim65/paravirt.c @@ -124,7 +124,7 @@ static unsigned PopParam (unsigned char Incr) static void PVExit (CPURegs* Regs) { Print (stderr, 1, "PVExit ($%02X)\n", Regs->AC); - SimExit (Regs->AC); + SimExit (Regs->AC); /* Error code in range 0-255. */ } From f15e9c41593cec0aed3eb17b01a91985e778f1e7 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Sun, 7 May 2023 17:41:54 -0400 Subject: [PATCH 6/6] Linux build rejects %llu in ErrorCode --- src/sim65/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sim65/main.c b/src/sim65/main.c index f5ace1909..3c7cdc157 100644 --- a/src/sim65/main.c +++ b/src/sim65/main.c @@ -315,7 +315,7 @@ int main (int argc, char* argv[]) TotalCycles += Cycles; if (MaxCycles) { if (Cycles > RemainCycles) { - ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles (%llu) reached.", MaxCycles); + ErrorCode (SIM65_ERROR_TIMEOUT, "Maximum number of cycles reached."); } RemainCycles -= Cycles; }