diff --git a/doc/sim65.sgml b/doc/sim65.sgml index 310de4667..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

@@ -126,9 +133,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: 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..6dbee974c 100644 --- a/src/sim65/error.h +++ b/src/sim65/error.h @@ -49,12 +49,17 @@ -#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; +/* flag to print cycles at program termination */ + /*****************************************************************************/ @@ -75,6 +80,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..3c7cdc157 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 reached."); + } + 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..2e52d6e7e 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); /* Error code in range 0-255. */ } 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. 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)