From ba946c39a28b4987c2d0d8bea2fc2a6bc07f9f5e Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Tue, 23 Apr 2013 17:17:48 -0500 Subject: [PATCH] Initializing vm6502 repository --- 65b2js.c | 42 + EhBASIC/basic-v6502.asm | 8820 ++++++++++++++++++++ EhBASIC/build.sh | 7 + EhBASIC/original/basic.asm | 8691 +++++++++++++++++++ EhBASIC/original/min_mon.asm | 134 + EhBASIC/original/readme.txt | 26 + Krusader/Krusader 1.3 65C02.asm | 3023 +++++++ Krusader/krusader 1.3.asm | 3038 +++++++ Krusader/krusader13.pdf | Bin 0 -> 334084 bytes urls.txt | 4 + vm/ConsoleDummy.js | 12 + vm/LICENSE.txt | 339 + vm/README.txt | 137 + vm/computer/clock.js | 57 + vm/computer/cpu6502.js | 1533 ++++ vm/computer/keyboard.js | 151 + vm/computer/prng.js | 44 + vm/computer/ram.js | 118 + vm/computer/textDisplay.js | 322 + vm/machine.html | 38 + vm/machine.js | 101 + vm/software/hello.asm | 26 + xa-2.3.5/COPYING | 340 + xa-2.3.5/ChangeLog | 297 + xa-2.3.5/Makefile | 62 + xa-2.3.5/README.1st | 116 + xa-2.3.5/TODO | 10 + xa-2.3.5/doc/README | 6 + xa-2.3.5/doc/fileformat.txt | 582 ++ xa-2.3.5/doc/xa-de.log | 89 + xa-2.3.5/doc/xa-de.txt | 394 + xa-2.3.5/doc/xa.html | 362 + xa-2.3.5/doc/xa.log | 254 + xa-2.3.5/doc/xa.txt | 358 + xa-2.3.5/doc/xaannounce | 105 + xa-2.3.5/examples/a.o65 | Bin 0 -> 2259 bytes xa-2.3.5/examples/c64def.def | 309 + xa-2.3.5/examples/pack.doc | 12 + xa-2.3.5/examples/pack_eng.a65 | 1070 +++ xa-2.3.5/examples/pack_ger.a65 | 1070 +++ xa-2.3.5/examples/peng.l | 180 + xa-2.3.5/loader/Makefile | 18 + xa-2.3.5/loader/README | 8 + xa-2.3.5/loader/ex2 | Bin 0 -> 126 bytes xa-2.3.5/loader/example2 | Bin 0 -> 90 bytes xa-2.3.5/loader/file.def | 36 + xa-2.3.5/loader/loader.a65 | 909 ++ xa-2.3.5/loader/test.a | 36 + xa-2.3.5/loader/test2.a | 28 + xa-2.3.5/loader/test3.a | 3 + xa-2.3.5/man/README | 3 + xa-2.3.5/man/file65.1 | 58 + xa-2.3.5/man/ldo65.1 | 60 + xa-2.3.5/man/printcbm.1 | 37 + xa-2.3.5/man/reloc65.1 | 68 + xa-2.3.5/man/uncpk.1 | 77 + xa-2.3.5/man/xa.1 | 837 ++ xa-2.3.5/misc/Makefile | 44 + xa-2.3.5/misc/file65.c | 312 + xa-2.3.5/misc/ldo65.c | 698 ++ xa-2.3.5/misc/mkrom.sh | 92 + xa-2.3.5/misc/printcbm.c | 109 + xa-2.3.5/misc/reloc65.c | 384 + xa-2.3.5/misc/uncpk.c | 207 + xa-2.3.5/misc/version.h | 37 + xa-2.3.5/src/Makefile | 18 + xa-2.3.5/src/version.h | 37 + xa-2.3.5/src/xa.c | 1131 +++ xa-2.3.5/src/xa.h | 49 + xa-2.3.5/src/xaa.c | 274 + xa-2.3.5/src/xaa.h | 26 + xa-2.3.5/src/xacharset.c | 125 + xa-2.3.5/src/xacharset.h | 31 + xa-2.3.5/src/xad.h | 41 + xa-2.3.5/src/xah.h | 227 + xa-2.3.5/src/xah2.h | 30 + xa-2.3.5/src/xal.c | 595 ++ xa-2.3.5/src/xal.h | 53 + xa-2.3.5/src/xam.c | 188 + xa-2.3.5/src/xam.h | 25 + xa-2.3.5/src/xao.c | 102 + xa-2.3.5/src/xao.h | 31 + xa-2.3.5/src/xap.c | 1020 +++ xa-2.3.5/src/xap.h | 41 + xa-2.3.5/src/xar.c | 284 + xa-2.3.5/src/xar.h | 48 + xa-2.3.5/src/xar2.c | 132 + xa-2.3.5/src/xat.c | 2097 +++++ xa-2.3.5/src/xat.h | 28 + xa-2.3.5/src/xau.c | 73 + xa-2.3.5/src/xau.h | 24 + xa-2.3.5/tests/README | 24 + xa-2.3.5/tests/adrm/02.asm | 33 + xa-2.3.5/tests/adrm/816.asm | 29 + xa-2.3.5/tests/adrm/a.o65 | Bin 0 -> 50 bytes xa-2.3.5/tests/adrm/bip.inc | 28 + xa-2.3.5/tests/adrm/c02.asm | 33 + xa-2.3.5/tests/adrm/zab.asm | 4 + xa-2.3.5/tests/adrm/zpa.asm | 4 + xa-2.3.5/tests/binclude/README.1st | 117 + xa-2.3.5/tests/binclude/test.asm | 19 + xa-2.3.5/tests/binclude/test2.asm | 18 + xa-2.3.5/tests/chardelimiter/a.o65 | 1 + xa-2.3.5/tests/chardelimiter/delimtest.a65 | 16 + xa-2.3.5/tests/charset/README.1st | 116 + xa-2.3.5/tests/charset/a.o65 | Bin 0 -> 87 bytes xa-2.3.5/tests/charset/test.asm | 16 + xa-2.3.5/tests/charset/test2.s | 1 + xa-2.3.5/tests/chppch/a.o65 | 1 + xa-2.3.5/tests/chppch/qwerty.h | 4 + xa-2.3.5/tests/chppch/test.c | 3 + xa-2.3.5/tests/chppch/test.out | 10 + xa-2.3.5/tests/comcom/a.o65 | Bin 0 -> 9 bytes xa-2.3.5/tests/comcom/comcom.asm | 18 + xa-2.3.5/tests/comcom/scomcom.asm | 5 + xa-2.3.5/tests/cpp/Makefile | 3 + xa-2.3.5/tests/cpp/over.asm | 32 + xa-2.3.5/tests/cpp/over.c | 19 + xa-2.3.5/tests/cpp/over.h | 10 + xa-2.3.5/tests/fordef/a.o65 | Bin 0 -> 11 bytes xa-2.3.5/tests/fordef/test.asm | 20 + xa-2.3.5/tests/fordef/test2.asm | 5 + xa-2.3.5/tests/fordef/test3.asm | 21 + xa-2.3.5/tests/incerr/test.6502 | 1 + xa-2.3.5/tests/incerr/test.s | 1 + xa-2.3.5/tests/ldoreloc/1.s | 1 + xa-2.3.5/tests/ldoreloc/2.s | 1 + xa-2.3.5/tests/ldoreloc/Makefile | 23 + xa-2.3.5/tests/ldoreloc/t.ok | 1 + xa-2.3.5/tests/ldoreloc/xatestanalysis.txt | 109 + xa-2.3.5/tests/nonl/a.o65 | 0 xa-2.3.5/tests/nonl/test.asm | 3 + xa-2.3.5/tests/nonl/test2.asm | 1 + xa-2.3.5/tests/openpp/test.asm | 27 + xa-2.3.5/tests/openpp/test.inc | 4 + xa-2.3.5/tests/recmac/a.o65 | 1 + xa-2.3.5/tests/recmac/cpu.inc | 5 + xa-2.3.5/tests/recmac/test.asm | 31 + xa-2.3.5/tests/recmac/testi.asm | 13 + xa-2.3.5/tests/relocundef/Makefile | 25 + xa-2.3.5/tests/relocundef/test1.a65 | 9 + xa-2.3.5/tests/relocundef/test2.a65 | 11 + xa-2.3.5/vstudio/00readme.txt | 8 + xa-2.3.5/vstudio/file65.vcproj | 197 + xa-2.3.5/vstudio/ldo65.vcproj | 197 + xa-2.3.5/vstudio/printcbm.vcproj | 197 + xa-2.3.5/vstudio/uncpk.vcproj | 197 + xa-2.3.5/vstudio/vstudio.sln | 44 + xa-2.3.5/vstudio/xa.vcproj | 289 + 149 files changed, 45076 insertions(+) create mode 100644 65b2js.c create mode 100644 EhBASIC/basic-v6502.asm create mode 100755 EhBASIC/build.sh create mode 100644 EhBASIC/original/basic.asm create mode 100644 EhBASIC/original/min_mon.asm create mode 100644 EhBASIC/original/readme.txt create mode 100644 Krusader/Krusader 1.3 65C02.asm create mode 100644 Krusader/krusader 1.3.asm create mode 100644 Krusader/krusader13.pdf create mode 100644 urls.txt create mode 100644 vm/ConsoleDummy.js create mode 100644 vm/LICENSE.txt create mode 100644 vm/README.txt create mode 100644 vm/computer/clock.js create mode 100644 vm/computer/cpu6502.js create mode 100644 vm/computer/keyboard.js create mode 100644 vm/computer/prng.js create mode 100644 vm/computer/ram.js create mode 100644 vm/computer/textDisplay.js create mode 100644 vm/machine.html create mode 100644 vm/machine.js create mode 100644 vm/software/hello.asm create mode 100644 xa-2.3.5/COPYING create mode 100644 xa-2.3.5/ChangeLog create mode 100644 xa-2.3.5/Makefile create mode 100644 xa-2.3.5/README.1st create mode 100644 xa-2.3.5/TODO create mode 100644 xa-2.3.5/doc/README create mode 100644 xa-2.3.5/doc/fileformat.txt create mode 100644 xa-2.3.5/doc/xa-de.log create mode 100644 xa-2.3.5/doc/xa-de.txt create mode 100644 xa-2.3.5/doc/xa.html create mode 100644 xa-2.3.5/doc/xa.log create mode 100644 xa-2.3.5/doc/xa.txt create mode 100644 xa-2.3.5/doc/xaannounce create mode 100644 xa-2.3.5/examples/a.o65 create mode 100644 xa-2.3.5/examples/c64def.def create mode 100644 xa-2.3.5/examples/pack.doc create mode 100644 xa-2.3.5/examples/pack_eng.a65 create mode 100644 xa-2.3.5/examples/pack_ger.a65 create mode 100644 xa-2.3.5/examples/peng.l create mode 100644 xa-2.3.5/loader/Makefile create mode 100644 xa-2.3.5/loader/README create mode 100644 xa-2.3.5/loader/ex2 create mode 100644 xa-2.3.5/loader/example2 create mode 100644 xa-2.3.5/loader/file.def create mode 100644 xa-2.3.5/loader/loader.a65 create mode 100644 xa-2.3.5/loader/test.a create mode 100644 xa-2.3.5/loader/test2.a create mode 100644 xa-2.3.5/loader/test3.a create mode 100644 xa-2.3.5/man/README create mode 100644 xa-2.3.5/man/file65.1 create mode 100644 xa-2.3.5/man/ldo65.1 create mode 100644 xa-2.3.5/man/printcbm.1 create mode 100644 xa-2.3.5/man/reloc65.1 create mode 100644 xa-2.3.5/man/uncpk.1 create mode 100644 xa-2.3.5/man/xa.1 create mode 100644 xa-2.3.5/misc/Makefile create mode 100644 xa-2.3.5/misc/file65.c create mode 100644 xa-2.3.5/misc/ldo65.c create mode 100755 xa-2.3.5/misc/mkrom.sh create mode 100644 xa-2.3.5/misc/printcbm.c create mode 100644 xa-2.3.5/misc/reloc65.c create mode 100644 xa-2.3.5/misc/uncpk.c create mode 100644 xa-2.3.5/misc/version.h create mode 100644 xa-2.3.5/src/Makefile create mode 100644 xa-2.3.5/src/version.h create mode 100644 xa-2.3.5/src/xa.c create mode 100644 xa-2.3.5/src/xa.h create mode 100644 xa-2.3.5/src/xaa.c create mode 100644 xa-2.3.5/src/xaa.h create mode 100644 xa-2.3.5/src/xacharset.c create mode 100644 xa-2.3.5/src/xacharset.h create mode 100644 xa-2.3.5/src/xad.h create mode 100644 xa-2.3.5/src/xah.h create mode 100644 xa-2.3.5/src/xah2.h create mode 100644 xa-2.3.5/src/xal.c create mode 100644 xa-2.3.5/src/xal.h create mode 100644 xa-2.3.5/src/xam.c create mode 100644 xa-2.3.5/src/xam.h create mode 100644 xa-2.3.5/src/xao.c create mode 100644 xa-2.3.5/src/xao.h create mode 100644 xa-2.3.5/src/xap.c create mode 100644 xa-2.3.5/src/xap.h create mode 100644 xa-2.3.5/src/xar.c create mode 100644 xa-2.3.5/src/xar.h create mode 100644 xa-2.3.5/src/xar2.c create mode 100644 xa-2.3.5/src/xat.c create mode 100644 xa-2.3.5/src/xat.h create mode 100644 xa-2.3.5/src/xau.c create mode 100644 xa-2.3.5/src/xau.h create mode 100644 xa-2.3.5/tests/README create mode 100644 xa-2.3.5/tests/adrm/02.asm create mode 100644 xa-2.3.5/tests/adrm/816.asm create mode 100644 xa-2.3.5/tests/adrm/a.o65 create mode 100644 xa-2.3.5/tests/adrm/bip.inc create mode 100644 xa-2.3.5/tests/adrm/c02.asm create mode 100644 xa-2.3.5/tests/adrm/zab.asm create mode 100644 xa-2.3.5/tests/adrm/zpa.asm create mode 100644 xa-2.3.5/tests/binclude/README.1st create mode 100644 xa-2.3.5/tests/binclude/test.asm create mode 100644 xa-2.3.5/tests/binclude/test2.asm create mode 100644 xa-2.3.5/tests/chardelimiter/a.o65 create mode 100644 xa-2.3.5/tests/chardelimiter/delimtest.a65 create mode 100644 xa-2.3.5/tests/charset/README.1st create mode 100644 xa-2.3.5/tests/charset/a.o65 create mode 100644 xa-2.3.5/tests/charset/test.asm create mode 100644 xa-2.3.5/tests/charset/test2.s create mode 100644 xa-2.3.5/tests/chppch/a.o65 create mode 100644 xa-2.3.5/tests/chppch/qwerty.h create mode 100644 xa-2.3.5/tests/chppch/test.c create mode 100644 xa-2.3.5/tests/chppch/test.out create mode 100644 xa-2.3.5/tests/comcom/a.o65 create mode 100644 xa-2.3.5/tests/comcom/comcom.asm create mode 100644 xa-2.3.5/tests/comcom/scomcom.asm create mode 100644 xa-2.3.5/tests/cpp/Makefile create mode 100644 xa-2.3.5/tests/cpp/over.asm create mode 100644 xa-2.3.5/tests/cpp/over.c create mode 100644 xa-2.3.5/tests/cpp/over.h create mode 100644 xa-2.3.5/tests/fordef/a.o65 create mode 100644 xa-2.3.5/tests/fordef/test.asm create mode 100644 xa-2.3.5/tests/fordef/test2.asm create mode 100644 xa-2.3.5/tests/fordef/test3.asm create mode 100644 xa-2.3.5/tests/incerr/test.6502 create mode 100644 xa-2.3.5/tests/incerr/test.s create mode 100644 xa-2.3.5/tests/ldoreloc/1.s create mode 100644 xa-2.3.5/tests/ldoreloc/2.s create mode 100644 xa-2.3.5/tests/ldoreloc/Makefile create mode 100644 xa-2.3.5/tests/ldoreloc/t.ok create mode 100644 xa-2.3.5/tests/ldoreloc/xatestanalysis.txt create mode 100644 xa-2.3.5/tests/nonl/a.o65 create mode 100644 xa-2.3.5/tests/nonl/test.asm create mode 100644 xa-2.3.5/tests/nonl/test2.asm create mode 100644 xa-2.3.5/tests/openpp/test.asm create mode 100644 xa-2.3.5/tests/openpp/test.inc create mode 100644 xa-2.3.5/tests/recmac/a.o65 create mode 100644 xa-2.3.5/tests/recmac/cpu.inc create mode 100644 xa-2.3.5/tests/recmac/test.asm create mode 100644 xa-2.3.5/tests/recmac/testi.asm create mode 100644 xa-2.3.5/tests/relocundef/Makefile create mode 100644 xa-2.3.5/tests/relocundef/test1.a65 create mode 100644 xa-2.3.5/tests/relocundef/test2.a65 create mode 100644 xa-2.3.5/vstudio/00readme.txt create mode 100644 xa-2.3.5/vstudio/file65.vcproj create mode 100644 xa-2.3.5/vstudio/ldo65.vcproj create mode 100644 xa-2.3.5/vstudio/printcbm.vcproj create mode 100644 xa-2.3.5/vstudio/uncpk.vcproj create mode 100644 xa-2.3.5/vstudio/vstudio.sln create mode 100644 xa-2.3.5/vstudio/xa.vcproj diff --git a/65b2js.c b/65b2js.c new file mode 100644 index 0000000..9e62c9e --- /dev/null +++ b/65b2js.c @@ -0,0 +1,42 @@ +/* + * 65b2js.c + * + * Created on: May 19, 2011 + * Author: Scott Duensing + */ + +#include + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("usage: %s filename\n", argv[0]); + return 1; + } else { + FILE *file = fopen(argv[1], "r"); + if (file == 0) { + printf("Could not open file\n"); + return 2; + } else { + int c; + int count = 0; + int first = 1; + printf("var PROGRAM = [\n"); + while ((c = fgetc(file)) != EOF) { + if (first == 1) + first = 0; + else + printf(", "); + if (++count > 15) { + printf("\n"); + count = 0; + } + printf("0x%02x", c); + } + if (count != 0) + printf("\n"); + printf("];\n"); + fclose(file); + } + } + return 0; +} diff --git a/EhBASIC/basic-v6502.asm b/EhBASIC/basic-v6502.asm new file mode 100644 index 0000000..9f2c9b4 --- /dev/null +++ b/EhBASIC/basic-v6502.asm @@ -0,0 +1,8820 @@ +; Enhanced BASIC to assemble under 6502 simulator for use with Fugazi, $ver 2.22 + +; $E7E1 $E7CF $E7C6 $E7D3 $E7D1 $E7D5 $E7CF $E81E $E825 + +; 2.00 new revision numbers start here +; 2.01 fixed LCASE$() and UCASE$() +; 2.02 new get value routine done +; 2.03 changed RND() to galoise method +; 2.04 fixed SPC() +; 2.05 new get value routine fixed +; 2.06 changed USR() code +; 2.07 fixed STR$() +; 2.08 changed INPUT and READ to remove need for $00 start to input buffer +; 2.09 fixed RND() +; 2.10 integrated missed changes from an earlier version +; 2.20 added ELSE to IF .. THEN and fixed IF .. GOTO to cause error +; 2.21 fixed IF .. THEN RETURN to not cause error +; 2.22 fixed RND() breaking the get byte routine + + *= $0000 ; needed so following PC padding works + +; zero page use .. + +LAB_WARM = $00 ; BASIC warm start entry point +Wrmjpl = LAB_WARM+1; BASIC warm start vector jump low byte +Wrmjph = LAB_WARM+2; BASIC warm start vector jump high byte + +Usrjmp = $0A ; USR function JMP address +Usrjpl = Usrjmp+1 ; USR function JMP vector low byte +Usrjph = Usrjmp+2 ; USR function JMP vector high byte +Nullct = $0D ; nulls output after each line +TPos = $0E ; BASIC terminal position byte +TWidth = $0F ; BASIC terminal width byte +Iclim = $10 ; input column limit +Itempl = $11 ; temporary integer low byte +Itemph = Itempl+1 ; temporary integer high byte + +nums_1 = Itempl ; number to bin/hex string convert MSB +nums_2 = nums_1+1 ; number to bin/hex string convert +nums_3 = nums_1+2 ; number to bin/hex string convert LSB + +Srchc = $5B ; search character +Temp3 = Srchc ; temp byte used in number routines +Scnquo = $5C ; scan-between-quotes flag +Asrch = Scnquo ; alt search character + +XOAw_l = Srchc ; eXclusive OR, OR and AND word low byte +XOAw_h = Scnquo ; eXclusive OR, OR and AND word high byte + +Ibptr = $5D ; input buffer pointer +Dimcnt = Ibptr ; # of dimensions +Tindx = Ibptr ; token index + +Defdim = $5E ; default DIM flag +Dtypef = $5F ; data type flag, $FF=string, $00=numeric +Oquote = $60 ; open quote flag (b7) (Flag: DATA scan; LIST quote; memory) +Gclctd = $60 ; garbage collected flag +Sufnxf = $61 ; subscript/FNX flag, 1xxx xxx = FN(0xxx xxx) +Imode = $62 ; input mode flag, $00=INPUT, $80=READ + +Cflag = $63 ; comparison evaluation flag + +TabSiz = $64 ; TAB step size (was input flag) + +next_s = $65 ; next descriptor stack address + + ; these two bytes form a word pointer to the item + ; currently on top of the descriptor stack +last_sl = $66 ; last descriptor stack address low byte +last_sh = $67 ; last descriptor stack address high byte (always $00) + +des_sk = $68 ; descriptor stack start address (temp strings) + +; = $70 ; End of descriptor stack + +ut1_pl = $71 ; utility pointer 1 low byte +ut1_ph = ut1_pl+1 ; utility pointer 1 high byte +ut2_pl = $73 ; utility pointer 2 low byte +ut2_ph = ut2_pl+1 ; utility pointer 2 high byte + +Temp_2 = ut1_pl ; temp byte for block move + +FACt_1 = $75 ; FAC temp mantissa1 +FACt_2 = FACt_1+1 ; FAC temp mantissa2 +FACt_3 = FACt_2+1 ; FAC temp mantissa3 + +dims_l = FACt_2 ; array dimension size low byte +dims_h = FACt_3 ; array dimension size high byte + +TempB = $78 ; temp page 0 byte + +Smeml = $79 ; start of mem low byte (Start-of-Basic) +Smemh = Smeml+1 ; start of mem high byte (Start-of-Basic) +Svarl = $7B ; start of vars low byte (Start-of-Variables) +Svarh = Svarl+1 ; start of vars high byte (Start-of-Variables) +Sarryl = $7D ; var mem end low byte (Start-of-Arrays) +Sarryh = Sarryl+1 ; var mem end high byte (Start-of-Arrays) +Earryl = $7F ; array mem end low byte (End-of-Arrays) +Earryh = Earryl+1 ; array mem end high byte (End-of-Arrays) +Sstorl = $81 ; string storage low byte (String storage (moving down)) +Sstorh = Sstorl+1 ; string storage high byte (String storage (moving down)) +Sutill = $83 ; string utility ptr low byte +Sutilh = Sutill+1 ; string utility ptr high byte +Ememl = $85 ; end of mem low byte (Limit-of-memory) +Ememh = Ememl+1 ; end of mem high byte (Limit-of-memory) +Clinel = $87 ; current line low byte (Basic line number) +Clineh = Clinel+1 ; current line high byte (Basic line number) +Blinel = $89 ; break line low byte (Previous Basic line number) +Blineh = Blinel+1 ; break line high byte (Previous Basic line number) + +Cpntrl = $8B ; continue pointer low byte +Cpntrh = Cpntrl+1 ; continue pointer high byte + +Dlinel = $8D ; current DATA line low byte +Dlineh = Dlinel+1 ; current DATA line high byte + +Dptrl = $8F ; DATA pointer low byte +Dptrh = Dptrl+1 ; DATA pointer high byte + +Rdptrl = $91 ; read pointer low byte +Rdptrh = Rdptrl+1 ; read pointer high byte + +Varnm1 = $93 ; current var name 1st byte +Varnm2 = Varnm1+1 ; current var name 2nd byte + +Cvaral = $95 ; current var address low byte +Cvarah = Cvaral+1 ; current var address high byte + +Frnxtl = $97 ; var pointer for FOR/NEXT low byte +Frnxth = Frnxtl+1 ; var pointer for FOR/NEXT high byte + +Tidx1 = Frnxtl ; temp line index + +Lvarpl = Frnxtl ; let var pointer low byte +Lvarph = Frnxth ; let var pointer high byte + +prstk = $99 ; precedence stacked flag + +comp_f = $9B ; compare function flag, bits 0,1 and 2 used + ; bit 2 set if > + ; bit 1 set if = + ; bit 0 set if < + +func_l = $9C ; function pointer low byte +func_h = func_l+1 ; function pointer high byte + +garb_l = func_l ; garbage collection working pointer low byte +garb_h = func_h ; garbage collection working pointer high byte + +des_2l = $9E ; string descriptor_2 pointer low byte +des_2h = des_2l+1 ; string descriptor_2 pointer high byte + +g_step = $A0 ; garbage collect step size + +Fnxjmp = $A1 ; jump vector for functions +Fnxjpl = Fnxjmp+1 ; functions jump vector low byte +Fnxjph = Fnxjmp+2 ; functions jump vector high byte + +g_indx = Fnxjpl ; garbage collect temp index + +FAC2_r = $A3 ; FAC2 rounding byte + +Adatal = $A4 ; array data pointer low byte +Adatah = Adatal+1 ; array data pointer high byte + +Nbendl = Adatal ; new block end pointer low byte +Nbendh = Adatah ; new block end pointer high byte + +Obendl = $A6 ; old block end pointer low byte +Obendh = Obendl+1 ; old block end pointer high byte + +numexp = $A8 ; string to float number exponent count +expcnt = $A9 ; string to float exponent count + +numbit = numexp ; bit count for array element calculations + +numdpf = $AA ; string to float decimal point flag +expneg = $AB ; string to float eval exponent -ve flag + +Astrtl = numdpf ; array start pointer low byte +Astrth = expneg ; array start pointer high byte + +Histrl = numdpf ; highest string low byte +Histrh = expneg ; highest string high byte + +Baslnl = numdpf ; BASIC search line pointer low byte +Baslnh = expneg ; BASIC search line pointer high byte + +Fvar_l = numdpf ; find/found variable pointer low byte +Fvar_h = expneg ; find/found variable pointer high byte + +Ostrtl = numdpf ; old block start pointer low byte +Ostrth = expneg ; old block start pointer high byte + +Vrschl = numdpf ; variable search pointer low byte +Vrschh = expneg ; variable search pointer high byte + +FAC1_e = $AC ; FAC1 exponent +FAC1_1 = FAC1_e+1 ; FAC1 mantissa1 +FAC1_2 = FAC1_e+2 ; FAC1 mantissa2 +FAC1_3 = FAC1_e+3 ; FAC1 mantissa3 +FAC1_s = FAC1_e+4 ; FAC1 sign (b7) + +str_ln = FAC1_e ; string length +str_pl = FAC1_1 ; string pointer low byte +str_ph = FAC1_2 ; string pointer high byte + +des_pl = FAC1_2 ; string descriptor pointer low byte +des_ph = FAC1_3 ; string descriptor pointer high byte + +mids_l = FAC1_3 ; MID$ string temp length byte + +negnum = $B1 ; string to float eval -ve flag +numcon = $B1 ; series evaluation constant count + +FAC1_o = $B2 ; FAC1 overflow byte + +FAC2_e = $B3 ; FAC2 exponent +FAC2_1 = FAC2_e+1 ; FAC2 mantissa1 +FAC2_2 = FAC2_e+2 ; FAC2 mantissa2 +FAC2_3 = FAC2_e+3 ; FAC2 mantissa3 +FAC2_s = FAC2_e+4 ; FAC2 sign (b7) + +FAC_sc = $B8 ; FAC sign comparison, Acc#1 vs #2 +FAC1_r = $B9 ; FAC1 rounding byte + +ssptr_l = FAC_sc ; string start pointer low byte +ssptr_h = FAC1_r ; string start pointer high byte + +sdescr = FAC_sc ; string descriptor pointer + +csidx = $BA ; line crunch save index +Asptl = csidx ; array size/pointer low byte +Aspth = $BB ; array size/pointer high byte + +Btmpl = Asptl ; BASIC pointer temp low byte +Btmph = Aspth ; BASIC pointer temp low byte + +Cptrl = Asptl ; BASIC pointer temp low byte +Cptrh = Aspth ; BASIC pointer temp low byte + +Sendl = Asptl ; BASIC pointer temp low byte +Sendh = Aspth ; BASIC pointer temp low byte + +LAB_IGBY = $BC ; get next BASIC byte subroutine + +LAB_GBYT = $C2 ; get current BASIC byte subroutine +Bpntrl = $C3 ; BASIC execute (get byte) pointer low byte +Bpntrh = Bpntrl+1 ; BASIC execute (get byte) pointer high byte + +; = $D7 ; end of get BASIC char subroutine + +Rbyte4 = $D8 ; extra PRNG byte +Rbyte1 = Rbyte4+1 ; most significant PRNG byte +Rbyte2 = Rbyte4+2 ; middle PRNG byte +Rbyte3 = Rbyte4+3 ; least significant PRNG byte + +NmiBase = $DC ; NMI handler enabled/setup/triggered flags + ; bit function + ; === ======== + ; 7 interrupt enabled + ; 6 interrupt setup + ; 5 interrupt happened +; = $DD ; NMI handler addr low byte +; = $DE ; NMI handler addr high byte +IrqBase = $DF ; IRQ handler enabled/setup/triggered flags +; = $E0 ; IRQ handler addr low byte +; = $E1 ; IRQ handler addr high byte + +; = $DE ; unused +; = $DF ; unused +; = $E0 ; unused +; = $E1 ; unused +; = $E2 ; unused +; = $E3 ; unused +; = $E4 ; unused +; = $E5 ; unused +; = $E6 ; unused +; = $E7 ; unused +; = $E8 ; unused +; = $E9 ; unused +; = $EA ; unused +; = $EB ; unused +; = $EC ; unused +; = $ED ; unused +; = $EE ; unused + +Decss = $EF ; number to decimal string start +Decssp1 = Decss+1 ; number to decimal string start + +; = $FF ; decimal string end + +; token values needed for BASIC + +; primary command tokens (can start a statement) + +TK_END = $80 ; END token +TK_FOR = TK_END+1 ; FOR token +TK_NEXT = TK_FOR+1 ; NEXT token +TK_DATA = TK_NEXT+1 ; DATA token +TK_INPUT = TK_DATA+1 ; INPUT token +TK_DIM = TK_INPUT+1 ; DIM token +TK_READ = TK_DIM+1 ; READ token +TK_LET = TK_READ+1 ; LET token +TK_DEC = TK_LET+1 ; DEC token +TK_GOTO = TK_DEC+1 ; GOTO token +TK_RUN = TK_GOTO+1 ; RUN token +TK_IF = TK_RUN+1 ; IF token +TK_RESTORE = TK_IF+1 ; RESTORE token +TK_GOSUB = TK_RESTORE+1 ; GOSUB token +TK_RETIRQ = TK_GOSUB+1 ; RETIRQ token +TK_RETNMI = TK_RETIRQ+1 ; RETNMI token +TK_RETURN = TK_RETNMI+1 ; RETURN token +TK_REM = TK_RETURN+1 ; REM token +TK_STOP = TK_REM+1 ; STOP token +TK_ON = TK_STOP+1 ; ON token +TK_NULL = TK_ON+1 ; NULL token +TK_INC = TK_NULL+1 ; INC token +TK_WAIT = TK_INC+1 ; WAIT token +TK_LOAD = TK_WAIT+1 ; LOAD token +TK_SAVE = TK_LOAD+1 ; SAVE token +TK_DEF = TK_SAVE+1 ; DEF token +TK_POKE = TK_DEF+1 ; POKE token +TK_DOKE = TK_POKE+1 ; DOKE token +TK_CALL = TK_DOKE+1 ; CALL token +TK_DO = TK_CALL+1 ; DO token +TK_LOOP = TK_DO+1 ; LOOP token +TK_PRINT = TK_LOOP+1 ; PRINT token +TK_CONT = TK_PRINT+1 ; CONT token +TK_LIST = TK_CONT+1 ; LIST token +TK_CLEAR = TK_LIST+1 ; CLEAR token +TK_NEW = TK_CLEAR+1 ; NEW token +TK_WIDTH = TK_NEW+1 ; WIDTH token +TK_GET = TK_WIDTH+1 ; GET token +TK_SWAP = TK_GET+1 ; SWAP token +TK_BITSET = TK_SWAP+1 ; BITSET token +TK_BITCLR = TK_BITSET+1 ; BITCLR token +TK_IRQ = TK_BITCLR+1 ; IRQ token +TK_NMI = TK_IRQ+1 ; NMI token + +; secondary command tokens, can't start a statement + +TK_TAB = TK_NMI+1 ; TAB token +TK_ELSE = TK_TAB+1 ; ELSE token +TK_TO = TK_ELSE+1 ; TO token +TK_FN = TK_TO+1 ; FN token +TK_SPC = TK_FN+1 ; SPC token +TK_THEN = TK_SPC+1 ; THEN token +TK_NOT = TK_THEN+1 ; NOT token +TK_STEP = TK_NOT+1 ; STEP token +TK_UNTIL = TK_STEP+1 ; UNTIL token +TK_WHILE = TK_UNTIL+1 ; WHILE token +TK_OFF = TK_WHILE+1 ; OFF token + +; opperator tokens + +TK_PLUS = TK_OFF+1 ; + token +TK_MINUS = TK_PLUS+1 ; - token +TK_MUL = TK_MINUS+1 ; * token +TK_DIV = TK_MUL+1 ; / token +TK_POWER = TK_DIV+1 ; ^ token +TK_AND = TK_POWER+1 ; AND token +TK_EOR = TK_AND+1 ; EOR token +TK_OR = TK_EOR+1 ; OR token +TK_RSHIFT = TK_OR+1 ; RSHIFT token +TK_LSHIFT = TK_RSHIFT+1 ; LSHIFT token +TK_GT = TK_LSHIFT+1 ; > token +TK_EQUAL = TK_GT+1 ; = token +TK_LT = TK_EQUAL+1 ; < token + +; functions tokens + +TK_SGN = TK_LT+1 ; SGN token +TK_INT = TK_SGN+1 ; INT token +TK_ABS = TK_INT+1 ; ABS token +TK_USR = TK_ABS+1 ; USR token +TK_FRE = TK_USR+1 ; FRE token +TK_POS = TK_FRE+1 ; POS token +TK_SQR = TK_POS+1 ; SQR token +TK_RND = TK_SQR+1 ; RND token +TK_LOG = TK_RND+1 ; LOG token +TK_EXP = TK_LOG+1 ; EXP token +TK_COS = TK_EXP+1 ; COS token +TK_SIN = TK_COS+1 ; SIN token +TK_TAN = TK_SIN+1 ; TAN token +TK_ATN = TK_TAN+1 ; ATN token +TK_PEEK = TK_ATN+1 ; PEEK token +TK_DEEK = TK_PEEK+1 ; DEEK token +TK_SADD = TK_DEEK+1 ; SADD token +TK_LEN = TK_SADD+1 ; LEN token +TK_STRS = TK_LEN+1 ; STR$ token +TK_VAL = TK_STRS+1 ; VAL token +TK_ASC = TK_VAL+1 ; ASC token +TK_UCASES = TK_ASC+1 ; UCASE$ token +TK_LCASES = TK_UCASES+1 ; LCASE$ token +TK_CHRS = TK_LCASES+1 ; CHR$ token +TK_HEXS = TK_CHRS+1 ; HEX$ token +TK_BINS = TK_HEXS+1 ; BIN$ token +TK_BITTST = TK_BINS+1 ; BITTST token +TK_MAX = TK_BITTST+1 ; MAX token +TK_MIN = TK_MAX+1 ; MIN token +TK_PI = TK_MIN+1 ; PI token +TK_TWOPI = TK_PI+1 ; TWOPI token +TK_VPTR = TK_TWOPI+1 ; VARPTR token +TK_LEFTS = TK_VPTR+1 ; LEFT$ token +TK_RIGHTS = TK_LEFTS+1 ; RIGHT$ token +TK_MIDS = TK_RIGHTS+1 ; MID$ token + +; offsets from a base of X or Y + +PLUS_0 = $00 ; X or Y plus 0 +PLUS_1 = $01 ; X or Y plus 1 +PLUS_2 = $02 ; X or Y plus 2 +PLUS_3 = $03 ; X or Y plus 3 + +LAB_STAK = $0100 ; stack bottom, no offset + +LAB_SKFE = LAB_STAK+$FE + ; flushed stack address +LAB_SKFF = LAB_STAK+$FF + ; flushed stack address + +ccflag = $1200 ; BASIC CTRL-C flag, 00 = enabled, 01 = dis +ccbyte = ccflag+1 ; BASIC CTRL-C byte +ccnull = ccbyte+1 ; BASIC CTRL-C byte timeout + +VEC_CC = ccnull+1 ; ctrl c check vector + +VEC_IN = VEC_CC+2 ; input vector +VEC_OUT = VEC_IN+2 ; output vector +VEC_LD = VEC_OUT+2 ; load vector +VEC_SV = VEC_LD+2 ; save vector + +; Ibuffs can now be anywhere in RAM, ensure that the max length is < $80 + +Ibuffs = IRQ_vec+$14 + ; start of input buffer after IRQ/NMI code +Ibuffe = Ibuffs+$47; end of input buffer + +Ram_base = $1300 ; start of user RAM (set as needed, should be page aligned) +Ram_top = $B700 ; end of user RAM+1 (set as needed, should be page aligned) + +; This start can be changed to suit your system + + .dsb ($B700-*),0 ; pad to new PC with zeros + *= $B700 + +; BASIC cold start entry point + +; new page 2 initialisation, copy block to ccflag on + +LAB_COLD + LDY #PG2_TABE-PG2_TABS-1 + ; byte count-1 +LAB_2D13 + LDA PG2_TABS,Y ; get byte + STA ccflag,Y ; store in page 2 + DEY ; decrement count + BPL LAB_2D13 ; loop if not done + + LDX #$FF ; set byte + STX Clineh ; set current line high byte (set immediate mode) + TXS ; reset stack pointer + + LDA #$4C ; code for JMP + STA Fnxjmp ; save for jump vector for functions + +; copy block from LAB_2CEE to $00BC - $00D3 + + LDX #StrTab-LAB_2CEE ; set byte count +LAB_2D4E + LDA LAB_2CEE-1,X ; get byte from table + STA LAB_IGBY-1,X ; save byte in page zero + DEX ; decrement count + BNE LAB_2D4E ; loop if not all done + +; copy block from StrTab to $0000 - $0012 + +LAB_GMEM + LDX #EndTab-StrTab-1 ; set byte count-1 +TabLoop + LDA StrTab,X ; get byte from table + STA PLUS_0,X ; save byte in page zero + DEX ; decrement count + BPL TabLoop ; loop if not all done + +; set-up start values + + LDA #$00 ; clear A + STA NmiBase ; clear NMI handler enabled flag + STA IrqBase ; clear IRQ handler enabled flag + STA FAC1_o ; clear FAC1 overflow byte + STA last_sh ; clear descriptor stack top item pointer high byte + + LDA #$0E ; set default tab size + STA TabSiz ; save it + LDA #$03 ; set garbage collect step size for descriptor stack + STA g_step ; save it + LDX #des_sk ; descriptor stack start + STX next_s ; set descriptor stack pointer + JSR LAB_CRLF ; print CR/LF + LDA #LAB_MSZM ; point to memory size message (high addr) + JSR LAB_18C3 ; print null terminated string from memory + JSR LAB_INLN ; print "? " and get BASIC input + STX Bpntrl ; set BASIC execute pointer low byte + STY Bpntrh ; set BASIC execute pointer high byte + JSR LAB_GBYT ; get last byte back + + BNE LAB_2DAA ; branch if not null (user typed something) + + LDY #$00 ; else clear Y + ; character was null so get memory size the hard way + ; we get here with Y=0 and Itempl/h = Ram_base +LAB_2D93 + INC Itempl ; increment temporary integer low byte + BNE LAB_2D99 ; branch if no overflow + + INC Itemph ; increment temporary integer high byte + LDA Itemph ; get high byte + CMP #>Ram_top ; compare with top of RAM+1 + BEQ LAB_2DB6 ; branch if match (end of user RAM) + +LAB_2D99 + LDA #$55 ; set test byte + STA (Itempl),Y ; save via temporary integer + CMP (Itempl),Y ; compare via temporary integer + BNE LAB_2DB6 ; branch if fail + + ASL ; shift test byte left (now $AA) + STA (Itempl),Y ; save via temporary integer + CMP (Itempl),Y ; compare via temporary integer + BEQ LAB_2D93 ; if ok go do next byte + + BNE LAB_2DB6 ; branch if fail + +LAB_2DAA + JSR LAB_2887 ; get FAC1 from string + LDA FAC1_e ; get FAC1 exponent + CMP #$98 ; compare with exponent = 2^24 + BCS LAB_GMEM ; if too large go try again + + JSR LAB_F2FU ; save integer part of FAC1 in temporary integer + ; (no range check) + +LAB_2DB6 + LDA Itempl ; get temporary integer low byte + LDY Itemph ; get temporary integer high byte + CPY #Ram_top ; compare with top of RAM high byte +; BCC MEM_OK ; branch if < RAM top + +; BNE LAB_GMEM ; if too large go try again + ; else was = so compare low bytes +; CMP #Ram_base ; set start addr high byte + STY Smeml ; save start of mem low byte + STX Smemh ; save start of mem high byte + +; this line is only needed if Ram_base is not $xx00 + +; LDY #$00 ; clear Y + TYA ; clear A + STA (Smeml),Y ; clear first byte + INC Smeml ; increment start of mem low byte + +; these two lines are only needed if Ram_base is $xxFF + +; BNE LAB_2E05 ; branch if no rollover + +; INC Smemh ; increment start of mem high byte +LAB_2E05 + JSR LAB_CRLF ; print CR/LF + JSR LAB_1463 ; do "NEW" and "CLEAR" + LDA Ememl ; get end of mem low byte + SEC ; set carry for subtract + SBC Smeml ; subtract start of mem low byte + TAX ; copy to X + LDA Ememh ; get end of mem high byte + SBC Smemh ; subtract start of mem high byte + JSR LAB_295E ; print XA as unsigned integer (bytes free) + LDA #LAB_SMSG ; point to sign-on message (high addr) + JSR LAB_18C3 ; print null terminated string from memory + LDA #LAB_1274 ; warm start vector high byte + STA Wrmjpl ; save warm start vector low byte + STY Wrmjph ; save warm start vector high byte + JMP (Wrmjpl) ; go do warm start + +; open up space in memory +; move (Ostrtl)-(Obendl) to new block ending at (Nbendl) + +; Nbendl,Nbendh - new block end address (A/Y) +; Obendl,Obendh - old block end address +; Ostrtl,Ostrth - old block start address + +; returns with .. + +; Nbendl,Nbendh - new block start address (high byte - $100) +; Obendl,Obendh - old block start address (high byte - $100) +; Ostrtl,Ostrth - old block start address (unchanged) + +LAB_11CF + JSR LAB_121F ; check available memory, "Out of memory" error if no room + ; addr to check is in AY (low/high) + STA Earryl ; save new array mem end low byte + STY Earryh ; save new array mem end high byte + +; open up space in memory +; move (Ostrtl)-(Obendl) to new block ending at (Nbendl) +; don't set array end + +LAB_11D6 + SEC ; set carry for subtract + LDA Obendl ; get block end low byte + SBC Ostrtl ; subtract block start low byte + TAY ; copy MOD(block length/$100) byte to Y + LDA Obendh ; get block end high byte + SBC Ostrth ; subtract block start high byte + TAX ; copy block length high byte to X + INX ; +1 to allow for count=0 exit + TYA ; copy block length low byte to A + BEQ LAB_120A ; branch if length low byte=0 + + ; block is (X-1)*256+Y bytes, do the Y bytes first + + SEC ; set carry for add + 1, two's complement + EOR #$FF ; invert low byte for subtract + ADC Obendl ; add block end low byte + + STA Obendl ; save corrected old block end low byte + BCS LAB_11F3 ; branch if no underflow + + DEC Obendh ; else decrement block end high byte + SEC ; set carry for add + 1, two's complement +LAB_11F3 + TYA ; get MOD(block length/$100) byte + EOR #$FF ; invert low byte for subtract + ADC Nbendl ; add destination end low byte + STA Nbendl ; save modified new block end low byte + BCS LAB_1203 ; branch if no underflow + + DEC Nbendh ; else decrement block end high byte + BCC LAB_1203 ; branch always + +LAB_11FF + LDA (Obendl),Y ; get byte from source + STA (Nbendl),Y ; copy byte to destination +LAB_1203 + DEY ; decrement index + BNE LAB_11FF ; loop until Y=0 + + ; now do Y=0 indexed byte + LDA (Obendl),Y ; get byte from source + STA (Nbendl),Y ; save byte to destination +LAB_120A + DEC Obendh ; decrement source pointer high byte + DEC Nbendh ; decrement destination pointer high byte + DEX ; decrement block count + BNE LAB_1203 ; loop until count = $0 + + RTS + +; check room on stack for A bytes +; stack too deep? do OM error + +LAB_1212 + STA TempB ; save result in temp byte + TSX ; copy stack + CPX TempB ; compare new "limit" with stack + BCC LAB_OMER ; if stack < limit do "Out of memory" error then warm start + + RTS + +; check available memory, "Out of memory" error if no room +; addr to check is in AY (low/high) + +LAB_121F + CPY Sstorh ; compare bottom of string mem high byte + BCC LAB_124B ; if less then exit (is ok) + + BNE LAB_1229 ; skip next test if greater (tested <) + + ; high byte was =, now do low byte + CMP Sstorl ; compare with bottom of string mem low byte + BCC LAB_124B ; if less then exit (is ok) + + ; addr is > string storage ptr (oops!) +LAB_1229 + PHA ; push addr low byte + LDX #$08 ; set index to save Adatal to expneg inclusive + TYA ; copy addr high byte (to push on stack) + + ; save misc numeric work area +LAB_122D + PHA ; push byte + LDA Adatal-1,X ; get byte from Adatal to expneg ( ,$00 not pushed) + DEX ; decrement index + BPL LAB_122D ; loop until all done + + JSR LAB_GARB ; garbage collection routine + + ; restore misc numeric work area + LDX #$00 ; clear the index to restore bytes +LAB_1238 + PLA ; pop byte + STA Adatal,X ; save byte to Adatal to expneg + INX ; increment index + CPX #$08 ; compare with end + 1 + BMI LAB_1238 ; loop if more to do + + PLA ; pop addr high byte + TAY ; copy back to Y + PLA ; pop addr low byte + CPY Sstorh ; compare bottom of string mem high byte + BCC LAB_124B ; if less then exit (is ok) + + BNE LAB_OMER ; if greater do "Out of memory" error then warm start + + ; high byte was =, now do low byte + CMP Sstorl ; compare with bottom of string mem low byte + BCS LAB_OMER ; if >= do "Out of memory" error then warm start + + ; ok exit, carry clear +LAB_124B + RTS + +; do "Out of memory" error then warm start + +LAB_OMER + LDX #$0C ; error code $0C ("Out of memory" error) + +; do error #X, then warm start + +LAB_XERR + JSR LAB_CRLF ; print CR/LF + + LDA LAB_BAER,X ; get error message pointer low byte + LDY LAB_BAER+1,X ; get error message pointer high byte + JSR LAB_18C3 ; print null terminated string from memory + + JSR LAB_1491 ; flush stack and clear continue flag + LDA #LAB_EMSG ; point to " Error" high addr +LAB_1269 + JSR LAB_18C3 ; print null terminated string from memory + LDY Clineh ; get current line high byte + INY ; increment it + BEQ LAB_1274 ; go do warm start (was immediate mode) + + ; else print line number + JSR LAB_2953 ; print " in line [LINE #]" + +; BASIC warm start entry point +; wait for Basic command + +LAB_1274 + ; clear ON IRQ/NMI bytes + LDA #$00 ; clear A + STA IrqBase ; clear enabled byte + STA NmiBase ; clear enabled byte + LDA #LAB_RMSG ; point to "Ready" message high byte + + JSR LAB_18C3 ; go do print string + +; wait for Basic command (no "Ready") + +LAB_127D + JSR LAB_1357 ; call for BASIC input +LAB_1280 + STX Bpntrl ; set BASIC execute pointer low byte + STY Bpntrh ; set BASIC execute pointer high byte + JSR LAB_GBYT ; scan memory + BEQ LAB_127D ; loop while null + +; got to interpret input line now .. + + LDX #$FF ; current line to null value + STX Clineh ; set current line high byte + BCC LAB_1295 ; branch if numeric character (handle new BASIC line) + + ; no line number .. immediate mode + JSR LAB_13A6 ; crunch keywords into Basic tokens + JMP LAB_15F6 ; go scan and interpret code + +; handle new BASIC line + +LAB_1295 + JSR LAB_GFPN ; get fixed-point number into temp integer + JSR LAB_13A6 ; crunch keywords into Basic tokens + STY Ibptr ; save index pointer to end of crunched line + JSR LAB_SSLN ; search BASIC for temp integer line number + BCC LAB_12E6 ; branch if not found + + ; aroooogah! line # already exists! delete it + LDY #$01 ; set index to next line pointer high byte + LDA (Baslnl),Y ; get next line pointer high byte + STA ut1_ph ; save it + LDA Svarl ; get start of vars low byte + STA ut1_pl ; save it + LDA Baslnh ; get found line pointer high byte + STA ut2_ph ; save it + LDA Baslnl ; get found line pointer low byte + DEY ; decrement index + SBC (Baslnl),Y ; subtract next line pointer low byte + CLC ; clear carry for add + ADC Svarl ; add start of vars low byte + STA Svarl ; save new start of vars low byte + STA ut2_pl ; save destination pointer low byte + LDA Svarh ; get start of vars high byte + ADC #$FF ; -1 + carry + STA Svarh ; save start of vars high byte + SBC Baslnh ; subtract found line pointer high byte + TAX ; copy to block count + SEC ; set carry for subtract + LDA Baslnl ; get found line pointer low byte + SBC Svarl ; subtract start of vars low byte + TAY ; copy to bytes in first block count + BCS LAB_12D0 ; branch if overflow + + INX ; increment block count (correct for =0 loop exit) + DEC ut2_ph ; decrement destination high byte +LAB_12D0 + CLC ; clear carry for add + ADC ut1_pl ; add source pointer low byte + BCC LAB_12D8 ; branch if no overflow + + DEC ut1_ph ; else decrement source pointer high byte + CLC ; clear carry + + ; close up memory to delete old line +LAB_12D8 + LDA (ut1_pl),Y ; get byte from source + STA (ut2_pl),Y ; copy to destination + INY ; increment index + BNE LAB_12D8 ; while <> 0 do this block + + INC ut1_ph ; increment source pointer high byte + INC ut2_ph ; increment destination pointer high byte + DEX ; decrement block count + BNE LAB_12D8 ; loop until all done + + ; got new line in buffer and no existing same # +LAB_12E6 + LDA Ibuffs ; get byte from start of input buffer + BEQ LAB_1319 ; if null line just go flush stack/vars and exit + + ; got new line and it isn't empty line + LDA Ememl ; get end of mem low byte + LDY Ememh ; get end of mem high byte + STA Sstorl ; set bottom of string space low byte + STY Sstorh ; set bottom of string space high byte + LDA Svarl ; get start of vars low byte (end of BASIC) + STA Obendl ; save old block end low byte + LDY Svarh ; get start of vars high byte (end of BASIC) + STY Obendh ; save old block end high byte + ADC Ibptr ; add input buffer pointer (also buffer length) + BCC LAB_1301 ; branch if no overflow from add + + INY ; else increment high byte +LAB_1301 + STA Nbendl ; save new block end low byte (move to, low byte) + STY Nbendh ; save new block end high byte + JSR LAB_11CF ; open up space in memory + ; old start pointer Ostrtl,Ostrth set by the find line call + LDA Earryl ; get array mem end low byte + LDY Earryh ; get array mem end high byte + STA Svarl ; save start of vars low byte + STY Svarh ; save start of vars high byte + LDY Ibptr ; get input buffer pointer (also buffer length) + DEY ; adjust for loop type +LAB_1311 + LDA Ibuffs-4,Y ; get byte from crunched line + STA (Baslnl),Y ; save it to program memory + DEY ; decrement count + CPY #$03 ; compare with first byte-1 + BNE LAB_1311 ; continue while count <> 3 + + LDA Itemph ; get line # high byte + STA (Baslnl),Y ; save it to program memory + DEY ; decrement count + LDA Itempl ; get line # low byte + STA (Baslnl),Y ; save it to program memory + DEY ; decrement count + LDA #$FF ; set byte to allow chain rebuild. if you didn't set this + ; byte then a zero already here would stop the chain rebuild + ; as it would think it was the [EOT] marker. + STA (Baslnl),Y ; save it to program memory + +LAB_1319 + JSR LAB_1477 ; reset execution to start, clear vars and flush stack + LDX Smeml ; get start of mem low byte + LDA Smemh ; get start of mem high byte + LDY #$01 ; index to high byte of next line pointer +LAB_1325 + STX ut1_pl ; set line start pointer low byte + STA ut1_ph ; set line start pointer high byte + LDA (ut1_pl),Y ; get it + BEQ LAB_133E ; exit if end of program + +; rebuild chaining of Basic lines + + LDY #$04 ; point to first code byte of line + ; there is always 1 byte + [EOL] as null entries are deleted +LAB_1330 + INY ; next code byte + LDA (ut1_pl),Y ; get byte + BNE LAB_1330 ; loop if not [EOL] + + SEC ; set carry for add + 1 + TYA ; copy end index + ADC ut1_pl ; add to line start pointer low byte + TAX ; copy to X + LDY #$00 ; clear index, point to this line's next line pointer + STA (ut1_pl),Y ; set next line pointer low byte + TYA ; clear A + ADC ut1_ph ; add line start pointer high byte + carry + INY ; increment index to high byte + STA (ut1_pl),Y ; save next line pointer low byte + BCC LAB_1325 ; go do next line, branch always, carry clear + + +LAB_133E + JMP LAB_127D ; else we just wait for Basic command, no "Ready" + +; print "? " and get BASIC input + +LAB_INLN + JSR LAB_18E3 ; print "?" character + JSR LAB_18E0 ; print " " + BNE LAB_1357 ; call for BASIC input and return + +; receive line from keyboard + + ; $08 as delete key (BACKSPACE on standard keyboard) +LAB_134B + JSR LAB_PRNA ; go print the character + DEX ; decrement the buffer counter (delete) + .byte $2C ; make LDX into BIT abs + +; call for BASIC input (main entry point) + +LAB_1357 + LDX #$00 ; clear BASIC line buffer pointer +LAB_1359 + JSR V_INPT ; call scan input device + BCC LAB_1359 ; loop if no byte + + BEQ LAB_1359 ; loop until valid input (ignore NULLs) + + CMP #$07 ; compare with [BELL] + BEQ LAB_1378 ; branch if [BELL] + + CMP #$0D ; compare with [CR] + BEQ LAB_1384 ; do CR/LF exit if [CR] + + CPX #$00 ; compare pointer with $00 + BNE LAB_1374 ; branch if not empty + +; next two lines ignore any non print character and [SPACE] if input buffer empty + + CMP #$21 ; compare with [SP]+1 + BCC LAB_1359 ; if < ignore character + +LAB_1374 + CMP #$08 ; compare with [BACKSPACE] (delete last character) + BEQ LAB_134B ; go delete last character + +LAB_1378 + CPX #Ibuffe-Ibuffs ; compare character count with max + BCS LAB_138E ; skip store and do [BELL] if buffer full + + STA Ibuffs,X ; else store in buffer + INX ; increment pointer +LAB_137F + JSR LAB_PRNA ; go print the character + BNE LAB_1359 ; always loop for next character + +LAB_1384 + JMP LAB_1866 ; do CR/LF exit to BASIC + +; announce buffer full + +LAB_138E + LDA #$07 ; [BELL] character into A + BNE LAB_137F ; go print the [BELL] but ignore input character + ; branch always + +; crunch keywords into Basic tokens +; position independent buffer version .. +; faster, dictionary search version .... + +LAB_13A6 + LDY #$FF ; set save index (makes for easy math later) + + SEC ; set carry for subtract + LDA Bpntrl ; get basic execute pointer low byte + SBC #= go save byte then continue crunching + + CMP #'<' ; compare with "<" + BCS LAB_13CC ; if >= go crunch now + + CMP #'0' ; compare with "0" + BCS LAB_13EC ; if >= go save byte then continue crunching + + STA Scnquo ; save buffer byte as search character + CMP #$22 ; is it quote character? + BEQ LAB_1410 ; branch if so (copy quoted string) + + CMP #'*' ; compare with "*" + BCC LAB_13EC ; if < go save byte then continue crunching + + ; else crunch now +LAB_13CC + BIT Oquote ; get open quote/DATA token flag + BVS LAB_13EC ; branch if b6 of Oquote set (was DATA) + ; go save byte then continue crunching + + STX TempB ; save buffer read index + STY csidx ; copy buffer save index + LDY #TAB_1STC ; get keyword first character table high address + STY ut2_ph ; save pointer high byte + LDY #$00 ; clear table pointer + +LAB_13D0 + CMP (ut2_pl),Y ; compare with keyword first character table byte + BEQ LAB_13D1 ; go do word_table_chr if match + + BCC LAB_13EA ; if < keyword first character table byte go restore + ; Y and save to crunched + + INY ; else increment pointer + BNE LAB_13D0 ; and loop (branch always) + +; have matched first character of some keyword + +LAB_13D1 + TYA ; copy matching index + ASL ; *2 (bytes per pointer) + TAX ; copy to new index + LDA TAB_CHRT,X ; get keyword table pointer low byte + STA ut2_pl ; save pointer low byte + LDA TAB_CHRT+1,X ; get keyword table pointer high byte + STA ut2_ph ; save pointer high byte + + LDY #$FF ; clear table pointer (make -1 for start) + + LDX TempB ; restore buffer read index + +LAB_13D6 + INY ; next table byte + LDA (ut2_pl),Y ; get byte from table +LAB_13D8 + BMI LAB_13EA ; all bytes matched so go save token + + INX ; next buffer byte + CMP Ibuffs,X ; compare with byte from input buffer + BEQ LAB_13D6 ; go compare next if match + + BNE LAB_1417 ; branch if >< (not found keyword) + +LAB_13EA + LDY csidx ; restore save index + + ; save crunched to output +LAB_13EC + INX ; increment buffer index (to next input byte) + INY ; increment save index (to next output byte) + STA Ibuffs,Y ; save byte to output + CMP #$00 ; set the flags, set carry + BEQ LAB_142A ; do exit if was null [EOL] + + ; A holds token or byte here + SBC #":" ; subtract ":" (carry set by CMP #00) + BEQ LAB_13FF ; branch if it was ":" (is now $00) + + ; A now holds token-$3A + CMP #TK_DATA-$3A ; compare with DATA token - $3A + BNE LAB_1401 ; branch if not DATA + + ; token was : or DATA +LAB_13FF + STA Oquote ; save token-$3A (clear for ":", TK_DATA-$3A for DATA) +LAB_1401 + EOR #TK_REM-$3A ; effectively subtract REM token offset + BNE LAB_13AC ; If wasn't REM then go crunch rest of line + + STA Asrch ; else was REM so set search for [EOL] + + ; loop for REM, "..." etc. +LAB_1408 + LDA Ibuffs,X ; get byte from input buffer + BEQ LAB_13EC ; branch if null [EOL] + + CMP Asrch ; compare with stored character + BEQ LAB_13EC ; branch if match (end quote) + + ; entry for copy string in quotes, don't crunch +LAB_1410 + INY ; increment buffer save index + STA Ibuffs,Y ; save byte to output + INX ; increment buffer read index + BNE LAB_1408 ; loop while <> 0 (should never be 0!) + + ; not found keyword this go +LAB_1417 + LDX TempB ; compare has failed, restore buffer index (start byte!) + + ; now find the end of this word in the table +LAB_141B + LDA (ut2_pl),Y ; get table byte + PHP ; save status + INY ; increment table index + PLP ; restore byte status + BPL LAB_141B ; if not end of keyword go do next + + LDA (ut2_pl),Y ; get byte from keyword table + BNE LAB_13D8 ; go test next word if not zero byte (end of table) + + ; reached end of table with no match + LDA Ibuffs,X ; restore byte from input buffer + BPL LAB_13EA ; branch always (all bytes in buffer are $00-$7F) + ; go save byte in output and continue crunching + + ; reached [EOL] +LAB_142A + INY ; increment pointer + INY ; increment pointer (makes it next line pointer high byte) + STA Ibuffs,Y ; save [EOL] (marks [EOT] in immediate mode) + INY ; adjust for line copy + INY ; adjust for line copy + INY ; adjust for line copy + DEC Bpntrl ; allow for increment (change if buffer starts at $xxFF) + RTS + +; search Basic for temp integer line number from start of mem + +LAB_SSLN + LDA Smeml ; get start of mem low byte + LDX Smemh ; get start of mem high byte + +; search Basic for temp integer line number from AX +; returns carry set if found +; returns Baslnl/Baslnh pointer to found or next higher (not found) line + +; old 541 new 507 + +LAB_SHLN + LDY #$01 ; set index + STA Baslnl ; save low byte as current + STX Baslnh ; save high byte as current + LDA (Baslnl),Y ; get pointer high byte from addr + BEQ LAB_145F ; pointer was zero so we're done, do 'not found' exit + + LDY #$03 ; set index to line # high byte + LDA (Baslnl),Y ; get line # high byte + DEY ; decrement index (point to low byte) + CMP Itemph ; compare with temporary integer high byte + BNE LAB_1455 ; if <> skip low byte check + + LDA (Baslnl),Y ; get line # low byte + CMP Itempl ; compare with temporary integer low byte +LAB_1455 + BCS LAB_145E ; else if temp < this line, exit (passed line#) + +LAB_1456 + DEY ; decrement index to next line ptr high byte + LDA (Baslnl),Y ; get next line pointer high byte + TAX ; copy to X + DEY ; decrement index to next line ptr low byte + LDA (Baslnl),Y ; get next line pointer low byte + BCC LAB_SHLN ; go search for line # in temp (Itempl/Itemph) from AX + ; (carry always clear) + +LAB_145E + BEQ LAB_1460 ; exit if temp = found line #, carry is set + +LAB_145F + CLC ; clear found flag +LAB_1460 + RTS + +; perform NEW + +LAB_NEW + BNE LAB_1460 ; exit if not end of statement (to do syntax error) + +LAB_1463 + LDA #$00 ; clear A + TAY ; clear Y + STA (Smeml),Y ; clear first line, next line pointer, low byte + INY ; increment index + STA (Smeml),Y ; clear first line, next line pointer, high byte + CLC ; clear carry + LDA Smeml ; get start of mem low byte + ADC #$02 ; calculate end of BASIC low byte + STA Svarl ; save start of vars low byte + LDA Smemh ; get start of mem high byte + ADC #$00 ; add any carry + STA Svarh ; save start of vars high byte + +; reset execution to start, clear vars and flush stack + +LAB_1477 + CLC ; clear carry + LDA Smeml ; get start of mem low byte + ADC #$FF ; -1 + STA Bpntrl ; save BASIC execute pointer low byte + LDA Smemh ; get start of mem high byte + ADC #$FF ; -1+carry + STA Bpntrh ; save BASIC execute pointer high byte + +; "CLEAR" command gets here + +LAB_147A + LDA Ememl ; get end of mem low byte + LDY Ememh ; get end of mem high byte + STA Sstorl ; set bottom of string space low byte + STY Sstorh ; set bottom of string space high byte + LDA Svarl ; get start of vars low byte + LDY Svarh ; get start of vars high byte + STA Sarryl ; save var mem end low byte + STY Sarryh ; save var mem end high byte + STA Earryl ; save array mem end low byte + STY Earryh ; save array mem end high byte + JSR LAB_161A ; perform RESTORE command + +; flush stack and clear continue flag + +LAB_1491 + LDX #des_sk ; set descriptor stack pointer + STX next_s ; save descriptor stack pointer + PLA ; pull return address low byte + TAX ; copy return address low byte + PLA ; pull return address high byte + STX LAB_SKFE ; save to cleared stack + STA LAB_SKFF ; save to cleared stack + LDX #$FD ; new stack pointer + TXS ; reset stack + LDA #$00 ; clear byte + STA Cpntrh ; clear continue pointer high byte + STA Sufnxf ; clear subscript/FNX flag +LAB_14A6 + RTS + +; perform CLEAR + +LAB_CLEAR + BEQ LAB_147A ; if no following token go do "CLEAR" + + ; else there was a following token (go do syntax error) + RTS + +; perform LIST [n][-m] +; bigger, faster version (a _lot_ faster) + +LAB_LIST + BCC LAB_14BD ; branch if next character numeric (LIST n..) + + BEQ LAB_14BD ; branch if next character [NULL] (LIST) + + CMP #TK_MINUS ; compare with token for - + BNE LAB_14A6 ; exit if not - (LIST -m) + + ; LIST [[n][-m]] + ; this bit sets the n , if present, as the start and end +LAB_14BD + JSR LAB_GFPN ; get fixed-point number into temp integer + JSR LAB_SSLN ; search BASIC for temp integer line number + ; (pointer in Baslnl/Baslnh) + JSR LAB_GBYT ; scan memory + BEQ LAB_14D4 ; branch if no more characters + + ; this bit checks the - is present + CMP #TK_MINUS ; compare with token for - + BNE LAB_1460 ; return if not "-" (will be Syntax error) + + ; LIST [n]-m + ; the - was there so set m as the end value + JSR LAB_IGBY ; increment and scan memory + JSR LAB_GFPN ; get fixed-point number into temp integer + BNE LAB_1460 ; exit if not ok + +LAB_14D4 + LDA Itempl ; get temporary integer low byte + ORA Itemph ; OR temporary integer high byte + BNE LAB_14E2 ; branch if start set + + LDA #$FF ; set for -1 + STA Itempl ; set temporary integer low byte + STA Itemph ; set temporary integer high byte +LAB_14E2 + LDY #$01 ; set index for line + STY Oquote ; clear open quote flag + JSR LAB_CRLF ; print CR/LF + LDA (Baslnl),Y ; get next line pointer high byte + ; pointer initially set by search at LAB_14BD + BEQ LAB_152B ; if null all done so exit + JSR LAB_1629 ; do CRTL-C check vector + + INY ; increment index for line + LDA (Baslnl),Y ; get line # low byte + TAX ; copy to X + INY ; increment index + LDA (Baslnl),Y ; get line # high byte + CMP Itemph ; compare with temporary integer high byte + BNE LAB_14FF ; branch if no high byte match + + CPX Itempl ; compare with temporary integer low byte + BEQ LAB_1501 ; branch if = last line to do (< will pass next branch) + +LAB_14FF ; else .. + BCS LAB_152B ; if greater all done so exit + +LAB_1501 + STY Tidx1 ; save index for line + JSR LAB_295E ; print XA as unsigned integer + LDA #$20 ; space is the next character +LAB_1508 + LDY Tidx1 ; get index for line + AND #$7F ; mask top out bit of character +LAB_150C + JSR LAB_PRNA ; go print the character + CMP #$22 ; was it " character + BNE LAB_1519 ; branch if not + + ; we are either entering or leaving a pair of quotes + LDA Oquote ; get open quote flag + EOR #$FF ; toggle it + STA Oquote ; save it back +LAB_1519 + INY ; increment index + LDA (Baslnl),Y ; get next byte + BNE LAB_152E ; branch if not [EOL] (go print character) + TAY ; else clear index + LDA (Baslnl),Y ; get next line pointer low byte + TAX ; copy to X + INY ; increment index + LDA (Baslnl),Y ; get next line pointer high byte + STX Baslnl ; set pointer to line low byte + STA Baslnh ; set pointer to line high byte + BNE LAB_14E2 ; go do next line if not [EOT] + ; else .. +LAB_152B + RTS + +LAB_152E + BPL LAB_150C ; just go print it if not token byte + + ; else was token byte so uncrunch it (maybe) + BIT Oquote ; test the open quote flag + BMI LAB_150C ; just go print character if open quote set + + LDX #>LAB_KEYT ; get table address high byte + ASL ; *2 + ASL ; *4 + BCC LAB_152F ; branch if no carry + + INX ; else increment high byte + CLC ; clear carry for add +LAB_152F + ADC #LAB_159F ; set return address high byte + STA ut1_pl ; save return address low byte + STY ut1_ph ; save return address high byte + JMP LAB_1B66 ; round FAC1 and put on stack (returns to next instruction) + +LAB_159F + LDA #LAB_259C ; set 1 pointer high addr + JSR LAB_UFAC ; unpack memory (AY) into FAC1 + JSR LAB_GBYT ; scan memory + CMP #TK_STEP ; compare with STEP token + BNE LAB_15B3 ; jump if not "STEP" + + ;.was step so .. + JSR LAB_IGBY ; increment and scan memory + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch +LAB_15B3 + JSR LAB_27CA ; return A=FF,C=1/-ve A=01,C=0/+ve + STA FAC1_s ; set FAC1 sign (b7) + ; this is +1 for +ve step and -1 for -ve step, in NEXT we + ; compare the FOR value and the TO value and return +1 if + ; FOR > TO, 0 if FOR = TO and -1 if FOR < TO. the value + ; here (+/-1) is then compared to that result and if they + ; are the same (+ve and FOR > TO or -ve and FOR < TO) then + ; the loop is done + JSR LAB_1B5B ; push sign, round FAC1 and put on stack + LDA Frnxth ; get var pointer for FOR/NEXT high byte + PHA ; push on stack + LDA Frnxtl ; get var pointer for FOR/NEXT low byte + PHA ; push on stack + LDA #TK_FOR ; get FOR token + PHA ; push on stack + +; interpreter inner loop + +LAB_15C2 + JSR LAB_1629 ; do CRTL-C check vector + LDA Bpntrl ; get BASIC execute pointer low byte + LDY Bpntrh ; get BASIC execute pointer high byte + + LDX Clineh ; continue line is $FFxx for immediate mode + ; ($00xx for RUN from immediate mode) + INX ; increment it (now $00 if immediate mode) + BEQ LAB_15D1 ; branch if null (immediate mode) + + STA Cpntrl ; save continue pointer low byte + STY Cpntrh ; save continue pointer high byte +LAB_15D1 + LDY #$00 ; clear index + LDA (Bpntrl),Y ; get next byte + BEQ LAB_15DC ; branch if null [EOL] + + CMP #":" ; compare with ":" + BEQ LAB_15F6 ; branch if = (statement separator) + +LAB_15D9 + JMP LAB_SNER ; else syntax error then warm start + + ; have reached [EOL] +LAB_15DC + LDY #$02 ; set index + LDA (Bpntrl),Y ; get next line pointer high byte + CLC ; clear carry for no "BREAK" message + BEQ LAB_1651 ; if null go to immediate mode (was immediate or [EOT] + ; marker) + + INY ; increment index + LDA (Bpntrl),Y ; get line # low byte + STA Clinel ; save current line low byte + INY ; increment index + LDA (Bpntrl),Y ; get line # high byte + STA Clineh ; save current line high byte + TYA ; A now = 4 + ADC Bpntrl ; add BASIC execute pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + BCC LAB_15F6 ; branch if no overflow + + INC Bpntrh ; else increment BASIC execute pointer high byte +LAB_15F6 + JSR LAB_IGBY ; increment and scan memory + +LAB_15F9 + JSR LAB_15FF ; go interpret BASIC code from (Bpntrl) + +LAB_15FC + JMP LAB_15C2 ; loop + +; interpret BASIC code from (Bpntrl) + +LAB_15FF + BEQ LAB_1628 ; exit if zero [EOL] + +LAB_1602 + ASL ; *2 bytes per vector and normalise token + BCS LAB_1609 ; branch if was token + + JMP LAB_LET ; else go do implied LET + +LAB_1609 + CMP #(TK_TAB-$80)*2 ; compare normalised token * 2 with TAB + BCS LAB_15D9 ; branch if A>=TAB (do syntax error then warm start) + ; only tokens before TAB can start a line + TAY ; copy to index + LDA LAB_CTBL+1,Y ; get vector high byte + PHA ; onto stack + LDA LAB_CTBL,Y ; get vector low byte + PHA ; onto stack + JMP LAB_IGBY ; jump to increment and scan memory + ; then "return" to vector + +; CTRL-C check jump. this is called as a subroutine but exits back via a jump if a +; key press is detected. + +LAB_1629 + JMP (VEC_CC) ; ctrl c check vector + +; if there was a key press it gets back here .. + +LAB_1636 + CMP #$03 ; compare with CTRL-C + +; perform STOP + +LAB_STOP + BCS LAB_163B ; branch if token follows STOP + ; else just END +; END + +LAB_END + CLC ; clear the carry, indicate a normal program end +LAB_163B + BNE LAB_167A ; if wasn't CTRL-C or there is a following byte return + + LDA Bpntrh ; get the BASIC execute pointer high byte + EOR #>Ibuffs ; compare with buffer address high byte (Cb unchanged) + BEQ LAB_164F ; branch if the BASIC pointer is in the input buffer + ; (can't continue in immediate mode) + + ; else .. + EOR #>Ibuffs ; correct the bits + LDY Bpntrl ; get BASIC execute pointer low byte + STY Cpntrl ; save continue pointer low byte + STA Cpntrh ; save continue pointer high byte +LAB_1647 + LDA Clinel ; get current line low byte + LDY Clineh ; get current line high byte + STA Blinel ; save break line low byte + STY Blineh ; save break line high byte +LAB_164F + PLA ; pull return address low + PLA ; pull return address high +LAB_1651 + BCC LAB_165E ; if was program end just do warm start + + ; else .. + LDA #LAB_BMSG ; point to "Break" high byte + JMP LAB_1269 ; print "Break" and do warm start + +LAB_165E + JMP LAB_1274 ; go do warm start + +; perform RESTORE + +LAB_RESTORE + BNE LAB_RESTOREn ; branch if next character not null (RESTORE n) + +LAB_161A + SEC ; set carry for subtract + LDA Smeml ; get start of mem low byte + SBC #$01 ; -1 + LDY Smemh ; get start of mem high byte + BCS LAB_1624 ; branch if no underflow + +LAB_uflow + DEY ; else decrement high byte +LAB_1624 + STA Dptrl ; save DATA pointer low byte + STY Dptrh ; save DATA pointer high byte +LAB_1628 + RTS + + ; is RESTORE n +LAB_RESTOREn + JSR LAB_GFPN ; get fixed-point number into temp integer + JSR LAB_SNBL ; scan for next BASIC line + LDA Clineh ; get current line high byte + CMP Itemph ; compare with temporary integer high byte + BCS LAB_reset_search ; branch if >= (start search from beginning) + + TYA ; else copy line index to A + SEC ; set carry (+1) + ADC Bpntrl ; add BASIC execute pointer low byte + LDX Bpntrh ; get BASIC execute pointer high byte + BCC LAB_go_search ; branch if no overflow to high byte + + INX ; increment high byte + BCS LAB_go_search ; branch always (can never be carry clear) + +; search for line # in temp (Itempl/Itemph) from start of mem pointer (Smeml) + +LAB_reset_search + LDA Smeml ; get start of mem low byte + LDX Smemh ; get start of mem high byte + +; search for line # in temp (Itempl/Itemph) from (AX) + +LAB_go_search + + JSR LAB_SHLN ; search Basic for temp integer line number from AX + BCS LAB_line_found ; if carry set go set pointer + + JMP LAB_16F7 ; else go do "Undefined statement" error + +LAB_line_found + ; carry already set for subtract + LDA Baslnl ; get pointer low byte + SBC #$01 ; -1 + LDY Baslnh ; get pointer high byte + BCS LAB_1624 ; branch if no underflow (save DATA pointer and return) + + BCC LAB_uflow ; else decrement high byte then save DATA pointer and + ; return (branch always) + +; perform NULL + +LAB_NULL + JSR LAB_GTBY ; get byte parameter + STX Nullct ; save new NULL count +LAB_167A + RTS + +; perform CONT + +LAB_CONT + BNE LAB_167A ; if following byte exit to do syntax error + + LDY Cpntrh ; get continue pointer high byte + BNE LAB_166C ; go do continue if we can + + LDX #$1E ; error code $1E ("Can't continue" error) + JMP LAB_XERR ; do error #X, then warm start + + ; we can continue so .. +LAB_166C + LDA #TK_ON ; set token for ON + JSR LAB_IRQ ; set IRQ flags + LDA #TK_ON ; set token for ON + JSR LAB_NMI ; set NMI flags + + STY Bpntrh ; save BASIC execute pointer high byte + LDA Cpntrl ; get continue pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + LDA Blinel ; get break line low byte + LDY Blineh ; get break line high byte + STA Clinel ; set current line low byte + STY Clineh ; set current line high byte + RTS + +; perform RUN + +LAB_RUN + BNE LAB_1696 ; branch if RUN n + JMP LAB_1477 ; reset execution to start, clear variables, flush stack and + ; return + +; does RUN n + +LAB_1696 + JSR LAB_147A ; go do "CLEAR" + BEQ LAB_16B0 ; get n and do GOTO n (branch always as CLEAR sets Z=1) + +; perform DO + +LAB_DO + LDA #$05 ; need 5 bytes for DO + JSR LAB_1212 ; check room on stack for A bytes + LDA Bpntrh ; get BASIC execute pointer high byte + PHA ; push on stack + LDA Bpntrl ; get BASIC execute pointer low byte + PHA ; push on stack + LDA Clineh ; get current line high byte + PHA ; push on stack + LDA Clinel ; get current line low byte + PHA ; push on stack + LDA #TK_DO ; token for DO + PHA ; push on stack + JSR LAB_GBYT ; scan memory + JMP LAB_15C2 ; go do interpreter inner loop + +; perform GOSUB + +LAB_GOSUB + LDA #$05 ; need 5 bytes for GOSUB + JSR LAB_1212 ; check room on stack for A bytes + LDA Bpntrh ; get BASIC execute pointer high byte + PHA ; push on stack + LDA Bpntrl ; get BASIC execute pointer low byte + PHA ; push on stack + LDA Clineh ; get current line high byte + PHA ; push on stack + LDA Clinel ; get current line low byte + PHA ; push on stack + LDA #TK_GOSUB ; token for GOSUB + PHA ; push on stack +LAB_16B0 + JSR LAB_GBYT ; scan memory + JSR LAB_GOTO ; perform GOTO n + JMP LAB_15C2 ; go do interpreter inner loop + ; (can't RTS, we used the stack!) + +; perform GOTO + +LAB_GOTO + JSR LAB_GFPN ; get fixed-point number into temp integer + JSR LAB_SNBL ; scan for next BASIC line + LDA Clineh ; get current line high byte + CMP Itemph ; compare with temporary integer high byte + BCS LAB_16D0 ; branch if >= (start search from beginning) + + TYA ; else copy line index to A + SEC ; set carry (+1) + ADC Bpntrl ; add BASIC execute pointer low byte + LDX Bpntrh ; get BASIC execute pointer high byte + BCC LAB_16D4 ; branch if no overflow to high byte + + INX ; increment high byte + BCS LAB_16D4 ; branch always (can never be carry) + +; search for line # in temp (Itempl/Itemph) from start of mem pointer (Smeml) + +LAB_16D0 + LDA Smeml ; get start of mem low byte + LDX Smemh ; get start of mem high byte + +; search for line # in temp (Itempl/Itemph) from (AX) + +LAB_16D4 + JSR LAB_SHLN ; search Basic for temp integer line number from AX + BCC LAB_16F7 ; if carry clear go do "Undefined statement" error + ; (unspecified statement) + + ; carry already set for subtract + LDA Baslnl ; get pointer low byte + SBC #$01 ; -1 + STA Bpntrl ; save BASIC execute pointer low byte + LDA Baslnh ; get pointer high byte + SBC #$00 ; subtract carry + STA Bpntrh ; save BASIC execute pointer high byte +LAB_16E5 + RTS + +LAB_DONOK + LDX #$22 ; error code $22 ("LOOP without DO" error) + JMP LAB_XERR ; do error #X, then warm start + +; perform LOOP + +LAB_LOOP + TAY ; save following token + TSX ; copy stack pointer + LDA LAB_STAK+3,X ; get token byte from stack + CMP #TK_DO ; compare with DO token + BNE LAB_DONOK ; branch if no matching DO + + INX ; dump calling routine return address + INX ; dump calling routine return address + TXS ; correct stack + TYA ; get saved following token back + BEQ LoopAlways ; if no following token loop forever + ; (stack pointer in X) + + CMP #":" ; could be ':' + BEQ LoopAlways ; if :... loop forever + + SBC #TK_UNTIL ; subtract token for UNTIL, we know carry is set here + TAX ; copy to X (if it was UNTIL then Y will be correct) + BEQ DoRest ; branch if was UNTIL + + DEX ; decrement result + BNE LAB_16FC ; if not WHILE go do syntax error and warm start + ; only if the token was WHILE will this fail + + DEX ; set invert result byte +DoRest + STX Frnxth ; save invert result byte + JSR LAB_IGBY ; increment and scan memory + JSR LAB_EVEX ; evaluate expression + LDA FAC1_e ; get FAC1 exponent + BEQ DoCmp ; if =0 go do straight compare + + LDA #$FF ; else set all bits +DoCmp + TSX ; copy stack pointer + EOR Frnxth ; EOR with invert byte + BNE LoopDone ; if <> 0 clear stack and back to interpreter loop + + ; loop condition wasn't met so do it again +LoopAlways + LDA LAB_STAK+2,X ; get current line low byte + STA Clinel ; save current line low byte + LDA LAB_STAK+3,X ; get current line high byte + STA Clineh ; save current line high byte + LDA LAB_STAK+4,X ; get BASIC execute pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + LDA LAB_STAK+5,X ; get BASIC execute pointer high byte + STA Bpntrh ; save BASIC execute pointer high byte + JSR LAB_GBYT ; scan memory + JMP LAB_15C2 ; go do interpreter inner loop + + ; clear stack and back to interpreter loop +LoopDone + INX ; dump DO token + INX ; dump current line low byte + INX ; dump current line high byte + INX ; dump BASIC execute pointer low byte + INX ; dump BASIC execute pointer high byte + TXS ; correct stack + JMP LAB_DATA ; go perform DATA (find : or [EOL]) + +; do the return without gosub error + +LAB_16F4 + LDX #$04 ; error code $04 ("RETURN without GOSUB" error) + .byte $2C ; makes next line BIT LAB_0EA2 + +LAB_16F7 ; do undefined statement error + LDX #$0E ; error code $0E ("Undefined statement" error) + JMP LAB_XERR ; do error #X, then warm start + +; perform RETURN + +LAB_RETURN + BNE LAB_16E5 ; exit if following token (to allow syntax error) + +LAB_16E8 + PLA ; dump calling routine return address + PLA ; dump calling routine return address + PLA ; pull token + CMP #TK_GOSUB ; compare with GOSUB token + BNE LAB_16F4 ; branch if no matching GOSUB + +LAB_16FF + PLA ; pull current line low byte + STA Clinel ; save current line low byte + PLA ; pull current line high byte + STA Clineh ; save current line high byte + PLA ; pull BASIC execute pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + PLA ; pull BASIC execute pointer high byte + STA Bpntrh ; save BASIC execute pointer high byte + + ; now do the DATA statement as we could be returning into + ; the middle of an ON GOSUB n,m,p,q line + ; (the return address used by the DATA statement is the one + ; pushed before the GOSUB was executed!) + +; perform DATA + +LAB_DATA + JSR LAB_SNBS ; scan for next BASIC statement ([:] or [EOL]) + + ; set BASIC execute pointer +LAB_170F + TYA ; copy index to A + CLC ; clear carry for add + ADC Bpntrl ; add BASIC execute pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + BCC LAB_1719 ; skip next if no carry + + INC Bpntrh ; else increment BASIC execute pointer high byte +LAB_1719 + RTS + +LAB_16FC + JMP LAB_SNER ; do syntax error then warm start + +; scan for next BASIC statement ([:] or [EOL]) +; returns Y as index to [:] or [EOL] + +LAB_SNBS + LDX #":" ; set look for character = ":" + .byte $2C ; makes next line BIT $00A2 + +; scan for next BASIC line +; returns Y as index to [EOL] + +LAB_SNBL + LDX #$00 ; set alt search character = [EOL] + LDY #$00 ; set search character = [EOL] + STY Asrch ; store search character +LAB_1725 + TXA ; get alt search character + EOR Asrch ; toggle search character, effectively swap with $00 + STA Asrch ; save swapped search character +LAB_172D + LDA (Bpntrl),Y ; get next byte + BEQ LAB_1719 ; exit if null [EOL] + + CMP Asrch ; compare with search character + BEQ LAB_1719 ; exit if found + + INY ; increment index + CMP #$22 ; compare current character with open quote + BNE LAB_172D ; if not open quote go get next character + + BEQ LAB_1725 ; if found go swap search character for alt search character + +; perform IF + +LAB_IF + JSR LAB_EVEX ; evaluate the expression + JSR LAB_GBYT ; scan memory + CMP #TK_THEN ; compare with THEN token + BEQ LAB_174B ; if it was THEN go do IF + + ; wasn't IF .. THEN so must be IF .. GOTO + CMP #TK_GOTO ; compare with GOTO token + BNE LAB_16FC ; if it wasn't GOTO go do syntax error + + LDX Bpntrl ; save the basic pointer low byte + LDY Bpntrh ; save the basic pointer high byte + JSR LAB_IGBY ; increment and scan memory + BCS LAB_16FC ; if not numeric go do syntax error + + STX Bpntrl ; restore the basic pointer low byte + STY Bpntrh ; restore the basic pointer high byte +LAB_174B + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_174E ; if the result was zero go look for an ELSE + + JSR LAB_IGBY ; else increment and scan memory + BCS LAB_174D ; if not numeric go do var or keyword + +LAB_174C + JMP LAB_GOTO ; else was numeric so do GOTO n + + ; is var or keyword +LAB_174D + CMP #TK_RETURN ; compare the byte with the token for RETURN + BNE LAB_174G ; if it wasn't RETURN go interpret BASIC code from (Bpntrl) + ; and return to this code to process any following code + + JMP LAB_1602 ; else it was RETURN so interpret BASIC code from (Bpntrl) + ; but don't return here + +LAB_174G + JSR LAB_15FF ; interpret BASIC code from (Bpntrl) + +; the IF was executed and there may be a following ELSE so the code needs to return +; here to check and ignore the ELSE if present + + LDY #$00 ; clear the index + LDA (Bpntrl),Y ; get the next BASIC byte + CMP #TK_ELSE ; compare it with the token for ELSE + BEQ LAB_DATA ; if ELSE ignore the following statement + +; there was no ELSE so continue execution of IF THEN [: ]. any +; following ELSE will, correctly, cause a syntax error + + RTS ; else return to the interpreter inner loop + +; perform ELSE after IF + +LAB_174E + LDY #$00 ; clear the BASIC byte index + LDX #$01 ; clear the nesting depth +LAB_1750 + INY ; increment the BASIC byte index + LDA (Bpntrl),Y ; get the next BASIC byte + BEQ LAB_1753 ; if EOL go add the pointer and return + + CMP #TK_IF ; compare the byte with the token for IF + BNE LAB_1752 ; if not IF token skip the depth increment + + INX ; else increment the nesting depth .. + BNE LAB_1750 ; .. and continue looking + +LAB_1752 + CMP #TK_ELSE ; compare the byte with the token for ELSE + BNE LAB_1750 ; if not ELSE token continue looking + + DEX ; was ELSE so decrement the nesting depth + BNE LAB_1750 ; loop if still nested + + INY ; increment the BASIC byte index past the ELSE + +; found the matching ELSE, now do <{n|statement}> + +LAB_1753 + TYA ; else copy line index to A + CLC ; clear carry for add + ADC Bpntrl ; add the BASIC execute pointer low byte + STA Bpntrl ; save the BASIC execute pointer low byte + BCC LAB_1754 ; branch if no overflow to high byte + + INC Bpntrh ; else increment the BASIC execute pointer high byte +LAB_1754 + JSR LAB_GBYT ; scan memory + BCC LAB_174C ; if numeric do GOTO n + ; the code will return to the interpreter loop at the + ; tail end of the GOTO + + JMP LAB_15FF ; interpret BASIC code from (Bpntrl) + ; the code will return to the interpreter loop at the + ; tail end of the + +; perform REM, skip (rest of) line + +LAB_REM + JSR LAB_SNBL ; scan for next BASIC line + JMP LAB_170F ; go set BASIC execute pointer and return, branch always + +LAB_16FD + JMP LAB_SNER ; do syntax error then warm start + +; perform ON + +LAB_ON + CMP #TK_IRQ ; was it IRQ token ? + BNE LAB_NOIN ; if not go check NMI + + JMP LAB_SIRQ ; else go set-up IRQ + +LAB_NOIN + CMP #TK_NMI ; was it NMI token ? + BNE LAB_NONM ; if not go do normal ON command + + JMP LAB_SNMI ; else go set-up NMI + +LAB_NONM + JSR LAB_GTBY ; get byte parameter + PHA ; push GOTO/GOSUB token + CMP #TK_GOSUB ; compare with GOSUB token + BEQ LAB_176B ; branch if GOSUB + + CMP #TK_GOTO ; compare with GOTO token +LAB_1767 + BNE LAB_16FD ; if not GOTO do syntax error then warm start + + +; next character was GOTO or GOSUB + +LAB_176B + DEC FAC1_3 ; decrement index (byte value) + BNE LAB_1773 ; branch if not zero + + PLA ; pull GOTO/GOSUB token + JMP LAB_1602 ; go execute it + +LAB_1773 + JSR LAB_IGBY ; increment and scan memory + JSR LAB_GFPN ; get fixed-point number into temp integer (skip this n) + ; (we could LDX #',' and JSR LAB_SNBL+2, then we + ; just BNE LAB_176B for the loop. should be quicker .. + ; no we can't, what if we meet a colon or [EOL]?) + CMP #$2C ; compare next character with "," + BEQ LAB_176B ; loop if "," + +LAB_177E + PLA ; else pull keyword token (run out of options) + ; also dump +/-1 pointer low byte and exit +LAB_177F + RTS + +; takes n * 106 + 11 cycles where n is the number of digits + +; get fixed-point number into temp integer + +LAB_GFPN + LDX #$00 ; clear reg + STX Itempl ; clear temporary integer low byte +LAB_1785 + STX Itemph ; save temporary integer high byte + BCS LAB_177F ; return if carry set, end of scan, character was + ; not 0-9 + + CPX #$19 ; compare high byte with $19 + TAY ; ensure Zb = 0 if the branch is taken + BCS LAB_1767 ; branch if >=, makes max line # 63999 because next + ; bit does *$0A, = 64000, compare at target will fail + ; and do syntax error + + SBC #'0'-1 ; subtract "0", $2F + carry, from byte + TAY ; copy binary digit + LDA Itempl ; get temporary integer low byte + ASL ; *2 low byte + ROL Itemph ; *2 high byte + ASL ; *2 low byte + ROL Itemph ; *2 high byte, *4 + ADC Itempl ; + low byte, *5 + STA Itempl ; save it + TXA ; get high byte copy to A + ADC Itemph ; + high byte, *5 + ASL Itempl ; *2 low byte, *10d + ROL ; *2 high byte, *10d + TAX ; copy high byte back to X + TYA ; get binary digit back + ADC Itempl ; add number low byte + STA Itempl ; save number low byte + BCC LAB_17B3 ; if no overflow to high byte get next character + + INX ; else increment high byte +LAB_17B3 + JSR LAB_IGBY ; increment and scan memory + JMP LAB_1785 ; loop for next character + +; perform DEC + +LAB_DEC + LDA #LAB_259C ; set +/-1 pointer high byte (both the same) + JSR LAB_246C ; add (AY) to FAC1 + JSR LAB_PFAC ; pack FAC1 into variable (Lvarpl) + + JSR LAB_GBYT ; scan memory + CMP #',' ; compare with "," + BNE LAB_177E ; exit if not "," (either end or error) + + ; was "," so another INCR variable to do + JSR LAB_IGBY ; increment and scan memory + JMP LAB_17B7 ; go do next var + +IncrErr + JMP LAB_1ABC ; do "Type mismatch" error then warm start + +; perform LET + +LAB_LET + JSR LAB_GVAR ; get var address + STA Lvarpl ; save var address low byte + STY Lvarph ; save var address high byte + LDA #TK_EQUAL ; get = token + JSR LAB_SCCA ; scan for CHR$(A), else do syntax error then warm start + LDA Dtypef ; get data type flag, $FF=string, $00=numeric + PHA ; push data type flag + JSR LAB_EVEX ; evaluate expression + PLA ; pop data type flag + ROL ; set carry if type = string + JSR LAB_CKTM ; type match check, set C for string + BNE LAB_17D5 ; branch if string + + JMP LAB_PFAC ; pack FAC1 into variable (Lvarpl) and return + +; string LET + +LAB_17D5 + LDY #$02 ; set index to pointer high byte + LDA (des_pl),Y ; get string pointer high byte + CMP Sstorh ; compare bottom of string space high byte + BCC LAB_17F4 ; if less assign value and exit (was in program memory) + + BNE LAB_17E6 ; branch if > + ; else was equal so compare low bytes + DEY ; decrement index + LDA (des_pl),Y ; get pointer low byte + CMP Sstorl ; compare bottom of string space low byte + BCC LAB_17F4 ; if less assign value and exit (was in program memory) + + ; pointer was >= to bottom of string space pointer +LAB_17E6 + LDY des_ph ; get descriptor pointer high byte + CPY Svarh ; compare start of vars high byte + BCC LAB_17F4 ; branch if less (descriptor is on stack) + + BNE LAB_17FB ; branch if greater (descriptor is not on stack) + + ; else high bytes were equal so .. + LDA des_pl ; get descriptor pointer low byte + CMP Svarl ; compare start of vars low byte + BCS LAB_17FB ; branch if >= (descriptor is not on stack) + +LAB_17F4 + LDA des_pl ; get descriptor pointer low byte + LDY des_ph ; get descriptor pointer high byte + JMP LAB_1811 ; clean stack, copy descriptor to variable and return + + ; make space and copy string +LAB_17FB + LDY #$00 ; index to length + LDA (des_pl),Y ; get string length + JSR LAB_209C ; copy string + LDA des_2l ; get descriptor pointer low byte + LDY des_2h ; get descriptor pointer high byte + STA ssptr_l ; save descriptor pointer low byte + STY ssptr_h ; save descriptor pointer high byte + JSR LAB_228A ; copy string from descriptor (sdescr) to (Sutill) + LDA #FAC1_e ; get descriptor pointer high byte + + ; clean stack and assign value to string variable +LAB_1811 + STA des_2l ; save descriptor_2 pointer low byte + STY des_2h ; save descriptor_2 pointer high byte + JSR LAB_22EB ; clean descriptor stack, YA = pointer + LDY #$00 ; index to length + LDA (des_2l),Y ; get string length + STA (Lvarpl),Y ; copy to let string variable + INY ; index to string pointer low byte + LDA (des_2l),Y ; get string pointer low byte + STA (Lvarpl),Y ; copy to let string variable + INY ; index to string pointer high byte + LDA (des_2l),Y ; get string pointer high byte + STA (Lvarpl),Y ; copy to let string variable + RTS + +; perform GET + +LAB_GET + JSR LAB_GVAR ; get var address + STA Lvarpl ; save var address low byte + STY Lvarph ; save var address high byte + JSR INGET ; get input byte + LDX Dtypef ; get data type flag, $FF=string, $00=numeric + BMI LAB_GETS ; go get string character + + ; was numeric get + TAY ; copy character to Y + JSR LAB_1FD0 ; convert Y to byte in FAC1 + JMP LAB_PFAC ; pack FAC1 into variable (Lvarpl) and return + +LAB_GETS + PHA ; save character + LDA #$01 ; string is single byte + BCS LAB_IsByte ; branch if byte received + + PLA ; string is null +LAB_IsByte + JSR LAB_MSSP ; make string space A bytes long A=$AC=length, + ; X=$AD=Sutill=ptr low byte, Y=$AE=Sutilh=ptr high byte + BEQ LAB_NoSt ; skip store if null string + + PLA ; get character back + LDY #$00 ; clear index + STA (str_pl),Y ; save byte in string (byte IS string!) +LAB_NoSt + JSR LAB_RTST ; check for space on descriptor stack then put address + ; and length on descriptor stack and update stack pointers + + JMP LAB_17D5 ; do string LET and return + +; perform PRINT + +LAB_1829 + JSR LAB_18C6 ; print string from Sutill/Sutilh +LAB_182C + JSR LAB_GBYT ; scan memory + +; PRINT + +LAB_PRINT + BEQ LAB_CRLF ; if nothing following just print CR/LF + +LAB_1831 + CMP #TK_TAB ; compare with TAB( token + BEQ LAB_18A2 ; go do TAB/SPC + + CMP #TK_SPC ; compare with SPC( token + BEQ LAB_18A2 ; go do TAB/SPC + + CMP #',' ; compare with "," + BEQ LAB_188B ; go do move to next TAB mark + + CMP #';' ; compare with ";" + BEQ LAB_18BD ; if ";" continue with PRINT processing + + JSR LAB_EVEX ; evaluate expression + BIT Dtypef ; test data type flag, $FF=string, $00=numeric + BMI LAB_1829 ; branch if string + + JSR LAB_296E ; convert FAC1 to string + JSR LAB_20AE ; print " terminated string to Sutill/Sutilh + LDY #$00 ; clear index + +; don't check fit if terminal width byte is zero + + LDA TWidth ; get terminal width byte + BEQ LAB_185E ; skip check if zero + + SEC ; set carry for subtract + SBC TPos ; subtract terminal position + SBC (des_pl),Y ; subtract string length + BCS LAB_185E ; branch if less than terminal width + + JSR LAB_CRLF ; else print CR/LF +LAB_185E + JSR LAB_18C6 ; print string from Sutill/Sutilh + BEQ LAB_182C ; always go continue processing line + +; CR/LF return to BASIC from BASIC input handler + +LAB_1866 + LDA #$00 ; clear byte + STA Ibuffs,X ; null terminate input + LDX #Ibuffs ; set Y to buffer start-1 high byte + +; print CR/LF + +LAB_CRLF + LDA #$0D ; load [CR] + JSR LAB_PRNA ; go print the character + LDA #$0A ; load [LF] + BNE LAB_PRNA ; go print the character and return, branch always + +LAB_188B + LDA TPos ; get terminal position + CMP Iclim ; compare with input column limit + BCC LAB_1897 ; branch if less + + JSR LAB_CRLF ; else print CR/LF (next line) + BNE LAB_18BD ; continue with PRINT processing (branch always) + +LAB_1897 + SEC ; set carry for subtract +LAB_1898 + SBC TabSiz ; subtract TAB size + BCS LAB_1898 ; loop if result was +ve + + EOR #$FF ; complement it + ADC #$01 ; +1 (twos complement) + BNE LAB_18B6 ; always print A spaces (result is never $00) + + ; do TAB/SPC +LAB_18A2 + PHA ; save token + JSR LAB_SGBY ; scan and get byte parameter + CMP #$29 ; is next character ) + BNE LAB_1910 ; if not do syntax error then warm start + + PLA ; get token back + CMP #TK_TAB ; was it TAB ? + BNE LAB_18B7 ; if not go do SPC + + ; calculate TAB offset + TXA ; copy integer value to A + SBC TPos ; subtract terminal position + BCC LAB_18BD ; branch if result was < 0 (can't TAB backwards) + + ; print A spaces +LAB_18B6 + TAX ; copy result to X +LAB_18B7 + TXA ; set flags on size for SPC + BEQ LAB_18BD ; branch if result was = $0, already here + + ; print X spaces +LAB_18BA + JSR LAB_18E0 ; print " " + DEX ; decrement count + BNE LAB_18BA ; loop if not all done + + ; continue with PRINT processing +LAB_18BD + JSR LAB_IGBY ; increment and scan memory + BNE LAB_1831 ; if more to print go do it + + RTS + +; print null terminated string from memory + +LAB_18C3 + JSR LAB_20AE ; print " terminated string to Sutill/Sutilh + +; print string from Sutill/Sutilh + +LAB_18C6 + JSR LAB_22B6 ; pop string off descriptor stack, or from top of string + ; space returns with A = length, X=$71=pointer low byte, + ; Y=$72=pointer high byte + LDY #$00 ; reset index + TAX ; copy length to X + BEQ LAB_188C ; exit (RTS) if null string + +LAB_18CD + + LDA (ut1_pl),Y ; get next byte + JSR LAB_PRNA ; go print the character + INY ; increment index + DEX ; decrement count + BNE LAB_18CD ; loop if not done yet + + RTS + + ; Print single format character +; print " " + +LAB_18E0 + LDA #$20 ; load " " + .byte $2C ; change next line to BIT LAB_3FA9 + +; print "?" character + +LAB_18E3 + LDA #$3F ; load "?" character + +; print character in A +; now includes the null handler +; also includes infinite line length code +; note! some routines expect this one to exit with Zb=0 + +LAB_PRNA + CMP #' ' ; compare with " " + BCC LAB_18F9 ; branch if less (non printing) + + ; else printable character + PHA ; save the character + +; don't check fit if terminal width byte is zero + + LDA TWidth ; get terminal width + BNE LAB_18F0 ; branch if not zero (not infinite length) + +; is "infinite line" so check TAB position + + LDA TPos ; get position + SBC TabSiz ; subtract TAB size, carry set by CMP #$20 above + BNE LAB_18F7 ; skip reset if different + + STA TPos ; else reset position + BEQ LAB_18F7 ; go print character + +LAB_18F0 + CMP TPos ; compare with terminal character position + BNE LAB_18F7 ; branch if not at end of line + + JSR LAB_CRLF ; else print CR/LF +LAB_18F7 + INC TPos ; increment terminal position + PLA ; get character back +LAB_18F9 + JSR V_OUTP ; output byte via output vector + CMP #$0D ; compare with [CR] + BNE LAB_188A ; branch if not [CR] + + ; else print nullct nulls after the [CR] + STX TempB ; save buffer index + LDX Nullct ; get null count + BEQ LAB_1886 ; branch if no nulls + + LDA #$00 ; load [NULL] +LAB_1880 + JSR LAB_PRNA ; go print the character + DEX ; decrement count + BNE LAB_1880 ; loop if not all done + + LDA #$0D ; restore the character (and set the flags) +LAB_1886 + STX TPos ; clear terminal position (X always = zero when we get here) + LDX TempB ; restore buffer index +LAB_188A + AND #$FF ; set the flags +LAB_188C + RTS + +; handle bad input data + +LAB_1904 + LDA Imode ; get input mode flag, $00=INPUT, $00=READ + BPL LAB_1913 ; branch if INPUT (go do redo) + + LDA Dlinel ; get current DATA line low byte + LDY Dlineh ; get current DATA line high byte + STA Clinel ; save current line low byte + STY Clineh ; save current line high byte +LAB_1910 + JMP LAB_SNER ; do syntax error then warm start + + ; mode was INPUT +LAB_1913 + LDA #LAB_REDO ; point to redo message (high addr) + JSR LAB_18C3 ; print null terminated string from memory + LDA Cpntrl ; get continue pointer low byte + LDY Cpntrh ; get continue pointer high byte + STA Bpntrl ; save BASIC execute pointer low byte + STY Bpntrh ; save BASIC execute pointer high byte + RTS + +; perform INPUT + +LAB_INPUT + CMP #$22 ; compare next byte with open quote + BNE LAB_1934 ; branch if no prompt string + + JSR LAB_1BC1 ; print "..." string + LDA #$3B ; load A with ";" + JSR LAB_SCCA ; scan for CHR$(A), else do syntax error then warm start + JSR LAB_18C6 ; print string from Sutill/Sutilh + + ; done with prompt, now get data +LAB_1934 + JSR LAB_CKRN ; check not Direct, back here if ok + JSR LAB_INLN ; print "? " and get BASIC input + LDA #$00 ; set mode = INPUT + CMP Ibuffs ; test first byte in buffer + BNE LAB_1953 ; branch if not null input + + CLC ; was null input so clear carry to exit program + JMP LAB_1647 ; go do BREAK exit + +; perform READ + +LAB_READ + LDX Dptrl ; get DATA pointer low byte + LDY Dptrh ; get DATA pointer high byte + LDA #$80 ; set mode = READ + +LAB_1953 + STA Imode ; set input mode flag, $00=INPUT, $80=READ + STX Rdptrl ; save READ pointer low byte + STY Rdptrh ; save READ pointer high byte + + ; READ or INPUT next variable from list +LAB_195B + JSR LAB_GVAR ; get (var) address + STA Lvarpl ; save address low byte + STY Lvarph ; save address high byte + LDA Bpntrl ; get BASIC execute pointer low byte + LDY Bpntrh ; get BASIC execute pointer high byte + STA Itempl ; save as temporary integer low byte + STY Itemph ; save as temporary integer high byte + LDX Rdptrl ; get READ pointer low byte + LDY Rdptrh ; get READ pointer high byte + STX Bpntrl ; set BASIC execute pointer low byte + STY Bpntrh ; set BASIC execute pointer high byte + JSR LAB_GBYT ; scan memory + BNE LAB_1988 ; branch if not null + + ; pointer was to null entry + BIT Imode ; test input mode flag, $00=INPUT, $80=READ + BMI LAB_19DD ; branch if READ + + ; mode was INPUT + JSR LAB_18E3 ; print "?" character (double ? for extended input) + JSR LAB_INLN ; print "? " and get BASIC input + STX Bpntrl ; set BASIC execute pointer low byte + STY Bpntrh ; set BASIC execute pointer high byte +LAB_1985 + JSR LAB_GBYT ; scan memory +LAB_1988 + BIT Dtypef ; test data type flag, $FF=string, $00=numeric + BPL LAB_19B0 ; branch if numeric + + ; else get string + STA Srchc ; save search character + CMP #$22 ; was it " ? + BEQ LAB_1999 ; branch if so + + LDA #":" ; else search character is ":" + STA Srchc ; set new search character + LDA #',' ; other search character is "," + CLC ; clear carry for add +LAB_1999 + STA Asrch ; set second search character + LDA Bpntrl ; get BASIC execute pointer low byte + LDY Bpntrh ; get BASIC execute pointer high byte + + ADC #$00 ; c is =1 if we came via the BEQ LAB_1999, else =0 + BCC LAB_19A4 ; branch if no execute pointer low byte rollover + + INY ; else increment high byte +LAB_19A4 + JSR LAB_20B4 ; print Srchc or Asrch terminated string to Sutill/Sutilh + JSR LAB_23F3 ; restore BASIC execute pointer from temp (Btmpl/Btmph) + JSR LAB_17D5 ; go do string LET + JMP LAB_19B6 ; go check string terminator + + ; get numeric INPUT +LAB_19B0 + JSR LAB_2887 ; get FAC1 from string + JSR LAB_PFAC ; pack FAC1 into (Lvarpl) +LAB_19B6 + JSR LAB_GBYT ; scan memory + BEQ LAB_19C5 ; branch if null (last entry) + + CMP #',' ; else compare with "," + BEQ LAB_19C2 ; branch if "," + + JMP LAB_1904 ; else go handle bad input data + + ; got good input data +LAB_19C2 + JSR LAB_IGBY ; increment and scan memory +LAB_19C5 + LDA Bpntrl ; get BASIC execute pointer low byte (temp READ/INPUT ptr) + LDY Bpntrh ; get BASIC execute pointer high byte (temp READ/INPUT ptr) + STA Rdptrl ; save for now + STY Rdptrh ; save for now + LDA Itempl ; get temporary integer low byte (temp BASIC execute ptr) + LDY Itemph ; get temporary integer high byte (temp BASIC execute ptr) + STA Bpntrl ; set BASIC execute pointer low byte + STY Bpntrh ; set BASIC execute pointer high byte + JSR LAB_GBYT ; scan memory + BEQ LAB_1A03 ; if null go do extra ignored message + + JSR LAB_1C01 ; else scan for "," , else do syntax error then warm start + JMP LAB_195B ; go INPUT next variable from list + + ; find next DATA statement or do "Out of DATA" error +LAB_19DD + JSR LAB_SNBS ; scan for next BASIC statement ([:] or [EOL]) + INY ; increment index + TAX ; copy character ([:] or [EOL]) + BNE LAB_19F6 ; branch if [:] + + LDX #$06 ; set for "Out of DATA" error + INY ; increment index, now points to next line pointer high byte + LDA (Bpntrl),Y ; get next line pointer high byte + BEQ LAB_1A54 ; branch if end (eventually does error X) + + INY ; increment index + LDA (Bpntrl),Y ; get next line # low byte + STA Dlinel ; save current DATA line low byte + INY ; increment index + LDA (Bpntrl),Y ; get next line # high byte + INY ; increment index + STA Dlineh ; save current DATA line high byte +LAB_19F6 + LDA (Bpntrl),Y ; get byte + INY ; increment index + TAX ; copy to X + JSR LAB_170F ; set BASIC execute pointer + CPX #TK_DATA ; compare with "DATA" token + BEQ LAB_1985 ; was "DATA" so go do next READ + + BNE LAB_19DD ; go find next statement if not "DATA" + +; end of INPUT/READ routine + +LAB_1A03 + LDA Rdptrl ; get temp READ pointer low byte + LDY Rdptrh ; get temp READ pointer high byte + LDX Imode ; get input mode flag, $00=INPUT, $80=READ + BPL LAB_1A0E ; branch if INPUT + + JMP LAB_1624 ; save AY as DATA pointer and return + + ; we were getting INPUT +LAB_1A0E + LDY #$00 ; clear index + LDA (Rdptrl),Y ; get next byte + BNE LAB_1A1B ; error if not end of INPUT + + RTS + + ; user typed too much +LAB_1A1B + LDA #LAB_IMSG ; point to extra ignored message (high addr) + JMP LAB_18C3 ; print null terminated string from memory and return + +; search the stack for FOR activity +; exit with z=1 if FOR else exit with z=0 + +LAB_11A1 + TSX ; copy stack pointer + INX ; +1 pass return address + INX ; +2 pass return address + INX ; +3 pass calling routine return address + INX ; +4 pass calling routine return address +LAB_11A6 + LDA LAB_STAK+1,X ; get token byte from stack + CMP #TK_FOR ; is it FOR token + BNE LAB_11CE ; exit if not FOR token + + ; was FOR token + LDA Frnxth ; get var pointer for FOR/NEXT high byte + BNE LAB_11BB ; branch if not null + + LDA LAB_STAK+2,X ; get FOR variable pointer low byte + STA Frnxtl ; save var pointer for FOR/NEXT low byte + LDA LAB_STAK+3,X ; get FOR variable pointer high byte + STA Frnxth ; save var pointer for FOR/NEXT high byte +LAB_11BB + CMP LAB_STAK+3,X ; compare var pointer with stacked var pointer (high byte) + BNE LAB_11C7 ; branch if no match + + LDA Frnxtl ; get var pointer for FOR/NEXT low byte + CMP LAB_STAK+2,X ; compare var pointer with stacked var pointer (low byte) + BEQ LAB_11CE ; exit if match found + +LAB_11C7 + TXA ; copy index + CLC ; clear carry for add + ADC #$10 ; add FOR stack use size + TAX ; copy back to index + BNE LAB_11A6 ; loop if not at start of stack + +LAB_11CE + RTS + +; perform NEXT + +LAB_NEXT + BNE LAB_1A46 ; branch if NEXT var + + LDY #$00 ; else clear Y + BEQ LAB_1A49 ; branch always (no variable to search for) + +; NEXT var + +LAB_1A46 + JSR LAB_GVAR ; get variable address +LAB_1A49 + STA Frnxtl ; store variable pointer low byte + STY Frnxth ; store variable pointer high byte + ; (both cleared if no variable defined) + JSR LAB_11A1 ; search the stack for FOR activity + BEQ LAB_1A56 ; branch if found + + LDX #$00 ; else set error $00 ("NEXT without FOR" error) +LAB_1A54 + BEQ LAB_1ABE ; do error #X, then warm start + +LAB_1A56 + TXS ; set stack pointer, X set by search, dumps return addresses + + TXA ; copy stack pointer + SEC ; set carry for subtract + SBC #$F7 ; point to TO var + STA ut2_pl ; save pointer to TO var for compare + ADC #$FB ; point to STEP var + + LDY #>LAB_STAK ; point to stack page high byte + JSR LAB_UFAC ; unpack memory (STEP value) into FAC1 + TSX ; get stack pointer back + LDA LAB_STAK+8,X ; get step sign + STA FAC1_s ; save FAC1 sign (b7) + LDA Frnxtl ; get FOR variable pointer low byte + LDY Frnxth ; get FOR variable pointer high byte + JSR LAB_246C ; add (FOR variable) to FAC1 + JSR LAB_PFAC ; pack FAC1 into (FOR variable) + LDY #>LAB_STAK ; point to stack page high byte + JSR LAB_27FA ; compare FAC1 with (Y,ut2_pl) (TO value) + TSX ; get stack pointer back + CMP LAB_STAK+8,X ; compare step sign + BEQ LAB_1A9B ; branch if = (loop complete) + + ; loop back and do it all again + LDA LAB_STAK+$0D,X ; get FOR line low byte + STA Clinel ; save current line low byte + LDA LAB_STAK+$0E,X ; get FOR line high byte + STA Clineh ; save current line high byte + LDA LAB_STAK+$10,X ; get BASIC execute pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + LDA LAB_STAK+$0F,X ; get BASIC execute pointer high byte + STA Bpntrh ; save BASIC execute pointer high byte +LAB_1A98 + JMP LAB_15C2 ; go do interpreter inner loop + + ; loop complete so carry on +LAB_1A9B + TXA ; stack copy to A + ADC #$0F ; add $10 ($0F+carry) to dump FOR structure + TAX ; copy back to index + TXS ; copy to stack pointer + JSR LAB_GBYT ; scan memory + CMP #',' ; compare with "," + BNE LAB_1A98 ; branch if not "," (go do interpreter inner loop) + + ; was "," so another NEXT variable to do + JSR LAB_IGBY ; else increment and scan memory + JSR LAB_1A46 ; do NEXT (var) + +; evaluate expression and check is numeric, else do type mismatch + +LAB_EVNM + JSR LAB_EVEX ; evaluate expression + +; check if source is numeric, else do type mismatch + +LAB_CTNM + CLC ; destination is numeric + .byte $24 ; makes next line BIT $38 + +; check if source is string, else do type mismatch + +LAB_CTST + SEC ; required type is string + +; type match check, set C for string, clear C for numeric + +LAB_CKTM + BIT Dtypef ; test data type flag, $FF=string, $00=numeric + BMI LAB_1ABA ; branch if data type is string + + ; else data type was numeric + BCS LAB_1ABC ; if required type is string do type mismatch error +LAB_1AB9 + RTS + + ; data type was string, now check required type +LAB_1ABA + BCS LAB_1AB9 ; exit if required type is string + + ; else do type mismatch error +LAB_1ABC + LDX #$18 ; error code $18 ("Type mismatch" error) +LAB_1ABE + JMP LAB_XERR ; do error #X, then warm start + +; evaluate expression + +LAB_EVEX + LDX Bpntrl ; get BASIC execute pointer low byte + BNE LAB_1AC7 ; skip next if not zero + + DEC Bpntrh ; else decrement BASIC execute pointer high byte +LAB_1AC7 + DEC Bpntrl ; decrement BASIC execute pointer low byte + +LAB_EVEZ + LDA #$00 ; set null precedence (flag done) +LAB_1ACC + PHA ; push precedence byte + LDA #$02 ; 2 bytes + JSR LAB_1212 ; check room on stack for A bytes + JSR LAB_GVAL ; get value from line + LDA #$00 ; clear A + STA comp_f ; clear compare function flag +LAB_1ADB + JSR LAB_GBYT ; scan memory +LAB_1ADE + SEC ; set carry for subtract + SBC #TK_GT ; subtract token for > (lowest comparison function) + BCC LAB_1AFA ; branch if < TK_GT + + CMP #$03 ; compare with ">" to "<" tokens + BCS LAB_1AFA ; branch if >= TK_SGN (highest evaluation function +1) + + ; was token for > = or < (A = 0, 1 or 2) + CMP #$01 ; compare with token for = + ROL ; *2, b0 = carry (=1 if token was = or <) + ; (A = 0, 3 or 5) + EOR #$01 ; toggle b0 + ; (A = 1, 2 or 4. 1 if >, 2 if =, 4 if <) + EOR comp_f ; EOR with compare function flag bits + CMP comp_f ; compare with compare function flag + BCC LAB_1B53 ; if <(comp_f) do syntax error then warm start + ; was more than one <, = or >) + + STA comp_f ; save new compare function flag + JSR LAB_IGBY ; increment and scan memory + JMP LAB_1ADE ; go do next character + + ; token is < ">" or > "<" tokens +LAB_1AFA + LDX comp_f ; get compare function flag + BNE LAB_1B2A ; branch if compare function + + BCS LAB_1B78 ; go do functions + + ; else was < TK_GT so is operator or lower + ADC #TK_GT-TK_PLUS ; add # of operators (+, -, *, /, ^, AND, OR or EOR) + BCC LAB_1B78 ; branch if < + operator + + ; carry was set so token was +, -, *, /, ^, AND, OR or EOR + BNE LAB_1B0B ; branch if not + token + + BIT Dtypef ; test data type flag, $FF=string, $00=numeric + BPL LAB_1B0B ; branch if not string + + ; will only be $00 if type is string and token was + + JMP LAB_224D ; add strings, string 1 is in descriptor des_pl, string 2 + ; is in line, and return + +LAB_1B0B + STA ut1_pl ; save it + ASL ; *2 + ADC ut1_pl ; *3 + TAY ; copy to index +LAB_1B13 + PLA ; pull previous precedence + CMP LAB_OPPT,Y ; compare with precedence byte + BCS LAB_1B7D ; branch if A >= + + JSR LAB_CTNM ; check if source is numeric, else do type mismatch +LAB_1B1C + PHA ; save precedence +LAB_1B1D + JSR LAB_1B43 ; get vector, execute function then continue evaluation + PLA ; restore precedence + LDY prstk ; get precedence stacked flag + BPL LAB_1B3C ; branch if stacked values + + TAX ; copy precedence (set flags) + BEQ LAB_1B9D ; exit if done + + BNE LAB_1B86 ; else pop FAC2 and return, branch always + +LAB_1B2A + ROL Dtypef ; shift data type flag into Cb + TXA ; copy compare function flag + STA Dtypef ; clear data type flag, X is 0xxx xxxx + ROL ; shift data type into compare function byte b0 + LDX Bpntrl ; get BASIC execute pointer low byte + BNE LAB_1B34 ; branch if no underflow + + DEC Bpntrh ; else decrement BASIC execute pointer high byte +LAB_1B34 + DEC Bpntrl ; decrement BASIC execute pointer low byte +TK_LT_PLUS = TK_LT-TK_PLUS + LDY #TK_LT_PLUS*3 ; set offset to last operator entry + STA comp_f ; save new compare function flag + BNE LAB_1B13 ; branch always + +LAB_1B3C + CMP LAB_OPPT,Y ;.compare with stacked function precedence + BCS LAB_1B86 ; branch if A >=, pop FAC2 and return + + BCC LAB_1B1C ; branch always + +;.get vector, execute function then continue evaluation + +LAB_1B43 + LDA LAB_OPPT+2,Y ; get function vector high byte + PHA ; onto stack + LDA LAB_OPPT+1,Y ; get function vector low byte + PHA ; onto stack + ; now push sign, round FAC1 and put on stack + JSR LAB_1B5B ; function will return here, then the next RTS will call + ; the function + LDA comp_f ; get compare function flag + PHA ; push compare evaluation byte + LDA LAB_OPPT,Y ; get precedence byte + JMP LAB_1ACC ; continue evaluating expression + +LAB_1B53 + JMP LAB_SNER ; do syntax error then warm start + +; push sign, round FAC1 and put on stack + +LAB_1B5B + PLA ; get return addr low byte + STA ut1_pl ; save it + INC ut1_pl ; increment it (was ret-1 pushed? yes!) + ; note! no check is made on the high byte! if the calling + ; routine assembles to a page edge then this all goes + ; horribly wrong !!! + PLA ; get return addr high byte + STA ut1_ph ; save it + LDA FAC1_s ; get FAC1 sign (b7) + PHA ; push sign + +; round FAC1 and put on stack + +LAB_1B66 + JSR LAB_27BA ; round FAC1 + LDA FAC1_3 ; get FAC1 mantissa3 + PHA ; push on stack + LDA FAC1_2 ; get FAC1 mantissa2 + PHA ; push on stack + LDA FAC1_1 ; get FAC1 mantissa1 + PHA ; push on stack + LDA FAC1_e ; get FAC1 exponent + PHA ; push on stack + JMP (ut1_pl) ; return, sort of + +; do functions + +LAB_1B78 + LDY #$FF ; flag function + PLA ; pull precedence byte +LAB_1B7B + BEQ LAB_1B9D ; exit if done + +LAB_1B7D + CMP #$64 ; compare previous precedence with $64 + BEQ LAB_1B84 ; branch if was $64 (< function) + + JSR LAB_CTNM ; check if source is numeric, else do type mismatch +LAB_1B84 + STY prstk ; save precedence stacked flag + + ; pop FAC2 and return +LAB_1B86 + PLA ; pop byte + LSR ; shift out comparison evaluation lowest bit + STA Cflag ; save comparison evaluation flag + PLA ; pop exponent + STA FAC2_e ; save FAC2 exponent + PLA ; pop mantissa1 + STA FAC2_1 ; save FAC2 mantissa1 + PLA ; pop mantissa2 + STA FAC2_2 ; save FAC2 mantissa2 + PLA ; pop mantissa3 + STA FAC2_3 ; save FAC2 mantissa3 + PLA ; pop sign + STA FAC2_s ; save FAC2 sign (b7) + EOR FAC1_s ; EOR FAC1 sign (b7) + STA FAC_sc ; save sign compare (FAC1 EOR FAC2) +LAB_1B9D + LDA FAC1_e ; get FAC1 exponent + RTS + +; print "..." string to string util area + +LAB_1BC1 + LDA Bpntrl ; get BASIC execute pointer low byte + LDY Bpntrh ; get BASIC execute pointer high byte + ADC #$00 ; add carry to low byte + BCC LAB_1BCA ; branch if no overflow + + INY ; increment high byte +LAB_1BCA + JSR LAB_20AE ; print " terminated string to Sutill/Sutilh + JMP LAB_23F3 ; restore BASIC execute pointer from temp and return + +; get value from line + +LAB_GVAL + JSR LAB_IGBY ; increment and scan memory + BCS LAB_1BAC ; branch if not numeric character + + ; else numeric string found (e.g. 123) +LAB_1BA9 + JMP LAB_2887 ; get FAC1 from string and return + +; get value from line .. continued + + ; wasn't a number so .. +LAB_1BAC + TAX ; set the flags + BMI LAB_1BD0 ; if -ve go test token values + + ; else it is either a string, number, variable or () + CMP #'$' ; compare with "$" + BEQ LAB_1BA9 ; branch if "$", hex number + + CMP #'%' ; else compare with "%" + BEQ LAB_1BA9 ; branch if "%", binary number + + CMP #'.' ; compare with "." + BEQ LAB_1BA9 ; if so get FAC1 from string and return (e.g. was .123) + + ; it wasn't any sort of number so .. + CMP #$22 ; compare with " + BEQ LAB_1BC1 ; branch if open quote + + ; wasn't any sort of number so .. + +; evaluate expression within parentheses + + CMP #'(' ; compare with "(" + BNE LAB_1C18 ; if not "(" get (var), return value in FAC1 and $ flag + +LAB_1BF7 + JSR LAB_EVEZ ; evaluate expression, no decrement + +; all the 'scan for' routines return the character after the sought character + +; scan for ")" , else do syntax error then warm start + +LAB_1BFB + LDA #$29 ; load A with ")" + +; scan for CHR$(A) , else do syntax error then warm start + +LAB_SCCA + LDY #$00 ; clear index + CMP (Bpntrl),Y ; check next byte is = A + BNE LAB_SNER ; if not do syntax error then warm start + + JMP LAB_IGBY ; increment and scan memory then return + +; scan for "(" , else do syntax error then warm start + +LAB_1BFE + LDA #$28 ; load A with "(" + BNE LAB_SCCA ; scan for CHR$(A), else do syntax error then warm start + ; (branch always) + +; scan for "," , else do syntax error then warm start + +LAB_1C01 + LDA #$2C ; load A with "," + BNE LAB_SCCA ; scan for CHR$(A), else do syntax error then warm start + ; (branch always) + +; syntax error then warm start + +LAB_SNER + LDX #$02 ; error code $02 ("Syntax" error) + JMP LAB_XERR ; do error #X, then warm start + +; get value from line .. continued +; do tokens + +LAB_1BD0 + CMP #TK_MINUS ; compare with token for - + BEQ LAB_1C11 ; branch if - token (do set-up for functions) + + ; wasn't -n so .. + CMP #TK_PLUS ; compare with token for + + BEQ LAB_GVAL ; branch if + token (+n = n so ignore leading +) + + CMP #TK_NOT ; compare with token for NOT + BNE LAB_1BE7 ; branch if not token for NOT + + ; was NOT token +TK_EQUAL_PLUS = TK_EQUAL-TK_PLUS + LDY #TK_EQUAL_PLUS*3 ; offset to NOT function + BNE LAB_1C13 ; do set-up for function then execute (branch always) + +; do = compare + +LAB_EQUAL + JSR LAB_EVIR ; evaluate integer expression (no sign check) + LDA FAC1_3 ; get FAC1 mantissa3 + EOR #$FF ; invert it + TAY ; copy it + LDA FAC1_2 ; get FAC1 mantissa2 + EOR #$FF ; invert it + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; get value from line .. continued + + ; wasn't +, -, or NOT so .. +LAB_1BE7 + CMP #TK_FN ; compare with token for FN + BNE LAB_1BEE ; branch if not token for FN + + JMP LAB_201E ; go evaluate FNx + +; get value from line .. continued + + ; wasn't +, -, NOT or FN so .. +LAB_1BEE + SBC #TK_SGN ; subtract with token for SGN + BCS LAB_1C27 ; if a function token go do it + + JMP LAB_SNER ; else do syntax error + +; set-up for functions + +LAB_1C11 +TK_GT_PLUS = TK_GT-TK_PLUS + LDY #TK_GT_PLUS*3 ; set offset from base to > operator +LAB_1C13 + PLA ; dump return address low byte + PLA ; dump return address high byte + JMP LAB_1B1D ; execute function then continue evaluation + +; variable name set-up +; get (var), return value in FAC_1 and $ flag + +LAB_1C18 + JSR LAB_GVAR ; get (var) address + STA FAC1_2 ; save address low byte in FAC1 mantissa2 + STY FAC1_3 ; save address high byte in FAC1 mantissa3 + LDX Dtypef ; get data type flag, $FF=string, $00=numeric + BMI LAB_1C25 ; if string then return (does RTS) + +LAB_1C24 + JMP LAB_UFAC ; unpack memory (AY) into FAC1 + +LAB_1C25 + RTS + +; get value from line .. continued +; only functions left so .. + +; set up function references + +; new for V2.0+ this replaces a lot of IF .. THEN .. ELSEIF .. THEN .. that was needed +; to process function calls. now the function vector is computed and pushed on the stack +; and the preprocess offset is read. if the preprocess offset is non zero then the vector +; is calculated and the routine called, if not this routine just does RTS. whichever +; happens the RTS at the end of this routine, or the end of the preprocess routine, calls +; the function code + +; this also removes some less than elegant code that was used to bypass type checking +; for functions that returned strings + +LAB_1C27 + ASL ; *2 (2 bytes per function address) + TAY ; copy to index + + LDA LAB_FTBM,Y ; get function jump vector high byte + PHA ; push functions jump vector high byte + LDA LAB_FTBL,Y ; get function jump vector low byte + PHA ; push functions jump vector low byte + + LDA LAB_FTPM,Y ; get function pre process vector high byte + BEQ LAB_1C56 ; skip pre process if null vector + + PHA ; push functions pre process vector high byte + LDA LAB_FTPL,Y ; get function pre process vector low byte + PHA ; push functions pre process vector low byte + +LAB_1C56 + RTS ; do function, or pre process, call + +; process string expression in parenthesis + +LAB_PPFS + JSR LAB_1BF7 ; process expression in parenthesis + JMP LAB_CTST ; check if source is string then do function, + ; else do type mismatch + +; process numeric expression in parenthesis + +LAB_PPFN + JSR LAB_1BF7 ; process expression in parenthesis + JMP LAB_CTNM ; check if source is numeric then do function, + ; else do type mismatch + +; set numeric data type and increment BASIC execute pointer + +LAB_PPBI + LSR Dtypef ; clear data type flag, $FF=string, $00=numeric + JMP LAB_IGBY ; increment and scan memory then do function + +; process string for LEFT$, RIGHT$ or MID$ + +LAB_LRMS + JSR LAB_EVEZ ; evaluate (should be string) expression + JSR LAB_1C01 ; scan for ",", else do syntax error then warm start + JSR LAB_CTST ; check if source is string, else do type mismatch + + PLA ; get function jump vector low byte + TAX ; save functions jump vector low byte + PLA ; get function jump vector high byte + TAY ; save functions jump vector high byte + LDA des_ph ; get descriptor pointer high byte + PHA ; push string pointer high byte + LDA des_pl ; get descriptor pointer low byte + PHA ; push string pointer low byte + TYA ; get function jump vector high byte back + PHA ; save functions jump vector high byte + TXA ; get function jump vector low byte back + PHA ; save functions jump vector low byte + JSR LAB_GTBY ; get byte parameter + TXA ; copy byte parameter to A + RTS ; go do function + +; process numeric expression(s) for BIN$ or HEX$ + +LAB_BHSS + JSR LAB_EVEZ ; process expression + JSR LAB_CTNM ; check if source is numeric, else do type mismatch + LDA FAC1_e ; get FAC1 exponent + CMP #$98 ; compare with exponent = 2^24 + BCS LAB_BHER ; branch if n>=2^24 (is too big) + + JSR LAB_2831 ; convert FAC1 floating-to-fixed + LDX #$02 ; 3 bytes to do +LAB_CFAC + LDA FAC1_1,X ; get byte from FAC1 + STA nums_1,X ; save byte to temp + DEX ; decrement index + BPL LAB_CFAC ; copy FAC1 mantissa to temp + + JSR LAB_GBYT ; get next BASIC byte + LDX #$00 ; set default to no leading "0"s + CMP #')' ; compare with close bracket + BEQ LAB_1C54 ; if ")" go do rest of function + + JSR LAB_SCGB ; scan for "," and get byte + JSR LAB_GBYT ; get last byte back + CMP #')' ; is next character ) + BNE LAB_BHER ; if not ")" go do error + +LAB_1C54 + RTS ; else do function + +LAB_BHER + JMP LAB_FCER ; do function call error then warm start + +; perform EOR + +; added operator format is the same as AND or OR, precedence is the same as OR + +; this bit worked first time but it took a while to sort out the operator table +; pointers and offsets afterwards! + +LAB_EOR + JSR GetFirst ; get first integer expression (no sign check) + EOR XOAw_l ; EOR with expression 1 low byte + TAY ; save in Y + LDA FAC1_2 ; get FAC1 mantissa2 + EOR XOAw_h ; EOR with expression 1 high byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform OR + +LAB_OR + JSR GetFirst ; get first integer expression (no sign check) + ORA XOAw_l ; OR with expression 1 low byte + TAY ; save in Y + LDA FAC1_2 ; get FAC1 mantissa2 + ORA XOAw_h ; OR with expression 1 high byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform AND + +LAB_AND + JSR GetFirst ; get first integer expression (no sign check) + AND XOAw_l ; AND with expression 1 low byte + TAY ; save in Y + LDA FAC1_2 ; get FAC1 mantissa2 + AND XOAw_h ; AND with expression 1 high byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; get first value for OR, AND or EOR + +GetFirst + JSR LAB_EVIR ; evaluate integer expression (no sign check) + LDA FAC1_2 ; get FAC1 mantissa2 + STA XOAw_h ; save it + LDA FAC1_3 ; get FAC1 mantissa3 + STA XOAw_l ; save it + JSR LAB_279B ; copy FAC2 to FAC1 (get 2nd value in expression) + JSR LAB_EVIR ; evaluate integer expression (no sign check) + LDA FAC1_3 ; get FAC1 mantissa3 +LAB_1C95 + RTS + +; perform comparisons + +; do < compare + +LAB_LTHAN + JSR LAB_CKTM ; type match check, set C for string + BCS LAB_1CAE ; branch if string + + ; do numeric < compare + LDA FAC2_s ; get FAC2 sign (b7) + ORA #$7F ; set all non sign bits + AND FAC2_1 ; and FAC2 mantissa1 (AND in sign bit) + STA FAC2_1 ; save FAC2 mantissa1 + LDA #FAC2_e ; set pointer high byte to FAC2 + JSR LAB_27F8 ; compare FAC1 with FAC2 (AY) + TAX ; copy result + JMP LAB_1CE1 ; go evaluate result + + ; do string < compare +LAB_1CAE + LSR Dtypef ; clear data type flag, $FF=string, $00=numeric + DEC comp_f ; clear < bit in compare function flag + JSR LAB_22B6 ; pop string off descriptor stack, or from top of string + ; space returns with A = length, X=pointer low byte, + ; Y=pointer high byte + STA str_ln ; save length + STX str_pl ; save string pointer low byte + STY str_ph ; save string pointer high byte + LDA FAC2_2 ; get descriptor pointer low byte + LDY FAC2_3 ; get descriptor pointer high byte + JSR LAB_22BA ; pop (YA) descriptor off stack or from top of string space + ; returns with A = length, X=pointer low byte, + ; Y=pointer high byte + STX FAC2_2 ; save string pointer low byte + STY FAC2_3 ; save string pointer high byte + TAX ; copy length + SEC ; set carry for subtract + SBC str_ln ; subtract string 1 length + BEQ LAB_1CD6 ; branch if str 1 length = string 2 length + + LDA #$01 ; set str 1 length > string 2 length + BCC LAB_1CD6 ; branch if so + + LDX str_ln ; get string 1 length + LDA #$FF ; set str 1 length < string 2 length +LAB_1CD6 + STA FAC1_s ; save length compare + LDY #$FF ; set index + INX ; adjust for loop +LAB_1CDB + INY ; increment index + DEX ; decrement count + BNE LAB_1CE6 ; branch if still bytes to do + + LDX FAC1_s ; get length compare back +LAB_1CE1 + BMI LAB_1CF2 ; branch if str 1 < str 2 + + CLC ; flag str 1 <= str 2 + BCC LAB_1CF2 ; go evaluate result + +LAB_1CE6 + LDA (FAC2_2),Y ; get string 2 byte + CMP (FAC1_1),Y ; compare with string 1 byte + BEQ LAB_1CDB ; loop if bytes = + + LDX #$FF ; set str 1 < string 2 + BCS LAB_1CF2 ; branch if so + + LDX #$01 ; set str 1 > string 2 +LAB_1CF2 + INX ; x = 0, 1 or 2 + TXA ; copy to A + ROL ; *2 (1, 2 or 4) + AND Cflag ; AND with comparison evaluation flag + BEQ LAB_1CFB ; branch if 0 (compare is false) + + LDA #$FF ; else set result true +LAB_1CFB + JMP LAB_27DB ; save A as integer byte and return + +LAB_1CFE + JSR LAB_1C01 ; scan for ",", else do syntax error then warm start + +; perform DIM + +LAB_DIM + TAX ; copy "DIM" flag to X + JSR LAB_1D10 ; search for variable + JSR LAB_GBYT ; scan memory + BNE LAB_1CFE ; scan for "," and loop if not null + + RTS + +; perform << (left shift) + +LAB_LSHIFT + JSR GetPair ; get integer expression and byte (no sign check) + LDA FAC1_2 ; get expression high byte + LDX TempB ; get shift count + BEQ NoShift ; branch if zero + + CPX #$10 ; compare bit count with 16d + BCS TooBig ; branch if >= + +Ls_loop + ASL FAC1_3 ; shift low byte + ROL ; shift high byte + DEX ; decrement bit count + BNE Ls_loop ; loop if shift not complete + + LDY FAC1_3 ; get expression low byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform >> (right shift) + +LAB_RSHIFT + JSR GetPair ; get integer expression and byte (no sign check) + LDA FAC1_2 ; get expression high byte + LDX TempB ; get shift count + BEQ NoShift ; branch if zero + + CPX #$10 ; compare bit count with 16d + BCS TooBig ; branch if >= + +Rs_loop + LSR ; shift high byte + ROR FAC1_3 ; shift low byte + DEX ; decrement bit count + BNE Rs_loop ; loop if shift not complete + +NoShift + LDY FAC1_3 ; get expression low byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +TooBig + LDA #$00 ; clear high byte + TAY ; copy to low byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +GetPair + JSR LAB_EVBY ; evaluate byte expression, result in X + STX TempB ; save it + JSR LAB_279B ; copy FAC2 to FAC1 (get 2nd value in expression) + JMP LAB_EVIR ; evaluate integer expression (no sign check) + +; search for variable + +; return pointer to variable in Cvaral/Cvarah + +LAB_GVAR + LDX #$00 ; set DIM flag = $00 + JSR LAB_GBYT ; scan memory (1st character) +LAB_1D10 + STX Defdim ; save DIM flag +LAB_1D12 + STA Varnm1 ; save 1st character + AND #$7F ; clear FN flag bit + JSR LAB_CASC ; check byte, return C=0 if<"A" or >"Z" + BCS LAB_1D1F ; branch if ok + + JMP LAB_SNER ; else syntax error then warm start + + ; was variable name so .. +LAB_1D1F + LDX #$00 ; clear 2nd character temp + STX Dtypef ; clear data type flag, $FF=string, $00=numeric + JSR LAB_IGBY ; increment and scan memory (2nd character) + BCC LAB_1D2D ; branch if character = "0"-"9" (ok) + + ; 2nd character wasn't "0" to "9" so .. + JSR LAB_CASC ; check byte, return C=0 if<"A" or >"Z" + BCC LAB_1D38 ; branch if <"A" or >"Z" (go check if string) + +LAB_1D2D + TAX ; copy 2nd character + + ; ignore further (valid) characters in the variable name +LAB_1D2E + JSR LAB_IGBY ; increment and scan memory (3rd character) + BCC LAB_1D2E ; loop if character = "0"-"9" (ignore) + + JSR LAB_CASC ; check byte, return C=0 if<"A" or >"Z" + BCS LAB_1D2E ; loop if character = "A"-"Z" (ignore) + + ; check if string variable +LAB_1D38 + CMP #'$' ; compare with "$" + BNE LAB_1D47 ; branch if not string + +; to introduce a new variable type (% suffix for integers say) then this branch +; will need to go to that check and then that branch, if it fails, go to LAB_1D47 + + ; type is string + LDA #$FF ; set data type = string + STA Dtypef ; set data type flag, $FF=string, $00=numeric + TXA ; get 2nd character back + ORA #$80 ; set top bit (indicate string var) + TAX ; copy back to 2nd character temp + JSR LAB_IGBY ; increment and scan memory + +; after we have determined the variable type we need to come back here to determine +; if it's an array of type. this would plug in a%(b[,c[,d]])) integer arrays nicely + + +LAB_1D47 ; gets here with character after var name in A + STX Varnm2 ; save 2nd character + ORA Sufnxf ; or with subscript/FNX flag (or FN name) + CMP #'(' ; compare with "(" + BNE LAB_1D53 ; branch if not "(" + + JMP LAB_1E17 ; go find, or make, array + +; either find or create var +; var name (1st two characters only!) is in Varnm1,Varnm2 + + ; variable name wasn't var(... so look for plain var +LAB_1D53 + LDA #$00 ; clear A + STA Sufnxf ; clear subscript/FNX flag + LDA Svarl ; get start of vars low byte + LDX Svarh ; get start of vars high byte + LDY #$00 ; clear index +LAB_1D5D + STX Vrschh ; save search address high byte +LAB_1D5F + STA Vrschl ; save search address low byte + CPX Sarryh ; compare high address with var space end + BNE LAB_1D69 ; skip next compare if <> + + ; high addresses were = so compare low addresses + CMP Sarryl ; compare low address with var space end + BEQ LAB_1D8B ; if not found go make new var + +LAB_1D69 + LDA Varnm1 ; get 1st character of var to find + CMP (Vrschl),Y ; compare with variable name 1st character + BNE LAB_1D77 ; branch if no match + + ; 1st characters match so compare 2nd characters + LDA Varnm2 ; get 2nd character of var to find + INY ; index to point to variable name 2nd character + CMP (Vrschl),Y ; compare with variable name 2nd character + BEQ LAB_1DD7 ; branch if match (found var) + + DEY ; else decrement index (now = $00) +LAB_1D77 + CLC ; clear carry for add + LDA Vrschl ; get search address low byte + ADC #$06 ; +6 (offset to next var name) + BCC LAB_1D5F ; loop if no overflow to high byte + + INX ; else increment high byte + BNE LAB_1D5D ; loop always (RAM doesn't extend to $FFFF !) + +; check byte, return C=0 if<"A" or >"Z" or "a" to "z" + +LAB_CASC + CMP #'a' ; compare with "a" + BCS LAB_1D83 ; go check <"z"+1 + +; check byte, return C=0 if<"A" or >"Z" + +LAB_1D82 + CMP #'A' ; compare with "A" + BCC LAB_1D8A ; exit if less + + ; carry is set + SBC #$5B ; subtract "Z"+1 + SEC ; set carry + SBC #$A5 ; subtract $A5 (restore byte) + ; carry clear if byte>$5A +LAB_1D8A + RTS + +LAB_1D83 + SBC #$7B ; subtract "z"+1 + SEC ; set carry + SBC #$85 ; subtract $85 (restore byte) + ; carry clear if byte>$7A + RTS + + ; reached end of variable mem without match + ; .. so create new variable +LAB_1D8B + PLA ; pop return address low byte + PHA ; push return address low byte +LAB_1C18p2 = LAB_1C18+2 + CMP #LAB_1D96 ; high byte point to $00,$00 + RTS + + ; create new numeric variable +LAB_1D98 + LDA Sarryl ; get var mem end low byte + LDY Sarryh ; get var mem end high byte + STA Ostrtl ; save old block start low byte + STY Ostrth ; save old block start high byte + LDA Earryl ; get array mem end low byte + LDY Earryh ; get array mem end high byte + STA Obendl ; save old block end low byte + STY Obendh ; save old block end high byte + CLC ; clear carry for add + ADC #$06 ; +6 (space for one var) + BCC LAB_1DAE ; branch if no overflow to high byte + + INY ; else increment high byte +LAB_1DAE + STA Nbendl ; set new block end low byte + STY Nbendh ; set new block end high byte + JSR LAB_11CF ; open up space in memory + LDA Nbendl ; get new start low byte + LDY Nbendh ; get new start high byte (-$100) + INY ; correct high byte + STA Sarryl ; save new var mem end low byte + STY Sarryh ; save new var mem end high byte + LDY #$00 ; clear index + LDA Varnm1 ; get var name 1st character + STA (Vrschl),Y ; save var name 1st character + INY ; increment index + LDA Varnm2 ; get var name 2nd character + STA (Vrschl),Y ; save var name 2nd character + LDA #$00 ; clear A + INY ; increment index + STA (Vrschl),Y ; initialise var byte + INY ; increment index + STA (Vrschl),Y ; initialise var byte + INY ; increment index + STA (Vrschl),Y ; initialise var byte + INY ; increment index + STA (Vrschl),Y ; initialise var byte + + ; found a match for var ((Vrschl) = ptr) +LAB_1DD7 + LDA Vrschl ; get var address low byte + CLC ; clear carry for add + ADC #$02 ; +2 (offset past var name bytes) + LDY Vrschh ; get var address high byte + BCC LAB_1DE1 ; branch if no overflow from add + + INY ; else increment high byte +LAB_1DE1 + STA Cvaral ; save current var address low byte + STY Cvarah ; save current var address high byte + RTS + +; set-up array pointer (Adatal/h) to first element in array +; set Adatal,Adatah to Astrtl,Astrth+2*Dimcnt+#$05 + +LAB_1DE6 + LDA Dimcnt ; get # of dimensions (1, 2 or 3) + ASL ; *2 (also clears the carry !) + ADC #$05 ; +5 (result is 7, 9 or 11 here) + ADC Astrtl ; add array start pointer low byte + LDY Astrth ; get array pointer high byte + BCC LAB_1DF2 ; branch if no overflow + + INY ; else increment high byte +LAB_1DF2 + STA Adatal ; save array data pointer low byte + STY Adatah ; save array data pointer high byte + RTS + +; evaluate integer expression + +LAB_EVIN + JSR LAB_IGBY ; increment and scan memory + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + +; evaluate integer expression (no check) + +LAB_EVPI + LDA FAC1_s ; get FAC1 sign (b7) + BMI LAB_1E12 ; do function call error if -ve + +; evaluate integer expression (no sign check) + +LAB_EVIR + LDA FAC1_e ; get FAC1 exponent + CMP #$90 ; compare with exponent = 2^16 (n>2^15) + BCC LAB_1E14 ; branch if n<2^16 (is ok) + + LDA #LAB_1DF7 ; set pointer high byte to -32768 + JSR LAB_27F8 ; compare FAC1 with (AY) +LAB_1E12 + BNE LAB_FCER ; if <> do function call error then warm start + +LAB_1E14 + JMP LAB_2831 ; convert FAC1 floating-to-fixed and return + +; find or make array + +LAB_1E17 + LDA Defdim ; get DIM flag + PHA ; push it + LDA Dtypef ; get data type flag, $FF=string, $00=numeric + PHA ; push it + LDY #$00 ; clear dimensions count + +; now get the array dimension(s) and stack it (them) before the data type and DIM flag + +LAB_1E1F + TYA ; copy dimensions count + PHA ; save it + LDA Varnm2 ; get array name 2nd byte + PHA ; save it + LDA Varnm1 ; get array name 1st byte + PHA ; save it + JSR LAB_EVIN ; evaluate integer expression + PLA ; pull array name 1st byte + STA Varnm1 ; restore array name 1st byte + PLA ; pull array name 2nd byte + STA Varnm2 ; restore array name 2nd byte + PLA ; pull dimensions count + TAY ; restore it + TSX ; copy stack pointer + LDA LAB_STAK+2,X ; get DIM flag + PHA ; push it + LDA LAB_STAK+1,X ; get data type flag + PHA ; push it + LDA FAC1_2 ; get this dimension size high byte + STA LAB_STAK+2,X ; stack before flag bytes + LDA FAC1_3 ; get this dimension size low byte + STA LAB_STAK+1,X ; stack before flag bytes + INY ; increment dimensions count + JSR LAB_GBYT ; scan memory + CMP #',' ; compare with "," + BEQ LAB_1E1F ; if found go do next dimension + + STY Dimcnt ; store dimensions count + JSR LAB_1BFB ; scan for ")" , else do syntax error then warm start + PLA ; pull data type flag + STA Dtypef ; restore data type flag, $FF=string, $00=numeric + PLA ; pull DIM flag + STA Defdim ; restore DIM flag + LDX Sarryl ; get array mem start low byte + LDA Sarryh ; get array mem start high byte + +; now check to see if we are at the end of array memory (we would be if there were +; no arrays). + +LAB_1E5C + STX Astrtl ; save as array start pointer low byte + STA Astrth ; save as array start pointer high byte + CMP Earryh ; compare with array mem end high byte + BNE LAB_1E68 ; branch if not reached array mem end + + CPX Earryl ; else compare with array mem end low byte + BEQ LAB_1EA1 ; go build array if not found + + ; search for array +LAB_1E68 + LDY #$00 ; clear index + LDA (Astrtl),Y ; get array name first byte + INY ; increment index to second name byte + CMP Varnm1 ; compare with this array name first byte + BNE LAB_1E77 ; branch if no match + + LDA Varnm2 ; else get this array name second byte + CMP (Astrtl),Y ; compare with array name second byte + BEQ LAB_1E8D ; array found so branch + + ; no match +LAB_1E77 + INY ; increment index + LDA (Astrtl),Y ; get array size low byte + CLC ; clear carry for add + ADC Astrtl ; add array start pointer low byte + TAX ; copy low byte to X + INY ; increment index + LDA (Astrtl),Y ; get array size high byte + ADC Astrth ; add array mem pointer high byte + BCC LAB_1E5C ; if no overflow go check next array + +; do array bounds error + +LAB_1E85 + LDX #$10 ; error code $10 ("Array bounds" error) + .byte $2C ; makes next bit BIT LAB_08A2 + +; do function call error + +LAB_FCER + LDX #$08 ; error code $08 ("Function call" error) +LAB_1E8A + JMP LAB_XERR ; do error #X, then warm start + + ; found array, are we trying to dimension it? +LAB_1E8D + LDX #$12 ; set error $12 ("Double dimension" error) + LDA Defdim ; get DIM flag + BNE LAB_1E8A ; if we are trying to dimension it do error #X, then warm + ; start + +; found the array and we're not dimensioning it so we must find an element in it + + JSR LAB_1DE6 ; set-up array pointer (Adatal/h) to first element in array + ; (Astrtl,Astrth points to start of array) + LDA Dimcnt ; get dimensions count + LDY #$04 ; set index to array's # of dimensions + CMP (Astrtl),Y ; compare with no of dimensions + BNE LAB_1E85 ; if wrong do array bounds error, could do "Wrong + ; dimensions" error here .. if we want a different + ; error message + + JMP LAB_1F28 ; found array so go get element + ; (could jump to LAB_1F28 as all LAB_1F24 does is take + ; Dimcnt and save it at (Astrtl),Y which is already the + ; same or we would have taken the BNE) + + ; array not found, so build it +LAB_1EA1 + JSR LAB_1DE6 ; set-up array pointer (Adatal/h) to first element in array + ; (Astrtl,Astrth points to start of array) + JSR LAB_121F ; check available memory, "Out of memory" error if no room + ; addr to check is in AY (low/high) + LDY #$00 ; clear Y (don't need to clear A) + STY Aspth ; clear array data size high byte + LDA Varnm1 ; get variable name 1st byte + STA (Astrtl),Y ; save array name 1st byte + INY ; increment index + LDA Varnm2 ; get variable name 2nd byte + STA (Astrtl),Y ; save array name 2nd byte + LDA Dimcnt ; get dimensions count + LDY #$04 ; index to dimension count + STY Asptl ; set array data size low byte (four bytes per element) + STA (Astrtl),Y ; set array's dimensions count + + ; now calculate the size of the data space for the array + CLC ; clear carry for add (clear on subsequent loops) +LAB_1EC0 + LDX #$0B ; set default dimension value low byte + LDA #$00 ; set default dimension value high byte + BIT Defdim ; test default DIM flag + BVC LAB_1ED0 ; branch if b6 of Defdim is clear + + PLA ; else pull dimension value low byte + ADC #$01 ; +1 (allow for zeroeth element) + TAX ; copy low byte to X + PLA ; pull dimension value high byte + ADC #$00 ; add carry from low byte + +LAB_1ED0 + INY ; index to dimension value high byte + STA (Astrtl),Y ; save dimension value high byte + INY ; index to dimension value high byte + TXA ; get dimension value low byte + STA (Astrtl),Y ; save dimension value low byte + JSR LAB_1F7C ; does XY = (Astrtl),Y * (Asptl) + STX Asptl ; save array data size low byte + STA Aspth ; save array data size high byte + LDY ut1_pl ; restore index (saved by subroutine) + DEC Dimcnt ; decrement dimensions count + BNE LAB_1EC0 ; loop while not = 0 + + ADC Adatah ; add size high byte to first element high byte + ; (carry is always clear here) + BCS LAB_1F45 ; if overflow go do "Out of memory" error + + STA Adatah ; save end of array high byte + TAY ; copy end high byte to Y + TXA ; get array size low byte + ADC Adatal ; add array start low byte + BCC LAB_1EF3 ; branch if no carry + + INY ; else increment end of array high byte + BEQ LAB_1F45 ; if overflow go do "Out of memory" error + + ; set-up mostly complete, now zero the array +LAB_1EF3 + JSR LAB_121F ; check available memory, "Out of memory" error if no room + ; addr to check is in AY (low/high) + STA Earryl ; save array mem end low byte + STY Earryh ; save array mem end high byte + LDA #$00 ; clear byte for array clear + INC Aspth ; increment array size high byte (now block count) + LDY Asptl ; get array size low byte (now index to block) + BEQ LAB_1F07 ; branch if low byte = $00 + +LAB_1F02 + DEY ; decrement index (do 0 to n-1) + STA (Adatal),Y ; zero byte + BNE LAB_1F02 ; loop until this block done + +LAB_1F07 + DEC Adatah ; decrement array pointer high byte + DEC Aspth ; decrement block count high byte + BNE LAB_1F02 ; loop until all blocks done + + INC Adatah ; correct for last loop + SEC ; set carry for subtract + LDY #$02 ; index to array size low byte + LDA Earryl ; get array mem end low byte + SBC Astrtl ; subtract array start low byte + STA (Astrtl),Y ; save array size low byte + INY ; index to array size high byte + LDA Earryh ; get array mem end high byte + SBC Astrth ; subtract array start high byte + STA (Astrtl),Y ; save array size high byte + LDA Defdim ; get default DIM flag + BNE LAB_1F7B ; exit (RET) if this was a DIM command + + ; else, find element + INY ; index to # of dimensions + +LAB_1F24 + LDA (Astrtl),Y ; get array's dimension count + STA Dimcnt ; save it + +; we have found, or built, the array. now we need to find the element + +LAB_1F28 + LDA #$00 ; clear byte + STA Asptl ; clear array data pointer low byte +LAB_1F2C + STA Aspth ; save array data pointer high byte + INY ; increment index (point to array bound high byte) + PLA ; pull array index low byte + TAX ; copy to X + STA FAC1_2 ; save index low byte to FAC1 mantissa2 + PLA ; pull array index high byte + STA FAC1_3 ; save index high byte to FAC1 mantissa3 + CMP (Astrtl),Y ; compare with array bound high byte + BCC LAB_1F48 ; branch if within bounds + + BNE LAB_1F42 ; if outside bounds do array bounds error + + ; else high byte was = so test low bytes + INY ; index to array bound low byte + TXA ; get array index low byte + CMP (Astrtl),Y ; compare with array bound low byte + BCC LAB_1F49 ; branch if within bounds + +LAB_1F42 + JMP LAB_1E85 ; else do array bounds error + +LAB_1F45 + JMP LAB_OMER ; do "Out of memory" error then warm start + +LAB_1F48 + INY ; index to array bound low byte +LAB_1F49 + LDA Aspth ; get array data pointer high byte + ORA Asptl ; OR with array data pointer low byte + BEQ LAB_1F5A ; branch if array data pointer = null (skip multiply) + + JSR LAB_1F7C ; does XY = (Astrtl),Y * (Asptl) + TXA ; get result low byte + ADC FAC1_2 ; add index low byte from FAC1 mantissa2 + TAX ; save result low byte + TYA ; get result high byte + LDY ut1_pl ; restore index +LAB_1F5A + ADC FAC1_3 ; add index high byte from FAC1 mantissa3 + STX Asptl ; save array data pointer low byte + DEC Dimcnt ; decrement dimensions count + BNE LAB_1F2C ; loop if dimensions still to do + + ASL Asptl ; array data pointer low byte * 2 + ROL ; array data pointer high byte * 2 + ASL Asptl ; array data pointer low byte * 4 + ROL ; array data pointer high byte * 4 + TAY ; copy high byte + LDA Asptl ; get low byte + ADC Adatal ; add array data start pointer low byte + STA Cvaral ; save as current var address low byte + TYA ; get high byte back + ADC Adatah ; add array data start pointer high byte + STA Cvarah ; save as current var address high byte + TAY ; copy high byte to Y + LDA Cvaral ; get current var address low byte +LAB_1F7B + RTS + +; does XY = (Astrtl),Y * (Asptl) + +LAB_1F7C + STY ut1_pl ; save index + LDA (Astrtl),Y ; get dimension size low byte + STA dims_l ; save dimension size low byte + DEY ; decrement index + LDA (Astrtl),Y ; get dimension size high byte + STA dims_h ; save dimension size high byte + + LDA #$10 ; count = $10 (16 bit multiply) + STA numbit ; save bit count + LDX #$00 ; clear result low byte + LDY #$00 ; clear result high byte +LAB_1F8F + TXA ; get result low byte + ASL ; *2 + TAX ; save result low byte + TYA ; get result high byte + ROL ; *2 + TAY ; save result high byte + BCS LAB_1F45 ; if overflow go do "Out of memory" error + + ASL Asptl ; shift multiplier low byte + ROL Aspth ; shift multiplier high byte + BCC LAB_1FA8 ; skip add if no carry + + CLC ; else clear carry for add + TXA ; get result low byte + ADC dims_l ; add dimension size low byte + TAX ; save result low byte + TYA ; get result high byte + ADC dims_h ; add dimension size high byte + TAY ; save result high byte + BCS LAB_1F45 ; if overflow go do "Out of memory" error + +LAB_1FA8 + DEC numbit ; decrement bit count + BNE LAB_1F8F ; loop until all done + + RTS + +; perform FRE() + +LAB_FRE + LDA Dtypef ; get data type flag, $FF=string, $00=numeric + BPL LAB_1FB4 ; branch if numeric + + JSR LAB_22B6 ; pop string off descriptor stack, or from top of string + ; space returns with A = length, X=$71=pointer low byte, + ; Y=$72=pointer high byte + + ; FRE(n) was numeric so do this +LAB_1FB4 + JSR LAB_GARB ; go do garbage collection + SEC ; set carry for subtract + LDA Sstorl ; get bottom of string space low byte + SBC Earryl ; subtract array mem end low byte + TAY ; copy result to Y + LDA Sstorh ; get bottom of string space high byte + SBC Earryh ; subtract array mem end high byte + +; save and convert integer AY to FAC1 + +LAB_AYFC + LSR Dtypef ; clear data type flag, $FF=string, $00=numeric + STA FAC1_1 ; save FAC1 mantissa1 + STY FAC1_2 ; save FAC1 mantissa2 + LDX #$90 ; set exponent=2^16 (integer) + JMP LAB_27E3 ; set exp=X, clear FAC1_3, normalise and return + +; perform POS() + +LAB_POS + LDY TPos ; get terminal position + +; convert Y to byte in FAC1 + +LAB_1FD0 + LDA #$00 ; clear high byte + BEQ LAB_AYFC ; always save and convert integer AY to FAC1 and return + +; check not Direct (used by DEF and INPUT) + +LAB_CKRN + LDX Clineh ; get current line high byte + INX ; increment it + BNE LAB_1F7B ; return if can continue not direct mode + + ; else do illegal direct error +LAB_1FD9 + LDX #$16 ; error code $16 ("Illegal direct" error) +LAB_1FDB + JMP LAB_XERR ; go do error #X, then warm start + +; perform DEF + +LAB_DEF + JSR LAB_200B ; check FNx syntax + STA func_l ; save function pointer low byte + STY func_h ; save function pointer high byte + JSR LAB_CKRN ; check not Direct (back here if ok) + JSR LAB_1BFE ; scan for "(" , else do syntax error then warm start + LDA #$80 ; set flag for FNx + STA Sufnxf ; save subscript/FNx flag + JSR LAB_GVAR ; get (var) address + JSR LAB_CTNM ; check if source is numeric, else do type mismatch + JSR LAB_1BFB ; scan for ")" , else do syntax error then warm start + LDA #TK_EQUAL ; get = token + JSR LAB_SCCA ; scan for CHR$(A), else do syntax error then warm start + LDA Cvarah ; get current var address high byte + PHA ; push it + LDA Cvaral ; get current var address low byte + PHA ; push it + LDA Bpntrh ; get BASIC execute pointer high byte + PHA ; push it + LDA Bpntrl ; get BASIC execute pointer low byte + PHA ; push it + JSR LAB_DATA ; go perform DATA + JMP LAB_207A ; put execute pointer and variable pointer into function + ; and return + +; check FNx syntax + +LAB_200B + LDA #TK_FN ; get FN" token + JSR LAB_SCCA ; scan for CHR$(A) , else do syntax error then warm start + ; return character after A + ORA #$80 ; set FN flag bit + STA Sufnxf ; save FN flag so array variable test fails + JSR LAB_1D12 ; search for FN variable + JMP LAB_CTNM ; check if source is numeric and return, else do type + ; mismatch + + ; Evaluate FNx +LAB_201E + JSR LAB_200B ; check FNx syntax + PHA ; push function pointer low byte + TYA ; copy function pointer high byte + PHA ; push function pointer high byte + JSR LAB_1BFE ; scan for "(", else do syntax error then warm start + JSR LAB_EVEX ; evaluate expression + JSR LAB_1BFB ; scan for ")", else do syntax error then warm start + JSR LAB_CTNM ; check if source is numeric, else do type mismatch + PLA ; pop function pointer high byte + STA func_h ; restore it + PLA ; pop function pointer low byte + STA func_l ; restore it + LDX #$20 ; error code $20 ("Undefined function" error) + LDY #$03 ; index to variable pointer high byte + LDA (func_l),Y ; get variable pointer high byte + BEQ LAB_1FDB ; if zero go do undefined function error + + STA Cvarah ; save variable address high byte + DEY ; index to variable address low byte + LDA (func_l),Y ; get variable address low byte + STA Cvaral ; save variable address low byte + TAX ; copy address low byte + + ; now stack the function variable value before use + INY ; index to mantissa_3 +LAB_2043 + LDA (Cvaral),Y ; get byte from variable + PHA ; stack it + DEY ; decrement index + BPL LAB_2043 ; loop until variable stacked + + LDY Cvarah ; get variable address high byte + JSR LAB_2778 ; pack FAC1 (function expression value) into (XY) + ; (function variable), return Y=0, always + LDA Bpntrh ; get BASIC execute pointer high byte + PHA ; push it + LDA Bpntrl ; get BASIC execute pointer low byte + PHA ; push it + LDA (func_l),Y ; get function execute pointer low byte + STA Bpntrl ; save as BASIC execute pointer low byte + INY ; index to high byte + LDA (func_l),Y ; get function execute pointer high byte + STA Bpntrh ; save as BASIC execute pointer high byte + LDA Cvarah ; get variable address high byte + PHA ; push it + LDA Cvaral ; get variable address low byte + PHA ; push it + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + PLA ; pull variable address low byte + STA func_l ; save variable address low byte + PLA ; pull variable address high byte + STA func_h ; save variable address high byte + JSR LAB_GBYT ; scan memory + BEQ LAB_2074 ; branch if null (should be [EOL] marker) + + JMP LAB_SNER ; else syntax error then warm start + +; restore Bpntrl,Bpntrh and function variable from stack + +LAB_2074 + PLA ; pull BASIC execute pointer low byte + STA Bpntrl ; restore BASIC execute pointer low byte + PLA ; pull BASIC execute pointer high byte + STA Bpntrh ; restore BASIC execute pointer high byte + +; put execute pointer and variable pointer into function + +LAB_207A + LDY #$00 ; clear index + PLA ; pull BASIC execute pointer low byte + STA (func_l),Y ; save to function + INY ; increment index + PLA ; pull BASIC execute pointer high byte + STA (func_l),Y ; save to function + INY ; increment index + PLA ; pull current var address low byte + STA (func_l),Y ; save to function + INY ; increment index + PLA ; pull current var address high byte + STA (func_l),Y ; save to function + RTS + +; perform STR$() + +LAB_STRS + JSR LAB_CTNM ; check if source is numeric, else do type mismatch + JSR LAB_296E ; convert FAC1 to string + LDA #Decssp1 ; set result string high pointer + BEQ LAB_20AE ; print null terminated string to Sutill/Sutilh + +; Do string vector +; copy des_pl/h to des_2l/h and make string space A bytes long + +LAB_209C + LDX des_pl ; get descriptor pointer low byte + LDY des_ph ; get descriptor pointer high byte + STX des_2l ; save descriptor pointer low byte + STY des_2h ; save descriptor pointer high byte + +; make string space A bytes long +; A=length, X=Sutill=ptr low byte, Y=Sutilh=ptr high byte + +LAB_MSSP + JSR LAB_2115 ; make space in string memory for string A long + ; return X=Sutill=ptr low byte, Y=Sutilh=ptr high byte + STX str_pl ; save string pointer low byte + STY str_ph ; save string pointer high byte + STA str_ln ; save length + RTS + +; Scan, set up string +; print " terminated string to Sutill/Sutilh + +LAB_20AE + LDX #$22 ; set terminator to " + STX Srchc ; set search character (terminator 1) + STX Asrch ; set terminator 2 + +; print [Srchc] or [Asrch] terminated string to Sutill/Sutilh +; source is AY + +LAB_20B4 + STA ssptr_l ; store string start low byte + STY ssptr_h ; store string start high byte + STA str_pl ; save string pointer low byte + STY str_ph ; save string pointer high byte + LDY #$FF ; set length to -1 +LAB_20BE + INY ; increment length + LDA (ssptr_l),Y ; get byte from string + BEQ LAB_20CF ; exit loop if null byte [EOS] + + CMP Srchc ; compare with search character (terminator 1) + BEQ LAB_20CB ; branch if terminator + + CMP Asrch ; compare with terminator 2 + BNE LAB_20BE ; loop if not terminator 2 + +LAB_20CB + CMP #$22 ; compare with " + BEQ LAB_20D0 ; branch if " (carry set if = !) + +LAB_20CF + CLC ; clear carry for add (only if [EOL] terminated string) +LAB_20D0 + STY str_ln ; save length in FAC1 exponent + TYA ; copy length to A + ADC ssptr_l ; add string start low byte + STA Sendl ; save string end low byte + LDX ssptr_h ; get string start high byte + BCC LAB_20DC ; branch if no low byte overflow + + INX ; else increment high byte +LAB_20DC + STX Sendh ; save string end high byte + LDA ssptr_h ; get string start high byte + CMP #>Ram_base ; compare with start of program memory + BCS LAB_RTST ; branch if not in utility area + + ; string in utility area, move to string memory + TYA ; copy length to A + JSR LAB_209C ; copy des_pl/h to des_2l/h and make string space A bytes + ; long + LDX ssptr_l ; get string start low byte + LDY ssptr_h ; get string start high byte + JSR LAB_2298 ; store string A bytes long from XY to (Sutill) + +; check for space on descriptor stack then .. +; put string address and length on descriptor stack and update stack pointers + +LAB_RTST + LDX next_s ; get string stack pointer + CPX #des_sk+$09 ; compare with max+1 + BNE LAB_20F8 ; branch if space on string stack + + ; else do string too complex error + LDX #$1C ; error code $1C ("String too complex" error) +LAB_20F5 + JMP LAB_XERR ; do error #X, then warm start + +; put string address and length on descriptor stack and update stack pointers + +LAB_20F8 + LDA str_ln ; get string length + STA PLUS_0,X ; put on string stack + LDA str_pl ; get string pointer low byte + STA PLUS_1,X ; put on string stack + LDA str_ph ; get string pointer high byte + STA PLUS_2,X ; put on string stack + LDY #$00 ; clear Y + STX des_pl ; save string descriptor pointer low byte + STY des_ph ; save string descriptor pointer high byte (always $00) + DEY ; Y = $FF + STY Dtypef ; save data type flag, $FF=string + STX last_sl ; save old stack pointer (current top item) + INX ; update stack pointer + INX ; update stack pointer + INX ; update stack pointer + STX next_s ; save new top item value + RTS + +; Build descriptor +; make space in string memory for string A long +; return X=Sutill=ptr low byte, Y=Sutill=ptr high byte + +LAB_2115 + LSR Gclctd ; clear garbage collected flag (b7) + + ; make space for string A long +LAB_2117 + PHA ; save string length + EOR #$FF ; complement it + SEC ; set carry for subtract (twos comp add) + ADC Sstorl ; add bottom of string space low byte (subtract length) + LDY Sstorh ; get bottom of string space high byte + BCS LAB_2122 ; skip decrement if no underflow + + DEY ; decrement bottom of string space high byte +LAB_2122 + CPY Earryh ; compare with array mem end high byte + BCC LAB_2137 ; do out of memory error if less + + BNE LAB_212C ; if not = skip next test + + CMP Earryl ; compare with array mem end low byte + BCC LAB_2137 ; do out of memory error if less + +LAB_212C + STA Sstorl ; save bottom of string space low byte + STY Sstorh ; save bottom of string space high byte + STA Sutill ; save string utility ptr low byte + STY Sutilh ; save string utility ptr high byte + TAX ; copy low byte to X + PLA ; get string length back + RTS + +LAB_2137 + LDX #$0C ; error code $0C ("Out of memory" error) + LDA Gclctd ; get garbage collected flag + BMI LAB_20F5 ; if set then do error code X + + JSR LAB_GARB ; else go do garbage collection + LDA #$80 ; flag for garbage collected + STA Gclctd ; set garbage collected flag + PLA ; pull length + BNE LAB_2117 ; go try again (loop always, length should never be = $00) + +; garbage collection routine + +LAB_GARB + LDX Ememl ; get end of mem low byte + LDA Ememh ; get end of mem high byte + +; re-run routine from last ending + +LAB_214B + STX Sstorl ; set string storage low byte + STA Sstorh ; set string storage high byte + LDY #$00 ; clear index + STY garb_h ; clear working pointer high byte (flag no strings to move) + LDA Earryl ; get array mem end low byte + LDX Earryh ; get array mem end high byte + STA Histrl ; save as highest string low byte + STX Histrh ; save as highest string high byte + LDA #des_sk ; set descriptor stack pointer + STA ut1_pl ; save descriptor stack pointer low byte + STY ut1_ph ; save descriptor stack pointer high byte ($00) +LAB_2161 + CMP next_s ; compare with descriptor stack pointer + BEQ LAB_216A ; branch if = + + JSR LAB_21D7 ; go garbage collect descriptor stack + BEQ LAB_2161 ; loop always + + ; done stacked strings, now do string vars +LAB_216A + ASL g_step ; set step size = $06 + LDA Svarl ; get start of vars low byte + LDX Svarh ; get start of vars high byte + STA ut1_pl ; save as pointer low byte + STX ut1_ph ; save as pointer high byte +LAB_2176 + CPX Sarryh ; compare start of arrays high byte + BNE LAB_217E ; branch if no high byte match + + CMP Sarryl ; else compare start of arrays low byte + BEQ LAB_2183 ; branch if = var mem end + +LAB_217E + JSR LAB_21D1 ; go garbage collect strings + BEQ LAB_2176 ; loop always + + ; done string vars, now do string arrays +LAB_2183 + STA Nbendl ; save start of arrays low byte as working pointer + STX Nbendh ; save start of arrays high byte as working pointer + LDA #$04 ; set step size + STA g_step ; save step size +LAB_218B + LDA Nbendl ; get pointer low byte + LDX Nbendh ; get pointer high byte +LAB_218F + CPX Earryh ; compare with array mem end high byte + BNE LAB_219A ; branch if not at end + + CMP Earryl ; else compare with array mem end low byte + BEQ LAB_2216 ; tidy up and exit if at end + +LAB_219A + STA ut1_pl ; save pointer low byte + STX ut1_ph ; save pointer high byte + LDY #$02 ; set index + LDA (ut1_pl),Y ; get array size low byte + ADC Nbendl ; add start of this array low byte + STA Nbendl ; save start of next array low byte + INY ; increment index + LDA (ut1_pl),Y ; get array size high byte + ADC Nbendh ; add start of this array high byte + STA Nbendh ; save start of next array high byte + LDY #$01 ; set index + LDA (ut1_pl),Y ; get name second byte + BPL LAB_218B ; skip if not string array + +; was string array so .. + + LDY #$04 ; set index + LDA (ut1_pl),Y ; get # of dimensions + ASL ; *2 + ADC #$05 ; +5 (array header size) + JSR LAB_2208 ; go set up for first element +LAB_21C4 + CPX Nbendh ; compare with start of next array high byte + BNE LAB_21CC ; branch if <> (go do this array) + + CMP Nbendl ; else compare element pointer low byte with next array + ; low byte + BEQ LAB_218F ; if equal then go do next array + +LAB_21CC + JSR LAB_21D7 ; go defrag array strings + BEQ LAB_21C4 ; go do next array string (loop always) + +; defrag string variables +; enter with XA = variable pointer +; return with XA = next variable pointer + +LAB_21D1 + INY ; increment index (Y was $00) + LDA (ut1_pl),Y ; get var name byte 2 + BPL LAB_2206 ; if not string, step pointer to next var and return + + INY ; else increment index +LAB_21D7 + LDA (ut1_pl),Y ; get string length + BEQ LAB_2206 ; if null, step pointer to next string and return + + INY ; else increment index + LDA (ut1_pl),Y ; get string pointer low byte + TAX ; copy to X + INY ; increment index + LDA (ut1_pl),Y ; get string pointer high byte + CMP Sstorh ; compare bottom of string space high byte + BCC LAB_21EC ; branch if less + + BNE LAB_2206 ; if greater, step pointer to next string and return + + ; high bytes were = so compare low bytes + CPX Sstorl ; compare bottom of string space low byte + BCS LAB_2206 ; if >=, step pointer to next string and return + + ; string pointer is < string storage pointer (pos in mem) +LAB_21EC + CMP Histrh ; compare to highest string high byte + BCC LAB_2207 ; if <, step pointer to next string and return + + BNE LAB_21F6 ; if > update pointers, step to next and return + + ; high bytes were = so compare low bytes + CPX Histrl ; compare to highest string low byte + BCC LAB_2207 ; if <, step pointer to next string and return + + ; string is in string memory space +LAB_21F6 + STX Histrl ; save as new highest string low byte + STA Histrh ; save as new highest string high byte + LDA ut1_pl ; get start of vars(descriptors) low byte + LDX ut1_ph ; get start of vars(descriptors) high byte + STA garb_l ; save as working pointer low byte + STX garb_h ; save as working pointer high byte + DEY ; decrement index DIFFERS + DEY ; decrement index (should point to descriptor start) + STY g_indx ; save index pointer + + ; step pointer to next string +LAB_2206 + CLC ; clear carry for add +LAB_2207 + LDA g_step ; get step size +LAB_2208 + ADC ut1_pl ; add pointer low byte + STA ut1_pl ; save pointer low byte + BCC LAB_2211 ; branch if no overflow + + INC ut1_ph ; else increment high byte +LAB_2211 + LDX ut1_ph ; get pointer high byte + LDY #$00 ; clear Y + RTS + +; search complete, now either exit or set-up and move string + +LAB_2216 + DEC g_step ; decrement step size (now $03 for descriptor stack) + LDX garb_h ; get string to move high byte + BEQ LAB_2211 ; exit if nothing to move + + LDY g_indx ; get index byte back (points to descriptor) + CLC ; clear carry for add + LDA (garb_l),Y ; get string length + ADC Histrl ; add highest string low byte + STA Obendl ; save old block end low pointer + LDA Histrh ; get highest string high byte + ADC #$00 ; add any carry + STA Obendh ; save old block end high byte + LDA Sstorl ; get bottom of string space low byte + LDX Sstorh ; get bottom of string space high byte + STA Nbendl ; save new block end low byte + STX Nbendh ; save new block end high byte + JSR LAB_11D6 ; open up space in memory, don't set array end + LDY g_indx ; get index byte + INY ; point to descriptor low byte + LDA Nbendl ; get string pointer low byte + STA (garb_l),Y ; save new string pointer low byte + TAX ; copy string pointer low byte + INC Nbendh ; correct high byte (move sets high byte -1) + LDA Nbendh ; get new string pointer high byte + INY ; point to descriptor high byte + STA (garb_l),Y ; save new string pointer high byte + JMP LAB_214B ; re-run routine from last ending + ; (but don't collect this string) + +; concatenate +; add strings, string 1 is in descriptor des_pl, string 2 is in line + +LAB_224D + LDA des_ph ; get descriptor pointer high byte + PHA ; put on stack + LDA des_pl ; get descriptor pointer low byte + PHA ; put on stack + JSR LAB_GVAL ; get value from line + JSR LAB_CTST ; check if source is string, else do type mismatch + PLA ; get descriptor pointer low byte back + STA ssptr_l ; set pointer low byte + PLA ; get descriptor pointer high byte back + STA ssptr_h ; set pointer high byte + LDY #$00 ; clear index + LDA (ssptr_l),Y ; get length_1 from descriptor + CLC ; clear carry for add + ADC (des_pl),Y ; add length_2 + BCC LAB_226D ; branch if no overflow + + LDX #$1A ; else set error code $1A ("String too long" error) + JMP LAB_XERR ; do error #X, then warm start + +LAB_226D + JSR LAB_209C ; copy des_pl/h to des_2l/h and make string space A bytes + ; long + JSR LAB_228A ; copy string from descriptor (sdescr) to (Sutill) + LDA des_2l ; get descriptor pointer low byte + LDY des_2h ; get descriptor pointer high byte + JSR LAB_22BA ; pop (YA) descriptor off stack or from top of string space + ; returns with A = length, ut1_pl = pointer low byte, + ; ut1_ph = pointer high byte + JSR LAB_229C ; store string A bytes long from (ut1_pl) to (Sutill) + LDA ssptr_l ;.set descriptor pointer low byte + LDY ssptr_h ;.set descriptor pointer high byte + JSR LAB_22BA ; pop (YA) descriptor off stack or from top of string space + ; returns with A = length, X=ut1_pl=pointer low byte, + ; Y=ut1_ph=pointer high byte + JSR LAB_RTST ; check for space on descriptor stack then put string + ; address and length on descriptor stack and update stack + ; pointers + JMP LAB_1ADB ;.continue evaluation + +; copy string from descriptor (sdescr) to (Sutill) + +LAB_228A + LDY #$00 ; clear index + LDA (sdescr),Y ; get string length + PHA ; save on stack + INY ; increment index + LDA (sdescr),Y ; get source string pointer low byte + TAX ; copy to X + INY ; increment index + LDA (sdescr),Y ; get source string pointer high byte + TAY ; copy to Y + PLA ; get length back + +; store string A bytes long from YX to (Sutill) + +LAB_2298 + STX ut1_pl ; save source string pointer low byte + STY ut1_ph ; save source string pointer high byte + +; store string A bytes long from (ut1_pl) to (Sutill) + +LAB_229C + TAX ; copy length to index (don't count with Y) + BEQ LAB_22B2 ; branch if = $0 (null string) no need to add zero length + + LDY #$00 ; zero pointer (copy forward) +LAB_22A0 + LDA (ut1_pl),Y ; get source byte + STA (Sutill),Y ; save destination byte + + INY ; increment index + DEX ; decrement counter + BNE LAB_22A0 ; loop while <> 0 + + TYA ; restore length from Y +LAB_22A9 + CLC ; clear carry for add + ADC Sutill ; add string utility ptr low byte + STA Sutill ; save string utility ptr low byte + BCC LAB_22B2 ; branch if no carry + + INC Sutilh ; else increment string utility ptr high byte +LAB_22B2 + RTS + +; evaluate string + +LAB_EVST + JSR LAB_CTST ; check if source is string, else do type mismatch + +; pop string off descriptor stack, or from top of string space +; returns with A = length, X=pointer low byte, Y=pointer high byte + +LAB_22B6 + LDA des_pl ; get descriptor pointer low byte + LDY des_ph ; get descriptor pointer high byte + +; pop (YA) descriptor off stack or from top of string space +; returns with A = length, X=ut1_pl=pointer low byte, Y=ut1_ph=pointer high byte + +LAB_22BA + STA ut1_pl ; save descriptor pointer low byte + STY ut1_ph ; save descriptor pointer high byte + JSR LAB_22EB ; clean descriptor stack, YA = pointer + PHP ; save status flags + LDY #$00 ; clear index + LDA (ut1_pl),Y ; get length from string descriptor + PHA ; put on stack + INY ; increment index + LDA (ut1_pl),Y ; get string pointer low byte from descriptor + TAX ; copy to X + INY ; increment index + LDA (ut1_pl),Y ; get string pointer high byte from descriptor + TAY ; copy to Y + PLA ; get string length back + PLP ; restore status + BNE LAB_22E6 ; branch if pointer <> last_sl,last_sh + + CPY Sstorh ; compare bottom of string space high byte + BNE LAB_22E6 ; branch if <> + + CPX Sstorl ; else compare bottom of string space low byte + BNE LAB_22E6 ; branch if <> + + PHA ; save string length + CLC ; clear carry for add + ADC Sstorl ; add bottom of string space low byte + STA Sstorl ; save bottom of string space low byte + BCC LAB_22E5 ; skip increment if no overflow + + INC Sstorh ; increment bottom of string space high byte +LAB_22E5 + PLA ; restore string length +LAB_22E6 + STX ut1_pl ; save string pointer low byte + STY ut1_ph ; save string pointer high byte + RTS + +; clean descriptor stack, YA = pointer +; checks if AY is on the descriptor stack, if so does a stack discard + +LAB_22EB + CPY last_sh ; compare pointer high byte + BNE LAB_22FB ; exit if <> + + CMP last_sl ; compare pointer low byte + BNE LAB_22FB ; exit if <> + + STA next_s ; save descriptor stack pointer + SBC #$03 ; -3 + STA last_sl ; save low byte -3 + LDY #$00 ; clear high byte +LAB_22FB + RTS + +; perform CHR$() + +LAB_CHRS + JSR LAB_EVBY ; evaluate byte expression, result in X + TXA ; copy to A + PHA ; save character + LDA #$01 ; string is single byte + JSR LAB_MSSP ; make string space A bytes long A=$AC=length, + ; X=$AD=Sutill=ptr low byte, Y=$AE=Sutilh=ptr high byte + PLA ; get character back + LDY #$00 ; clear index + STA (str_pl),Y ; save byte in string (byte IS string!) + JMP LAB_RTST ; check for space on descriptor stack then put string + ; address and length on descriptor stack and update stack + ; pointers + +; perform LEFT$() + +LAB_LEFT + PHA ; push byte parameter + JSR LAB_236F ; pull string data and byte parameter from stack + ; return pointer in des_2l/h, byte in A (and X), Y=0 + CMP (des_2l),Y ; compare byte parameter with string length + TYA ; clear A + BEQ LAB_2316 ; go do string copy (branch always) + +; perform RIGHT$() + +LAB_RIGHT + PHA ; push byte parameter + JSR LAB_236F ; pull string data and byte parameter from stack + ; return pointer in des_2l/h, byte in A (and X), Y=0 + CLC ; clear carry for add-1 + SBC (des_2l),Y ; subtract string length + EOR #$FF ; invert it (A=LEN(expression$)-l) + +LAB_2316 + BCC LAB_231C ; branch if string length > byte parameter + + LDA (des_2l),Y ; else make parameter = length + TAX ; copy to byte parameter copy + TYA ; clear string start offset +LAB_231C + PHA ; save string start offset +LAB_231D + TXA ; copy byte parameter (or string length if <) +LAB_231E + PHA ; save string length + JSR LAB_MSSP ; make string space A bytes long A=$AC=length, + ; X=$AD=Sutill=ptr low byte, Y=$AE=Sutilh=ptr high byte + LDA des_2l ; get descriptor pointer low byte + LDY des_2h ; get descriptor pointer high byte + JSR LAB_22BA ; pop (YA) descriptor off stack or from top of string space + ; returns with A = length, X=ut1_pl=pointer low byte, + ; Y=ut1_ph=pointer high byte + PLA ; get string length back + TAY ; copy length to Y + PLA ; get string start offset back + CLC ; clear carry for add + ADC ut1_pl ; add start offset to string start pointer low byte + STA ut1_pl ; save string start pointer low byte + BCC LAB_2335 ; branch if no overflow + + INC ut1_ph ; else increment string start pointer high byte +LAB_2335 + TYA ; copy length to A + JSR LAB_229C ; store string A bytes long from (ut1_pl) to (Sutill) + JMP LAB_RTST ; check for space on descriptor stack then put string + ; address and length on descriptor stack and update stack + ; pointers + +; perform MID$() + +LAB_MIDS + PHA ; push byte parameter + LDA #$FF ; set default length = 255 + STA mids_l ; save default length + JSR LAB_GBYT ; scan memory + CMP #')' ; compare with ")" + BEQ LAB_2358 ; branch if = ")" (skip second byte get) + + JSR LAB_1C01 ; scan for "," , else do syntax error then warm start + JSR LAB_GTBY ; get byte parameter (use copy in mids_l) +LAB_2358 + JSR LAB_236F ; pull string data and byte parameter from stack + ; return pointer in des_2l/h, byte in A (and X), Y=0 + DEX ; decrement start index + TXA ; copy to A + PHA ; save string start offset + CLC ; clear carry for sub-1 + LDX #$00 ; clear output string length + SBC (des_2l),Y ; subtract string length + BCS LAB_231D ; if start>string length go do null string + + EOR #$FF ; complement -length + CMP mids_l ; compare byte parameter + BCC LAB_231E ; if length>remaining string go do RIGHT$ + + LDA mids_l ; get length byte + BCS LAB_231E ; go do string copy (branch always) + +; pull string data and byte parameter from stack +; return pointer in des_2l/h, byte in A (and X), Y=0 + +LAB_236F + JSR LAB_1BFB ; scan for ")" , else do syntax error then warm start + PLA ; pull return address low byte (return address) + STA Fnxjpl ; save functions jump vector low byte + PLA ; pull return address high byte (return address) + STA Fnxjph ; save functions jump vector high byte + PLA ; pull byte parameter + TAX ; copy byte parameter to X + PLA ; pull string pointer low byte + STA des_2l ; save it + PLA ; pull string pointer high byte + STA des_2h ; save it + LDY #$00 ; clear index + TXA ; copy byte parameter + BEQ LAB_23A8 ; if null do function call error then warm start + + INC Fnxjpl ; increment function jump vector low byte + ; (JSR pushes return addr-1. this is all very nice + ; but will go tits up if either call is on a page + ; boundary!) + JMP (Fnxjpl) ; in effect, RTS + +; perform LCASE$() + +LAB_LCASE + JSR LAB_EVST ; evaluate string + STA str_ln ; set string length + TAY ; copy length to Y + BEQ NoString ; branch if null string + + JSR LAB_MSSP ; make string space A bytes long A=length, + ; X=Sutill=ptr low byte, Y=Sutilh=ptr high byte + STX str_pl ; save string pointer low byte + STY str_ph ; save string pointer high byte + TAY ; get string length back + +LC_loop + DEY ; decrement index + LDA (ut1_pl),Y ; get byte from string + JSR LAB_1D82 ; is character "A" to "Z" + BCC NoUcase ; branch if not upper case alpha + + ORA #$20 ; convert upper to lower case +NoUcase + STA (Sutill),Y ; save byte back to string + TYA ; test index + BNE LC_loop ; loop if not all done + + BEQ NoString ; tidy up and exit, branch always + +; perform UCASE$() + +LAB_UCASE + JSR LAB_EVST ; evaluate string + STA str_ln ; set string length + TAY ; copy length to Y + BEQ NoString ; branch if null string + + JSR LAB_MSSP ; make string space A bytes long A=length, + ; X=Sutill=ptr low byte, Y=Sutilh=ptr high byte + STX str_pl ; save string pointer low byte + STY str_ph ; save string pointer high byte + TAY ; get string length back + +UC_loop + DEY ; decrement index + LDA (ut1_pl),Y ; get byte from string + JSR LAB_CASC ; is character "a" to "z" (or "A" to "Z") + BCC NoLcase ; branch if not alpha + + AND #$DF ; convert lower to upper case +NoLcase + STA (Sutill),Y ; save byte back to string + TYA ; test index + BNE UC_loop ; loop if not all done + +NoString + JMP LAB_RTST ; check for space on descriptor stack then put string + ; address and length on descriptor stack and update stack + ; pointers + +; perform SADD() + +LAB_SADD + JSR LAB_IGBY ; increment and scan memory + JSR LAB_GVAR ; get var address + + JSR LAB_1BFB ; scan for ")", else do syntax error then warm start + JSR LAB_CTST ; check if source is string, else do type mismatch + + LDY #$02 ; index to string pointer high byte + LDA (Cvaral),Y ; get string pointer high byte + TAX ; copy string pointer high byte to X + DEY ; index to string pointer low byte + LDA (Cvaral),Y ; get string pointer low byte + TAY ; copy string pointer low byte to Y + TXA ; copy string pointer high byte to A + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform LEN() + +LAB_LENS + JSR LAB_ESGL ; evaluate string, get length in A (and Y) + JMP LAB_1FD0 ; convert Y to byte in FAC1 and return + +; evaluate string, get length in Y + +LAB_ESGL + JSR LAB_EVST ; evaluate string + TAY ; copy length to Y + RTS + +; perform ASC() + +LAB_ASC + JSR LAB_ESGL ; evaluate string, get length in A (and Y) + BEQ LAB_23A8 ; if null do function call error then warm start + + LDY #$00 ; set index to first character + LDA (ut1_pl),Y ; get byte + TAY ; copy to Y + JMP LAB_1FD0 ; convert Y to byte in FAC1 and return + +; do function call error then warm start + +LAB_23A8 + JMP LAB_FCER ; do function call error then warm start + +; scan and get byte parameter + +LAB_SGBY + JSR LAB_IGBY ; increment and scan memory + +; get byte parameter + +LAB_GTBY + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + +; evaluate byte expression, result in X + +LAB_EVBY + JSR LAB_EVPI ; evaluate integer expression (no check) + + LDY FAC1_2 ; get FAC1 mantissa2 + BNE LAB_23A8 ; if top byte <> 0 do function call error then warm start + + LDX FAC1_3 ; get FAC1 mantissa3 + JMP LAB_GBYT ; scan memory and return + +; perform VAL() + +LAB_VAL + JSR LAB_ESGL ; evaluate string, get length in A (and Y) + BNE LAB_23C5 ; branch if not null string + + ; string was null so set result = $00 + JMP LAB_24F1 ; clear FAC1 exponent and sign and return + +LAB_23C5 + LDX Bpntrl ; get BASIC execute pointer low byte + LDY Bpntrh ; get BASIC execute pointer high byte + STX Btmpl ; save BASIC execute pointer low byte + STY Btmph ; save BASIC execute pointer high byte + LDX ut1_pl ; get string pointer low byte + STX Bpntrl ; save as BASIC execute pointer low byte + CLC ; clear carry + ADC ut1_pl ; add string length + STA ut2_pl ; save string end low byte + LDA ut1_ph ; get string pointer high byte + STA Bpntrh ; save as BASIC execute pointer high byte + ADC #$00 ; add carry to high byte + STA ut2_ph ; save string end high byte + LDY #$00 ; set index to $00 + LDA (ut2_pl),Y ; get string end +1 byte + PHA ; push it + TYA ; clear A + STA (ut2_pl),Y ; terminate string with $00 + JSR LAB_GBYT ; scan memory + JSR LAB_2887 ; get FAC1 from string + PLA ; restore string end +1 byte + LDY #$00 ; set index to zero + STA (ut2_pl),Y ; put string end byte back + +; restore BASIC execute pointer from temp (Btmpl/Btmph) + +LAB_23F3 + LDX Btmpl ; get BASIC execute pointer low byte back + LDY Btmph ; get BASIC execute pointer high byte back + STX Bpntrl ; save BASIC execute pointer low byte + STY Bpntrh ; save BASIC execute pointer high byte + RTS + +; get two parameters for POKE or WAIT + +LAB_GADB + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + JSR LAB_F2FX ; save integer part of FAC1 in temporary integer + +; scan for "," and get byte, else do Syntax error then warm start + +LAB_SCGB + JSR LAB_1C01 ; scan for "," , else do syntax error then warm start + LDA Itemph ; save temporary integer high byte + PHA ; on stack + LDA Itempl ; save temporary integer low byte + PHA ; on stack + JSR LAB_GTBY ; get byte parameter + PLA ; pull low byte + STA Itempl ; restore temporary integer low byte + PLA ; pull high byte + STA Itemph ; restore temporary integer high byte + RTS + +; convert float to fixed routine. accepts any value that fits in 24 bits, +ve or +; -ve and converts it into a right truncated integer in Itempl and Itemph + +; save unsigned 16 bit integer part of FAC1 in temporary integer + +LAB_F2FX + LDA FAC1_e ; get FAC1 exponent + CMP #$98 ; compare with exponent = 2^24 + BCS LAB_23A8 ; if >= do function call error then warm start + +LAB_F2FU + JSR LAB_2831 ; convert FAC1 floating-to-fixed + LDA FAC1_2 ; get FAC1 mantissa2 + LDY FAC1_3 ; get FAC1 mantissa3 + STY Itempl ; save temporary integer low byte + STA Itemph ; save temporary integer high byte + RTS + +; perform PEEK() + +LAB_PEEK + JSR LAB_F2FX ; save integer part of FAC1 in temporary integer + LDX #$00 ; clear index + LDA (Itempl,X) ; get byte via temporary integer (addr) + TAY ; copy byte to Y + JMP LAB_1FD0 ; convert Y to byte in FAC1 and return + +; perform POKE + +LAB_POKE + JSR LAB_GADB ; get two parameters for POKE or WAIT + TXA ; copy byte argument to A + LDX #$00 ; clear index + STA (Itempl,X) ; save byte via temporary integer (addr) + RTS + +; perform DEEK() + +LAB_DEEK + JSR LAB_F2FX ; save integer part of FAC1 in temporary integer + LDX #$00 ; clear index + LDA (Itempl,X) ; PEEK low byte + TAY ; copy to Y + INC Itempl ; increment pointer low byte + BNE Deekh ; skip high increment if no rollover + + INC Itemph ; increment pointer high byte +Deekh + LDA (Itempl,X) ; PEEK high byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform DOKE + +LAB_DOKE + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + JSR LAB_F2FX ; convert floating-to-fixed + + STY Frnxtl ; save pointer low byte (float to fixed returns word in AY) + STA Frnxth ; save pointer high byte + + JSR LAB_1C01 ; scan for "," , else do syntax error then warm start + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + JSR LAB_F2FX ; convert floating-to-fixed + + TYA ; copy value low byte (float to fixed returns word in AY) + LDX #$00 ; clear index + STA (Frnxtl,X) ; POKE low byte + INC Frnxtl ; increment pointer low byte + BNE Dokeh ; skip high increment if no rollover + + INC Frnxth ; increment pointer high byte +Dokeh + LDA Itemph ; get value high byte + STA (Frnxtl,X) ; POKE high byte + JMP LAB_GBYT ; scan memory and return + +; perform SWAP + +LAB_SWAP + JSR LAB_GVAR ; get var1 address + STA Lvarpl ; save var1 address low byte + STY Lvarph ; save var1 address high byte + LDA Dtypef ; get data type flag, $FF=string, $00=numeric + PHA ; save data type flag + + JSR LAB_1C01 ; scan for "," , else do syntax error then warm start + JSR LAB_GVAR ; get var2 address (pointer in Cvaral/h) + PLA ; pull var1 data type flag + EOR Dtypef ; compare with var2 data type + BPL SwapErr ; exit if not both the same type + + LDY #$03 ; four bytes to swap (either value or descriptor+1) +SwapLp + LDA (Lvarpl),Y ; get byte from var1 + TAX ; save var1 byte + LDA (Cvaral),Y ; get byte from var2 + STA (Lvarpl),Y ; save byte to var1 + TXA ; restore var1 byte + STA (Cvaral),Y ; save byte to var2 + DEY ; decrement index + BPL SwapLp ; loop until done + + RTS + +SwapErr + JMP LAB_1ABC ; do "Type mismatch" error then warm start + +; perform CALL + +LAB_CALL + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + JSR LAB_F2FX ; convert floating-to-fixed + LDA #>CallExit ; set return address high byte + PHA ; put on stack + LDA #8 shifts) + BCC LAB_24A8 ;.go subtract mantissas + +; add 0.5 to FAC1 + +LAB_244E + LDA #LAB_2A96 ; set 0.5 pointer high byte + +; add (AY) to FAC1 + +LAB_246C + JSR LAB_264D ; unpack memory (AY) into FAC2 + +; add FAC2 to FAC1 + +LAB_ADD + BNE LAB_2474 ; branch if FAC1 was not zero + +; copy FAC2 to FAC1 + +LAB_279B + LDA FAC2_s ; get FAC2 sign (b7) + +; save FAC1 sign and copy ABS(FAC2) to FAC1 + +LAB_279D + STA FAC1_s ; save FAC1 sign (b7) + LDX #$04 ; 4 bytes to copy +LAB_27A1 + LDA FAC1_o,X ; get byte from FAC2,X + STA FAC1_e-1,X ; save byte at FAC1,X + DEX ; decrement count + BNE LAB_27A1 ; loop if not all done + + STX FAC1_r ; clear FAC1 rounding byte + RTS + + ; FAC1 is non zero +LAB_2474 + LDX FAC1_r ; get FAC1 rounding byte + STX FAC2_r ; save as FAC2 rounding byte + LDX #FAC2_e ; set index to FAC2 exponent addr + LDA FAC2_e ; get FAC2 exponent +LAB_247C + TAY ; copy exponent + BEQ LAB_244D ; exit if zero + + SEC ; set carry for subtract + SBC FAC1_e ; subtract FAC1 exponent + BEQ LAB_24A8 ; branch if = (go add mantissa) + + BCC LAB_2498 ; branch if < + + ; FAC2>FAC1 + STY FAC1_e ; save FAC1 exponent + LDY FAC2_s ; get FAC2 sign (b7) + STY FAC1_s ; save FAC1 sign (b7) + EOR #$FF ; complement A + ADC #$00 ; +1 (twos complement, carry is set) + LDY #$00 ; clear Y + STY FAC2_r ; clear FAC2 rounding byte + LDX #FAC1_e ; set index to FAC1 exponent addr + BNE LAB_249C ; branch always + +LAB_2498 + LDY #$00 ; clear Y + STY FAC1_r ; clear FAC1 rounding byte +LAB_249C + CMP #$F9 ; compare exponent diff with $F9 + BMI LAB_2467 ; branch if range $79-$F8 + + TAY ; copy exponent difference to Y + LDA FAC1_r ; get FAC1 rounding byte + LSR PLUS_1,X ; shift FAC? mantissa1 + JSR LAB_2592 ; shift FACX Y times right + + ; exponents are equal now do mantissa subtract +LAB_24A8 + BIT FAC_sc ; test sign compare (FAC1 EOR FAC2) + BPL LAB_24F8 ; if = add FAC2 mantissa to FAC1 mantissa and return + + LDY #FAC1_e ; set index to FAC1 exponent addr + CPX #FAC2_e ; compare X to FAC2 exponent addr + BEQ LAB_24B4 ; branch if = + + LDY #FAC2_e ; else set index to FAC2 exponent addr + + ; subtract smaller from bigger (take sign of bigger) +LAB_24B4 + SEC ; set carry for subtract + EOR #$FF ; ones complement A + ADC FAC2_r ; add FAC2 rounding byte + STA FAC1_r ; save FAC1 rounding byte + LDA PLUS_3,Y ; get FACY mantissa3 + SBC PLUS_3,X ; subtract FACX mantissa3 + STA FAC1_3 ; save FAC1 mantissa3 + LDA PLUS_2,Y ; get FACY mantissa2 + SBC PLUS_2,X ; subtract FACX mantissa2 + STA FAC1_2 ; save FAC1 mantissa2 + LDA PLUS_1,Y ; get FACY mantissa1 + SBC PLUS_1,X ; subtract FACX mantissa1 + STA FAC1_1 ; save FAC1 mantissa1 + +; do ABS and normalise FAC1 + +LAB_24D0 + BCS LAB_24D5 ; branch if number is +ve + + JSR LAB_2537 ; negate FAC1 + +; normalise FAC1 + +LAB_24D5 + LDY #$00 ; clear Y + TYA ; clear A + CLC ; clear carry for add +LAB_24D9 + LDX FAC1_1 ; get FAC1 mantissa1 + BNE LAB_251B ; if not zero normalise FAC1 + + LDX FAC1_2 ; get FAC1 mantissa2 + STX FAC1_1 ; save FAC1 mantissa1 + LDX FAC1_3 ; get FAC1 mantissa3 + STX FAC1_2 ; save FAC1 mantissa2 + LDX FAC1_r ; get FAC1 rounding byte + STX FAC1_3 ; save FAC1 mantissa3 + STY FAC1_r ; clear FAC1 rounding byte + ADC #$08 ; add x to exponent offset + CMP #$18 ; compare with $18 (max offset, all bits would be =0) + BNE LAB_24D9 ; loop if not max + +; clear FAC1 exponent and sign + +LAB_24F1 + LDA #$00 ; clear A +LAB_24F3 + STA FAC1_e ; set FAC1 exponent + +; save FAC1 sign + +LAB_24F5 + STA FAC1_s ; save FAC1 sign (b7) + RTS + +; add FAC2 mantissa to FAC1 mantissa + +LAB_24F8 + ADC FAC2_r ; add FAC2 rounding byte + STA FAC1_r ; save FAC1 rounding byte + LDA FAC1_3 ; get FAC1 mantissa3 + ADC FAC2_3 ; add FAC2 mantissa3 + STA FAC1_3 ; save FAC1 mantissa3 + LDA FAC1_2 ; get FAC1 mantissa2 + ADC FAC2_2 ; add FAC2 mantissa2 + STA FAC1_2 ; save FAC1 mantissa2 + LDA FAC1_1 ; get FAC1 mantissa1 + ADC FAC2_1 ; add FAC2 mantissa1 + STA FAC1_1 ; save FAC1 mantissa1 + BCS LAB_252A ; if carry then normalise FAC1 for C=1 + + RTS ; else just exit + +LAB_2511 + ADC #$01 ; add 1 to exponent offset + ASL FAC1_r ; shift FAC1 rounding byte + ROL FAC1_3 ; shift FAC1 mantissa3 + ROL FAC1_2 ; shift FAC1 mantissa2 + ROL FAC1_1 ; shift FAC1 mantissa1 + +; normalise FAC1 + +LAB_251B + BPL LAB_2511 ; loop if not normalised + + SEC ; set carry for subtract + SBC FAC1_e ; subtract FAC1 exponent + BCS LAB_24F1 ; branch if underflow (set result = $0) + + EOR #$FF ; complement exponent + ADC #$01 ; +1 (twos complement) + STA FAC1_e ; save FAC1 exponent + +; test and normalise FAC1 for C=0/1 + +LAB_2528 + BCC LAB_2536 ; exit if no overflow + +; normalise FAC1 for C=1 + +LAB_252A + INC FAC1_e ; increment FAC1 exponent + BEQ LAB_2564 ; if zero do overflow error and warm start + + ROR FAC1_1 ; shift FAC1 mantissa1 + ROR FAC1_2 ; shift FAC1 mantissa2 + ROR FAC1_3 ; shift FAC1 mantissa3 + ROR FAC1_r ; shift FAC1 rounding byte +LAB_2536 + RTS + +; negate FAC1 + +LAB_2537 + LDA FAC1_s ; get FAC1 sign (b7) + EOR #$FF ; complement it + STA FAC1_s ; save FAC1 sign (b7) + +; twos complement FAC1 mantissa + +LAB_253D + LDA FAC1_1 ; get FAC1 mantissa1 + EOR #$FF ; complement it + STA FAC1_1 ; save FAC1 mantissa1 + LDA FAC1_2 ; get FAC1 mantissa2 + EOR #$FF ; complement it + STA FAC1_2 ; save FAC1 mantissa2 + LDA FAC1_3 ; get FAC1 mantissa3 + EOR #$FF ; complement it + STA FAC1_3 ; save FAC1 mantissa3 + LDA FAC1_r ; get FAC1 rounding byte + EOR #$FF ; complement it + STA FAC1_r ; save FAC1 rounding byte + INC FAC1_r ; increment FAC1 rounding byte + BNE LAB_2563 ; exit if no overflow + +; increment FAC1 mantissa + +LAB_2559 + INC FAC1_3 ; increment FAC1 mantissa3 + BNE LAB_2563 ; finished if no rollover + + INC FAC1_2 ; increment FAC1 mantissa2 + BNE LAB_2563 ; finished if no rollover + + INC FAC1_1 ; increment FAC1 mantissa1 +LAB_2563 + RTS + +; do overflow error (overflow exit) + +LAB_2564 + LDX #$0A ; error code $0A ("Overflow" error) + JMP LAB_XERR ; do error #X, then warm start + +; shift FCAtemp << A+8 times + +LAB_2569 + LDX #FACt_1-1 ; set offset to FACtemp +LAB_256B + LDY PLUS_3,X ; get FACX mantissa3 + STY FAC1_r ; save as FAC1 rounding byte + LDY PLUS_2,X ; get FACX mantissa2 + STY PLUS_3,X ; save FACX mantissa3 + LDY PLUS_1,X ; get FACX mantissa1 + STY PLUS_2,X ; save FACX mantissa2 + LDY FAC1_o ; get FAC1 overflow byte + STY PLUS_1,X ; save FACX mantissa1 + +; shift FACX -A times right (> 8 shifts) + +LAB_257B + ADC #$08 ; add 8 to shift count + BMI LAB_256B ; go do 8 shift if still -ve + + BEQ LAB_256B ; go do 8 shift if zero + + SBC #$08 ; else subtract 8 again + TAY ; save count to Y + LDA FAC1_r ; get FAC1 rounding byte + BCS LAB_259A ;. + +LAB_2588 + ASL PLUS_1,X ; shift FACX mantissa1 + BCC LAB_258E ; branch if +ve + + INC PLUS_1,X ; this sets b7 eventually +LAB_258E + ROR PLUS_1,X ; shift FACX mantissa1 (correct for ASL) + ROR PLUS_1,X ; shift FACX mantissa1 (put carry in b7) + +; shift FACX Y times right + +LAB_2592 + ROR PLUS_2,X ; shift FACX mantissa2 + ROR PLUS_3,X ; shift FACX mantissa3 + ROR ; shift FACX rounding byte + INY ; increment exponent diff + BNE LAB_2588 ; branch if range adjust not complete + +LAB_259A + CLC ; just clear it + RTS + +; perform LOG() + +LAB_LOG + JSR LAB_27CA ; test sign and zero + BEQ LAB_25C4 ; if zero do function call error then warm start + + BPL LAB_25C7 ; skip error if +ve + +LAB_25C4 + JMP LAB_FCER ; do function call error then warm start (-ve) + +LAB_25C7 + LDA FAC1_e ; get FAC1 exponent + SBC #$7F ; normalise it + PHA ; save it + LDA #$80 ; set exponent to zero + STA FAC1_e ; save FAC1 exponent + LDA #LAB_25AD ; set 1/root2 pointer high byte + JSR LAB_246C ; add (AY) to FAC1 (1/root2) + LDA #LAB_25B1 ; set root2 pointer high byte + JSR LAB_26CA ; convert AY and do (AY)/FAC1 (root2/(x+(1/root2))) + LDA #LAB_259C ; set 1 pointer high byte + JSR LAB_2455 ; subtract (AY) from FAC1 ((root2/(x+(1/root2)))-1) + LDA #LAB_25A0 ; set pointer high byte to counter + JSR LAB_2B6E ; ^2 then series evaluation + LDA #LAB_25B5 ; set -0.5 pointer high byte + JSR LAB_246C ; add (AY) to FAC1 + PLA ; restore FAC1 exponent + JSR LAB_2912 ; evaluate new ASCII digit + LDA #LAB_25B9 ; set LOG(2) pointer high byte + +; do convert AY, FCA1*(AY) + +LAB_25FB + JSR LAB_264D ; unpack memory (AY) into FAC2 +LAB_MULTIPLY + BEQ LAB_264C ; exit if zero + + JSR LAB_2673 ; test and adjust accumulators + LDA #$00 ; clear A + STA FACt_1 ; clear temp mantissa1 + STA FACt_2 ; clear temp mantissa2 + STA FACt_3 ; clear temp mantissa3 + LDA FAC1_r ; get FAC1 rounding byte + JSR LAB_2622 ; go do shift/add FAC2 + LDA FAC1_3 ; get FAC1 mantissa3 + JSR LAB_2622 ; go do shift/add FAC2 + LDA FAC1_2 ; get FAC1 mantissa2 + JSR LAB_2622 ; go do shift/add FAC2 + LDA FAC1_1 ; get FAC1 mantissa1 + JSR LAB_2627 ; go do shift/add FAC2 + JMP LAB_273C ; copy temp to FAC1, normalise and return + +LAB_2622 + BNE LAB_2627 ; branch if byte <> zero + + JMP LAB_2569 ; shift FCAtemp << A+8 times + + ; else do shift and add +LAB_2627 + LSR ; shift byte + ORA #$80 ; set top bit (mark for 8 times) +LAB_262A + TAY ; copy result + BCC LAB_2640 ; skip next if bit was zero + + CLC ; clear carry for add + LDA FACt_3 ; get temp mantissa3 + ADC FAC2_3 ; add FAC2 mantissa3 + STA FACt_3 ; save temp mantissa3 + LDA FACt_2 ; get temp mantissa2 + ADC FAC2_2 ; add FAC2 mantissa2 + STA FACt_2 ; save temp mantissa2 + LDA FACt_1 ; get temp mantissa1 + ADC FAC2_1 ; add FAC2 mantissa1 + STA FACt_1 ; save temp mantissa1 +LAB_2640 + ROR FACt_1 ; shift temp mantissa1 + ROR FACt_2 ; shift temp mantissa2 + ROR FACt_3 ; shift temp mantissa3 + ROR FAC1_r ; shift temp rounding byte + TYA ; get byte back + LSR ; shift byte + BNE LAB_262A ; loop if all bits not done + +LAB_264C + RTS + +; unpack memory (AY) into FAC2 + +LAB_264D + STA ut1_pl ; save pointer low byte + STY ut1_ph ; save pointer high byte + LDY #$03 ; 4 bytes to get (0-3) + LDA (ut1_pl),Y ; get mantissa3 + STA FAC2_3 ; save FAC2 mantissa3 + DEY ; decrement index + LDA (ut1_pl),Y ; get mantissa2 + STA FAC2_2 ; save FAC2 mantissa2 + DEY ; decrement index + LDA (ut1_pl),Y ; get mantissa1+sign + STA FAC2_s ; save FAC2 sign (b7) + EOR FAC1_s ; EOR with FAC1 sign (b7) + STA FAC_sc ; save sign compare (FAC1 EOR FAC2) + LDA FAC2_s ; recover FAC2 sign (b7) + ORA #$80 ; set 1xxx xxx (set normal bit) + STA FAC2_1 ; save FAC2 mantissa1 + DEY ; decrement index + LDA (ut1_pl),Y ; get exponent byte + STA FAC2_e ; save FAC2 exponent + LDA FAC1_e ; get FAC1 exponent + RTS + +; test and adjust accumulators + +LAB_2673 + LDA FAC2_e ; get FAC2 exponent +LAB_2675 + BEQ LAB_2696 ; branch if FAC2 = $00 (handle underflow) + + CLC ; clear carry for add + ADC FAC1_e ; add FAC1 exponent + BCC LAB_2680 ; branch if sum of exponents <$0100 + + BMI LAB_269B ; do overflow error + + CLC ; clear carry for the add + .byte $2C ; makes next line BIT $1410 +LAB_2680 + BPL LAB_2696 ; if +ve go handle underflow + + ADC #$80 ; adjust exponent + STA FAC1_e ; save FAC1 exponent + BNE LAB_268B ; branch if not zero + + JMP LAB_24F5 ; save FAC1 sign and return + +LAB_268B + LDA FAC_sc ; get sign compare (FAC1 EOR FAC2) + STA FAC1_s ; save FAC1 sign (b7) +LAB_268F + RTS + +; handle overflow and underflow + +LAB_2690 + LDA FAC1_s ; get FAC1 sign (b7) + BPL LAB_269B ; do overflow error + + ; handle underflow +LAB_2696 + PLA ; pop return address low byte + PLA ; pop return address high byte + JMP LAB_24F1 ; clear FAC1 exponent and sign and return + +; multiply by 10 + +LAB_269E + JSR LAB_27AB ; round and copy FAC1 to FAC2 + TAX ; copy exponent (set the flags) + BEQ LAB_268F ; exit if zero + + CLC ; clear carry for add + ADC #$02 ; add two to exponent (*4) + BCS LAB_269B ; do overflow error if > $FF + + LDX #$00 ; clear byte + STX FAC_sc ; clear sign compare (FAC1 EOR FAC2) + JSR LAB_247C ; add FAC2 to FAC1 (*5) + INC FAC1_e ; increment FAC1 exponent (*10) + BNE LAB_268F ; if non zero just do RTS + +LAB_269B + JMP LAB_2564 ; do overflow error and warm start + +; divide by 10 + +LAB_26B9 + JSR LAB_27AB ; round and copy FAC1 to FAC2 + LDA #LAB_26B5 ; set pointer to 10d high addr + LDX #$00 ; clear sign + +; divide by (AY) (X=sign) + +LAB_26C2 + STX FAC_sc ; save sign compare (FAC1 EOR FAC2) + JSR LAB_UFAC ; unpack memory (AY) into FAC1 + JMP LAB_DIVIDE ; do FAC2/FAC1 + + ; Perform divide-by +; convert AY and do (AY)/FAC1 + +LAB_26CA + JSR LAB_264D ; unpack memory (AY) into FAC2 + + ; Perform divide-into +LAB_DIVIDE + BEQ LAB_2737 ; if zero go do /0 error + + JSR LAB_27BA ; round FAC1 + LDA #$00 ; clear A + SEC ; set carry for subtract + SBC FAC1_e ; subtract FAC1 exponent (2s complement) + STA FAC1_e ; save FAC1 exponent + JSR LAB_2673 ; test and adjust accumulators + INC FAC1_e ; increment FAC1 exponent + BEQ LAB_269B ; if zero do overflow error + + LDX #$FF ; set index for pre increment + LDA #$01 ; set bit to flag byte save +LAB_26E4 + LDY FAC2_1 ; get FAC2 mantissa1 + CPY FAC1_1 ; compare FAC1 mantissa1 + BNE LAB_26F4 ; branch if <> + + LDY FAC2_2 ; get FAC2 mantissa2 + CPY FAC1_2 ; compare FAC1 mantissa2 + BNE LAB_26F4 ; branch if <> + + LDY FAC2_3 ; get FAC2 mantissa3 + CPY FAC1_3 ; compare FAC1 mantissa3 +LAB_26F4 + PHP ; save FAC2-FAC1 compare status + ROL ; shift the result byte + BCC LAB_2702 ; if no carry skip the byte save + + LDY #$01 ; set bit to flag byte save + INX ; else increment the index to FACt + CPX #$02 ; compare with the index to FACt_3 + BMI LAB_2701 ; if not last byte just go save it + + BNE LAB_272B ; if all done go save FAC1 rounding byte, normalise and + ; return + + LDY #$40 ; set bit to flag byte save for the rounding byte +LAB_2701 + STA FACt_1,X ; write result byte to FACt_1 + index + TYA ; copy the next save byte flag +LAB_2702 + PLP ; restore FAC2-FAC1 compare status + BCC LAB_2704 ; if FAC2 < FAC1 then skip the subtract + + TAY ; save FAC2-FAC1 compare status + LDA FAC2_3 ; get FAC2 mantissa3 + SBC FAC1_3 ; subtract FAC1 mantissa3 + STA FAC2_3 ; save FAC2 mantissa3 + LDA FAC2_2 ; get FAC2 mantissa2 + SBC FAC1_2 ; subtract FAC1 mantissa2 + STA FAC2_2 ; save FAC2 mantissa2 + LDA FAC2_1 ; get FAC2 mantissa1 + SBC FAC1_1 ; subtract FAC1 mantissa1 + STA FAC2_1 ; save FAC2 mantissa1 + TYA ; restore FAC2-FAC1 compare status + + ; FAC2 = FAC2*2 +LAB_2704 + ASL FAC2_3 ; shift FAC2 mantissa3 + ROL FAC2_2 ; shift FAC2 mantissa2 + ROL FAC2_1 ; shift FAC2 mantissa1 + BCS LAB_26F4 ; loop with no compare + + BMI LAB_26E4 ; loop with compare + + BPL LAB_26F4 ; loop always with no compare + +; do A<<6, save as FAC1 rounding byte, normalise and return + +LAB_272B + LSR ; shift b1 - b0 .. + ROR ; .. + ROR ; .. to b7 - b6 + STA FAC1_r ; save FAC1 rounding byte + PLP ; dump FAC2-FAC1 compare status + JMP LAB_273C ; copy temp to FAC1, normalise and return + +; do "Divide by zero" error + +LAB_2737 + LDX #$14 ; error code $14 ("Divide by zero" error) + JMP LAB_XERR ; do error #X, then warm start + +; copy temp to FAC1 and normalise + +LAB_273C + LDA FACt_1 ; get temp mantissa1 + STA FAC1_1 ; save FAC1 mantissa1 + LDA FACt_2 ; get temp mantissa2 + STA FAC1_2 ; save FAC1 mantissa2 + LDA FACt_3 ; get temp mantissa3 + STA FAC1_3 ; save FAC1 mantissa3 + JMP LAB_24D5 ; normalise FAC1 and return + +; unpack memory (AY) into FAC1 + +LAB_UFAC + STA ut1_pl ; save pointer low byte + STY ut1_ph ; save pointer high byte + LDY #$03 ; 4 bytes to do + LDA (ut1_pl),Y ; get last byte + STA FAC1_3 ; save FAC1 mantissa3 + DEY ; decrement index + LDA (ut1_pl),Y ; get last-1 byte + STA FAC1_2 ; save FAC1 mantissa2 + DEY ; decrement index + LDA (ut1_pl),Y ; get second byte + STA FAC1_s ; save FAC1 sign (b7) + ORA #$80 ; set 1xxx xxxx (add normal bit) + STA FAC1_1 ; save FAC1 mantissa1 + DEY ; decrement index + LDA (ut1_pl),Y ; get first byte (exponent) + STA FAC1_e ; save FAC1 exponent + STY FAC1_r ; clear FAC1 rounding byte + RTS + +; pack FAC1 into Adatal + +LAB_276E + LDX #Adatal ; set pointer high byte + BEQ LAB_2778 ; pack FAC1 into (XY) and return + +; pack FAC1 into (Lvarpl) + +LAB_PFAC + LDX Lvarpl ; get destination pointer low byte + LDY Lvarph ; get destination pointer high byte + +; pack FAC1 into (XY) + +LAB_2778 + JSR LAB_27BA ; round FAC1 + STX ut1_pl ; save pointer low byte + STY ut1_ph ; save pointer high byte + LDY #$03 ; set index + LDA FAC1_3 ; get FAC1 mantissa3 + STA (ut1_pl),Y ; store in destination + DEY ; decrement index + LDA FAC1_2 ; get FAC1 mantissa2 + STA (ut1_pl),Y ; store in destination + DEY ; decrement index + LDA FAC1_s ; get FAC1 sign (b7) + ORA #$7F ; set bits x111 1111 + AND FAC1_1 ; AND in FAC1 mantissa1 + STA (ut1_pl),Y ; store in destination + DEY ; decrement index + LDA FAC1_e ; get FAC1 exponent + STA (ut1_pl),Y ; store in destination + STY FAC1_r ; clear FAC1 rounding byte + RTS + +; round and copy FAC1 to FAC2 + +LAB_27AB + JSR LAB_27BA ; round FAC1 + +; copy FAC1 to FAC2 + +LAB_27AE + LDX #$05 ; 5 bytes to copy +LAB_27B0 + LDA FAC1_e-1,X ; get byte from FAC1,X + STA FAC1_o,X ; save byte at FAC2,X + DEX ; decrement count + BNE LAB_27B0 ; loop if not all done + + STX FAC1_r ; clear FAC1 rounding byte +LAB_27B9 + RTS + +; round FAC1 + +LAB_27BA + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_27B9 ; exit if zero + + ASL FAC1_r ; shift FAC1 rounding byte + BCC LAB_27B9 ; exit if no overflow + +; round FAC1 (no check) + +LAB_27C2 + JSR LAB_2559 ; increment FAC1 mantissa + BNE LAB_27B9 ; branch if no overflow + + JMP LAB_252A ; normalise FAC1 for C=1 and return + +; get FAC1 sign +; return A=FF,C=1/-ve A=01,C=0/+ve + +LAB_27CA + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_27D7 ; exit if zero (already correct SGN(0)=0) + +; return A=FF,C=1/-ve A=01,C=0/+ve +; no = 0 check + +LAB_27CE + LDA FAC1_s ; else get FAC1 sign (b7) + +; return A=FF,C=1/-ve A=01,C=0/+ve +; no = 0 check, sign in A + +LAB_27D0 + ROL ; move sign bit to carry + LDA #$FF ; set byte for -ve result + BCS LAB_27D7 ; return if sign was set (-ve) + + LDA #$01 ; else set byte for +ve result +LAB_27D7 + RTS + +; perform SGN() + +LAB_SGN + JSR LAB_27CA ; get FAC1 sign + ; return A=$FF/-ve A=$01/+ve +; save A as integer byte + +LAB_27DB + STA FAC1_1 ; save FAC1 mantissa1 + LDA #$00 ; clear A + STA FAC1_2 ; clear FAC1 mantissa2 + LDX #$88 ; set exponent + +; set exp=X, clearFAC1 mantissa3 and normalise + +LAB_27E3 + LDA FAC1_1 ; get FAC1 mantissa1 + EOR #$FF ; complement it + ROL ; sign bit into carry + +; set exp=X, clearFAC1 mantissa3 and normalise + +LAB_STFA + LDA #$00 ; clear A + STA FAC1_3 ; clear FAC1 mantissa3 + STX FAC1_e ; set FAC1 exponent + STA FAC1_r ; clear FAC1 rounding byte + STA FAC1_s ; clear FAC1 sign (b7) + JMP LAB_24D0 ; do ABS and normalise FAC1 + +; perform ABS() + +LAB_ABS + LSR FAC1_s ; clear FAC1 sign (put zero in b7) + RTS + +; compare FAC1 with (AY) +; returns A=$00 if FAC1 = (AY) +; returns A=$01 if FAC1 > (AY) +; returns A=$FF if FAC1 < (AY) + +LAB_27F8 + STA ut2_pl ; save pointer low byte +LAB_27FA + STY ut2_ph ; save pointer high byte + LDY #$00 ; clear index + LDA (ut2_pl),Y ; get exponent + INY ; increment index + TAX ; copy (AY) exponent to X + BEQ LAB_27CA ; branch if (AY) exponent=0 and get FAC1 sign + ; A=FF,C=1/-ve A=01,C=0/+ve + + LDA (ut2_pl),Y ; get (AY) mantissa1 (with sign) + EOR FAC1_s ; EOR FAC1 sign (b7) + BMI LAB_27CE ; if signs <> do return A=FF,C=1/-ve + ; A=01,C=0/+ve and return + + CPX FAC1_e ; compare (AY) exponent with FAC1 exponent + BNE LAB_2828 ; branch if different + + LDA (ut2_pl),Y ; get (AY) mantissa1 (with sign) + ORA #$80 ; normalise top bit + CMP FAC1_1 ; compare with FAC1 mantissa1 + BNE LAB_2828 ; branch if different + + INY ; increment index + LDA (ut2_pl),Y ; get mantissa2 + CMP FAC1_2 ; compare with FAC1 mantissa2 + BNE LAB_2828 ; branch if different + + INY ; increment index + LDA #$7F ; set for 1/2 value rounding byte + CMP FAC1_r ; compare with FAC1 rounding byte (set carry) + LDA (ut2_pl),Y ; get mantissa3 + SBC FAC1_3 ; subtract FAC1 mantissa3 + BEQ LAB_2850 ; exit if mantissa3 equal + +; gets here if number <> FAC1 + +LAB_2828 + LDA FAC1_s ; get FAC1 sign (b7) + BCC LAB_282E ; branch if FAC1 > (AY) + + EOR #$FF ; else toggle FAC1 sign +LAB_282E + JMP LAB_27D0 ; return A=FF,C=1/-ve A=01,C=0/+ve + +; convert FAC1 floating-to-fixed + +LAB_2831 + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_287F ; if zero go clear FAC1 and return + + SEC ; set carry for subtract + SBC #$98 ; subtract maximum integer range exponent + BIT FAC1_s ; test FAC1 sign (b7) + BPL LAB_2845 ; branch if FAC1 +ve + + ; FAC1 was -ve + TAX ; copy subtracted exponent + LDA #$FF ; overflow for -ve number + STA FAC1_o ; set FAC1 overflow byte + JSR LAB_253D ; twos complement FAC1 mantissa + TXA ; restore subtracted exponent +LAB_2845 + LDX #FAC1_e ; set index to FAC1 + CMP #$F9 ; compare exponent result + BPL LAB_2851 ; if < 8 shifts shift FAC1 A times right and return + + JSR LAB_257B ; shift FAC1 A times right (> 8 shifts) + STY FAC1_o ; clear FAC1 overflow byte +LAB_2850 + RTS + +; shift FAC1 A times right + +LAB_2851 + TAY ; copy shift count + LDA FAC1_s ; get FAC1 sign (b7) + AND #$80 ; mask sign bit only (x000 0000) + LSR FAC1_1 ; shift FAC1 mantissa1 + ORA FAC1_1 ; OR sign in b7 FAC1 mantissa1 + STA FAC1_1 ; save FAC1 mantissa1 + JSR LAB_2592 ; shift FAC1 Y times right + STY FAC1_o ; clear FAC1 overflow byte + RTS + +; perform INT() + +LAB_INT + LDA FAC1_e ; get FAC1 exponent + CMP #$98 ; compare with max int + BCS LAB_2886 ; exit if >= (already int, too big for fractional part!) + + JSR LAB_2831 ; convert FAC1 floating-to-fixed + STY FAC1_r ; save FAC1 rounding byte + LDA FAC1_s ; get FAC1 sign (b7) + STY FAC1_s ; save FAC1 sign (b7) + EOR #$80 ; toggle FAC1 sign + ROL ; shift into carry + LDA #$98 ; set new exponent + STA FAC1_e ; save FAC1 exponent + LDA FAC1_3 ; get FAC1 mantissa3 + STA Temp3 ; save for EXP() function + JMP LAB_24D0 ; do ABS and normalise FAC1 + +; clear FAC1 and return + +LAB_287F + STA FAC1_1 ; clear FAC1 mantissa1 + STA FAC1_2 ; clear FAC1 mantissa2 + STA FAC1_3 ; clear FAC1 mantissa3 + TAY ; clear Y +LAB_2886 + RTS + +; get FAC1 from string +; this routine now handles hex and binary values from strings +; starting with "$" and "%" respectively + +LAB_2887 + LDY #$00 ; clear Y + STY Dtypef ; clear data type flag, $FF=string, $00=numeric + LDX #$09 ; set index +LAB_288B + STY numexp,X ; clear byte + DEX ; decrement index + BPL LAB_288B ; loop until numexp to negnum (and FAC1) = $00 + + BCC LAB_28FE ; branch if 1st character numeric + +; get FAC1 from string .. first character wasn't numeric + + CMP #'-' ; else compare with "-" + BNE LAB_289A ; branch if not "-" + + STX negnum ; set flag for -ve number (X = $FF) + BEQ LAB_289C ; branch always (go scan and check for hex/bin) + +; get FAC1 from string .. first character wasn't numeric or - + +LAB_289A + CMP #'+' ; else compare with "+" + BNE LAB_289D ; branch if not "+" (go check for hex/bin) + +; was "+" or "-" to start, so get next character + +LAB_289C + JSR LAB_IGBY ; increment and scan memory + BCC LAB_28FE ; branch if numeric character + +; code here for hex and binary numbers + +LAB_289D + CMP #'$' ; else compare with "$" + BNE LAB_NHEX ; branch if not "$" + + JMP LAB_CHEX ; branch if "$" + +LAB_NHEX + CMP #'%' ; else compare with "%" + BNE LAB_28A3 ; branch if not "%" (continue original code) + + JMP LAB_CBIN ; branch if "%" + +LAB_289E + JSR LAB_IGBY ; increment and scan memory (ignore + or get next number) +LAB_28A1 + BCC LAB_28FE ; branch if numeric character + +; get FAC1 from string .. character wasn't numeric, -, +, hex or binary + +LAB_28A3 + CMP #'.' ; else compare with "." + BEQ LAB_28D5 ; branch if "." + +; get FAC1 from string .. character wasn't numeric, -, + or . + + CMP #'E' ; else compare with "E" + BNE LAB_28DB ; branch if not "E" + + ; was "E" so evaluate exponential part + JSR LAB_IGBY ; increment and scan memory + BCC LAB_28C7 ; branch if numeric character + + CMP #TK_MINUS ; else compare with token for - + BEQ LAB_28C2 ; branch if token for - + + CMP #'-' ; else compare with "-" + BEQ LAB_28C2 ; branch if "-" + + CMP #TK_PLUS ; else compare with token for + + BEQ LAB_28C4 ; branch if token for + + + CMP #'+' ; else compare with "+" + BEQ LAB_28C4 ; branch if "+" + + BNE LAB_28C9 ; branch always + +LAB_28C2 + ROR expneg ; set exponent -ve flag (C, which=1, into b7) +LAB_28C4 + JSR LAB_IGBY ; increment and scan memory +LAB_28C7 + BCC LAB_2925 ; branch if numeric character + +LAB_28C9 + BIT expneg ; test exponent -ve flag + BPL LAB_28DB ; if +ve go evaluate exponent + + ; else do exponent = -exponent + LDA #$00 ; clear result + SEC ; set carry for subtract + SBC expcnt ; subtract exponent byte + JMP LAB_28DD ; go evaluate exponent + +LAB_28D5 + ROR numdpf ; set decimal point flag + BIT numdpf ; test decimal point flag + BVC LAB_289E ; branch if only one decimal point so far + + ; evaluate exponent +LAB_28DB + LDA expcnt ; get exponent count byte +LAB_28DD + SEC ; set carry for subtract + SBC numexp ; subtract numerator exponent + STA expcnt ; save exponent count byte + BEQ LAB_28F6 ; branch if no adjustment + + BPL LAB_28EF ; else if +ve go do FAC1*10^expcnt + + ; else go do FAC1/10^(0-expcnt) +LAB_28E6 + JSR LAB_26B9 ; divide by 10 + INC expcnt ; increment exponent count byte + BNE LAB_28E6 ; loop until all done + + BEQ LAB_28F6 ; branch always + +LAB_28EF + JSR LAB_269E ; multiply by 10 + DEC expcnt ; decrement exponent count byte + BNE LAB_28EF ; loop until all done + +LAB_28F6 + LDA negnum ; get -ve flag + BMI LAB_28FB ; if -ve do - FAC1 and return + + RTS + +; do - FAC1 and return + +LAB_28FB + JMP LAB_GTHAN ; do - FAC1 and return + +; do unsigned FAC1*10+number + +LAB_28FE + PHA ; save character + BIT numdpf ; test decimal point flag + BPL LAB_2905 ; skip exponent increment if not set + + INC numexp ; else increment number exponent +LAB_2905 + JSR LAB_269E ; multiply FAC1 by 10 + PLA ; restore character + AND #$0F ; convert to binary + JSR LAB_2912 ; evaluate new ASCII digit + JMP LAB_289E ; go do next character + +; evaluate new ASCII digit + +LAB_2912 + PHA ; save digit + JSR LAB_27AB ; round and copy FAC1 to FAC2 + PLA ; restore digit + JSR LAB_27DB ; save A as integer byte + LDA FAC2_s ; get FAC2 sign (b7) + EOR FAC1_s ; toggle with FAC1 sign (b7) + STA FAC_sc ; save sign compare (FAC1 EOR FAC2) + LDX FAC1_e ; get FAC1 exponent + JMP LAB_ADD ; add FAC2 to FAC1 and return + +; evaluate next character of exponential part of number + +LAB_2925 + LDA expcnt ; get exponent count byte + CMP #$0A ; compare with 10 decimal + BCC LAB_2934 ; branch if less + + LDA #$64 ; make all -ve exponents = -100 decimal (causes underflow) + BIT expneg ; test exponent -ve flag + BMI LAB_2942 ; branch if -ve + + JMP LAB_2564 ; else do overflow error + +LAB_2934 + ASL ; * 2 + ASL ; * 4 + ADC expcnt ; * 5 + ASL ; * 10 + LDY #$00 ; set index + ADC (Bpntrl),Y ; add character (will be $30 too much!) + SBC #'0'-1 ; convert character to binary +LAB_2942 + STA expcnt ; save exponent count byte + JMP LAB_28C4 ; go get next character + +; print " in line [LINE #]" + +LAB_2953 + LDA #LAB_LMSG ; point to " in line " message high byte + JSR LAB_18C3 ; print null terminated string from memory + + ; print Basic line # + LDA Clineh ; get current line high byte + LDX Clinel ; get current line low byte + +; print XA as unsigned integer + +LAB_295E + STA FAC1_1 ; save low byte as FAC1 mantissa1 + STX FAC1_2 ; save high byte as FAC1 mantissa2 + LDX #$90 ; set exponent to 16d bits + SEC ; set integer is +ve flag + JSR LAB_STFA ; set exp=X, clearFAC1 mantissa3 and normalise + LDY #$00 ; clear index + TYA ; clear A + JSR LAB_297B ; convert FAC1 to string, skip sign character save + JMP LAB_18C3 ; print null terminated string from memory and return + +; convert FAC1 to ASCII string result in (AY) +; not any more, moved scratchpad to page 0 + +LAB_296E + LDY #$01 ; set index = 1 + LDA #$20 ; character = " " (assume +ve) + BIT FAC1_s ; test FAC1 sign (b7) + BPL LAB_2978 ; branch if +ve + + LDA #$2D ; else character = "-" +LAB_2978 + STA Decss,Y ; save leading character (" " or "-") +LAB_297B + STA FAC1_s ; clear FAC1 sign (b7) + STY Sendl ; save index + INY ; increment index + LDX FAC1_e ; get FAC1 exponent + BNE LAB_2989 ; branch if FAC1<>0 + + ; exponent was $00 so FAC1 is 0 + LDA #'0' ; set character = "0" + JMP LAB_2A89 ; save last character, [EOT] and exit + + ; FAC1 is some non zero value +LAB_2989 + LDA #$00 ; clear (number exponent count) + CPX #$81 ; compare FAC1 exponent with $81 (>1.00000) + + BCS LAB_299A ; branch if FAC1=>1 + + ; FAC1<1 + LDA #LAB_294F ; set pointer high byte to 1,000,000 + JSR LAB_25FB ; do convert AY, FCA1*(AY) + LDA #$FA ; set number exponent count (-6) +LAB_299A + STA numexp ; save number exponent count +LAB_299C + LDA #LAB_294B ; set pointer high byte to 999999.4375 + JSR LAB_27F8 ; compare FAC1 with (AY) + BEQ LAB_29C3 ; exit if FAC1 = (AY) + + BPL LAB_29B9 ; go do /10 if FAC1 > (AY) + + ; FAC1 < (AY) +LAB_29A7 + LDA #LAB_2947 ; set pointer high byte to 99999.9375 + JSR LAB_27F8 ; compare FAC1 with (AY) + BEQ LAB_29B2 ; branch if FAC1 = (AY) (allow decimal places) + + BPL LAB_29C0 ; branch if FAC1 > (AY) (no decimal places) + + ; FAC1 <= (AY) +LAB_29B2 + JSR LAB_269E ; multiply by 10 + DEC numexp ; decrement number exponent count + BNE LAB_29A7 ; go test again (branch always) + +LAB_29B9 + JSR LAB_26B9 ; divide by 10 + INC numexp ; increment number exponent count + BNE LAB_299C ; go test again (branch always) + +; now we have just the digits to do + +LAB_29C0 + JSR LAB_244E ; add 0.5 to FAC1 (round FAC1) +LAB_29C3 + JSR LAB_2831 ; convert FAC1 floating-to-fixed + LDX #$01 ; set default digits before dp = 1 + LDA numexp ; get number exponent count + CLC ; clear carry for add + ADC #$07 ; up to 6 digits before point + BMI LAB_29D8 ; if -ve then 1 digit before dp + + CMP #$08 ; A>=8 if n>=1E6 + BCS LAB_29D9 ; branch if >= $08 + + ; carry is clear + ADC #$FF ; take 1 from digit count + TAX ; copy to A + LDA #$02 ;.set exponent adjust +LAB_29D8 + SEC ; set carry for subtract +LAB_29D9 + SBC #$02 ; -2 + STA expcnt ;.save exponent adjust + STX numexp ; save digits before dp count + TXA ; copy to A + BEQ LAB_29E4 ; branch if no digits before dp + + BPL LAB_29F7 ; branch if digits before dp + +LAB_29E4 + LDY Sendl ; get output string index + LDA #$2E ; character "." + INY ; increment index + STA Decss,Y ; save to output string + TXA ;. + BEQ LAB_29F5 ;. + + LDA #'0' ; character "0" + INY ; increment index + STA Decss,Y ; save to output string +LAB_29F5 + STY Sendl ; save output string index +LAB_29F7 + LDY #$00 ; clear index (point to 100,000) + LDX #$80 ; +LAB_29FB + LDA FAC1_3 ; get FAC1 mantissa3 + CLC ; clear carry for add + ADC LAB_2A9C,Y ; add -ve LSB + STA FAC1_3 ; save FAC1 mantissa3 + LDA FAC1_2 ; get FAC1 mantissa2 + ADC LAB_2A9B,Y ; add -ve NMSB + STA FAC1_2 ; save FAC1 mantissa2 + LDA FAC1_1 ; get FAC1 mantissa1 + ADC LAB_2A9A,Y ; add -ve MSB + STA FAC1_1 ; save FAC1 mantissa1 + INX ; + BCS LAB_2A18 ; + + BPL LAB_29FB ; not -ve so try again + + BMI LAB_2A1A ; + +LAB_2A18 + BMI LAB_29FB ; + +LAB_2A1A + TXA ; + BCC LAB_2A21 ; + + EOR #$FF ; + ADC #$0A ; +LAB_2A21 + ADC #'0'-1 ; add "0"-1 to result + INY ; increment index .. + INY ; .. to next less .. + INY ; .. power of ten + STY Cvaral ; save as current var address low byte + LDY Sendl ; get output string index + INY ; increment output string index + TAX ; copy character to X + AND #$7F ; mask out top bit + STA Decss,Y ; save to output string + DEC numexp ; decrement # of characters before the dp + BNE LAB_2A3B ; branch if still characters to do + + ; else output the point + LDA #$2E ; character "." + INY ; increment output string index + STA Decss,Y ; save to output string +LAB_2A3B + STY Sendl ; save output string index + LDY Cvaral ; get current var address low byte + TXA ; get character back + EOR #$FF ; + AND #$80 ; + TAX ; + CPY #$12 ; compare index with max + BNE LAB_29FB ; loop if not max + + ; now remove trailing zeroes + LDY Sendl ; get output string index +LAB_2A4B + LDA Decss,Y ; get character from output string + DEY ; decrement output string index + CMP #'0' ; compare with "0" + BEQ LAB_2A4B ; loop until non "0" character found + + CMP #'.' ; compare with "." + BEQ LAB_2A58 ; branch if was dp + + ; restore last character + INY ; increment output string index +LAB_2A58 + LDA #$2B ; character "+" + LDX expcnt ; get exponent count + BEQ LAB_2A8C ; if zero go set null terminator and exit + + ; exponent isn't zero so write exponent + BPL LAB_2A68 ; branch if exponent count +ve + + LDA #$00 ; clear A + SEC ; set carry for subtract + SBC expcnt ; subtract exponent count adjust (convert -ve to +ve) + TAX ; copy exponent count to X + LDA #'-' ; character "-" +LAB_2A68 + STA Decss+2,Y ; save to output string + LDA #$45 ; character "E" + STA Decss+1,Y ; save exponent sign to output string + TXA ; get exponent count back + LDX #'0'-1 ; one less than "0" character + SEC ; set carry for subtract +LAB_2A74 + INX ; increment 10's character + SBC #$0A ;.subtract 10 from exponent count + BCS LAB_2A74 ; loop while still >= 0 + + ADC #":" ; add character ":" ($30+$0A, result is 10 less that value) + STA Decss+4,Y ; save to output string + TXA ; copy 10's character + STA Decss+3,Y ; save to output string + LDA #$00 ; set null terminator + STA Decss+5,Y ; save to output string + BEQ LAB_2A91 ; go set string pointer (AY) and exit (branch always) + + ; save last character, [EOT] and exit +LAB_2A89 + STA Decss,Y ; save last character to output string + + ; set null terminator and exit +LAB_2A8C + LDA #$00 ; set null terminator + STA Decss+1,Y ; save after last character + + ; set string pointer (AY) and exit +LAB_2A91 + LDA #Decssp1 ; set result string high pointer + RTS + +; perform power function + +LAB_POWER + BEQ LAB_EXP ; go do EXP() + + LDA FAC2_e ; get FAC2 exponent + BNE LAB_2ABF ; branch if FAC2<>0 + + JMP LAB_24F3 ; clear FAC1 exponent and sign and return + +LAB_2ABF + LDX #func_l ; set destination pointer high byte + JSR LAB_2778 ; pack FAC1 into (XY) + LDA FAC2_s ; get FAC2 sign (b7) + BPL LAB_2AD9 ; branch if FAC2>0 + + ; else FAC2 is -ve and can only be raised to an + ; integer power which gives an x +j0 result + JSR LAB_INT ; perform INT + LDA #func_l ; set source pointer high byte + JSR LAB_27F8 ; compare FAC1 with (AY) + BNE LAB_2AD9 ; branch if FAC1 <> (AY) to allow Function Call error + ; this will leave FAC1 -ve and cause a Function Call + ; error when LOG() is called + + TYA ; clear sign b7 + LDY Temp3 ; save mantissa 3 from INT() function as sign in Y + ; for possible later negation, b0 +LAB_2AD9 + JSR LAB_279D ; save FAC1 sign and copy ABS(FAC2) to FAC1 + TYA ; copy sign back .. + PHA ; .. and save it + JSR LAB_LOG ; do LOG(n) + LDA #garb_l ; set pointer high byte + JSR LAB_25FB ; do convert AY, FCA1*(AY) (square the value) + JSR LAB_EXP ; go do EXP(n) + PLA ; pull sign from stack + LSR ; b0 is to be tested, shift to Cb + BCC LAB_2AF9 ; if no bit then exit + + ; Perform negation +; do - FAC1 + +LAB_GTHAN + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_2AF9 ; exit if FAC1_e = $00 + + LDA FAC1_s ; get FAC1 sign (b7) + EOR #$FF ; complement it + STA FAC1_s ; save FAC1 sign (b7) +LAB_2AF9 + RTS + +; perform EXP() (x^e) + +LAB_EXP + LDA #LAB_2AFA ; set 1.443 pointer high byte + JSR LAB_25FB ; do convert AY, FCA1*(AY) + LDA FAC1_r ; get FAC1 rounding byte + ADC #$50 ; +$50/$100 + BCC LAB_2B2B ; skip rounding if no carry + + JSR LAB_27C2 ; round FAC1 (no check) +LAB_2B2B + STA FAC2_r ; save FAC2 rounding byte + JSR LAB_27AE ; copy FAC1 to FAC2 + LDA FAC1_e ; get FAC1 exponent + CMP #$88 ; compare with EXP limit (256d) + BCC LAB_2B39 ; branch if less + +LAB_2B36 + JSR LAB_2690 ; handle overflow and underflow +LAB_2B39 + JSR LAB_INT ; perform INT + LDA Temp3 ; get mantissa 3 from INT() function + CLC ; clear carry for add + ADC #$81 ; normalise +1 + BEQ LAB_2B36 ; if $00 go handle overflow + + SEC ; set carry for subtract + SBC #$01 ; now correct for exponent + PHA ; save FAC2 exponent + + ; swap FAC1 and FAC2 + LDX #$04 ; 4 bytes to do +LAB_2B49 + LDA FAC2_e,X ; get FAC2,X + LDY FAC1_e,X ; get FAC1,X + STA FAC1_e,X ; save FAC1,X + STY FAC2_e,X ; save FAC2,X + DEX ; decrement count/index + BPL LAB_2B49 ; loop if not all done + + LDA FAC2_r ; get FAC2 rounding byte + STA FAC1_r ; save as FAC1 rounding byte + JSR LAB_SUBTRACT ; perform subtraction, FAC2 from FAC1 + JSR LAB_GTHAN ; do - FAC1 + LDA #LAB_2AFE ; set counter pointer high byte + JSR LAB_2B84 ; go do series evaluation + LDA #$00 ; clear A + STA FAC_sc ; clear sign compare (FAC1 EOR FAC2) + PLA ;.get saved FAC2 exponent + JMP LAB_2675 ; test and adjust accumulators and return + +; ^2 then series evaluation + +LAB_2B6E + STA Cptrl ; save count pointer low byte + STY Cptrh ; save count pointer high byte + JSR LAB_276E ; pack FAC1 into Adatal + LDA #Adatal ; pointer to original # high byte + JMP LAB_25FB ; do convert AY, FCA1*(AY) and return + +; series evaluation + +LAB_2B84 + STA Cptrl ; save count pointer low byte + STY Cptrh ; save count pointer high byte +LAB_2B88 + LDX #numexp ; set pointer high byte to partial @ numexp + DEC numcon ; decrement constants count + BNE LAB_2B9B ; loop until all done + + RTS + +; RND(n), 32 bit Galoise version. make n=0 for 19th next number in sequence or n<>0 +; to get 19th next number in sequence after seed n. This version of the PRNG uses +; the Galois method and a sample of 65536 bytes produced gives the following values. + +; Entropy = 7.997442 bits per byte +; Optimum compression would reduce these 65536 bytes by 0 percent + +; Chi square distribution for 65536 samples is 232.01, and +; randomly would exceed this value 75.00 percent of the time + +; Arithmetic mean value of data bytes is 127.6724, 127.5 would be random +; Monte Carlo value for Pi is 3.122871269, error 0.60 percent +; Serial correlation coefficient is -0.000370, totally uncorrelated would be 0.0 + +LAB_RND + LDA FAC1_e ; get FAC1 exponent + BEQ NextPRN ; do next random # if zero + + ; else get seed into random number store + LDX #Rbyte4 ; set PRNG pointer low byte + LDY #$00 ; set PRNG pointer high byte + JSR LAB_2778 ; pack FAC1 into (XY) +NextPRN + LDX #$AF ; set EOR byte + LDY #$13 ; do this nineteen times +LoopPRN + ASL Rbyte1 ; shift PRNG most significant byte + ROL Rbyte2 ; shift PRNG middle byte + ROL Rbyte3 ; shift PRNG least significant byte + ROL Rbyte4 ; shift PRNG extra byte + BCC Ninc1 ; branch if bit 32 clear + + TXA ; set EOR byte + EOR Rbyte1 ; EOR PRNG extra byte + STA Rbyte1 ; save new PRNG extra byte +Ninc1 + DEY ; decrement loop count + BNE LoopPRN ; loop if not all done + + LDX #$02 ; three bytes to copy +CopyPRNG + LDA Rbyte1,X ; get PRNG byte + STA FAC1_1,X ; save FAC1 byte + DEX + BPL CopyPRNG ; loop if not complete + + LDA #$80 ; set the exponent + STA FAC1_e ; save FAC1 exponent + + ASL ; clear A + STA FAC1_s ; save FAC1 sign + + JMP LAB_24D5 ; normalise FAC1 and return + +; perform COS() + +LAB_COS + LDA #LAB_2C78 ; set (pi/2) pointer high byte + JSR LAB_246C ; add (AY) to FAC1 + +; perform SIN() + +LAB_SIN + JSR LAB_27AB ; round and copy FAC1 to FAC2 + LDA #LAB_2C7C ; set (2*pi) pointer high byte + LDX FAC2_s ; get FAC2 sign (b7) + JSR LAB_26C2 ; divide by (AY) (X=sign) + JSR LAB_27AB ; round and copy FAC1 to FAC2 + JSR LAB_INT ; perform INT + LDA #$00 ; clear byte + STA FAC_sc ; clear sign compare (FAC1 EOR FAC2) + JSR LAB_SUBTRACT ; perform subtraction, FAC2 from FAC1 + LDA #LAB_2C80 ; set 0.25 pointer high byte + JSR LAB_2455 ; perform subtraction, (AY) from FAC1 + LDA FAC1_s ; get FAC1 sign (b7) + PHA ; save FAC1 sign + BPL LAB_2C35 ; branch if +ve + + ; FAC1 sign was -ve + JSR LAB_244E ; add 0.5 to FAC1 + LDA FAC1_s ; get FAC1 sign (b7) + BMI LAB_2C38 ; branch if -ve + + LDA Cflag ; get comparison evaluation flag + EOR #$FF ; toggle flag + STA Cflag ; save comparison evaluation flag +LAB_2C35 + JSR LAB_GTHAN ; do - FAC1 +LAB_2C38 + LDA #LAB_2C80 ; set 0.25 pointer high byte + JSR LAB_246C ; add (AY) to FAC1 + PLA ; restore FAC1 sign + BPL LAB_2C45 ; branch if was +ve + + ; else correct FAC1 + JSR LAB_GTHAN ; do - FAC1 +LAB_2C45 + LDA #LAB_2C84 ; set pointer high byte to counter + JMP LAB_2B6E ; ^2 then series evaluation and return + +; perform TAN() + +LAB_TAN + JSR LAB_276E ; pack FAC1 into Adatal + LDA #$00 ; clear byte + STA Cflag ; clear comparison evaluation flag + JSR LAB_SIN ; go do SIN(n) + LDX #func_l ; set sin(n) pointer high byte + JSR LAB_2778 ; pack FAC1 into (XY) + LDA #Adatal ; set n pointer high addr + JSR LAB_UFAC ; unpack memory (AY) into FAC1 + LDA #$00 ; clear byte + STA FAC1_s ; clear FAC1 sign (b7) + LDA Cflag ; get comparison evaluation flag + JSR LAB_2C74 ; save flag and go do series evaluation + + LDA #func_l ; set sin(n) pointer high byte + JMP LAB_26CA ; convert AY and do (AY)/FAC1 + +LAB_2C74 + PHA ; save comparison evaluation flag + JMP LAB_2C35 ; go do series evaluation + +; perform USR() + +LAB_USR + JSR Usrjmp ; call user code + JMP LAB_1BFB ; scan for ")", else do syntax error then warm start + +; perform ATN() + +LAB_ATN + LDA FAC1_s ; get FAC1 sign (b7) + PHA ; save sign + BPL LAB_2CA1 ; branch if +ve + + JSR LAB_GTHAN ; else do - FAC1 +LAB_2CA1 + LDA FAC1_e ; get FAC1 exponent + PHA ; push exponent + CMP #$81 ; compare with 1 + BCC LAB_2CAF ; branch if FAC1<1 + + LDA #LAB_259C ; set 1 pointer high byte + JSR LAB_26CA ; convert AY and do (AY)/FAC1 +LAB_2CAF + LDA #LAB_2CC9 ; set pointer high byte to counter + JSR LAB_2B6E ; ^2 then series evaluation + PLA ; restore old FAC1 exponent + CMP #$81 ; compare with 1 + BCC LAB_2CC2 ; branch if FAC1<1 + + LDA #LAB_2C78 ; set (pi/2) pointer high byte + JSR LAB_2455 ; perform subtraction, (AY) from FAC1 +LAB_2CC2 + PLA ; restore FAC1 sign + BPL LAB_2D04 ; exit if was +ve + + JMP LAB_GTHAN ; else do - FAC1 and return + +; perform BITSET + +LAB_BITSET + JSR LAB_GADB ; get two parameters for POKE or WAIT + CPX #$08 ; only 0 to 7 are allowed + BCS FCError ; branch if > 7 + + LDA #$00 ; clear A + SEC ; set the carry +S_Bits + ROL ; shift bit + DEX ; decrement bit number + BPL S_Bits ; loop if still +ve + + INX ; make X = $00 + ORA (Itempl,X) ; or with byte via temporary integer (addr) + STA (Itempl,X) ; save byte via temporary integer (addr) +LAB_2D04 + RTS + +; perform BITCLR + +LAB_BITCLR + JSR LAB_GADB ; get two parameters for POKE or WAIT + CPX #$08 ; only 0 to 7 are allowed + BCS FCError ; branch if > 7 + + LDA #$FF ; set A +S_Bitc + ROL ; shift bit + DEX ; decrement bit number + BPL S_Bitc ; loop if still +ve + + INX ; make X = $00 + AND (Itempl,X) ; and with byte via temporary integer (addr) + STA (Itempl,X) ; save byte via temporary integer (addr) + RTS + +FCError + JMP LAB_FCER ; do function call error then warm start + +; perform BITTST() + +LAB_BTST + JSR LAB_IGBY ; increment BASIC pointer + JSR LAB_GADB ; get two parameters for POKE or WAIT + CPX #$08 ; only 0 to 7 are allowed + BCS FCError ; branch if > 7 + + JSR LAB_GBYT ; get next BASIC byte + CMP #')' ; is next character ")" + BEQ TST_OK ; if ")" go do rest of function + + JMP LAB_SNER ; do syntax error then warm start + +TST_OK + JSR LAB_IGBY ; update BASIC execute pointer (to character past ")") + LDA #$00 ; clear A + SEC ; set the carry +T_Bits + ROL ; shift bit + DEX ; decrement bit number + BPL T_Bits ; loop if still +ve + + INX ; make X = $00 + AND (Itempl,X) ; AND with byte via temporary integer (addr) + BEQ LAB_NOTT ; branch if zero (already correct) + + LDA #$FF ; set for -1 result +LAB_NOTT + JMP LAB_27DB ; go do SGN tail + +; perform BIN$() + +LAB_BINS + CPX #$19 ; max + 1 + BCS BinFErr ; exit if too big ( > or = ) + + STX TempB ; save # of characters ($00 = leading zero remove) + LDA #$18 ; need A byte long space + JSR LAB_MSSP ; make string space A bytes long + LDY #$17 ; set index + LDX #$18 ; character count +NextB1 + LSR nums_1 ; shift highest byte + ROR nums_2 ; shift middle byte + ROR nums_3 ; shift lowest byte bit 0 to carry + TXA ; load with "0"/2 + ROL ; shift in carry + STA (str_pl),Y ; save to temp string + index + DEY ; decrement index + BPL NextB1 ; loop if not done + + LDA TempB ; get # of characters + BEQ EndBHS ; branch if truncate + + TAX ; copy length to X + SEC ; set carry for add ! + EOR #$FF ; 1's complement + ADC #$18 ; add 24d + BEQ GoPr2 ; if zero print whole string + + BNE GoPr1 ; else go make output string + +; this is the exit code and is also used by HEX$() +; truncate string to remove leading "0"s + +EndBHS + TAY ; clear index (A=0, X=length here) +NextB2 + LDA (str_pl),Y ; get character from string + CMP #'0' ; compare with "0" + BNE GoPr ; if not "0" then go print string from here + + DEX ; decrement character count + BEQ GoPr3 ; if zero then end of string so go print it + + INY ; else increment index + BPL NextB2 ; loop always + +; make fixed length output string - ignore overflows! + +GoPr3 + INX ; need at least 1 character +GoPr + TYA ; copy result +GoPr1 + CLC ; clear carry for add + ADC str_pl ; add low address + STA str_pl ; save low address + LDA #$00 ; do high byte + ADC str_ph ; add high address + STA str_ph ; save high address +GoPr2 + STX str_ln ; X holds string length + JSR LAB_IGBY ; update BASIC execute pointer (to character past ")") + JMP LAB_RTST ; check for space on descriptor stack then put address + ; and length on descriptor stack and update stack pointers + +BinFErr + JMP LAB_FCER ; do function call error then warm start + +; perform HEX$() + +LAB_HEXS + CPX #$07 ; max + 1 + BCS BinFErr ; exit if too big ( > or = ) + + STX TempB ; save # of characters + + LDA #$06 ; need 6 bytes for string + JSR LAB_MSSP ; make string space A bytes long + LDY #$05 ; set string index + + SED ; need decimal mode for nibble convert + LDA nums_3 ; get lowest byte + JSR LAB_A2HX ; convert A to ASCII hex byte and output + LDA nums_2 ; get middle byte + JSR LAB_A2HX ; convert A to ASCII hex byte and output + LDA nums_1 ; get highest byte + JSR LAB_A2HX ; convert A to ASCII hex byte and output + CLD ; back to binary + + LDX #$06 ; character count + LDA TempB ; get # of characters + BEQ EndBHS ; branch if truncate + + TAX ; copy length to X + SEC ; set carry for add ! + EOR #$FF ; 1's complement + ADC #$06 ; add 6d + BEQ GoPr2 ; if zero print whole string + + BNE GoPr1 ; else go make output string (branch always) + +; convert A to ASCII hex byte and output .. note set decimal mode before calling + +LAB_A2HX + TAX ; save byte + AND #$0F ; mask off top bits + JSR LAB_AL2X ; convert low nibble to ASCII and output + TXA ; get byte back + LSR ; /2 shift high nibble to low nibble + LSR ; /4 + LSR ; /8 + LSR ; /16 +LAB_AL2X + CMP #$0A ; set carry for +1 if >9 + ADC #'0' ; add ASCII "0" + STA (str_pl),Y ; save to temp string + DEY ; decrement counter + RTS + +LAB_NLTO + STA FAC1_e ; save FAC1 exponent + LDA #$00 ; clear sign compare +LAB_MLTE + STA FAC_sc ; save sign compare (FAC1 EOR FAC2) + TXA ; restore character + JSR LAB_2912 ; evaluate new ASCII digit + +; gets here if the first character was "$" for hex +; get hex number + +LAB_CHEX + JSR LAB_IGBY ; increment and scan memory + BCC LAB_ISHN ; branch if numeric character + + ORA #$20 ; case convert, allow "A" to "F" and "a" to "f" + SBC #'a' ; subtract "a" (carry set here) + CMP #$06 ; compare normalised with $06 (max+1) + BCS LAB_EXCH ; exit if >"f" or <"0" + + ADC #$0A ; convert to nibble +LAB_ISHN + AND #$0F ; convert to binary + TAX ; save nibble + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_MLTE ; skip multiply if zero + + ADC #$04 ; add four to exponent (*16 - carry clear here) + BCC LAB_NLTO ; if no overflow do evaluate digit + +LAB_MLTO + JMP LAB_2564 ; do overflow error and warm start + +LAB_NXCH + TAX ; save bit + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_MLBT ; skip multiply if zero + + INC FAC1_e ; increment FAC1 exponent (*2) + BEQ LAB_MLTO ; do overflow error if = $00 + + LDA #$00 ; clear sign compare +LAB_MLBT + STA FAC_sc ; save sign compare (FAC1 EOR FAC2) + TXA ; restore bit + JSR LAB_2912 ; evaluate new ASCII digit + +; gets here if the first character was "%" for binary +; get binary number + +LAB_CBIN + JSR LAB_IGBY ; increment and scan memory + EOR #'0' ; convert "0" to 0 etc. + CMP #$02 ; compare with max+1 + BCC LAB_NXCH ; branch exit if < 2 + +LAB_EXCH + JMP LAB_28F6 ; evaluate -ve flag and return + +; ctrl-c check routine. includes limited "life" byte save for INGET routine +; now also the code that checks to see if an interrupt has occurred + +CTRLC + LDA ccflag ; get [CTRL-C] check flag + BNE LAB_FBA2 ; exit if inhibited + + JSR V_INPT ; scan input device + BCC LAB_FBA0 ; exit if buffer empty + + STA ccbyte ; save received byte + LDX #$20 ; "life" timer for bytes + STX ccnull ; set countdown + JMP LAB_1636 ; return to BASIC + +LAB_FBA0 + LDX ccnull ; get countdown byte + BEQ LAB_FBA2 ; exit if finished + + DEC ccnull ; else decrement countdown +LAB_FBA2 + LDX #NmiBase ; set pointer to NMI values + JSR LAB_CKIN ; go check interrupt + LDX #IrqBase ; set pointer to IRQ values + JSR LAB_CKIN ; go check interrupt +LAB_CRTS + RTS + +; check whichever interrupt is indexed by X + +LAB_CKIN + LDA PLUS_0,X ; get interrupt flag byte + BPL LAB_CRTS ; branch if interrupt not enabled + +; we disable the interrupt here and make two new commands RETIRQ and RETNMI to +; automatically enable the interrupt when we exit + + ASL ; move happened bit to setup bit + AND #$40 ; mask happened bits + BEQ LAB_CRTS ; if no interrupt then exit + + STA PLUS_0,X ; save interrupt flag byte + + TXA ; copy index .. + TAY ; .. to Y + + PLA ; dump return address low byte, call from CTRL-C + PLA ; dump return address high byte + + LDA #$05 ; need 5 bytes for GOSUB + JSR LAB_1212 ; check room on stack for A bytes + LDA Bpntrh ; get BASIC execute pointer high byte + PHA ; push on stack + LDA Bpntrl ; get BASIC execute pointer low byte + PHA ; push on stack + LDA Clineh ; get current line high byte + PHA ; push on stack + LDA Clinel ; get current line low byte + PHA ; push on stack + LDA #TK_GOSUB ; token for GOSUB + PHA ; push on stack + + LDA PLUS_1,Y ; get interrupt code pointer low byte + STA Bpntrl ; save as BASIC execute pointer low byte + LDA PLUS_2,Y ; get interrupt code pointer high byte + STA Bpntrh ; save as BASIC execute pointer high byte + + JMP LAB_15C2 ; go do interpreter inner loop + ; can't RTS, we used the stack! the RTS from the ctrl-c + ; check will be taken when the RETIRQ/RETNMI/RETURN is + ; executed at the end of the subroutine + +; get byte from input device, no waiting +; returns with carry set if byte in A + +INGET + JSR V_INPT ; call scan input device + BCS LAB_FB95 ; if byte go reset timer + + LDA ccnull ; get countdown + BEQ LAB_FB96 ; exit if empty + + LDA ccbyte ; get last received byte + SEC ; flag we got a byte +LAB_FB95 + LDX #$00 ; clear X + STX ccnull ; clear timer because we got a byte +LAB_FB96 + RTS + +; these routines only enable the interrupts if the set-up flag is set +; if not they have no effect + +; perform IRQ {ON|OFF|CLEAR} + +LAB_IRQ + LDX #IrqBase ; set pointer to IRQ values + .byte $2C ; make next line BIT abs. + +; perform NMI {ON|OFF|CLEAR} + +LAB_NMI + LDX #NmiBase ; set pointer to NMI values + CMP #TK_ON ; compare with token for ON + BEQ LAB_INON ; go turn on interrupt + + CMP #TK_OFF ; compare with token for OFF + BEQ LAB_IOFF ; go turn off interrupt + + EOR #TK_CLEAR ; compare with token for CLEAR, A = $00 if = TK_CLEAR + BEQ LAB_INEX ; go clear interrupt flags and return + + JMP LAB_SNER ; do syntax error then warm start + +LAB_IOFF + LDA #$7F ; clear A + AND PLUS_0,X ; AND with interrupt setup flag + BPL LAB_INEX ; go clear interrupt enabled flag and return + +LAB_INON + LDA PLUS_0,X ; get interrupt setup flag + ASL ; Shift bit to enabled flag + ORA PLUS_0,X ; OR with flag byte +LAB_INEX + STA PLUS_0,X ; save interrupt flag byte + JMP LAB_IGBY ; update BASIC execute pointer and return + +; these routines set up the pointers and flags for the interrupt routines +; note that the interrupts are also enabled by these commands + +; perform ON IRQ + +LAB_SIRQ + CLI ; enable interrupts + LDX #IrqBase ; set pointer to IRQ values + .byte $2C ; make next line BIT abs. + +; perform ON NMI + +LAB_SNMI + LDX #NmiBase ; set pointer to NMI values + + STX TempB ; save interrupt pointer + JSR LAB_IGBY ; increment and scan memory (past token) + JSR LAB_GFPN ; get fixed-point number into temp integer + LDA Smeml ; get start of mem low byte + LDX Smemh ; get start of mem high byte + JSR LAB_SHLN ; search Basic for temp integer line number from AX + BCS LAB_LFND ; if carry set go set-up interrupt + + JMP LAB_16F7 ; else go do "Undefined statement" error and warm start + +LAB_LFND + LDX TempB ; get interrupt pointer + LDA Baslnl ; get pointer low byte + SBC #$01 ; -1 (carry already set for subtract) + STA PLUS_1,X ; save as interrupt pointer low byte + LDA Baslnh ; get pointer high byte + SBC #$00 ; subtract carry + STA PLUS_2,X ; save as interrupt pointer high byte + + LDA #$C0 ; set interrupt enabled/setup bits + STA PLUS_0,X ; set interrupt flags +LAB_IRTS + RTS + +; return from IRQ service, restores the enabled flag. + +; perform RETIRQ + +LAB_RETIRQ + BNE LAB_IRTS ; exit if following token (to allow syntax error) + + LDA IrqBase ; get interrupt flags + ASL ; copy setup to enabled (b7) + ORA IrqBase ; OR in setup flag + STA IrqBase ; save enabled flag + JMP LAB_16E8 ; go do rest of RETURN + +; return from NMI service, restores the enabled flag. + +; perform RETNMI + +LAB_RETNMI + BNE LAB_IRTS ; exit if following token (to allow syntax error) + + LDA NmiBase ; get set-up flag + ASL ; copy setup to enabled (b7) + ORA NmiBase ; OR in setup flag + STA NmiBase ; save enabled flag + JMP LAB_16E8 ; go do rest of RETURN + +; MAX() MIN() pre process + +LAB_MMPP + JSR LAB_EVEZ ; process expression + JMP LAB_CTNM ; check if source is numeric, else do type mismatch + +; perform MAX() + +LAB_MAX + JSR LAB_PHFA ; push FAC1, evaluate expression, + ; pull FAC2 and compare with FAC1 + BPL LAB_MAX ; branch if no swap to do + + LDA FAC2_1 ; get FAC2 mantissa1 + ORA #$80 ; set top bit (clear sign from compare) + STA FAC2_1 ; save FAC2 mantissa1 + JSR LAB_279B ; copy FAC2 to FAC1 + BEQ LAB_MAX ; go do next (branch always) + +; perform MIN() + +LAB_MIN + JSR LAB_PHFA ; push FAC1, evaluate expression, + ; pull FAC2 and compare with FAC1 + BMI LAB_MIN ; branch if no swap to do + + BEQ LAB_MIN ; branch if no swap to do + + LDA FAC2_1 ; get FAC2 mantissa1 + ORA #$80 ; set top bit (clear sign from compare) + STA FAC2_1 ; save FAC2 mantissa1 + JSR LAB_279B ; copy FAC2 to FAC1 + BEQ LAB_MIN ; go do next (branch always) + +; exit routine. don't bother returning to the loop code +; check for correct exit, else so syntax error + +LAB_MMEC + CMP #')' ; is it end of function? + BNE LAB_MMSE ; if not do MAX MIN syntax error + + PLA ; dump return address low byte + PLA ; dump return address high byte + JMP LAB_IGBY ; update BASIC execute pointer (to chr past ")") + +LAB_MMSE + JMP LAB_SNER ; do syntax error then warm start + +; check for next, evaluate and return or exit +; this is the routine that does most of the work + +LAB_PHFA + JSR LAB_GBYT ; get next BASIC byte + CMP #',' ; is there more ? + BNE LAB_MMEC ; if not go do end check + + ; push FAC1 + JSR LAB_27BA ; round FAC1 + LDA FAC1_s ; get FAC1 sign + ORA #$7F ; set all non sign bits + AND FAC1_1 ; AND FAC1 mantissa1 (AND in sign bit) + PHA ; push on stack + LDA FAC1_2 ; get FAC1 mantissa2 + PHA ; push on stack + LDA FAC1_3 ; get FAC1 mantissa3 + PHA ; push on stack + LDA FAC1_e ; get FAC1 exponent + PHA ; push on stack + + JSR LAB_IGBY ; scan and get next BASIC byte (after ",") + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + + ; pop FAC2 (MAX/MIN expression so far) + PLA ; pop exponent + STA FAC2_e ; save FAC2 exponent + PLA ; pop mantissa3 + STA FAC2_3 ; save FAC2 mantissa3 + PLA ; pop mantissa1 + STA FAC2_2 ; save FAC2 mantissa2 + PLA ; pop sign/mantissa1 + STA FAC2_1 ; save FAC2 sign/mantissa1 + STA FAC2_s ; save FAC2 sign + + ; compare FAC1 with (packed) FAC2 + LDA #FAC2_e ; set pointer high byte to FAC2 + JMP LAB_27F8 ; compare FAC1 with FAC2 (AY) and return + ; returns A=$00 if FAC1 = (AY) + ; returns A=$01 if FAC1 > (AY) + ; returns A=$FF if FAC1 < (AY) + +; perform WIDTH + +LAB_WDTH + CMP #',' ; is next byte "," + BEQ LAB_TBSZ ; if so do tab size + + JSR LAB_GTBY ; get byte parameter + TXA ; copy width to A + BEQ LAB_NSTT ; branch if set for infinite line + + CPX #$10 ; else make min width = 16d + BCC TabErr ; if less do function call error and exit + +; this next compare ensures that we can't exit WIDTH via an error leaving the +; tab size greater than the line length. + + CPX TabSiz ; compare with tab size + BCS LAB_NSTT ; branch if >= tab size + + STX TabSiz ; else make tab size = terminal width +LAB_NSTT + STX TWidth ; set the terminal width + JSR LAB_GBYT ; get BASIC byte back + BEQ WExit ; exit if no following + + CMP #',' ; else is it "," + BNE LAB_MMSE ; if not do syntax error + +LAB_TBSZ + JSR LAB_SGBY ; scan and get byte parameter + TXA ; copy TAB size + BMI TabErr ; if >127 do function call error and exit + + CPX #$01 ; compare with min-1 + BCC TabErr ; if <=1 do function call error and exit + + LDA TWidth ; set flags for width + BEQ LAB_SVTB ; skip check if infinite line + + CPX TWidth ; compare TAB with width + BEQ LAB_SVTB ; ok if = + + BCS TabErr ; branch if too big + +LAB_SVTB + STX TabSiz ; save TAB size + +; calculate tab column limit from TAB size. The Iclim is set to the last tab +; position on a line that still has at least one whole tab width between it +; and the end of the line. + +WExit + LDA TWidth ; get width + BEQ LAB_SULP ; branch if infinite line + + CMP TabSiz ; compare with tab size + BCS LAB_WDLP ; branch if >= tab size + + STA TabSiz ; else make tab size = terminal width +LAB_SULP + SEC ; set carry for subtract +LAB_WDLP + SBC TabSiz ; subtract tab size + BCS LAB_WDLP ; loop while no borrow + + ADC TabSiz ; add tab size back + CLC ; clear carry for add + ADC TabSiz ; add tab size back again + STA Iclim ; save for now + LDA TWidth ; get width back + SEC ; set carry for subtract + SBC Iclim ; subtract remainder + STA Iclim ; save tab column limit +LAB_NOSQ + RTS + +TabErr + JMP LAB_FCER ; do function call error then warm start + +; perform SQR() + +LAB_SQR + LDA FAC1_s ; get FAC1 sign + BMI TabErr ; if -ve do function call error + + LDA FAC1_e ; get exponent + BEQ LAB_NOSQ ; if zero just return + + ; else do root + JSR LAB_27AB ; round and copy FAC1 to FAC2 + LDA #$00 ; clear A + + STA FACt_3 ; clear remainder + STA FACt_2 ; .. + STA FACt_1 ; .. + STA TempB ; .. + + STA FAC1_3 ; clear root + STA FAC1_2 ; .. + STA FAC1_1 ; .. + + LDX #$18 ; 24 pairs of bits to do + LDA FAC2_e ; get exponent + LSR ; check odd/even + BCS LAB_SQE2 ; if odd only 1 shift first time + +LAB_SQE1 + ASL FAC2_3 ; shift highest bit of number .. + ROL FAC2_2 ; .. + ROL FAC2_1 ; .. + ROL FACt_3 ; .. into remainder + ROL FACt_2 ; .. + ROL FACt_1 ; .. + ROL TempB ; .. never overflows +LAB_SQE2 + ASL FAC2_3 ; shift highest bit of number .. + ROL FAC2_2 ; .. + ROL FAC2_1 ; .. + ROL FACt_3 ; .. into remainder + ROL FACt_2 ; .. + ROL FACt_1 ; .. + ROL TempB ; .. never overflows + + ASL FAC1_3 ; root = root * 2 + ROL FAC1_2 ; .. + ROL FAC1_1 ; .. never overflows + + LDA FAC1_3 ; get root low byte + ROL ; *2 + STA Temp3 ; save partial low byte + LDA FAC1_2 ; get root low mid byte + ROL ; *2 + STA Temp3+1 ; save partial low mid byte + LDA FAC1_1 ; get root high mid byte + ROL ; *2 + STA Temp3+2 ; save partial high mid byte + LDA #$00 ; get root high byte (always $00) + ROL ; *2 + STA Temp3+3 ; save partial high byte + + ; carry clear for subtract +1 + LDA FACt_3 ; get remainder low byte + SBC Temp3 ; subtract partial low byte + STA Temp3 ; save partial low byte + + LDA FACt_2 ; get remainder low mid byte + SBC Temp3+1 ; subtract partial low mid byte + STA Temp3+1 ; save partial low mid byte + + LDA FACt_1 ; get remainder high mid byte + SBC Temp3+2 ; subtract partial high mid byte + TAY ; copy partial high mid byte + + LDA TempB ; get remainder high byte + SBC Temp3+3 ; subtract partial high byte + BCC LAB_SQNS ; skip sub if remainder smaller + + STA TempB ; save remainder high byte + + STY FACt_1 ; save remainder high mid byte + + LDA Temp3+1 ; get remainder low mid byte + STA FACt_2 ; save remainder low mid byte + + LDA Temp3 ; get partial low byte + STA FACt_3 ; save remainder low byte + + INC FAC1_3 ; increment root low byte (never any rollover) +LAB_SQNS + DEX ; decrement bit pair count + BNE LAB_SQE1 ; loop if not all done + + SEC ; set carry for subtract + LDA FAC2_e ; get exponent + SBC #$80 ; normalise + ROR ; /2 and re-bias to $80 + ADC #$00 ; add bit zero back in (allow for half shift) + STA FAC1_e ; save it + JMP LAB_24D5 ; normalise FAC1 and return + +; perform VARPTR() + +LAB_VARPTR + JSR LAB_IGBY ; increment and scan memory + JSR LAB_GVAR ; get var address + JSR LAB_1BFB ; scan for ")" , else do syntax error then warm start + LDY Cvaral ; get var address low byte + LDA Cvarah ; get var address high byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform PI + +LAB_PI + LDA #LAB_2C7C ; set (2*pi) pointer high byte + JSR LAB_UFAC ; unpack memory (AY) into FAC1 + DEC FAC1_e ; make result = PI + RTS + +; perform TWOPI + +LAB_TWOPI + LDA #LAB_2C7C ; set (2*pi) pointer high byte + JMP LAB_UFAC ; unpack memory (AY) into FAC1 and return + +; system dependant i/o vectors +; these are in RAM and are set by the monitor at start-up + +V_INPT + JMP (VEC_IN) ; non halting scan input device +V_OUTP + JMP (VEC_OUT) ; send byte to output device +V_LOAD + JMP (VEC_LD) ; load BASIC program +V_SAVE + JMP (VEC_SV) ; save BASIC program + +; The rest are tables messages and code for RAM + +; the rest of the code is tables and BASIC start-up code + +PG2_TABS + .byte $00 ; ctrl-c flag - $00 = enabled + .byte $00 ; ctrl-c byte - GET needs this + .byte $00 ; ctrl-c byte timeout - GET needs this + .word CTRLC ; ctrl c check vector +; .word xxxx ; non halting key input - monitor to set this +; .word xxxx ; output vector - monitor to set this +; .word xxxx ; load vector - monitor to set this +; .word xxxx ; save vector - monitor to set this +PG2_TABE + +; character get subroutine for zero page + +; For a 1.8432MHz 6502 including the JSR and RTS +; fastest (>=":") = 29 cycles = 15.7uS +; slowest (<":") = 40 cycles = 21.7uS +; space skip = +21 cycles = +11.4uS +; inc across page = +4 cycles = +2.2uS + +; the target address for the LDA at LAB_2CF4 becomes the BASIC execute pointer once the +; block is copied to it's destination, any non zero page address will do at assembly +; time, to assemble a three byte instruction. + +; page 0 initialisation table from $BC +; increment and scan memory + +LAB_2CEE + INC Bpntrl ; increment BASIC execute pointer low byte + BNE LAB_2CF4 ; branch if no carry + ; else + INC Bpntrh ; increment BASIC execute pointer high byte + +; page 0 initialisation table from $C2 +; scan memory + +LAB_2CF4 + LDA $FFFF ; get byte to scan (addr set by call routine) + CMP #TK_ELSE ; compare with the token for ELSE + BEQ LAB_2D05 ; exit if ELSE, not numeric, carry set + + CMP #":" ; compare with ":" + BCS LAB_2D05 ; exit if >= ":", not numeric, carry set + + CMP #' ' ; compare with " " + BEQ LAB_2CEE ; if " " go do next + + SEC ; set carry for SBC + SBC #'0' ; subtract "0" + SEC ; set carry for SBC + SBC #$D0 ; subtract -"0" + ; clear carry if byte = "0"-"9" +LAB_2D05 + RTS + +; page zero initialisation table $00-$12 inclusive + +StrTab + .byte $4C ; JMP opcode + .word LAB_COLD ; initial warm start vector (cold start) + + .byte $00 ; these bytes are not used by BASIC + .word $0000 ; + .word $0000 ; + .word $0000 ; + + .byte $4C ; JMP opcode + .word LAB_FCER ; initial user function vector ("Function call" error) + .byte $00 ; default NULL count + .byte $00 ; clear terminal position + .byte $00 ; default terminal width byte + .byte $F2 ; default limit for TAB = 14 + .word Ram_base ; start of user RAM +EndTab + +LAB_MSZM + .byte $0D,$0A,"Memory size ",$00 + +LAB_SMSG + .byte " Bytes free",$0D,$0A,$0A + .byte "Enhanced BASIC 2.22",$0A,$00 + +; numeric constants and series + + ; constants and series for LOG(n) +LAB_25A0 + .byte $02 ; counter + .byte $80,$19,$56,$62 ; 0.59898 + .byte $80,$76,$22,$F3 ; 0.96147 +;## .byte $80,$76,$22,$F1 ; 0.96147 + .byte $82,$38,$AA,$40 ; 2.88539 +;## .byte $82,$38,$AA,$45 ; 2.88539 + +LAB_25AD + .byte $80,$35,$04,$F3 ; 0.70711 1/root 2 +LAB_25B1 + .byte $81,$35,$04,$F3 ; 1.41421 root 2 +LAB_25B5 + .byte $80,$80,$00,$00 ; -0.5 +LAB_25B9 + .byte $80,$31,$72,$18 ; 0.69315 LOG(2) + + ; numeric PRINT constants +LAB_2947 + .byte $91,$43,$4F,$F8 ; 99999.9375 (max value with at least one decimal) +LAB_294B + .byte $94,$74,$23,$F7 ; 999999.4375 (max value before scientific notation) +LAB_294F + .byte $94,$74,$24,$00 ; 1000000 + + ; EXP(n) constants and series +LAB_2AFA + .byte $81,$38,$AA,$3B ; 1.4427 (1/LOG base 2 e) +LAB_2AFE + .byte $06 ; counter + .byte $74,$63,$90,$8C ; 2.17023e-4 + .byte $77,$23,$0C,$AB ; 0.00124 + .byte $7A,$1E,$94,$00 ; 0.00968 + .byte $7C,$63,$42,$80 ; 0.05548 + .byte $7E,$75,$FE,$D0 ; 0.24023 + .byte $80,$31,$72,$15 ; 0.69315 + .byte $81,$00,$00,$00 ; 1.00000 + +;## .byte $07 ; counter +;## .byte $74,$94,$2E,$40 ; -1/7! (-1/5040) +;## .byte $77,$2E,$4F,$70 ; 1/6! ( 1/720) +;## .byte $7A,$88,$02,$6E ; -1/5! (-1/120) +;## .byte $7C,$2A,$A0,$E6 ; 1/4! ( 1/24) +;## .byte $7E,$AA,$AA,$50 ; -1/3! (-1/6) +;## .byte $7F,$7F,$FF,$FF ; 1/2! ( 1/2) +;## .byte $81,$80,$00,$00 ; -1/1! (-1/1) +;## .byte $81,$00,$00,$00 ; 1/0! ( 1/1) + + ; trigonometric constants and series +LAB_2C78 + .byte $81,$49,$0F,$DB ; 1.570796371 (pi/2) as floating # +LAB_2C84 + .byte $04 ; counter + .byte $86,$1E,$D7,$FB ; 39.7109 +;## .byte $86,$1E,$D7,$BA ; 39.7109 + .byte $87,$99,$26,$65 ;-76.575 +;## .byte $87,$99,$26,$64 ;-76.575 + .byte $87,$23,$34,$58 ; 81.6022 + .byte $86,$A5,$5D,$E1 ;-41.3417 +;## .byte $86,$A5,$5D,$E0 ;-41.3417 +LAB_2C7C + .byte $83,$49,$0F,$DB ; 6.28319 (2*pi) as floating # +;## .byte $83,$49,$0F,$DA ; 6.28319 (2*pi) as floating # + +LAB_2CC9 + .byte $08 ; counter + .byte $78,$3A,$C5,$37 ; 0.00285 + .byte $7B,$83,$A2,$5C ;-0.0160686 + .byte $7C,$2E,$DD,$4D ; 0.0426915 + .byte $7D,$99,$B0,$1E ;-0.0750429 + .byte $7D,$59,$ED,$24 ; 0.106409 + .byte $7E,$91,$72,$00 ;-0.142036 + .byte $7E,$4C,$B9,$73 ; 0.199926 + .byte $7F,$AA,$AA,$53 ;-0.333331 + +;## .byte $08 ; counter +;## .byte $78,$3B,$D7,$4A ; 1/17 +;## .byte $7B,$84,$6E,$02 ;-1/15 +;## .byte $7C,$2F,$C1,$FE ; 1/13 +;## .byte $7D,$9A,$31,$74 ;-1/11 +;## .byte $7D,$5A,$3D,$84 ; 1/9 +;## .byte $7E,$91,$7F,$C8 ;-1/7 +;## .byte $7E,$4C,$BB,$E4 ; 1/5 +;## .byte $7F,$AA,$AA,$6C ;-1/3 + +LAB_1D96 = *+1 ; $00,$00 used for undefined variables +LAB_259C + .byte $81,$00,$00,$00 ; 1.000000, used for INC +LAB_2AFD + .byte $81,$80,$00,$00 ; -1.00000, used for DEC. must be on the same page as +1.00 + + ; misc constants +LAB_1DF7 + .byte $90 ;-32768 (uses first three bytes from 0.5) +LAB_2A96 + .byte $80,$00,$00,$00 ; 0.5 +LAB_2C80 + .byte $7F,$00,$00,$00 ; 0.25 +LAB_26B5 + .byte $84,$20,$00,$00 ; 10.0000 divide by 10 constant + +; This table is used in converting numbers to ASCII. + +LAB_2A9A +LAB_2A9B = LAB_2A9A+1 +LAB_2A9C = LAB_2A9B+1 + .byte $FE,$79,$60 ; -100000 + .byte $00,$27,$10 ; 10000 + .byte $FF,$FC,$18 ; -1000 + .byte $00,$00,$64 ; 100 + .byte $FF,$FF,$F6 ; -10 + .byte $00,$00,$01 ; 1 + +LAB_CTBL + .word LAB_END-1 ; END + .word LAB_FOR-1 ; FOR + .word LAB_NEXT-1 ; NEXT + .word LAB_DATA-1 ; DATA + .word LAB_INPUT-1 ; INPUT + .word LAB_DIM-1 ; DIM + .word LAB_READ-1 ; READ + .word LAB_LET-1 ; LET + .word LAB_DEC-1 ; DEC new command + .word LAB_GOTO-1 ; GOTO + .word LAB_RUN-1 ; RUN + .word LAB_IF-1 ; IF + .word LAB_RESTORE-1 ; RESTORE modified command + .word LAB_GOSUB-1 ; GOSUB + .word LAB_RETIRQ-1 ; RETIRQ new command + .word LAB_RETNMI-1 ; RETNMI new command + .word LAB_RETURN-1 ; RETURN + .word LAB_REM-1 ; REM + .word LAB_STOP-1 ; STOP + .word LAB_ON-1 ; ON modified command + .word LAB_NULL-1 ; NULL modified command + .word LAB_INC-1 ; INC new command + .word LAB_WAIT-1 ; WAIT + .word V_LOAD-1 ; LOAD + .word V_SAVE-1 ; SAVE + .word LAB_DEF-1 ; DEF + .word LAB_POKE-1 ; POKE + .word LAB_DOKE-1 ; DOKE new command + .word LAB_CALL-1 ; CALL new command + .word LAB_DO-1 ; DO new command + .word LAB_LOOP-1 ; LOOP new command + .word LAB_PRINT-1 ; PRINT + .word LAB_CONT-1 ; CONT + .word LAB_LIST-1 ; LIST + .word LAB_CLEAR-1 ; CLEAR + .word LAB_NEW-1 ; NEW + .word LAB_WDTH-1 ; WIDTH new command + .word LAB_GET-1 ; GET new command + .word LAB_SWAP-1 ; SWAP new command + .word LAB_BITSET-1 ; BITSET new command + .word LAB_BITCLR-1 ; BITCLR new command + .word LAB_IRQ-1 ; IRQ new command + .word LAB_NMI-1 ; NMI new command + +; function pre process routine table + +LAB_FTPL +LAB_FTPM = LAB_FTPL+$01 + .word LAB_PPFN-1 ; SGN(n) process numeric expression in () + .word LAB_PPFN-1 ; INT(n) " + .word LAB_PPFN-1 ; ABS(n) " + .word LAB_EVEZ-1 ; USR(x) process any expression + .word LAB_1BF7-1 ; FRE(x) " + .word LAB_1BF7-1 ; POS(x) " + .word LAB_PPFN-1 ; SQR(n) process numeric expression in () + .word LAB_PPFN-1 ; RND(n) " + .word LAB_PPFN-1 ; LOG(n) " + .word LAB_PPFN-1 ; EXP(n) " + .word LAB_PPFN-1 ; COS(n) " + .word LAB_PPFN-1 ; SIN(n) " + .word LAB_PPFN-1 ; TAN(n) " + .word LAB_PPFN-1 ; ATN(n) " + .word LAB_PPFN-1 ; PEEK(n) " + .word LAB_PPFN-1 ; DEEK(n) " + .word $0000 ; SADD() none + .word LAB_PPFS-1 ; LEN($) process string expression in () + .word LAB_PPFN-1 ; STR$(n) process numeric expression in () + .word LAB_PPFS-1 ; VAL($) process string expression in () + .word LAB_PPFS-1 ; ASC($) " + .word LAB_PPFS-1 ; UCASE$($) " + .word LAB_PPFS-1 ; LCASE$($) " + .word LAB_PPFN-1 ; CHR$(n) process numeric expression in () + .word LAB_BHSS-1 ; HEX$(n) " + .word LAB_BHSS-1 ; BIN$(n) " + .word $0000 ; BITTST() none + .word LAB_MMPP-1 ; MAX() process numeric expression + .word LAB_MMPP-1 ; MIN() " + .word LAB_PPBI-1 ; PI advance pointer + .word LAB_PPBI-1 ; TWOPI " + .word $0000 ; VARPTR() none + .word LAB_LRMS-1 ; LEFT$() process string expression + .word LAB_LRMS-1 ; RIGHT$() " + .word LAB_LRMS-1 ; MID$() " + +; action addresses for functions + +LAB_FTBL +LAB_FTBM = LAB_FTBL+$01 + .word LAB_SGN-1 ; SGN() + .word LAB_INT-1 ; INT() + .word LAB_ABS-1 ; ABS() + .word LAB_USR-1 ; USR() + .word LAB_FRE-1 ; FRE() + .word LAB_POS-1 ; POS() + .word LAB_SQR-1 ; SQR() + .word LAB_RND-1 ; RND() modified function + .word LAB_LOG-1 ; LOG() + .word LAB_EXP-1 ; EXP() + .word LAB_COS-1 ; COS() + .word LAB_SIN-1 ; SIN() + .word LAB_TAN-1 ; TAN() + .word LAB_ATN-1 ; ATN() + .word LAB_PEEK-1 ; PEEK() + .word LAB_DEEK-1 ; DEEK() new function + .word LAB_SADD-1 ; SADD() new function + .word LAB_LENS-1 ; LEN() + .word LAB_STRS-1 ; STR$() + .word LAB_VAL-1 ; VAL() + .word LAB_ASC-1 ; ASC() + .word LAB_UCASE-1 ; UCASE$() new function + .word LAB_LCASE-1 ; LCASE$() new function + .word LAB_CHRS-1 ; CHR$() + .word LAB_HEXS-1 ; HEX$() new function + .word LAB_BINS-1 ; BIN$() new function + .word LAB_BTST-1 ; BITTST() new function + .word LAB_MAX-1 ; MAX() new function + .word LAB_MIN-1 ; MIN() new function + .word LAB_PI-1 ; PI new function + .word LAB_TWOPI-1 ; TWOPI new function + .word LAB_VARPTR-1 ; VARPTR() new function + .word LAB_LEFT-1 ; LEFT$() + .word LAB_RIGHT-1 ; RIGHT$() + .word LAB_MIDS-1 ; MID$() + +; hierarchy and action addresses for operator + +LAB_OPPT + .byte $79 ; + + .word LAB_ADD-1 + .byte $79 ; - + .word LAB_SUBTRACT-1 + .byte $7B ; * + .word LAB_MULTIPLY-1 + .byte $7B ; / + .word LAB_DIVIDE-1 + .byte $7F ; ^ + .word LAB_POWER-1 + .byte $50 ; AND + .word LAB_AND-1 + .byte $46 ; EOR new operator + .word LAB_EOR-1 + .byte $46 ; OR + .word LAB_OR-1 + .byte $56 ; >> new operator + .word LAB_RSHIFT-1 + .byte $56 ; << new operator + .word LAB_LSHIFT-1 + .byte $7D ; > + .word LAB_GTHAN-1 + .byte $5A ; = + .word LAB_EQUAL-1 + .byte $64 ; < + .word LAB_LTHAN-1 + +; keywords start with .. +; this is the first character table and must be in alphabetic order + +TAB_1STC + .byte "*" + .byte "+" + .byte "-" + .byte "/" + .byte "<" + .byte "=" + .byte ">" + .byte "?" + .byte "A" + .byte "B" + .byte "C" + .byte "D" + .byte "E" + .byte "F" + .byte "G" + .byte "H" + .byte "I" + .byte "L" + .byte "M" + .byte "N" + .byte "O" + .byte "P" + .byte "R" + .byte "S" + .byte "T" + .byte "U" + .byte "V" + .byte "W" + .byte "^" + .byte $00 ; table terminator + +; pointers to keyword tables + +TAB_CHRT + .word TAB_STAR ; table for "*" + .word TAB_PLUS ; table for "+" + .word TAB_MNUS ; table for "-" + .word TAB_SLAS ; table for "/" + .word TAB_LESS ; table for "<" + .word TAB_EQUL ; table for "=" + .word TAB_MORE ; table for ">" + .word TAB_QEST ; table for "?" + .word TAB_ASCA ; table for "A" + .word TAB_ASCB ; table for "B" + .word TAB_ASCC ; table for "C" + .word TAB_ASCD ; table for "D" + .word TAB_ASCE ; table for "E" + .word TAB_ASCF ; table for "F" + .word TAB_ASCG ; table for "G" + .word TAB_ASCH ; table for "H" + .word TAB_ASCI ; table for "I" + .word TAB_ASCL ; table for "L" + .word TAB_ASCM ; table for "M" + .word TAB_ASCN ; table for "N" + .word TAB_ASCO ; table for "O" + .word TAB_ASCP ; table for "P" + .word TAB_ASCR ; table for "R" + .word TAB_ASCS ; table for "S" + .word TAB_ASCT ; table for "T" + .word TAB_ASCU ; table for "U" + .word TAB_ASCV ; table for "V" + .word TAB_ASCW ; table for "W" + .word TAB_POWR ; table for "^" + +; tables for each start character, note if a longer keyword with the same start +; letters as a shorter one exists then it must come first, else the list is in +; alphabetical order as follows .. + +; [keyword,token +; [keyword,token]] +; end marker (#$00) + +TAB_STAR + .byte TK_MUL,$00 ; * +TAB_PLUS + .byte TK_PLUS,$00 ; + +TAB_MNUS + .byte TK_MINUS,$00 ; - +TAB_SLAS + .byte TK_DIV,$00 ; / +TAB_LESS +LBB_LSHIFT + .byte "<",TK_LSHIFT ; << note - "<<" must come before "<" + .byte TK_LT ; < + .byte $00 +TAB_EQUL + .byte TK_EQUAL,$00 ; = +TAB_MORE +LBB_RSHIFT + .byte ">",TK_RSHIFT ; >> note - ">>" must come before ">" + .byte TK_GT ; > + .byte $00 +TAB_QEST + .byte TK_PRINT,$00 ; ? +TAB_ASCA +LBB_ABS + .byte "BS(",TK_ABS ; ABS( +LBB_AND + .byte "ND",TK_AND ; AND +LBB_ASC + .byte "SC(",TK_ASC ; ASC( +LBB_ATN + .byte "TN(",TK_ATN ; ATN( + .byte $00 +TAB_ASCB +LBB_BINS + .byte "IN$(",TK_BINS ; BIN$( +LBB_BITCLR + .byte "ITCLR",TK_BITCLR ; BITCLR +LBB_BITSET + .byte "ITSET",TK_BITSET ; BITSET +LBB_BITTST + .byte "ITTST(",TK_BITTST + ; BITTST( + .byte $00 +TAB_ASCC +LBB_CALL + .byte "ALL",TK_CALL ; CALL +LBB_CHRS + .byte "HR$(",TK_CHRS ; CHR$( +LBB_CLEAR + .byte "LEAR",TK_CLEAR ; CLEAR +LBB_CONT + .byte "ONT",TK_CONT ; CONT +LBB_COS + .byte "OS(",TK_COS ; COS( + .byte $00 +TAB_ASCD +LBB_DATA + .byte "ATA",TK_DATA ; DATA +LBB_DEC + .byte "EC",TK_DEC ; DEC +LBB_DEEK + .byte "EEK(",TK_DEEK ; DEEK( +LBB_DEF + .byte "EF",TK_DEF ; DEF +LBB_DIM + .byte "IM",TK_DIM ; DIM +LBB_DOKE + .byte "OKE",TK_DOKE ; DOKE note - "DOKE" must come before "DO" +LBB_DO + .byte "O",TK_DO ; DO + .byte $00 +TAB_ASCE +LBB_ELSE + .byte "LSE",TK_ELSE ; ELSE +LBB_END + .byte "ND",TK_END ; END +LBB_EOR + .byte "OR",TK_EOR ; EOR +LBB_EXP + .byte "XP(",TK_EXP ; EXP( + .byte $00 +TAB_ASCF +LBB_FN + .byte "N",TK_FN ; FN +LBB_FOR + .byte "OR",TK_FOR ; FOR +LBB_FRE + .byte "RE(",TK_FRE ; FRE( + .byte $00 +TAB_ASCG +LBB_GET + .byte "ET",TK_GET ; GET +LBB_GOSUB + .byte "OSUB",TK_GOSUB ; GOSUB +LBB_GOTO + .byte "OTO",TK_GOTO ; GOTO + .byte $00 +TAB_ASCH +LBB_HEXS + .byte "EX$(",TK_HEXS ; HEX$( + .byte $00 +TAB_ASCI +LBB_IF + .byte "F",TK_IF ; IF +LBB_INC + .byte "NC",TK_INC ; INC +LBB_INPUT + .byte "NPUT",TK_INPUT ; INPUT +LBB_INT + .byte "NT(",TK_INT ; INT( +LBB_IRQ + .byte "RQ",TK_IRQ ; IRQ + .byte $00 +TAB_ASCL +LBB_LCASES + .byte "CASE$(",TK_LCASES + ; LCASE$( +LBB_LEFTS + .byte "EFT$(",TK_LEFTS ; LEFT$( +LBB_LEN + .byte "EN(",TK_LEN ; LEN( +LBB_LET + .byte "ET",TK_LET ; LET +LBB_LIST + .byte "IST",TK_LIST ; LIST +LBB_LOAD + .byte "OAD",TK_LOAD ; LOAD +LBB_LOG + .byte "OG(",TK_LOG ; LOG( +LBB_LOOP + .byte "OOP",TK_LOOP ; LOOP + .byte $00 +TAB_ASCM +LBB_MAX + .byte "AX(",TK_MAX ; MAX( +LBB_MIDS + .byte "ID$(",TK_MIDS ; MID$( +LBB_MIN + .byte "IN(",TK_MIN ; MIN( + .byte $00 +TAB_ASCN +LBB_NEW + .byte "EW",TK_NEW ; NEW +LBB_NEXT + .byte "EXT",TK_NEXT ; NEXT +LBB_NMI + .byte "MI",TK_NMI ; NMI +LBB_NOT + .byte "OT",TK_NOT ; NOT +LBB_NULL + .byte "ULL",TK_NULL ; NULL + .byte $00 +TAB_ASCO +LBB_OFF + .byte "FF",TK_OFF ; OFF +LBB_ON + .byte "N",TK_ON ; ON +LBB_OR + .byte "R",TK_OR ; OR + .byte $00 +TAB_ASCP +LBB_PEEK + .byte "EEK(",TK_PEEK ; PEEK( +LBB_PI + .byte "I",TK_PI ; PI +LBB_POKE + .byte "OKE",TK_POKE ; POKE +LBB_POS + .byte "OS(",TK_POS ; POS( +LBB_PRINT + .byte "RINT",TK_PRINT ; PRINT + .byte $00 +TAB_ASCR +LBB_READ + .byte "EAD",TK_READ ; READ +LBB_REM + .byte "EM",TK_REM ; REM +LBB_RESTORE + .byte "ESTORE",TK_RESTORE + ; RESTORE +LBB_RETIRQ + .byte "ETIRQ",TK_RETIRQ ; RETIRQ +LBB_RETNMI + .byte "ETNMI",TK_RETNMI ; RETNMI +LBB_RETURN + .byte "ETURN",TK_RETURN ; RETURN +LBB_RIGHTS + .byte "IGHT$(",TK_RIGHTS + ; RIGHT$( +LBB_RND + .byte "ND(",TK_RND ; RND( +LBB_RUN + .byte "UN",TK_RUN ; RUN + .byte $00 +TAB_ASCS +LBB_SADD + .byte "ADD(",TK_SADD ; SADD( +LBB_SAVE + .byte "AVE",TK_SAVE ; SAVE +LBB_SGN + .byte "GN(",TK_SGN ; SGN( +LBB_SIN + .byte "IN(",TK_SIN ; SIN( +LBB_SPC + .byte "PC(",TK_SPC ; SPC( +LBB_SQR + .byte "QR(",TK_SQR ; SQR( +LBB_STEP + .byte "TEP",TK_STEP ; STEP +LBB_STOP + .byte "TOP",TK_STOP ; STOP +LBB_STRS + .byte "TR$(",TK_STRS ; STR$( +LBB_SWAP + .byte "WAP",TK_SWAP ; SWAP + .byte $00 +TAB_ASCT +LBB_TAB + .byte "AB(",TK_TAB ; TAB( +LBB_TAN + .byte "AN(",TK_TAN ; TAN( +LBB_THEN + .byte "HEN",TK_THEN ; THEN +LBB_TO + .byte "O",TK_TO ; TO +LBB_TWOPI + .byte "WOPI",TK_TWOPI ; TWOPI + .byte $00 +TAB_ASCU +LBB_UCASES + .byte "CASE$(",TK_UCASES + ; UCASE$( +LBB_UNTIL + .byte "NTIL",TK_UNTIL ; UNTIL +LBB_USR + .byte "SR(",TK_USR ; USR( + .byte $00 +TAB_ASCV +LBB_VAL + .byte "AL(",TK_VAL ; VAL( +LBB_VPTR + .byte "ARPTR(",TK_VPTR ; VARPTR( + .byte $00 +TAB_ASCW +LBB_WAIT + .byte "AIT",TK_WAIT ; WAIT +LBB_WHILE + .byte "HILE",TK_WHILE ; WHILE +LBB_WIDTH + .byte "IDTH",TK_WIDTH ; WIDTH + .byte $00 +TAB_POWR + .byte TK_POWER,$00 ; ^ + +; new decode table for LIST +; Table is .. +; byte - keyword length, keyword first character +; word - pointer to rest of keyword from dictionary + +; note if length is 1 then the pointer is ignored + +LAB_KEYT + .byte 3,'E' + .word LBB_END ; END + .byte 3,'F' + .word LBB_FOR ; FOR + .byte 4,'N' + .word LBB_NEXT ; NEXT + .byte 4,'D' + .word LBB_DATA ; DATA + .byte 5,'I' + .word LBB_INPUT ; INPUT + .byte 3,'D' + .word LBB_DIM ; DIM + .byte 4,'R' + .word LBB_READ ; READ + .byte 3,'L' + .word LBB_LET ; LET + .byte 3,'D' + .word LBB_DEC ; DEC + .byte 4,'G' + .word LBB_GOTO ; GOTO + .byte 3,'R' + .word LBB_RUN ; RUN + .byte 2,'I' + .word LBB_IF ; IF + .byte 7,'R' + .word LBB_RESTORE ; RESTORE + .byte 5,'G' + .word LBB_GOSUB ; GOSUB + .byte 6,'R' + .word LBB_RETIRQ ; RETIRQ + .byte 6,'R' + .word LBB_RETNMI ; RETNMI + .byte 6,'R' + .word LBB_RETURN ; RETURN + .byte 3,'R' + .word LBB_REM ; REM + .byte 4,'S' + .word LBB_STOP ; STOP + .byte 2,'O' + .word LBB_ON ; ON + .byte 4,'N' + .word LBB_NULL ; NULL + .byte 3,'I' + .word LBB_INC ; INC + .byte 4,'W' + .word LBB_WAIT ; WAIT + .byte 4,'L' + .word LBB_LOAD ; LOAD + .byte 4,'S' + .word LBB_SAVE ; SAVE + .byte 3,'D' + .word LBB_DEF ; DEF + .byte 4,'P' + .word LBB_POKE ; POKE + .byte 4,'D' + .word LBB_DOKE ; DOKE + .byte 4,'C' + .word LBB_CALL ; CALL + .byte 2,'D' + .word LBB_DO ; DO + .byte 4,'L' + .word LBB_LOOP ; LOOP + .byte 5,'P' + .word LBB_PRINT ; PRINT + .byte 4,'C' + .word LBB_CONT ; CONT + .byte 4,'L' + .word LBB_LIST ; LIST + .byte 5,'C' + .word LBB_CLEAR ; CLEAR + .byte 3,'N' + .word LBB_NEW ; NEW + .byte 5,'W' + .word LBB_WIDTH ; WIDTH + .byte 3,'G' + .word LBB_GET ; GET + .byte 4,'S' + .word LBB_SWAP ; SWAP + .byte 6,'B' + .word LBB_BITSET ; BITSET + .byte 6,'B' + .word LBB_BITCLR ; BITCLR + .byte 3,'I' + .word LBB_IRQ ; IRQ + .byte 3,'N' + .word LBB_NMI ; NMI + +; secondary commands (can't start a statement) + + .byte 4,'T' + .word LBB_TAB ; TAB + .byte 4,'E' + .word LBB_ELSE ; ELSE + .byte 2,'T' + .word LBB_TO ; TO + .byte 2,'F' + .word LBB_FN ; FN + .byte 4,'S' + .word LBB_SPC ; SPC + .byte 4,'T' + .word LBB_THEN ; THEN + .byte 3,'N' + .word LBB_NOT ; NOT + .byte 4,'S' + .word LBB_STEP ; STEP + .byte 5,'U' + .word LBB_UNTIL ; UNTIL + .byte 5,'W' + .word LBB_WHILE ; WHILE + .byte 3,'O' + .word LBB_OFF ; OFF + +; opperators + + .byte 1,'+' + .word $0000 ; + + .byte 1,'-' + .word $0000 ; - + .byte 1,'*' + .word $0000 ; * + .byte 1,'/' + .word $0000 ; / + .byte 1,'^' + .word $0000 ; ^ + .byte 3,'A' + .word LBB_AND ; AND + .byte 3,'E' + .word LBB_EOR ; EOR + .byte 2,'O' + .word LBB_OR ; OR + .byte 2,'>' + .word LBB_RSHIFT ; >> + .byte 2,'<' + .word LBB_LSHIFT ; << + .byte 1,'>' + .word $0000 ; > + .byte 1,'=' + .word $0000 ; = + .byte 1,'<' + .word $0000 ; < + +; functions + + .byte 4,'S' ; + .word LBB_SGN ; SGN + .byte 4,'I' ; + .word LBB_INT ; INT + .byte 4,'A' ; + .word LBB_ABS ; ABS + .byte 4,'U' ; + .word LBB_USR ; USR + .byte 4,'F' ; + .word LBB_FRE ; FRE + .byte 4,'P' ; + .word LBB_POS ; POS + .byte 4,'S' ; + .word LBB_SQR ; SQR + .byte 4,'R' ; + .word LBB_RND ; RND + .byte 4,'L' ; + .word LBB_LOG ; LOG + .byte 4,'E' ; + .word LBB_EXP ; EXP + .byte 4,'C' ; + .word LBB_COS ; COS + .byte 4,'S' ; + .word LBB_SIN ; SIN + .byte 4,'T' ; + .word LBB_TAN ; TAN + .byte 4,'A' ; + .word LBB_ATN ; ATN + .byte 5,'P' ; + .word LBB_PEEK ; PEEK + .byte 5,'D' ; + .word LBB_DEEK ; DEEK + .byte 5,'S' ; + .word LBB_SADD ; SADD + .byte 4,'L' ; + .word LBB_LEN ; LEN + .byte 5,'S' ; + .word LBB_STRS ; STR$ + .byte 4,'V' ; + .word LBB_VAL ; VAL + .byte 4,'A' ; + .word LBB_ASC ; ASC + .byte 7,'U' ; + .word LBB_UCASES ; UCASE$ + .byte 7,'L' ; + .word LBB_LCASES ; LCASE$ + .byte 5,'C' ; + .word LBB_CHRS ; CHR$ + .byte 5,'H' ; + .word LBB_HEXS ; HEX$ + .byte 5,'B' ; + .word LBB_BINS ; BIN$ + .byte 7,'B' ; + .word LBB_BITTST ; BITTST + .byte 4,'M' ; + .word LBB_MAX ; MAX + .byte 4,'M' ; + .word LBB_MIN ; MIN + .byte 2,'P' ; + .word LBB_PI ; PI + .byte 5,'T' ; + .word LBB_TWOPI ; TWOPI + .byte 7,'V' ; + .word LBB_VPTR ; VARPTR + .byte 6,'L' ; + .word LBB_LEFTS ; LEFT$ + .byte 7,'R' ; + .word LBB_RIGHTS ; RIGHT$ + .byte 5,'M' ; + .word LBB_MIDS ; MID$ + +; BASIC messages, mostly error messages + +LAB_BAER + .word ERR_NF ;$00 NEXT without FOR + .word ERR_SN ;$02 syntax + .word ERR_RG ;$04 RETURN without GOSUB + .word ERR_OD ;$06 out of data + .word ERR_FC ;$08 function call + .word ERR_OV ;$0A overflow + .word ERR_OM ;$0C out of memory + .word ERR_US ;$0E undefined statement + .word ERR_BS ;$10 array bounds + .word ERR_DD ;$12 double dimension array + .word ERR_D0 ;$14 divide by 0 + .word ERR_ID ;$16 illegal direct + .word ERR_TM ;$18 type mismatch + .word ERR_LS ;$1A long string + .word ERR_ST ;$1C string too complex + .word ERR_CN ;$1E continue error + .word ERR_UF ;$20 undefined function + .word ERR_LD ;$22 LOOP without DO + +; I may implement these two errors to force definition of variables and +; dimensioning of arrays before use. + +; .word ERR_UV ;$24 undefined variable + +; the above error has been tested and works (see code and comments below LAB_1D8B) + +; .word ERR_UA ;$26 undimensioned array + +ERR_NF .byte "NEXT without FOR",$00 +ERR_SN .byte "Syntax",$00 +ERR_RG .byte "RETURN without GOSUB",$00 +ERR_OD .byte "Out of DATA",$00 +ERR_FC .byte "Function call",$00 +ERR_OV .byte "Overflow",$00 +ERR_OM .byte "Out of memory",$00 +ERR_US .byte "Undefined statement",$00 +ERR_BS .byte "Array bounds",$00 +ERR_DD .byte "Double dimension",$00 +ERR_D0 .byte "Divide by zero",$00 +ERR_ID .byte "Illegal direct",$00 +ERR_TM .byte "Type mismatch",$00 +ERR_LS .byte "String too long",$00 +ERR_ST .byte "String too complex",$00 +ERR_CN .byte "Can't continue",$00 +ERR_UF .byte "Undefined function",$00 +ERR_LD .byte "LOOP without DO",$00 + +;ERR_UV .byte "Undefined variable",$00 + +; the above error has been tested and works (see code and comments below LAB_1D8B) + +;ERR_UA .byte "Undimensioned array",$00 + +LAB_BMSG .byte $0D,$0A,"Break",$00 +LAB_EMSG .byte " Error",$00 +LAB_LMSG .byte " in line ",$00 +LAB_RMSG .byte $0D,$0A,"Ready",$0D,$0A,$00 + +LAB_IMSG .byte " Extra ignored",$0D,$0A,$00 +LAB_REDO .byte " Redo from start",$0D,$0A,$00 + +AA_end_basic + +; put the IRQ and MNI code in RAM so that it can be changed + +IRQ_vec = VEC_SV+2 ; IRQ code vector +NMI_vec = IRQ_vec+$0A ; NMI code vector + +; now the code. all this does is set up the vectors and interrupt code +; and wait for the user to select [C]old or [W]arm start. nothing else +; fits in less than 128 bytes + + .dsb ($FF00-*),0 ; pad to new PC with zeros + *= $FF00 ; (Was $FF80) pretend this is in a 1/8K ROM + +; reset vector points here + +RES_vec + CLD ; clear decimal mode + LDX #$FF ; empty stack + TXS ; set the stack + +; set up vectors and interrupt code, copy them to page 2 + + LDY #END_CODE-LAB_vec ; set index/count +LAB_stlp + LDA LAB_vec-1,Y ; get byte from interrupt code + STA VEC_IN-1,Y ; save to RAM + DEY ; decrement index/count + BNE LAB_stlp ; loop if more to do + +; now do the signon message, Y = $00 here + +LAB_signon + LDA LAB_mess,Y ; get byte from sign on message + BEQ LAB_nokey ; exit loop if done + + JSR V_OUTP ; output character + INY ; increment index + BNE LAB_signon ; loop, branch always + +LAB_nokey + JSR V_INPT ; call scan input device + BCC LAB_nokey ; loop if no key + + AND #$DF ; mask xx0x xxxx, ensure upper case + CMP #'W' ; compare with [W]arm start + BEQ LAB_dowarm ; branch if [W]arm start + + CMP #'C' ; compare with [C]old start + BNE RES_vec ; loop if not [C]old start + + JMP LAB_COLD ; do EhBASIC cold start + +LAB_dowarm + JMP LAB_WARM ; do EhBASIC warm start + +; byte out + +ByteOut + STA $efa0 ; save byte to data port + LDA $07 ; write to screen command + STA $efa1 ; save command to command port + RTS + +; byte in + +ByteIn + LDA $efa2 ; get byte from keyboard + BEQ LAB_nobyw ; branch if no byte waiting + PHA + LDA 0 + STA $efa2 + PLA + + SEC ; flag byte received + RTS + +LAB_nobyw + CLC ; flag no byte received +no_load ; empty load vector for EhBASIC +no_save ; empty save vector for EhBASIC + RTS + +; vector tables + +LAB_vec + .word ByteIn ; byte in + .word ByteOut ; byte out + .word no_load ; null load vector for EhBASIC + .word no_save ; null save vector for EhBASIC + +; EhBASIC IRQ support + +IRQ_CODE + PHA ; save A + LDA IrqBase ; get the IRQ flag byte + LSR ; shift the set b7 to b6, and on down ... + ORA IrqBase ; OR the original back in + STA IrqBase ; save the new IRQ flag byte + PLA ; restore A + RTI + +; EhBASIC NMI support + +NMI_CODE + PHA ; save A + LDA NmiBase ; get the NMI flag byte + LSR ; shift the set b7 to b6, and on down ... + ORA NmiBase ; OR the original back in + STA NmiBase ; save the new NMI flag byte + PLA ; restore A + RTI + +END_CODE + +LAB_mess + .byte $0D,$0A,"6502 EhBASIC [C]old/[W]arm ?",$00 + ; sign on string + +; system vectors + + .dsb ($FFFA-*),0 ; pad to new PC with zeros + *= $FFFA + + .word NMI_vec ; NMI vector + .word RES_vec ; RESET vector + .word IRQ_vec ; IRQ vector + diff --git a/EhBASIC/build.sh b/EhBASIC/build.sh new file mode 100755 index 0000000..39b5cfc --- /dev/null +++ b/EhBASIC/build.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +BASE=basic-v6502 + +../../xa-2.3.5/xa -M -e $BASE.error.txt -l $BASE.label.txt -o $BASE.out $BASE.asm +../../vm/65b2js/Debug/65b2js $BASE.out > $BASE.js +cp $BASE.js ../../vm/Virtual\ Computer/software/. diff --git a/EhBASIC/original/basic.asm b/EhBASIC/original/basic.asm new file mode 100644 index 0000000..75503e8 --- /dev/null +++ b/EhBASIC/original/basic.asm @@ -0,0 +1,8691 @@ + +; Enhanced BASIC to assemble under 6502 simulator, $ver 2.22 + +; $E7E1 $E7CF $E7C6 $E7D3 $E7D1 $E7D5 $E7CF $E81E $E825 + +; 2.00 new revision numbers start here +; 2.01 fixed LCASE$() and UCASE$() +; 2.02 new get value routine done +; 2.03 changed RND() to galoise method +; 2.04 fixed SPC() +; 2.05 new get value routine fixed +; 2.06 changed USR() code +; 2.07 fixed STR$() +; 2.08 changed INPUT and READ to remove need for $00 start to input buffer +; 2.09 fixed RND() +; 2.10 integrated missed changes from an earlier version +; 2.20 added ELSE to IF .. THEN and fixed IF .. GOTO to cause error +; 2.21 fixed IF .. THEN RETURN to not cause error +; 2.22 fixed RND() breaking the get byte routine + +; zero page use .. + +LAB_WARM = $00 ; BASIC warm start entry point +Wrmjpl = LAB_WARM+1; BASIC warm start vector jump low byte +Wrmjph = LAB_WARM+2; BASIC warm start vector jump high byte + +Usrjmp = $0A ; USR function JMP address +Usrjpl = Usrjmp+1 ; USR function JMP vector low byte +Usrjph = Usrjmp+2 ; USR function JMP vector high byte +Nullct = $0D ; nulls output after each line +TPos = $0E ; BASIC terminal position byte +TWidth = $0F ; BASIC terminal width byte +Iclim = $10 ; input column limit +Itempl = $11 ; temporary integer low byte +Itemph = Itempl+1 ; temporary integer high byte + +nums_1 = Itempl ; number to bin/hex string convert MSB +nums_2 = nums_1+1 ; number to bin/hex string convert +nums_3 = nums_1+2 ; number to bin/hex string convert LSB + +Srchc = $5B ; search character +Temp3 = Srchc ; temp byte used in number routines +Scnquo = $5C ; scan-between-quotes flag +Asrch = Scnquo ; alt search character + +XOAw_l = Srchc ; eXclusive OR, OR and AND word low byte +XOAw_h = Scnquo ; eXclusive OR, OR and AND word high byte + +Ibptr = $5D ; input buffer pointer +Dimcnt = Ibptr ; # of dimensions +Tindx = Ibptr ; token index + +Defdim = $5E ; default DIM flag +Dtypef = $5F ; data type flag, $FF=string, $00=numeric +Oquote = $60 ; open quote flag (b7) (Flag: DATA scan; LIST quote; memory) +Gclctd = $60 ; garbage collected flag +Sufnxf = $61 ; subscript/FNX flag, 1xxx xxx = FN(0xxx xxx) +Imode = $62 ; input mode flag, $00=INPUT, $80=READ + +Cflag = $63 ; comparison evaluation flag + +TabSiz = $64 ; TAB step size (was input flag) + +next_s = $65 ; next descriptor stack address + + ; these two bytes form a word pointer to the item + ; currently on top of the descriptor stack +last_sl = $66 ; last descriptor stack address low byte +last_sh = $67 ; last descriptor stack address high byte (always $00) + +des_sk = $68 ; descriptor stack start address (temp strings) + +; = $70 ; End of descriptor stack + +ut1_pl = $71 ; utility pointer 1 low byte +ut1_ph = ut1_pl+1 ; utility pointer 1 high byte +ut2_pl = $73 ; utility pointer 2 low byte +ut2_ph = ut2_pl+1 ; utility pointer 2 high byte + +Temp_2 = ut1_pl ; temp byte for block move + +FACt_1 = $75 ; FAC temp mantissa1 +FACt_2 = FACt_1+1 ; FAC temp mantissa2 +FACt_3 = FACt_2+1 ; FAC temp mantissa3 + +dims_l = FACt_2 ; array dimension size low byte +dims_h = FACt_3 ; array dimension size high byte + +TempB = $78 ; temp page 0 byte + +Smeml = $79 ; start of mem low byte (Start-of-Basic) +Smemh = Smeml+1 ; start of mem high byte (Start-of-Basic) +Svarl = $7B ; start of vars low byte (Start-of-Variables) +Svarh = Svarl+1 ; start of vars high byte (Start-of-Variables) +Sarryl = $7D ; var mem end low byte (Start-of-Arrays) +Sarryh = Sarryl+1 ; var mem end high byte (Start-of-Arrays) +Earryl = $7F ; array mem end low byte (End-of-Arrays) +Earryh = Earryl+1 ; array mem end high byte (End-of-Arrays) +Sstorl = $81 ; string storage low byte (String storage (moving down)) +Sstorh = Sstorl+1 ; string storage high byte (String storage (moving down)) +Sutill = $83 ; string utility ptr low byte +Sutilh = Sutill+1 ; string utility ptr high byte +Ememl = $85 ; end of mem low byte (Limit-of-memory) +Ememh = Ememl+1 ; end of mem high byte (Limit-of-memory) +Clinel = $87 ; current line low byte (Basic line number) +Clineh = Clinel+1 ; current line high byte (Basic line number) +Blinel = $89 ; break line low byte (Previous Basic line number) +Blineh = Blinel+1 ; break line high byte (Previous Basic line number) + +Cpntrl = $8B ; continue pointer low byte +Cpntrh = Cpntrl+1 ; continue pointer high byte + +Dlinel = $8D ; current DATA line low byte +Dlineh = Dlinel+1 ; current DATA line high byte + +Dptrl = $8F ; DATA pointer low byte +Dptrh = Dptrl+1 ; DATA pointer high byte + +Rdptrl = $91 ; read pointer low byte +Rdptrh = Rdptrl+1 ; read pointer high byte + +Varnm1 = $93 ; current var name 1st byte +Varnm2 = Varnm1+1 ; current var name 2nd byte + +Cvaral = $95 ; current var address low byte +Cvarah = Cvaral+1 ; current var address high byte + +Frnxtl = $97 ; var pointer for FOR/NEXT low byte +Frnxth = Frnxtl+1 ; var pointer for FOR/NEXT high byte + +Tidx1 = Frnxtl ; temp line index + +Lvarpl = Frnxtl ; let var pointer low byte +Lvarph = Frnxth ; let var pointer high byte + +prstk = $99 ; precedence stacked flag + +comp_f = $9B ; compare function flag, bits 0,1 and 2 used + ; bit 2 set if > + ; bit 1 set if = + ; bit 0 set if < + +func_l = $9C ; function pointer low byte +func_h = func_l+1 ; function pointer high byte + +garb_l = func_l ; garbage collection working pointer low byte +garb_h = func_h ; garbage collection working pointer high byte + +des_2l = $9E ; string descriptor_2 pointer low byte +des_2h = des_2l+1 ; string descriptor_2 pointer high byte + +g_step = $A0 ; garbage collect step size + +Fnxjmp = $A1 ; jump vector for functions +Fnxjpl = Fnxjmp+1 ; functions jump vector low byte +Fnxjph = Fnxjmp+2 ; functions jump vector high byte + +g_indx = Fnxjpl ; garbage collect temp index + +FAC2_r = $A3 ; FAC2 rounding byte + +Adatal = $A4 ; array data pointer low byte +Adatah = Adatal+1 ; array data pointer high byte + +Nbendl = Adatal ; new block end pointer low byte +Nbendh = Adatah ; new block end pointer high byte + +Obendl = $A6 ; old block end pointer low byte +Obendh = Obendl+1 ; old block end pointer high byte + +numexp = $A8 ; string to float number exponent count +expcnt = $A9 ; string to float exponent count + +numbit = numexp ; bit count for array element calculations + +numdpf = $AA ; string to float decimal point flag +expneg = $AB ; string to float eval exponent -ve flag + +Astrtl = numdpf ; array start pointer low byte +Astrth = expneg ; array start pointer high byte + +Histrl = numdpf ; highest string low byte +Histrh = expneg ; highest string high byte + +Baslnl = numdpf ; BASIC search line pointer low byte +Baslnh = expneg ; BASIC search line pointer high byte + +Fvar_l = numdpf ; find/found variable pointer low byte +Fvar_h = expneg ; find/found variable pointer high byte + +Ostrtl = numdpf ; old block start pointer low byte +Ostrth = expneg ; old block start pointer high byte + +Vrschl = numdpf ; variable search pointer low byte +Vrschh = expneg ; variable search pointer high byte + +FAC1_e = $AC ; FAC1 exponent +FAC1_1 = FAC1_e+1 ; FAC1 mantissa1 +FAC1_2 = FAC1_e+2 ; FAC1 mantissa2 +FAC1_3 = FAC1_e+3 ; FAC1 mantissa3 +FAC1_s = FAC1_e+4 ; FAC1 sign (b7) + +str_ln = FAC1_e ; string length +str_pl = FAC1_1 ; string pointer low byte +str_ph = FAC1_2 ; string pointer high byte + +des_pl = FAC1_2 ; string descriptor pointer low byte +des_ph = FAC1_3 ; string descriptor pointer high byte + +mids_l = FAC1_3 ; MID$ string temp length byte + +negnum = $B1 ; string to float eval -ve flag +numcon = $B1 ; series evaluation constant count + +FAC1_o = $B2 ; FAC1 overflow byte + +FAC2_e = $B3 ; FAC2 exponent +FAC2_1 = FAC2_e+1 ; FAC2 mantissa1 +FAC2_2 = FAC2_e+2 ; FAC2 mantissa2 +FAC2_3 = FAC2_e+3 ; FAC2 mantissa3 +FAC2_s = FAC2_e+4 ; FAC2 sign (b7) + +FAC_sc = $B8 ; FAC sign comparison, Acc#1 vs #2 +FAC1_r = $B9 ; FAC1 rounding byte + +ssptr_l = FAC_sc ; string start pointer low byte +ssptr_h = FAC1_r ; string start pointer high byte + +sdescr = FAC_sc ; string descriptor pointer + +csidx = $BA ; line crunch save index +Asptl = csidx ; array size/pointer low byte +Aspth = $BB ; array size/pointer high byte + +Btmpl = Asptl ; BASIC pointer temp low byte +Btmph = Aspth ; BASIC pointer temp low byte + +Cptrl = Asptl ; BASIC pointer temp low byte +Cptrh = Aspth ; BASIC pointer temp low byte + +Sendl = Asptl ; BASIC pointer temp low byte +Sendh = Aspth ; BASIC pointer temp low byte + +LAB_IGBY = $BC ; get next BASIC byte subroutine + +LAB_GBYT = $C2 ; get current BASIC byte subroutine +Bpntrl = $C3 ; BASIC execute (get byte) pointer low byte +Bpntrh = Bpntrl+1 ; BASIC execute (get byte) pointer high byte + +; = $D7 ; end of get BASIC char subroutine + +Rbyte4 = $D8 ; extra PRNG byte +Rbyte1 = Rbyte4+1 ; most significant PRNG byte +Rbyte2 = Rbyte4+2 ; middle PRNG byte +Rbyte3 = Rbyte4+3 ; least significant PRNG byte + +NmiBase = $DC ; NMI handler enabled/setup/triggered flags + ; bit function + ; === ======== + ; 7 interrupt enabled + ; 6 interrupt setup + ; 5 interrupt happened +; = $DD ; NMI handler addr low byte +; = $DE ; NMI handler addr high byte +IrqBase = $DF ; IRQ handler enabled/setup/triggered flags +; = $E0 ; IRQ handler addr low byte +; = $E1 ; IRQ handler addr high byte + +; = $DE ; unused +; = $DF ; unused +; = $E0 ; unused +; = $E1 ; unused +; = $E2 ; unused +; = $E3 ; unused +; = $E4 ; unused +; = $E5 ; unused +; = $E6 ; unused +; = $E7 ; unused +; = $E8 ; unused +; = $E9 ; unused +; = $EA ; unused +; = $EB ; unused +; = $EC ; unused +; = $ED ; unused +; = $EE ; unused + +Decss = $EF ; number to decimal string start +Decssp1 = Decss+1 ; number to decimal string start + +; = $FF ; decimal string end + +; token values needed for BASIC + +; primary command tokens (can start a statement) + +TK_END = $80 ; END token +TK_FOR = TK_END+1 ; FOR token +TK_NEXT = TK_FOR+1 ; NEXT token +TK_DATA = TK_NEXT+1 ; DATA token +TK_INPUT = TK_DATA+1 ; INPUT token +TK_DIM = TK_INPUT+1 ; DIM token +TK_READ = TK_DIM+1 ; READ token +TK_LET = TK_READ+1 ; LET token +TK_DEC = TK_LET+1 ; DEC token +TK_GOTO = TK_DEC+1 ; GOTO token +TK_RUN = TK_GOTO+1 ; RUN token +TK_IF = TK_RUN+1 ; IF token +TK_RESTORE = TK_IF+1 ; RESTORE token +TK_GOSUB = TK_RESTORE+1 ; GOSUB token +TK_RETIRQ = TK_GOSUB+1 ; RETIRQ token +TK_RETNMI = TK_RETIRQ+1 ; RETNMI token +TK_RETURN = TK_RETNMI+1 ; RETURN token +TK_REM = TK_RETURN+1 ; REM token +TK_STOP = TK_REM+1 ; STOP token +TK_ON = TK_STOP+1 ; ON token +TK_NULL = TK_ON+1 ; NULL token +TK_INC = TK_NULL+1 ; INC token +TK_WAIT = TK_INC+1 ; WAIT token +TK_LOAD = TK_WAIT+1 ; LOAD token +TK_SAVE = TK_LOAD+1 ; SAVE token +TK_DEF = TK_SAVE+1 ; DEF token +TK_POKE = TK_DEF+1 ; POKE token +TK_DOKE = TK_POKE+1 ; DOKE token +TK_CALL = TK_DOKE+1 ; CALL token +TK_DO = TK_CALL+1 ; DO token +TK_LOOP = TK_DO+1 ; LOOP token +TK_PRINT = TK_LOOP+1 ; PRINT token +TK_CONT = TK_PRINT+1 ; CONT token +TK_LIST = TK_CONT+1 ; LIST token +TK_CLEAR = TK_LIST+1 ; CLEAR token +TK_NEW = TK_CLEAR+1 ; NEW token +TK_WIDTH = TK_NEW+1 ; WIDTH token +TK_GET = TK_WIDTH+1 ; GET token +TK_SWAP = TK_GET+1 ; SWAP token +TK_BITSET = TK_SWAP+1 ; BITSET token +TK_BITCLR = TK_BITSET+1 ; BITCLR token +TK_IRQ = TK_BITCLR+1 ; IRQ token +TK_NMI = TK_IRQ+1 ; NMI token + +; secondary command tokens, can't start a statement + +TK_TAB = TK_NMI+1 ; TAB token +TK_ELSE = TK_TAB+1 ; ELSE token +TK_TO = TK_ELSE+1 ; TO token +TK_FN = TK_TO+1 ; FN token +TK_SPC = TK_FN+1 ; SPC token +TK_THEN = TK_SPC+1 ; THEN token +TK_NOT = TK_THEN+1 ; NOT token +TK_STEP = TK_NOT+1 ; STEP token +TK_UNTIL = TK_STEP+1 ; UNTIL token +TK_WHILE = TK_UNTIL+1 ; WHILE token +TK_OFF = TK_WHILE+1 ; OFF token + +; opperator tokens + +TK_PLUS = TK_OFF+1 ; + token +TK_MINUS = TK_PLUS+1 ; - token +TK_MUL = TK_MINUS+1 ; * token +TK_DIV = TK_MUL+1 ; / token +TK_POWER = TK_DIV+1 ; ^ token +TK_AND = TK_POWER+1 ; AND token +TK_EOR = TK_AND+1 ; EOR token +TK_OR = TK_EOR+1 ; OR token +TK_RSHIFT = TK_OR+1 ; RSHIFT token +TK_LSHIFT = TK_RSHIFT+1 ; LSHIFT token +TK_GT = TK_LSHIFT+1 ; > token +TK_EQUAL = TK_GT+1 ; = token +TK_LT = TK_EQUAL+1 ; < token + +; functions tokens + +TK_SGN = TK_LT+1 ; SGN token +TK_INT = TK_SGN+1 ; INT token +TK_ABS = TK_INT+1 ; ABS token +TK_USR = TK_ABS+1 ; USR token +TK_FRE = TK_USR+1 ; FRE token +TK_POS = TK_FRE+1 ; POS token +TK_SQR = TK_POS+1 ; SQR token +TK_RND = TK_SQR+1 ; RND token +TK_LOG = TK_RND+1 ; LOG token +TK_EXP = TK_LOG+1 ; EXP token +TK_COS = TK_EXP+1 ; COS token +TK_SIN = TK_COS+1 ; SIN token +TK_TAN = TK_SIN+1 ; TAN token +TK_ATN = TK_TAN+1 ; ATN token +TK_PEEK = TK_ATN+1 ; PEEK token +TK_DEEK = TK_PEEK+1 ; DEEK token +TK_SADD = TK_DEEK+1 ; SADD token +TK_LEN = TK_SADD+1 ; LEN token +TK_STRS = TK_LEN+1 ; STR$ token +TK_VAL = TK_STRS+1 ; VAL token +TK_ASC = TK_VAL+1 ; ASC token +TK_UCASES = TK_ASC+1 ; UCASE$ token +TK_LCASES = TK_UCASES+1 ; LCASE$ token +TK_CHRS = TK_LCASES+1 ; CHR$ token +TK_HEXS = TK_CHRS+1 ; HEX$ token +TK_BINS = TK_HEXS+1 ; BIN$ token +TK_BITTST = TK_BINS+1 ; BITTST token +TK_MAX = TK_BITTST+1 ; MAX token +TK_MIN = TK_MAX+1 ; MIN token +TK_PI = TK_MIN+1 ; PI token +TK_TWOPI = TK_PI+1 ; TWOPI token +TK_VPTR = TK_TWOPI+1 ; VARPTR token +TK_LEFTS = TK_VPTR+1 ; LEFT$ token +TK_RIGHTS = TK_LEFTS+1 ; RIGHT$ token +TK_MIDS = TK_RIGHTS+1 ; MID$ token + +; offsets from a base of X or Y + +PLUS_0 = $00 ; X or Y plus 0 +PLUS_1 = $01 ; X or Y plus 1 +PLUS_2 = $02 ; X or Y plus 2 +PLUS_3 = $03 ; X or Y plus 3 + +LAB_STAK = $0100 ; stack bottom, no offset + +LAB_SKFE = LAB_STAK+$FE + ; flushed stack address +LAB_SKFF = LAB_STAK+$FF + ; flushed stack address + +ccflag = $0200 ; BASIC CTRL-C flag, 00 = enabled, 01 = dis +ccbyte = ccflag+1 ; BASIC CTRL-C byte +ccnull = ccbyte+1 ; BASIC CTRL-C byte timeout + +VEC_CC = ccnull+1 ; ctrl c check vector + +VEC_IN = VEC_CC+2 ; input vector +VEC_OUT = VEC_IN+2 ; output vector +VEC_LD = VEC_OUT+2 ; load vector +VEC_SV = VEC_LD+2 ; save vector + +; Ibuffs can now be anywhere in RAM, ensure that the max length is < $80 + +Ibuffs = IRQ_vec+$14 + ; start of input buffer after IRQ/NMI code +Ibuffe = Ibuffs+$47; end of input buffer + +Ram_base = $0300 ; start of user RAM (set as needed, should be page aligned) +Ram_top = $C000 ; end of user RAM+1 (set as needed, should be page aligned) + +; This start can be changed to suit your system + + *= $C000 + +; BASIC cold start entry point + +; new page 2 initialisation, copy block to ccflag on + +LAB_COLD + LDY #PG2_TABE-PG2_TABS-1 + ; byte count-1 +LAB_2D13 + LDA PG2_TABS,Y ; get byte + STA ccflag,Y ; store in page 2 + DEY ; decrement count + BPL LAB_2D13 ; loop if not done + + LDX #$FF ; set byte + STX Clineh ; set current line high byte (set immediate mode) + TXS ; reset stack pointer + + LDA #$4C ; code for JMP + STA Fnxjmp ; save for jump vector for functions + +; copy block from LAB_2CEE to $00BC - $00D3 + + LDX #StrTab-LAB_2CEE ; set byte count +LAB_2D4E + LDA LAB_2CEE-1,X ; get byte from table + STA LAB_IGBY-1,X ; save byte in page zero + DEX ; decrement count + BNE LAB_2D4E ; loop if not all done + +; copy block from StrTab to $0000 - $0012 + +LAB_GMEM + LDX #EndTab-StrTab-1 ; set byte count-1 +TabLoop + LDA StrTab,X ; get byte from table + STA PLUS_0,X ; save byte in page zero + DEX ; decrement count + BPL TabLoop ; loop if not all done + +; set-up start values + + LDA #$00 ; clear A + STA NmiBase ; clear NMI handler enabled flag + STA IrqBase ; clear IRQ handler enabled flag + STA FAC1_o ; clear FAC1 overflow byte + STA last_sh ; clear descriptor stack top item pointer high byte + + LDA #$0E ; set default tab size + STA TabSiz ; save it + LDA #$03 ; set garbage collect step size for descriptor stack + STA g_step ; save it + LDX #des_sk ; descriptor stack start + STX next_s ; set descriptor stack pointer + JSR LAB_CRLF ; print CR/LF + LDA #LAB_MSZM ; point to memory size message (high addr) + JSR LAB_18C3 ; print null terminated string from memory + JSR LAB_INLN ; print "? " and get BASIC input + STX Bpntrl ; set BASIC execute pointer low byte + STY Bpntrh ; set BASIC execute pointer high byte + JSR LAB_GBYT ; get last byte back + + BNE LAB_2DAA ; branch if not null (user typed something) + + LDY #$00 ; else clear Y + ; character was null so get memory size the hard way + ; we get here with Y=0 and Itempl/h = Ram_base +LAB_2D93 + INC Itempl ; increment temporary integer low byte + BNE LAB_2D99 ; branch if no overflow + + INC Itemph ; increment temporary integer high byte + LDA Itemph ; get high byte + CMP #>Ram_top ; compare with top of RAM+1 + BEQ LAB_2DB6 ; branch if match (end of user RAM) + +LAB_2D99 + LDA #$55 ; set test byte + STA (Itempl),Y ; save via temporary integer + CMP (Itempl),Y ; compare via temporary integer + BNE LAB_2DB6 ; branch if fail + + ASL ; shift test byte left (now $AA) + STA (Itempl),Y ; save via temporary integer + CMP (Itempl),Y ; compare via temporary integer + BEQ LAB_2D93 ; if ok go do next byte + + BNE LAB_2DB6 ; branch if fail + +LAB_2DAA + JSR LAB_2887 ; get FAC1 from string + LDA FAC1_e ; get FAC1 exponent + CMP #$98 ; compare with exponent = 2^24 + BCS LAB_GMEM ; if too large go try again + + JSR LAB_F2FU ; save integer part of FAC1 in temporary integer + ; (no range check) + +LAB_2DB6 + LDA Itempl ; get temporary integer low byte + LDY Itemph ; get temporary integer high byte + CPY #Ram_top ; compare with top of RAM high byte +; BCC MEM_OK ; branch if < RAM top + +; BNE LAB_GMEM ; if too large go try again + ; else was = so compare low bytes +; CMP #Ram_base ; set start addr high byte + STY Smeml ; save start of mem low byte + STX Smemh ; save start of mem high byte + +; this line is only needed if Ram_base is not $xx00 + +; LDY #$00 ; clear Y + TYA ; clear A + STA (Smeml),Y ; clear first byte + INC Smeml ; increment start of mem low byte + +; these two lines are only needed if Ram_base is $xxFF + +; BNE LAB_2E05 ; branch if no rollover + +; INC Smemh ; increment start of mem high byte +LAB_2E05 + JSR LAB_CRLF ; print CR/LF + JSR LAB_1463 ; do "NEW" and "CLEAR" + LDA Ememl ; get end of mem low byte + SEC ; set carry for subtract + SBC Smeml ; subtract start of mem low byte + TAX ; copy to X + LDA Ememh ; get end of mem high byte + SBC Smemh ; subtract start of mem high byte + JSR LAB_295E ; print XA as unsigned integer (bytes free) + LDA #LAB_SMSG ; point to sign-on message (high addr) + JSR LAB_18C3 ; print null terminated string from memory + LDA #LAB_1274 ; warm start vector high byte + STA Wrmjpl ; save warm start vector low byte + STY Wrmjph ; save warm start vector high byte + JMP (Wrmjpl) ; go do warm start + +; open up space in memory +; move (Ostrtl)-(Obendl) to new block ending at (Nbendl) + +; Nbendl,Nbendh - new block end address (A/Y) +; Obendl,Obendh - old block end address +; Ostrtl,Ostrth - old block start address + +; returns with .. + +; Nbendl,Nbendh - new block start address (high byte - $100) +; Obendl,Obendh - old block start address (high byte - $100) +; Ostrtl,Ostrth - old block start address (unchanged) + +LAB_11CF + JSR LAB_121F ; check available memory, "Out of memory" error if no room + ; addr to check is in AY (low/high) + STA Earryl ; save new array mem end low byte + STY Earryh ; save new array mem end high byte + +; open up space in memory +; move (Ostrtl)-(Obendl) to new block ending at (Nbendl) +; don't set array end + +LAB_11D6 + SEC ; set carry for subtract + LDA Obendl ; get block end low byte + SBC Ostrtl ; subtract block start low byte + TAY ; copy MOD(block length/$100) byte to Y + LDA Obendh ; get block end high byte + SBC Ostrth ; subtract block start high byte + TAX ; copy block length high byte to X + INX ; +1 to allow for count=0 exit + TYA ; copy block length low byte to A + BEQ LAB_120A ; branch if length low byte=0 + + ; block is (X-1)*256+Y bytes, do the Y bytes first + + SEC ; set carry for add + 1, two's complement + EOR #$FF ; invert low byte for subtract + ADC Obendl ; add block end low byte + + STA Obendl ; save corrected old block end low byte + BCS LAB_11F3 ; branch if no underflow + + DEC Obendh ; else decrement block end high byte + SEC ; set carry for add + 1, two's complement +LAB_11F3 + TYA ; get MOD(block length/$100) byte + EOR #$FF ; invert low byte for subtract + ADC Nbendl ; add destination end low byte + STA Nbendl ; save modified new block end low byte + BCS LAB_1203 ; branch if no underflow + + DEC Nbendh ; else decrement block end high byte + BCC LAB_1203 ; branch always + +LAB_11FF + LDA (Obendl),Y ; get byte from source + STA (Nbendl),Y ; copy byte to destination +LAB_1203 + DEY ; decrement index + BNE LAB_11FF ; loop until Y=0 + + ; now do Y=0 indexed byte + LDA (Obendl),Y ; get byte from source + STA (Nbendl),Y ; save byte to destination +LAB_120A + DEC Obendh ; decrement source pointer high byte + DEC Nbendh ; decrement destination pointer high byte + DEX ; decrement block count + BNE LAB_1203 ; loop until count = $0 + + RTS + +; check room on stack for A bytes +; stack too deep? do OM error + +LAB_1212 + STA TempB ; save result in temp byte + TSX ; copy stack + CPX TempB ; compare new "limit" with stack + BCC LAB_OMER ; if stack < limit do "Out of memory" error then warm start + + RTS + +; check available memory, "Out of memory" error if no room +; addr to check is in AY (low/high) + +LAB_121F + CPY Sstorh ; compare bottom of string mem high byte + BCC LAB_124B ; if less then exit (is ok) + + BNE LAB_1229 ; skip next test if greater (tested <) + + ; high byte was =, now do low byte + CMP Sstorl ; compare with bottom of string mem low byte + BCC LAB_124B ; if less then exit (is ok) + + ; addr is > string storage ptr (oops!) +LAB_1229 + PHA ; push addr low byte + LDX #$08 ; set index to save Adatal to expneg inclusive + TYA ; copy addr high byte (to push on stack) + + ; save misc numeric work area +LAB_122D + PHA ; push byte + LDA Adatal-1,X ; get byte from Adatal to expneg ( ,$00 not pushed) + DEX ; decrement index + BPL LAB_122D ; loop until all done + + JSR LAB_GARB ; garbage collection routine + + ; restore misc numeric work area + LDX #$00 ; clear the index to restore bytes +LAB_1238 + PLA ; pop byte + STA Adatal,X ; save byte to Adatal to expneg + INX ; increment index + CPX #$08 ; compare with end + 1 + BMI LAB_1238 ; loop if more to do + + PLA ; pop addr high byte + TAY ; copy back to Y + PLA ; pop addr low byte + CPY Sstorh ; compare bottom of string mem high byte + BCC LAB_124B ; if less then exit (is ok) + + BNE LAB_OMER ; if greater do "Out of memory" error then warm start + + ; high byte was =, now do low byte + CMP Sstorl ; compare with bottom of string mem low byte + BCS LAB_OMER ; if >= do "Out of memory" error then warm start + + ; ok exit, carry clear +LAB_124B + RTS + +; do "Out of memory" error then warm start + +LAB_OMER + LDX #$0C ; error code $0C ("Out of memory" error) + +; do error #X, then warm start + +LAB_XERR + JSR LAB_CRLF ; print CR/LF + + LDA LAB_BAER,X ; get error message pointer low byte + LDY LAB_BAER+1,X ; get error message pointer high byte + JSR LAB_18C3 ; print null terminated string from memory + + JSR LAB_1491 ; flush stack and clear continue flag + LDA #LAB_EMSG ; point to " Error" high addr +LAB_1269 + JSR LAB_18C3 ; print null terminated string from memory + LDY Clineh ; get current line high byte + INY ; increment it + BEQ LAB_1274 ; go do warm start (was immediate mode) + + ; else print line number + JSR LAB_2953 ; print " in line [LINE #]" + +; BASIC warm start entry point +; wait for Basic command + +LAB_1274 + ; clear ON IRQ/NMI bytes + LDA #$00 ; clear A + STA IrqBase ; clear enabled byte + STA NmiBase ; clear enabled byte + LDA #LAB_RMSG ; point to "Ready" message high byte + + JSR LAB_18C3 ; go do print string + +; wait for Basic command (no "Ready") + +LAB_127D + JSR LAB_1357 ; call for BASIC input +LAB_1280 + STX Bpntrl ; set BASIC execute pointer low byte + STY Bpntrh ; set BASIC execute pointer high byte + JSR LAB_GBYT ; scan memory + BEQ LAB_127D ; loop while null + +; got to interpret input line now .. + + LDX #$FF ; current line to null value + STX Clineh ; set current line high byte + BCC LAB_1295 ; branch if numeric character (handle new BASIC line) + + ; no line number .. immediate mode + JSR LAB_13A6 ; crunch keywords into Basic tokens + JMP LAB_15F6 ; go scan and interpret code + +; handle new BASIC line + +LAB_1295 + JSR LAB_GFPN ; get fixed-point number into temp integer + JSR LAB_13A6 ; crunch keywords into Basic tokens + STY Ibptr ; save index pointer to end of crunched line + JSR LAB_SSLN ; search BASIC for temp integer line number + BCC LAB_12E6 ; branch if not found + + ; aroooogah! line # already exists! delete it + LDY #$01 ; set index to next line pointer high byte + LDA (Baslnl),Y ; get next line pointer high byte + STA ut1_ph ; save it + LDA Svarl ; get start of vars low byte + STA ut1_pl ; save it + LDA Baslnh ; get found line pointer high byte + STA ut2_ph ; save it + LDA Baslnl ; get found line pointer low byte + DEY ; decrement index + SBC (Baslnl),Y ; subtract next line pointer low byte + CLC ; clear carry for add + ADC Svarl ; add start of vars low byte + STA Svarl ; save new start of vars low byte + STA ut2_pl ; save destination pointer low byte + LDA Svarh ; get start of vars high byte + ADC #$FF ; -1 + carry + STA Svarh ; save start of vars high byte + SBC Baslnh ; subtract found line pointer high byte + TAX ; copy to block count + SEC ; set carry for subtract + LDA Baslnl ; get found line pointer low byte + SBC Svarl ; subtract start of vars low byte + TAY ; copy to bytes in first block count + BCS LAB_12D0 ; branch if overflow + + INX ; increment block count (correct for =0 loop exit) + DEC ut2_ph ; decrement destination high byte +LAB_12D0 + CLC ; clear carry for add + ADC ut1_pl ; add source pointer low byte + BCC LAB_12D8 ; branch if no overflow + + DEC ut1_ph ; else decrement source pointer high byte + CLC ; clear carry + + ; close up memory to delete old line +LAB_12D8 + LDA (ut1_pl),Y ; get byte from source + STA (ut2_pl),Y ; copy to destination + INY ; increment index + BNE LAB_12D8 ; while <> 0 do this block + + INC ut1_ph ; increment source pointer high byte + INC ut2_ph ; increment destination pointer high byte + DEX ; decrement block count + BNE LAB_12D8 ; loop until all done + + ; got new line in buffer and no existing same # +LAB_12E6 + LDA Ibuffs ; get byte from start of input buffer + BEQ LAB_1319 ; if null line just go flush stack/vars and exit + + ; got new line and it isn't empty line + LDA Ememl ; get end of mem low byte + LDY Ememh ; get end of mem high byte + STA Sstorl ; set bottom of string space low byte + STY Sstorh ; set bottom of string space high byte + LDA Svarl ; get start of vars low byte (end of BASIC) + STA Obendl ; save old block end low byte + LDY Svarh ; get start of vars high byte (end of BASIC) + STY Obendh ; save old block end high byte + ADC Ibptr ; add input buffer pointer (also buffer length) + BCC LAB_1301 ; branch if no overflow from add + + INY ; else increment high byte +LAB_1301 + STA Nbendl ; save new block end low byte (move to, low byte) + STY Nbendh ; save new block end high byte + JSR LAB_11CF ; open up space in memory + ; old start pointer Ostrtl,Ostrth set by the find line call + LDA Earryl ; get array mem end low byte + LDY Earryh ; get array mem end high byte + STA Svarl ; save start of vars low byte + STY Svarh ; save start of vars high byte + LDY Ibptr ; get input buffer pointer (also buffer length) + DEY ; adjust for loop type +LAB_1311 + LDA Ibuffs-4,Y ; get byte from crunched line + STA (Baslnl),Y ; save it to program memory + DEY ; decrement count + CPY #$03 ; compare with first byte-1 + BNE LAB_1311 ; continue while count <> 3 + + LDA Itemph ; get line # high byte + STA (Baslnl),Y ; save it to program memory + DEY ; decrement count + LDA Itempl ; get line # low byte + STA (Baslnl),Y ; save it to program memory + DEY ; decrement count + LDA #$FF ; set byte to allow chain rebuild. if you didn't set this + ; byte then a zero already here would stop the chain rebuild + ; as it would think it was the [EOT] marker. + STA (Baslnl),Y ; save it to program memory + +LAB_1319 + JSR LAB_1477 ; reset execution to start, clear vars and flush stack + LDX Smeml ; get start of mem low byte + LDA Smemh ; get start of mem high byte + LDY #$01 ; index to high byte of next line pointer +LAB_1325 + STX ut1_pl ; set line start pointer low byte + STA ut1_ph ; set line start pointer high byte + LDA (ut1_pl),Y ; get it + BEQ LAB_133E ; exit if end of program + +; rebuild chaining of Basic lines + + LDY #$04 ; point to first code byte of line + ; there is always 1 byte + [EOL] as null entries are deleted +LAB_1330 + INY ; next code byte + LDA (ut1_pl),Y ; get byte + BNE LAB_1330 ; loop if not [EOL] + + SEC ; set carry for add + 1 + TYA ; copy end index + ADC ut1_pl ; add to line start pointer low byte + TAX ; copy to X + LDY #$00 ; clear index, point to this line's next line pointer + STA (ut1_pl),Y ; set next line pointer low byte + TYA ; clear A + ADC ut1_ph ; add line start pointer high byte + carry + INY ; increment index to high byte + STA (ut1_pl),Y ; save next line pointer low byte + BCC LAB_1325 ; go do next line, branch always, carry clear + + +LAB_133E + JMP LAB_127D ; else we just wait for Basic command, no "Ready" + +; print "? " and get BASIC input + +LAB_INLN + JSR LAB_18E3 ; print "?" character + JSR LAB_18E0 ; print " " + BNE LAB_1357 ; call for BASIC input and return + +; receive line from keyboard + + ; $08 as delete key (BACKSPACE on standard keyboard) +LAB_134B + JSR LAB_PRNA ; go print the character + DEX ; decrement the buffer counter (delete) + .byte $2C ; make LDX into BIT abs + +; call for BASIC input (main entry point) + +LAB_1357 + LDX #$00 ; clear BASIC line buffer pointer +LAB_1359 + JSR V_INPT ; call scan input device + BCC LAB_1359 ; loop if no byte + + BEQ LAB_1359 ; loop until valid input (ignore NULLs) + + CMP #$07 ; compare with [BELL] + BEQ LAB_1378 ; branch if [BELL] + + CMP #$0D ; compare with [CR] + BEQ LAB_1384 ; do CR/LF exit if [CR] + + CPX #$00 ; compare pointer with $00 + BNE LAB_1374 ; branch if not empty + +; next two lines ignore any non print character and [SPACE] if input buffer empty + + CMP #$21 ; compare with [SP]+1 + BCC LAB_1359 ; if < ignore character + +LAB_1374 + CMP #$08 ; compare with [BACKSPACE] (delete last character) + BEQ LAB_134B ; go delete last character + +LAB_1378 + CPX #Ibuffe-Ibuffs ; compare character count with max + BCS LAB_138E ; skip store and do [BELL] if buffer full + + STA Ibuffs,X ; else store in buffer + INX ; increment pointer +LAB_137F + JSR LAB_PRNA ; go print the character + BNE LAB_1359 ; always loop for next character + +LAB_1384 + JMP LAB_1866 ; do CR/LF exit to BASIC + +; announce buffer full + +LAB_138E + LDA #$07 ; [BELL] character into A + BNE LAB_137F ; go print the [BELL] but ignore input character + ; branch always + +; crunch keywords into Basic tokens +; position independent buffer version .. +; faster, dictionary search version .... + +LAB_13A6 + LDY #$FF ; set save index (makes for easy math later) + + SEC ; set carry for subtract + LDA Bpntrl ; get basic execute pointer low byte + SBC #= go save byte then continue crunching + + CMP #'<' ; compare with "<" + BCS LAB_13CC ; if >= go crunch now + + CMP #'0' ; compare with "0" + BCS LAB_13EC ; if >= go save byte then continue crunching + + STA Scnquo ; save buffer byte as search character + CMP #$22 ; is it quote character? + BEQ LAB_1410 ; branch if so (copy quoted string) + + CMP #'*' ; compare with "*" + BCC LAB_13EC ; if < go save byte then continue crunching + + ; else crunch now +LAB_13CC + BIT Oquote ; get open quote/DATA token flag + BVS LAB_13EC ; branch if b6 of Oquote set (was DATA) + ; go save byte then continue crunching + + STX TempB ; save buffer read index + STY csidx ; copy buffer save index + LDY #TAB_1STC ; get keyword first character table high address + STY ut2_ph ; save pointer high byte + LDY #$00 ; clear table pointer + +LAB_13D0 + CMP (ut2_pl),Y ; compare with keyword first character table byte + BEQ LAB_13D1 ; go do word_table_chr if match + + BCC LAB_13EA ; if < keyword first character table byte go restore + ; Y and save to crunched + + INY ; else increment pointer + BNE LAB_13D0 ; and loop (branch always) + +; have matched first character of some keyword + +LAB_13D1 + TYA ; copy matching index + ASL ; *2 (bytes per pointer) + TAX ; copy to new index + LDA TAB_CHRT,X ; get keyword table pointer low byte + STA ut2_pl ; save pointer low byte + LDA TAB_CHRT+1,X ; get keyword table pointer high byte + STA ut2_ph ; save pointer high byte + + LDY #$FF ; clear table pointer (make -1 for start) + + LDX TempB ; restore buffer read index + +LAB_13D6 + INY ; next table byte + LDA (ut2_pl),Y ; get byte from table +LAB_13D8 + BMI LAB_13EA ; all bytes matched so go save token + + INX ; next buffer byte + CMP Ibuffs,X ; compare with byte from input buffer + BEQ LAB_13D6 ; go compare next if match + + BNE LAB_1417 ; branch if >< (not found keyword) + +LAB_13EA + LDY csidx ; restore save index + + ; save crunched to output +LAB_13EC + INX ; increment buffer index (to next input byte) + INY ; increment save index (to next output byte) + STA Ibuffs,Y ; save byte to output + CMP #$00 ; set the flags, set carry + BEQ LAB_142A ; do exit if was null [EOL] + + ; A holds token or byte here + SBC #':' ; subtract ":" (carry set by CMP #00) + BEQ LAB_13FF ; branch if it was ":" (is now $00) + + ; A now holds token-$3A + CMP #TK_DATA-$3A ; compare with DATA token - $3A + BNE LAB_1401 ; branch if not DATA + + ; token was : or DATA +LAB_13FF + STA Oquote ; save token-$3A (clear for ":", TK_DATA-$3A for DATA) +LAB_1401 + EOR #TK_REM-$3A ; effectively subtract REM token offset + BNE LAB_13AC ; If wasn't REM then go crunch rest of line + + STA Asrch ; else was REM so set search for [EOL] + + ; loop for REM, "..." etc. +LAB_1408 + LDA Ibuffs,X ; get byte from input buffer + BEQ LAB_13EC ; branch if null [EOL] + + CMP Asrch ; compare with stored character + BEQ LAB_13EC ; branch if match (end quote) + + ; entry for copy string in quotes, don't crunch +LAB_1410 + INY ; increment buffer save index + STA Ibuffs,Y ; save byte to output + INX ; increment buffer read index + BNE LAB_1408 ; loop while <> 0 (should never be 0!) + + ; not found keyword this go +LAB_1417 + LDX TempB ; compare has failed, restore buffer index (start byte!) + + ; now find the end of this word in the table +LAB_141B + LDA (ut2_pl),Y ; get table byte + PHP ; save status + INY ; increment table index + PLP ; restore byte status + BPL LAB_141B ; if not end of keyword go do next + + LDA (ut2_pl),Y ; get byte from keyword table + BNE LAB_13D8 ; go test next word if not zero byte (end of table) + + ; reached end of table with no match + LDA Ibuffs,X ; restore byte from input buffer + BPL LAB_13EA ; branch always (all bytes in buffer are $00-$7F) + ; go save byte in output and continue crunching + + ; reached [EOL] +LAB_142A + INY ; increment pointer + INY ; increment pointer (makes it next line pointer high byte) + STA Ibuffs,Y ; save [EOL] (marks [EOT] in immediate mode) + INY ; adjust for line copy + INY ; adjust for line copy + INY ; adjust for line copy + DEC Bpntrl ; allow for increment (change if buffer starts at $xxFF) + RTS + +; search Basic for temp integer line number from start of mem + +LAB_SSLN + LDA Smeml ; get start of mem low byte + LDX Smemh ; get start of mem high byte + +; search Basic for temp integer line number from AX +; returns carry set if found +; returns Baslnl/Baslnh pointer to found or next higher (not found) line + +; old 541 new 507 + +LAB_SHLN + LDY #$01 ; set index + STA Baslnl ; save low byte as current + STX Baslnh ; save high byte as current + LDA (Baslnl),Y ; get pointer high byte from addr + BEQ LAB_145F ; pointer was zero so we're done, do 'not found' exit + + LDY #$03 ; set index to line # high byte + LDA (Baslnl),Y ; get line # high byte + DEY ; decrement index (point to low byte) + CMP Itemph ; compare with temporary integer high byte + BNE LAB_1455 ; if <> skip low byte check + + LDA (Baslnl),Y ; get line # low byte + CMP Itempl ; compare with temporary integer low byte +LAB_1455 + BCS LAB_145E ; else if temp < this line, exit (passed line#) + +LAB_1456 + DEY ; decrement index to next line ptr high byte + LDA (Baslnl),Y ; get next line pointer high byte + TAX ; copy to X + DEY ; decrement index to next line ptr low byte + LDA (Baslnl),Y ; get next line pointer low byte + BCC LAB_SHLN ; go search for line # in temp (Itempl/Itemph) from AX + ; (carry always clear) + +LAB_145E + BEQ LAB_1460 ; exit if temp = found line #, carry is set + +LAB_145F + CLC ; clear found flag +LAB_1460 + RTS + +; perform NEW + +LAB_NEW + BNE LAB_1460 ; exit if not end of statement (to do syntax error) + +LAB_1463 + LDA #$00 ; clear A + TAY ; clear Y + STA (Smeml),Y ; clear first line, next line pointer, low byte + INY ; increment index + STA (Smeml),Y ; clear first line, next line pointer, high byte + CLC ; clear carry + LDA Smeml ; get start of mem low byte + ADC #$02 ; calculate end of BASIC low byte + STA Svarl ; save start of vars low byte + LDA Smemh ; get start of mem high byte + ADC #$00 ; add any carry + STA Svarh ; save start of vars high byte + +; reset execution to start, clear vars and flush stack + +LAB_1477 + CLC ; clear carry + LDA Smeml ; get start of mem low byte + ADC #$FF ; -1 + STA Bpntrl ; save BASIC execute pointer low byte + LDA Smemh ; get start of mem high byte + ADC #$FF ; -1+carry + STA Bpntrh ; save BASIC execute pointer high byte + +; "CLEAR" command gets here + +LAB_147A + LDA Ememl ; get end of mem low byte + LDY Ememh ; get end of mem high byte + STA Sstorl ; set bottom of string space low byte + STY Sstorh ; set bottom of string space high byte + LDA Svarl ; get start of vars low byte + LDY Svarh ; get start of vars high byte + STA Sarryl ; save var mem end low byte + STY Sarryh ; save var mem end high byte + STA Earryl ; save array mem end low byte + STY Earryh ; save array mem end high byte + JSR LAB_161A ; perform RESTORE command + +; flush stack and clear continue flag + +LAB_1491 + LDX #des_sk ; set descriptor stack pointer + STX next_s ; save descriptor stack pointer + PLA ; pull return address low byte + TAX ; copy return address low byte + PLA ; pull return address high byte + STX LAB_SKFE ; save to cleared stack + STA LAB_SKFF ; save to cleared stack + LDX #$FD ; new stack pointer + TXS ; reset stack + LDA #$00 ; clear byte + STA Cpntrh ; clear continue pointer high byte + STA Sufnxf ; clear subscript/FNX flag +LAB_14A6 + RTS + +; perform CLEAR + +LAB_CLEAR + BEQ LAB_147A ; if no following token go do "CLEAR" + + ; else there was a following token (go do syntax error) + RTS + +; perform LIST [n][-m] +; bigger, faster version (a _lot_ faster) + +LAB_LIST + BCC LAB_14BD ; branch if next character numeric (LIST n..) + + BEQ LAB_14BD ; branch if next character [NULL] (LIST) + + CMP #TK_MINUS ; compare with token for - + BNE LAB_14A6 ; exit if not - (LIST -m) + + ; LIST [[n][-m]] + ; this bit sets the n , if present, as the start and end +LAB_14BD + JSR LAB_GFPN ; get fixed-point number into temp integer + JSR LAB_SSLN ; search BASIC for temp integer line number + ; (pointer in Baslnl/Baslnh) + JSR LAB_GBYT ; scan memory + BEQ LAB_14D4 ; branch if no more characters + + ; this bit checks the - is present + CMP #TK_MINUS ; compare with token for - + BNE LAB_1460 ; return if not "-" (will be Syntax error) + + ; LIST [n]-m + ; the - was there so set m as the end value + JSR LAB_IGBY ; increment and scan memory + JSR LAB_GFPN ; get fixed-point number into temp integer + BNE LAB_1460 ; exit if not ok + +LAB_14D4 + LDA Itempl ; get temporary integer low byte + ORA Itemph ; OR temporary integer high byte + BNE LAB_14E2 ; branch if start set + + LDA #$FF ; set for -1 + STA Itempl ; set temporary integer low byte + STA Itemph ; set temporary integer high byte +LAB_14E2 + LDY #$01 ; set index for line + STY Oquote ; clear open quote flag + JSR LAB_CRLF ; print CR/LF + LDA (Baslnl),Y ; get next line pointer high byte + ; pointer initially set by search at LAB_14BD + BEQ LAB_152B ; if null all done so exit + JSR LAB_1629 ; do CRTL-C check vector + + INY ; increment index for line + LDA (Baslnl),Y ; get line # low byte + TAX ; copy to X + INY ; increment index + LDA (Baslnl),Y ; get line # high byte + CMP Itemph ; compare with temporary integer high byte + BNE LAB_14FF ; branch if no high byte match + + CPX Itempl ; compare with temporary integer low byte + BEQ LAB_1501 ; branch if = last line to do (< will pass next branch) + +LAB_14FF ; else .. + BCS LAB_152B ; if greater all done so exit + +LAB_1501 + STY Tidx1 ; save index for line + JSR LAB_295E ; print XA as unsigned integer + LDA #$20 ; space is the next character +LAB_1508 + LDY Tidx1 ; get index for line + AND #$7F ; mask top out bit of character +LAB_150C + JSR LAB_PRNA ; go print the character + CMP #$22 ; was it " character + BNE LAB_1519 ; branch if not + + ; we are either entering or leaving a pair of quotes + LDA Oquote ; get open quote flag + EOR #$FF ; toggle it + STA Oquote ; save it back +LAB_1519 + INY ; increment index + LDA (Baslnl),Y ; get next byte + BNE LAB_152E ; branch if not [EOL] (go print character) + TAY ; else clear index + LDA (Baslnl),Y ; get next line pointer low byte + TAX ; copy to X + INY ; increment index + LDA (Baslnl),Y ; get next line pointer high byte + STX Baslnl ; set pointer to line low byte + STA Baslnh ; set pointer to line high byte + BNE LAB_14E2 ; go do next line if not [EOT] + ; else .. +LAB_152B + RTS + +LAB_152E + BPL LAB_150C ; just go print it if not token byte + + ; else was token byte so uncrunch it (maybe) + BIT Oquote ; test the open quote flag + BMI LAB_150C ; just go print character if open quote set + + LDX #>LAB_KEYT ; get table address high byte + ASL ; *2 + ASL ; *4 + BCC LAB_152F ; branch if no carry + + INX ; else increment high byte + CLC ; clear carry for add +LAB_152F + ADC #LAB_159F ; set return address high byte + STA ut1_pl ; save return address low byte + STY ut1_ph ; save return address high byte + JMP LAB_1B66 ; round FAC1 and put on stack (returns to next instruction) + +LAB_159F + LDA #LAB_259C ; set 1 pointer high addr + JSR LAB_UFAC ; unpack memory (AY) into FAC1 + JSR LAB_GBYT ; scan memory + CMP #TK_STEP ; compare with STEP token + BNE LAB_15B3 ; jump if not "STEP" + + ;.was step so .. + JSR LAB_IGBY ; increment and scan memory + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch +LAB_15B3 + JSR LAB_27CA ; return A=FF,C=1/-ve A=01,C=0/+ve + STA FAC1_s ; set FAC1 sign (b7) + ; this is +1 for +ve step and -1 for -ve step, in NEXT we + ; compare the FOR value and the TO value and return +1 if + ; FOR > TO, 0 if FOR = TO and -1 if FOR < TO. the value + ; here (+/-1) is then compared to that result and if they + ; are the same (+ve and FOR > TO or -ve and FOR < TO) then + ; the loop is done + JSR LAB_1B5B ; push sign, round FAC1 and put on stack + LDA Frnxth ; get var pointer for FOR/NEXT high byte + PHA ; push on stack + LDA Frnxtl ; get var pointer for FOR/NEXT low byte + PHA ; push on stack + LDA #TK_FOR ; get FOR token + PHA ; push on stack + +; interpreter inner loop + +LAB_15C2 + JSR LAB_1629 ; do CRTL-C check vector + LDA Bpntrl ; get BASIC execute pointer low byte + LDY Bpntrh ; get BASIC execute pointer high byte + + LDX Clineh ; continue line is $FFxx for immediate mode + ; ($00xx for RUN from immediate mode) + INX ; increment it (now $00 if immediate mode) + BEQ LAB_15D1 ; branch if null (immediate mode) + + STA Cpntrl ; save continue pointer low byte + STY Cpntrh ; save continue pointer high byte +LAB_15D1 + LDY #$00 ; clear index + LDA (Bpntrl),Y ; get next byte + BEQ LAB_15DC ; branch if null [EOL] + + CMP #':' ; compare with ":" + BEQ LAB_15F6 ; branch if = (statement separator) + +LAB_15D9 + JMP LAB_SNER ; else syntax error then warm start + + ; have reached [EOL] +LAB_15DC + LDY #$02 ; set index + LDA (Bpntrl),Y ; get next line pointer high byte + CLC ; clear carry for no "BREAK" message + BEQ LAB_1651 ; if null go to immediate mode (was immediate or [EOT] + ; marker) + + INY ; increment index + LDA (Bpntrl),Y ; get line # low byte + STA Clinel ; save current line low byte + INY ; increment index + LDA (Bpntrl),Y ; get line # high byte + STA Clineh ; save current line high byte + TYA ; A now = 4 + ADC Bpntrl ; add BASIC execute pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + BCC LAB_15F6 ; branch if no overflow + + INC Bpntrh ; else increment BASIC execute pointer high byte +LAB_15F6 + JSR LAB_IGBY ; increment and scan memory + +LAB_15F9 + JSR LAB_15FF ; go interpret BASIC code from (Bpntrl) + +LAB_15FC + JMP LAB_15C2 ; loop + +; interpret BASIC code from (Bpntrl) + +LAB_15FF + BEQ LAB_1628 ; exit if zero [EOL] + +LAB_1602 + ASL ; *2 bytes per vector and normalise token + BCS LAB_1609 ; branch if was token + + JMP LAB_LET ; else go do implied LET + +LAB_1609 + CMP #[TK_TAB-$80]*2 ; compare normalised token * 2 with TAB + BCS LAB_15D9 ; branch if A>=TAB (do syntax error then warm start) + ; only tokens before TAB can start a line + TAY ; copy to index + LDA LAB_CTBL+1,Y ; get vector high byte + PHA ; onto stack + LDA LAB_CTBL,Y ; get vector low byte + PHA ; onto stack + JMP LAB_IGBY ; jump to increment and scan memory + ; then "return" to vector + +; CTRL-C check jump. this is called as a subroutine but exits back via a jump if a +; key press is detected. + +LAB_1629 + JMP (VEC_CC) ; ctrl c check vector + +; if there was a key press it gets back here .. + +LAB_1636 + CMP #$03 ; compare with CTRL-C + +; perform STOP + +LAB_STOP + BCS LAB_163B ; branch if token follows STOP + ; else just END +; END + +LAB_END + CLC ; clear the carry, indicate a normal program end +LAB_163B + BNE LAB_167A ; if wasn't CTRL-C or there is a following byte return + + LDA Bpntrh ; get the BASIC execute pointer high byte + EOR #>Ibuffs ; compare with buffer address high byte (Cb unchanged) + BEQ LAB_164F ; branch if the BASIC pointer is in the input buffer + ; (can't continue in immediate mode) + + ; else .. + EOR #>Ibuffs ; correct the bits + LDY Bpntrl ; get BASIC execute pointer low byte + STY Cpntrl ; save continue pointer low byte + STA Cpntrh ; save continue pointer high byte +LAB_1647 + LDA Clinel ; get current line low byte + LDY Clineh ; get current line high byte + STA Blinel ; save break line low byte + STY Blineh ; save break line high byte +LAB_164F + PLA ; pull return address low + PLA ; pull return address high +LAB_1651 + BCC LAB_165E ; if was program end just do warm start + + ; else .. + LDA #LAB_BMSG ; point to "Break" high byte + JMP LAB_1269 ; print "Break" and do warm start + +LAB_165E + JMP LAB_1274 ; go do warm start + +; perform RESTORE + +LAB_RESTORE + BNE LAB_RESTOREn ; branch if next character not null (RESTORE n) + +LAB_161A + SEC ; set carry for subtract + LDA Smeml ; get start of mem low byte + SBC #$01 ; -1 + LDY Smemh ; get start of mem high byte + BCS LAB_1624 ; branch if no underflow + +LAB_uflow + DEY ; else decrement high byte +LAB_1624 + STA Dptrl ; save DATA pointer low byte + STY Dptrh ; save DATA pointer high byte +LAB_1628 + RTS + + ; is RESTORE n +LAB_RESTOREn + JSR LAB_GFPN ; get fixed-point number into temp integer + JSR LAB_SNBL ; scan for next BASIC line + LDA Clineh ; get current line high byte + CMP Itemph ; compare with temporary integer high byte + BCS LAB_reset_search ; branch if >= (start search from beginning) + + TYA ; else copy line index to A + SEC ; set carry (+1) + ADC Bpntrl ; add BASIC execute pointer low byte + LDX Bpntrh ; get BASIC execute pointer high byte + BCC LAB_go_search ; branch if no overflow to high byte + + INX ; increment high byte + BCS LAB_go_search ; branch always (can never be carry clear) + +; search for line # in temp (Itempl/Itemph) from start of mem pointer (Smeml) + +LAB_reset_search + LDA Smeml ; get start of mem low byte + LDX Smemh ; get start of mem high byte + +; search for line # in temp (Itempl/Itemph) from (AX) + +LAB_go_search + + JSR LAB_SHLN ; search Basic for temp integer line number from AX + BCS LAB_line_found ; if carry set go set pointer + + JMP LAB_16F7 ; else go do "Undefined statement" error + +LAB_line_found + ; carry already set for subtract + LDA Baslnl ; get pointer low byte + SBC #$01 ; -1 + LDY Baslnh ; get pointer high byte + BCS LAB_1624 ; branch if no underflow (save DATA pointer and return) + + BCC LAB_uflow ; else decrement high byte then save DATA pointer and + ; return (branch always) + +; perform NULL + +LAB_NULL + JSR LAB_GTBY ; get byte parameter + STX Nullct ; save new NULL count +LAB_167A + RTS + +; perform CONT + +LAB_CONT + BNE LAB_167A ; if following byte exit to do syntax error + + LDY Cpntrh ; get continue pointer high byte + BNE LAB_166C ; go do continue if we can + + LDX #$1E ; error code $1E ("Can't continue" error) + JMP LAB_XERR ; do error #X, then warm start + + ; we can continue so .. +LAB_166C + LDA #TK_ON ; set token for ON + JSR LAB_IRQ ; set IRQ flags + LDA #TK_ON ; set token for ON + JSR LAB_NMI ; set NMI flags + + STY Bpntrh ; save BASIC execute pointer high byte + LDA Cpntrl ; get continue pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + LDA Blinel ; get break line low byte + LDY Blineh ; get break line high byte + STA Clinel ; set current line low byte + STY Clineh ; set current line high byte + RTS + +; perform RUN + +LAB_RUN + BNE LAB_1696 ; branch if RUN n + JMP LAB_1477 ; reset execution to start, clear variables, flush stack and + ; return + +; does RUN n + +LAB_1696 + JSR LAB_147A ; go do "CLEAR" + BEQ LAB_16B0 ; get n and do GOTO n (branch always as CLEAR sets Z=1) + +; perform DO + +LAB_DO + LDA #$05 ; need 5 bytes for DO + JSR LAB_1212 ; check room on stack for A bytes + LDA Bpntrh ; get BASIC execute pointer high byte + PHA ; push on stack + LDA Bpntrl ; get BASIC execute pointer low byte + PHA ; push on stack + LDA Clineh ; get current line high byte + PHA ; push on stack + LDA Clinel ; get current line low byte + PHA ; push on stack + LDA #TK_DO ; token for DO + PHA ; push on stack + JSR LAB_GBYT ; scan memory + JMP LAB_15C2 ; go do interpreter inner loop + +; perform GOSUB + +LAB_GOSUB + LDA #$05 ; need 5 bytes for GOSUB + JSR LAB_1212 ; check room on stack for A bytes + LDA Bpntrh ; get BASIC execute pointer high byte + PHA ; push on stack + LDA Bpntrl ; get BASIC execute pointer low byte + PHA ; push on stack + LDA Clineh ; get current line high byte + PHA ; push on stack + LDA Clinel ; get current line low byte + PHA ; push on stack + LDA #TK_GOSUB ; token for GOSUB + PHA ; push on stack +LAB_16B0 + JSR LAB_GBYT ; scan memory + JSR LAB_GOTO ; perform GOTO n + JMP LAB_15C2 ; go do interpreter inner loop + ; (can't RTS, we used the stack!) + +; perform GOTO + +LAB_GOTO + JSR LAB_GFPN ; get fixed-point number into temp integer + JSR LAB_SNBL ; scan for next BASIC line + LDA Clineh ; get current line high byte + CMP Itemph ; compare with temporary integer high byte + BCS LAB_16D0 ; branch if >= (start search from beginning) + + TYA ; else copy line index to A + SEC ; set carry (+1) + ADC Bpntrl ; add BASIC execute pointer low byte + LDX Bpntrh ; get BASIC execute pointer high byte + BCC LAB_16D4 ; branch if no overflow to high byte + + INX ; increment high byte + BCS LAB_16D4 ; branch always (can never be carry) + +; search for line # in temp (Itempl/Itemph) from start of mem pointer (Smeml) + +LAB_16D0 + LDA Smeml ; get start of mem low byte + LDX Smemh ; get start of mem high byte + +; search for line # in temp (Itempl/Itemph) from (AX) + +LAB_16D4 + JSR LAB_SHLN ; search Basic for temp integer line number from AX + BCC LAB_16F7 ; if carry clear go do "Undefined statement" error + ; (unspecified statement) + + ; carry already set for subtract + LDA Baslnl ; get pointer low byte + SBC #$01 ; -1 + STA Bpntrl ; save BASIC execute pointer low byte + LDA Baslnh ; get pointer high byte + SBC #$00 ; subtract carry + STA Bpntrh ; save BASIC execute pointer high byte +LAB_16E5 + RTS + +LAB_DONOK + LDX #$22 ; error code $22 ("LOOP without DO" error) + JMP LAB_XERR ; do error #X, then warm start + +; perform LOOP + +LAB_LOOP + TAY ; save following token + TSX ; copy stack pointer + LDA LAB_STAK+3,X ; get token byte from stack + CMP #TK_DO ; compare with DO token + BNE LAB_DONOK ; branch if no matching DO + + INX ; dump calling routine return address + INX ; dump calling routine return address + TXS ; correct stack + TYA ; get saved following token back + BEQ LoopAlways ; if no following token loop forever + ; (stack pointer in X) + + CMP #':' ; could be ':' + BEQ LoopAlways ; if :... loop forever + + SBC #TK_UNTIL ; subtract token for UNTIL, we know carry is set here + TAX ; copy to X (if it was UNTIL then Y will be correct) + BEQ DoRest ; branch if was UNTIL + + DEX ; decrement result + BNE LAB_16FC ; if not WHILE go do syntax error and warm start + ; only if the token was WHILE will this fail + + DEX ; set invert result byte +DoRest + STX Frnxth ; save invert result byte + JSR LAB_IGBY ; increment and scan memory + JSR LAB_EVEX ; evaluate expression + LDA FAC1_e ; get FAC1 exponent + BEQ DoCmp ; if =0 go do straight compare + + LDA #$FF ; else set all bits +DoCmp + TSX ; copy stack pointer + EOR Frnxth ; EOR with invert byte + BNE LoopDone ; if <> 0 clear stack and back to interpreter loop + + ; loop condition wasn't met so do it again +LoopAlways + LDA LAB_STAK+2,X ; get current line low byte + STA Clinel ; save current line low byte + LDA LAB_STAK+3,X ; get current line high byte + STA Clineh ; save current line high byte + LDA LAB_STAK+4,X ; get BASIC execute pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + LDA LAB_STAK+5,X ; get BASIC execute pointer high byte + STA Bpntrh ; save BASIC execute pointer high byte + JSR LAB_GBYT ; scan memory + JMP LAB_15C2 ; go do interpreter inner loop + + ; clear stack and back to interpreter loop +LoopDone + INX ; dump DO token + INX ; dump current line low byte + INX ; dump current line high byte + INX ; dump BASIC execute pointer low byte + INX ; dump BASIC execute pointer high byte + TXS ; correct stack + JMP LAB_DATA ; go perform DATA (find : or [EOL]) + +; do the return without gosub error + +LAB_16F4 + LDX #$04 ; error code $04 ("RETURN without GOSUB" error) + .byte $2C ; makes next line BIT LAB_0EA2 + +LAB_16F7 ; do undefined statement error + LDX #$0E ; error code $0E ("Undefined statement" error) + JMP LAB_XERR ; do error #X, then warm start + +; perform RETURN + +LAB_RETURN + BNE LAB_16E5 ; exit if following token (to allow syntax error) + +LAB_16E8 + PLA ; dump calling routine return address + PLA ; dump calling routine return address + PLA ; pull token + CMP #TK_GOSUB ; compare with GOSUB token + BNE LAB_16F4 ; branch if no matching GOSUB + +LAB_16FF + PLA ; pull current line low byte + STA Clinel ; save current line low byte + PLA ; pull current line high byte + STA Clineh ; save current line high byte + PLA ; pull BASIC execute pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + PLA ; pull BASIC execute pointer high byte + STA Bpntrh ; save BASIC execute pointer high byte + + ; now do the DATA statement as we could be returning into + ; the middle of an ON GOSUB n,m,p,q line + ; (the return address used by the DATA statement is the one + ; pushed before the GOSUB was executed!) + +; perform DATA + +LAB_DATA + JSR LAB_SNBS ; scan for next BASIC statement ([:] or [EOL]) + + ; set BASIC execute pointer +LAB_170F + TYA ; copy index to A + CLC ; clear carry for add + ADC Bpntrl ; add BASIC execute pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + BCC LAB_1719 ; skip next if no carry + + INC Bpntrh ; else increment BASIC execute pointer high byte +LAB_1719 + RTS + +LAB_16FC + JMP LAB_SNER ; do syntax error then warm start + +; scan for next BASIC statement ([:] or [EOL]) +; returns Y as index to [:] or [EOL] + +LAB_SNBS + LDX #':' ; set look for character = ":" + .byte $2C ; makes next line BIT $00A2 + +; scan for next BASIC line +; returns Y as index to [EOL] + +LAB_SNBL + LDX #$00 ; set alt search character = [EOL] + LDY #$00 ; set search character = [EOL] + STY Asrch ; store search character +LAB_1725 + TXA ; get alt search character + EOR Asrch ; toggle search character, effectively swap with $00 + STA Asrch ; save swapped search character +LAB_172D + LDA (Bpntrl),Y ; get next byte + BEQ LAB_1719 ; exit if null [EOL] + + CMP Asrch ; compare with search character + BEQ LAB_1719 ; exit if found + + INY ; increment index + CMP #$22 ; compare current character with open quote + BNE LAB_172D ; if not open quote go get next character + + BEQ LAB_1725 ; if found go swap search character for alt search character + +; perform IF + +LAB_IF + JSR LAB_EVEX ; evaluate the expression + JSR LAB_GBYT ; scan memory + CMP #TK_THEN ; compare with THEN token + BEQ LAB_174B ; if it was THEN go do IF + + ; wasn't IF .. THEN so must be IF .. GOTO + CMP #TK_GOTO ; compare with GOTO token + BNE LAB_16FC ; if it wasn't GOTO go do syntax error + + LDX Bpntrl ; save the basic pointer low byte + LDY Bpntrh ; save the basic pointer high byte + JSR LAB_IGBY ; increment and scan memory + BCS LAB_16FC ; if not numeric go do syntax error + + STX Bpntrl ; restore the basic pointer low byte + STY Bpntrh ; restore the basic pointer high byte +LAB_174B + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_174E ; if the result was zero go look for an ELSE + + JSR LAB_IGBY ; else increment and scan memory + BCS LAB_174D ; if not numeric go do var or keyword + +LAB_174C + JMP LAB_GOTO ; else was numeric so do GOTO n + + ; is var or keyword +LAB_174D + CMP #TK_RETURN ; compare the byte with the token for RETURN + BNE LAB_174G ; if it wasn't RETURN go interpret BASIC code from (Bpntrl) + ; and return to this code to process any following code + + JMP LAB_1602 ; else it was RETURN so interpret BASIC code from (Bpntrl) + ; but don't return here + +LAB_174G + JSR LAB_15FF ; interpret BASIC code from (Bpntrl) + +; the IF was executed and there may be a following ELSE so the code needs to return +; here to check and ignore the ELSE if present + + LDY #$00 ; clear the index + LDA (Bpntrl),Y ; get the next BASIC byte + CMP #TK_ELSE ; compare it with the token for ELSE + BEQ LAB_DATA ; if ELSE ignore the following statement + +; there was no ELSE so continue execution of IF THEN [: ]. any +; following ELSE will, correctly, cause a syntax error + + RTS ; else return to the interpreter inner loop + +; perform ELSE after IF + +LAB_174E + LDY #$00 ; clear the BASIC byte index + LDX #$01 ; clear the nesting depth +LAB_1750 + INY ; increment the BASIC byte index + LDA (Bpntrl),Y ; get the next BASIC byte + BEQ LAB_1753 ; if EOL go add the pointer and return + + CMP #TK_IF ; compare the byte with the token for IF + BNE LAB_1752 ; if not IF token skip the depth increment + + INX ; else increment the nesting depth .. + BNE LAB_1750 ; .. and continue looking + +LAB_1752 + CMP #TK_ELSE ; compare the byte with the token for ELSE + BNE LAB_1750 ; if not ELSE token continue looking + + DEX ; was ELSE so decrement the nesting depth + BNE LAB_1750 ; loop if still nested + + INY ; increment the BASIC byte index past the ELSE + +; found the matching ELSE, now do <{n|statement}> + +LAB_1753 + TYA ; else copy line index to A + CLC ; clear carry for add + ADC Bpntrl ; add the BASIC execute pointer low byte + STA Bpntrl ; save the BASIC execute pointer low byte + BCC LAB_1754 ; branch if no overflow to high byte + + INC Bpntrh ; else increment the BASIC execute pointer high byte +LAB_1754 + JSR LAB_GBYT ; scan memory + BCC LAB_174C ; if numeric do GOTO n + ; the code will return to the interpreter loop at the + ; tail end of the GOTO + + JMP LAB_15FF ; interpret BASIC code from (Bpntrl) + ; the code will return to the interpreter loop at the + ; tail end of the + +; perform REM, skip (rest of) line + +LAB_REM + JSR LAB_SNBL ; scan for next BASIC line + JMP LAB_170F ; go set BASIC execute pointer and return, branch always + +LAB_16FD + JMP LAB_SNER ; do syntax error then warm start + +; perform ON + +LAB_ON + CMP #TK_IRQ ; was it IRQ token ? + BNE LAB_NOIN ; if not go check NMI + + JMP LAB_SIRQ ; else go set-up IRQ + +LAB_NOIN + CMP #TK_NMI ; was it NMI token ? + BNE LAB_NONM ; if not go do normal ON command + + JMP LAB_SNMI ; else go set-up NMI + +LAB_NONM + JSR LAB_GTBY ; get byte parameter + PHA ; push GOTO/GOSUB token + CMP #TK_GOSUB ; compare with GOSUB token + BEQ LAB_176B ; branch if GOSUB + + CMP #TK_GOTO ; compare with GOTO token +LAB_1767 + BNE LAB_16FD ; if not GOTO do syntax error then warm start + + +; next character was GOTO or GOSUB + +LAB_176B + DEC FAC1_3 ; decrement index (byte value) + BNE LAB_1773 ; branch if not zero + + PLA ; pull GOTO/GOSUB token + JMP LAB_1602 ; go execute it + +LAB_1773 + JSR LAB_IGBY ; increment and scan memory + JSR LAB_GFPN ; get fixed-point number into temp integer (skip this n) + ; (we could LDX #',' and JSR LAB_SNBL+2, then we + ; just BNE LAB_176B for the loop. should be quicker .. + ; no we can't, what if we meet a colon or [EOL]?) + CMP #$2C ; compare next character with "," + BEQ LAB_176B ; loop if "," + +LAB_177E + PLA ; else pull keyword token (run out of options) + ; also dump +/-1 pointer low byte and exit +LAB_177F + RTS + +; takes n * 106 + 11 cycles where n is the number of digits + +; get fixed-point number into temp integer + +LAB_GFPN + LDX #$00 ; clear reg + STX Itempl ; clear temporary integer low byte +LAB_1785 + STX Itemph ; save temporary integer high byte + BCS LAB_177F ; return if carry set, end of scan, character was + ; not 0-9 + + CPX #$19 ; compare high byte with $19 + TAY ; ensure Zb = 0 if the branch is taken + BCS LAB_1767 ; branch if >=, makes max line # 63999 because next + ; bit does *$0A, = 64000, compare at target will fail + ; and do syntax error + + SBC #'0'-1 ; subtract "0", $2F + carry, from byte + TAY ; copy binary digit + LDA Itempl ; get temporary integer low byte + ASL ; *2 low byte + ROL Itemph ; *2 high byte + ASL ; *2 low byte + ROL Itemph ; *2 high byte, *4 + ADC Itempl ; + low byte, *5 + STA Itempl ; save it + TXA ; get high byte copy to A + ADC Itemph ; + high byte, *5 + ASL Itempl ; *2 low byte, *10d + ROL ; *2 high byte, *10d + TAX ; copy high byte back to X + TYA ; get binary digit back + ADC Itempl ; add number low byte + STA Itempl ; save number low byte + BCC LAB_17B3 ; if no overflow to high byte get next character + + INX ; else increment high byte +LAB_17B3 + JSR LAB_IGBY ; increment and scan memory + JMP LAB_1785 ; loop for next character + +; perform DEC + +LAB_DEC + LDA #LAB_259C ; set +/-1 pointer high byte (both the same) + JSR LAB_246C ; add (AY) to FAC1 + JSR LAB_PFAC ; pack FAC1 into variable (Lvarpl) + + JSR LAB_GBYT ; scan memory + CMP #',' ; compare with "," + BNE LAB_177E ; exit if not "," (either end or error) + + ; was "," so another INCR variable to do + JSR LAB_IGBY ; increment and scan memory + JMP LAB_17B7 ; go do next var + +IncrErr + JMP LAB_1ABC ; do "Type mismatch" error then warm start + +; perform LET + +LAB_LET + JSR LAB_GVAR ; get var address + STA Lvarpl ; save var address low byte + STY Lvarph ; save var address high byte + LDA #TK_EQUAL ; get = token + JSR LAB_SCCA ; scan for CHR$(A), else do syntax error then warm start + LDA Dtypef ; get data type flag, $FF=string, $00=numeric + PHA ; push data type flag + JSR LAB_EVEX ; evaluate expression + PLA ; pop data type flag + ROL ; set carry if type = string + JSR LAB_CKTM ; type match check, set C for string + BNE LAB_17D5 ; branch if string + + JMP LAB_PFAC ; pack FAC1 into variable (Lvarpl) and return + +; string LET + +LAB_17D5 + LDY #$02 ; set index to pointer high byte + LDA (des_pl),Y ; get string pointer high byte + CMP Sstorh ; compare bottom of string space high byte + BCC LAB_17F4 ; if less assign value and exit (was in program memory) + + BNE LAB_17E6 ; branch if > + ; else was equal so compare low bytes + DEY ; decrement index + LDA (des_pl),Y ; get pointer low byte + CMP Sstorl ; compare bottom of string space low byte + BCC LAB_17F4 ; if less assign value and exit (was in program memory) + + ; pointer was >= to bottom of string space pointer +LAB_17E6 + LDY des_ph ; get descriptor pointer high byte + CPY Svarh ; compare start of vars high byte + BCC LAB_17F4 ; branch if less (descriptor is on stack) + + BNE LAB_17FB ; branch if greater (descriptor is not on stack) + + ; else high bytes were equal so .. + LDA des_pl ; get descriptor pointer low byte + CMP Svarl ; compare start of vars low byte + BCS LAB_17FB ; branch if >= (descriptor is not on stack) + +LAB_17F4 + LDA des_pl ; get descriptor pointer low byte + LDY des_ph ; get descriptor pointer high byte + JMP LAB_1811 ; clean stack, copy descriptor to variable and return + + ; make space and copy string +LAB_17FB + LDY #$00 ; index to length + LDA (des_pl),Y ; get string length + JSR LAB_209C ; copy string + LDA des_2l ; get descriptor pointer low byte + LDY des_2h ; get descriptor pointer high byte + STA ssptr_l ; save descriptor pointer low byte + STY ssptr_h ; save descriptor pointer high byte + JSR LAB_228A ; copy string from descriptor (sdescr) to (Sutill) + LDA #FAC1_e ; get descriptor pointer high byte + + ; clean stack and assign value to string variable +LAB_1811 + STA des_2l ; save descriptor_2 pointer low byte + STY des_2h ; save descriptor_2 pointer high byte + JSR LAB_22EB ; clean descriptor stack, YA = pointer + LDY #$00 ; index to length + LDA (des_2l),Y ; get string length + STA (Lvarpl),Y ; copy to let string variable + INY ; index to string pointer low byte + LDA (des_2l),Y ; get string pointer low byte + STA (Lvarpl),Y ; copy to let string variable + INY ; index to string pointer high byte + LDA (des_2l),Y ; get string pointer high byte + STA (Lvarpl),Y ; copy to let string variable + RTS + +; perform GET + +LAB_GET + JSR LAB_GVAR ; get var address + STA Lvarpl ; save var address low byte + STY Lvarph ; save var address high byte + JSR INGET ; get input byte + LDX Dtypef ; get data type flag, $FF=string, $00=numeric + BMI LAB_GETS ; go get string character + + ; was numeric get + TAY ; copy character to Y + JSR LAB_1FD0 ; convert Y to byte in FAC1 + JMP LAB_PFAC ; pack FAC1 into variable (Lvarpl) and return + +LAB_GETS + PHA ; save character + LDA #$01 ; string is single byte + BCS LAB_IsByte ; branch if byte received + + PLA ; string is null +LAB_IsByte + JSR LAB_MSSP ; make string space A bytes long A=$AC=length, + ; X=$AD=Sutill=ptr low byte, Y=$AE=Sutilh=ptr high byte + BEQ LAB_NoSt ; skip store if null string + + PLA ; get character back + LDY #$00 ; clear index + STA (str_pl),Y ; save byte in string (byte IS string!) +LAB_NoSt + JSR LAB_RTST ; check for space on descriptor stack then put address + ; and length on descriptor stack and update stack pointers + + JMP LAB_17D5 ; do string LET and return + +; perform PRINT + +LAB_1829 + JSR LAB_18C6 ; print string from Sutill/Sutilh +LAB_182C + JSR LAB_GBYT ; scan memory + +; PRINT + +LAB_PRINT + BEQ LAB_CRLF ; if nothing following just print CR/LF + +LAB_1831 + CMP #TK_TAB ; compare with TAB( token + BEQ LAB_18A2 ; go do TAB/SPC + + CMP #TK_SPC ; compare with SPC( token + BEQ LAB_18A2 ; go do TAB/SPC + + CMP #',' ; compare with "," + BEQ LAB_188B ; go do move to next TAB mark + + CMP #';' ; compare with ";" + BEQ LAB_18BD ; if ";" continue with PRINT processing + + JSR LAB_EVEX ; evaluate expression + BIT Dtypef ; test data type flag, $FF=string, $00=numeric + BMI LAB_1829 ; branch if string + + JSR LAB_296E ; convert FAC1 to string + JSR LAB_20AE ; print " terminated string to Sutill/Sutilh + LDY #$00 ; clear index + +; don't check fit if terminal width byte is zero + + LDA TWidth ; get terminal width byte + BEQ LAB_185E ; skip check if zero + + SEC ; set carry for subtract + SBC TPos ; subtract terminal position + SBC (des_pl),Y ; subtract string length + BCS LAB_185E ; branch if less than terminal width + + JSR LAB_CRLF ; else print CR/LF +LAB_185E + JSR LAB_18C6 ; print string from Sutill/Sutilh + BEQ LAB_182C ; always go continue processing line + +; CR/LF return to BASIC from BASIC input handler + +LAB_1866 + LDA #$00 ; clear byte + STA Ibuffs,X ; null terminate input + LDX #Ibuffs ; set Y to buffer start-1 high byte + +; print CR/LF + +LAB_CRLF + LDA #$0D ; load [CR] + JSR LAB_PRNA ; go print the character + LDA #$0A ; load [LF] + BNE LAB_PRNA ; go print the character and return, branch always + +LAB_188B + LDA TPos ; get terminal position + CMP Iclim ; compare with input column limit + BCC LAB_1897 ; branch if less + + JSR LAB_CRLF ; else print CR/LF (next line) + BNE LAB_18BD ; continue with PRINT processing (branch always) + +LAB_1897 + SEC ; set carry for subtract +LAB_1898 + SBC TabSiz ; subtract TAB size + BCS LAB_1898 ; loop if result was +ve + + EOR #$FF ; complement it + ADC #$01 ; +1 (twos complement) + BNE LAB_18B6 ; always print A spaces (result is never $00) + + ; do TAB/SPC +LAB_18A2 + PHA ; save token + JSR LAB_SGBY ; scan and get byte parameter + CMP #$29 ; is next character ) + BNE LAB_1910 ; if not do syntax error then warm start + + PLA ; get token back + CMP #TK_TAB ; was it TAB ? + BNE LAB_18B7 ; if not go do SPC + + ; calculate TAB offset + TXA ; copy integer value to A + SBC TPos ; subtract terminal position + BCC LAB_18BD ; branch if result was < 0 (can't TAB backwards) + + ; print A spaces +LAB_18B6 + TAX ; copy result to X +LAB_18B7 + TXA ; set flags on size for SPC + BEQ LAB_18BD ; branch if result was = $0, already here + + ; print X spaces +LAB_18BA + JSR LAB_18E0 ; print " " + DEX ; decrement count + BNE LAB_18BA ; loop if not all done + + ; continue with PRINT processing +LAB_18BD + JSR LAB_IGBY ; increment and scan memory + BNE LAB_1831 ; if more to print go do it + + RTS + +; print null terminated string from memory + +LAB_18C3 + JSR LAB_20AE ; print " terminated string to Sutill/Sutilh + +; print string from Sutill/Sutilh + +LAB_18C6 + JSR LAB_22B6 ; pop string off descriptor stack, or from top of string + ; space returns with A = length, X=$71=pointer low byte, + ; Y=$72=pointer high byte + LDY #$00 ; reset index + TAX ; copy length to X + BEQ LAB_188C ; exit (RTS) if null string + +LAB_18CD + + LDA (ut1_pl),Y ; get next byte + JSR LAB_PRNA ; go print the character + INY ; increment index + DEX ; decrement count + BNE LAB_18CD ; loop if not done yet + + RTS + + ; Print single format character +; print " " + +LAB_18E0 + LDA #$20 ; load " " + .byte $2C ; change next line to BIT LAB_3FA9 + +; print "?" character + +LAB_18E3 + LDA #$3F ; load "?" character + +; print character in A +; now includes the null handler +; also includes infinite line length code +; note! some routines expect this one to exit with Zb=0 + +LAB_PRNA + CMP #' ' ; compare with " " + BCC LAB_18F9 ; branch if less (non printing) + + ; else printable character + PHA ; save the character + +; don't check fit if terminal width byte is zero + + LDA TWidth ; get terminal width + BNE LAB_18F0 ; branch if not zero (not infinite length) + +; is "infinite line" so check TAB position + + LDA TPos ; get position + SBC TabSiz ; subtract TAB size, carry set by CMP #$20 above + BNE LAB_18F7 ; skip reset if different + + STA TPos ; else reset position + BEQ LAB_18F7 ; go print character + +LAB_18F0 + CMP TPos ; compare with terminal character position + BNE LAB_18F7 ; branch if not at end of line + + JSR LAB_CRLF ; else print CR/LF +LAB_18F7 + INC TPos ; increment terminal position + PLA ; get character back +LAB_18F9 + JSR V_OUTP ; output byte via output vector + CMP #$0D ; compare with [CR] + BNE LAB_188A ; branch if not [CR] + + ; else print nullct nulls after the [CR] + STX TempB ; save buffer index + LDX Nullct ; get null count + BEQ LAB_1886 ; branch if no nulls + + LDA #$00 ; load [NULL] +LAB_1880 + JSR LAB_PRNA ; go print the character + DEX ; decrement count + BNE LAB_1880 ; loop if not all done + + LDA #$0D ; restore the character (and set the flags) +LAB_1886 + STX TPos ; clear terminal position (X always = zero when we get here) + LDX TempB ; restore buffer index +LAB_188A + AND #$FF ; set the flags +LAB_188C + RTS + +; handle bad input data + +LAB_1904 + LDA Imode ; get input mode flag, $00=INPUT, $00=READ + BPL LAB_1913 ; branch if INPUT (go do redo) + + LDA Dlinel ; get current DATA line low byte + LDY Dlineh ; get current DATA line high byte + STA Clinel ; save current line low byte + STY Clineh ; save current line high byte +LAB_1910 + JMP LAB_SNER ; do syntax error then warm start + + ; mode was INPUT +LAB_1913 + LDA #LAB_REDO ; point to redo message (high addr) + JSR LAB_18C3 ; print null terminated string from memory + LDA Cpntrl ; get continue pointer low byte + LDY Cpntrh ; get continue pointer high byte + STA Bpntrl ; save BASIC execute pointer low byte + STY Bpntrh ; save BASIC execute pointer high byte + RTS + +; perform INPUT + +LAB_INPUT + CMP #$22 ; compare next byte with open quote + BNE LAB_1934 ; branch if no prompt string + + JSR LAB_1BC1 ; print "..." string + LDA #$3B ; load A with ";" + JSR LAB_SCCA ; scan for CHR$(A), else do syntax error then warm start + JSR LAB_18C6 ; print string from Sutill/Sutilh + + ; done with prompt, now get data +LAB_1934 + JSR LAB_CKRN ; check not Direct, back here if ok + JSR LAB_INLN ; print "? " and get BASIC input + LDA #$00 ; set mode = INPUT + CMP Ibuffs ; test first byte in buffer + BNE LAB_1953 ; branch if not null input + + CLC ; was null input so clear carry to exit program + JMP LAB_1647 ; go do BREAK exit + +; perform READ + +LAB_READ + LDX Dptrl ; get DATA pointer low byte + LDY Dptrh ; get DATA pointer high byte + LDA #$80 ; set mode = READ + +LAB_1953 + STA Imode ; set input mode flag, $00=INPUT, $80=READ + STX Rdptrl ; save READ pointer low byte + STY Rdptrh ; save READ pointer high byte + + ; READ or INPUT next variable from list +LAB_195B + JSR LAB_GVAR ; get (var) address + STA Lvarpl ; save address low byte + STY Lvarph ; save address high byte + LDA Bpntrl ; get BASIC execute pointer low byte + LDY Bpntrh ; get BASIC execute pointer high byte + STA Itempl ; save as temporary integer low byte + STY Itemph ; save as temporary integer high byte + LDX Rdptrl ; get READ pointer low byte + LDY Rdptrh ; get READ pointer high byte + STX Bpntrl ; set BASIC execute pointer low byte + STY Bpntrh ; set BASIC execute pointer high byte + JSR LAB_GBYT ; scan memory + BNE LAB_1988 ; branch if not null + + ; pointer was to null entry + BIT Imode ; test input mode flag, $00=INPUT, $80=READ + BMI LAB_19DD ; branch if READ + + ; mode was INPUT + JSR LAB_18E3 ; print "?" character (double ? for extended input) + JSR LAB_INLN ; print "? " and get BASIC input + STX Bpntrl ; set BASIC execute pointer low byte + STY Bpntrh ; set BASIC execute pointer high byte +LAB_1985 + JSR LAB_GBYT ; scan memory +LAB_1988 + BIT Dtypef ; test data type flag, $FF=string, $00=numeric + BPL LAB_19B0 ; branch if numeric + + ; else get string + STA Srchc ; save search character + CMP #$22 ; was it " ? + BEQ LAB_1999 ; branch if so + + LDA #':' ; else search character is ":" + STA Srchc ; set new search character + LDA #',' ; other search character is "," + CLC ; clear carry for add +LAB_1999 + STA Asrch ; set second search character + LDA Bpntrl ; get BASIC execute pointer low byte + LDY Bpntrh ; get BASIC execute pointer high byte + + ADC #$00 ; c is =1 if we came via the BEQ LAB_1999, else =0 + BCC LAB_19A4 ; branch if no execute pointer low byte rollover + + INY ; else increment high byte +LAB_19A4 + JSR LAB_20B4 ; print Srchc or Asrch terminated string to Sutill/Sutilh + JSR LAB_23F3 ; restore BASIC execute pointer from temp (Btmpl/Btmph) + JSR LAB_17D5 ; go do string LET + JMP LAB_19B6 ; go check string terminator + + ; get numeric INPUT +LAB_19B0 + JSR LAB_2887 ; get FAC1 from string + JSR LAB_PFAC ; pack FAC1 into (Lvarpl) +LAB_19B6 + JSR LAB_GBYT ; scan memory + BEQ LAB_19C5 ; branch if null (last entry) + + CMP #',' ; else compare with "," + BEQ LAB_19C2 ; branch if "," + + JMP LAB_1904 ; else go handle bad input data + + ; got good input data +LAB_19C2 + JSR LAB_IGBY ; increment and scan memory +LAB_19C5 + LDA Bpntrl ; get BASIC execute pointer low byte (temp READ/INPUT ptr) + LDY Bpntrh ; get BASIC execute pointer high byte (temp READ/INPUT ptr) + STA Rdptrl ; save for now + STY Rdptrh ; save for now + LDA Itempl ; get temporary integer low byte (temp BASIC execute ptr) + LDY Itemph ; get temporary integer high byte (temp BASIC execute ptr) + STA Bpntrl ; set BASIC execute pointer low byte + STY Bpntrh ; set BASIC execute pointer high byte + JSR LAB_GBYT ; scan memory + BEQ LAB_1A03 ; if null go do extra ignored message + + JSR LAB_1C01 ; else scan for "," , else do syntax error then warm start + JMP LAB_195B ; go INPUT next variable from list + + ; find next DATA statement or do "Out of DATA" error +LAB_19DD + JSR LAB_SNBS ; scan for next BASIC statement ([:] or [EOL]) + INY ; increment index + TAX ; copy character ([:] or [EOL]) + BNE LAB_19F6 ; branch if [:] + + LDX #$06 ; set for "Out of DATA" error + INY ; increment index, now points to next line pointer high byte + LDA (Bpntrl),Y ; get next line pointer high byte + BEQ LAB_1A54 ; branch if end (eventually does error X) + + INY ; increment index + LDA (Bpntrl),Y ; get next line # low byte + STA Dlinel ; save current DATA line low byte + INY ; increment index + LDA (Bpntrl),Y ; get next line # high byte + INY ; increment index + STA Dlineh ; save current DATA line high byte +LAB_19F6 + LDA (Bpntrl),Y ; get byte + INY ; increment index + TAX ; copy to X + JSR LAB_170F ; set BASIC execute pointer + CPX #TK_DATA ; compare with "DATA" token + BEQ LAB_1985 ; was "DATA" so go do next READ + + BNE LAB_19DD ; go find next statement if not "DATA" + +; end of INPUT/READ routine + +LAB_1A03 + LDA Rdptrl ; get temp READ pointer low byte + LDY Rdptrh ; get temp READ pointer high byte + LDX Imode ; get input mode flag, $00=INPUT, $80=READ + BPL LAB_1A0E ; branch if INPUT + + JMP LAB_1624 ; save AY as DATA pointer and return + + ; we were getting INPUT +LAB_1A0E + LDY #$00 ; clear index + LDA (Rdptrl),Y ; get next byte + BNE LAB_1A1B ; error if not end of INPUT + + RTS + + ; user typed too much +LAB_1A1B + LDA #LAB_IMSG ; point to extra ignored message (high addr) + JMP LAB_18C3 ; print null terminated string from memory and return + +; search the stack for FOR activity +; exit with z=1 if FOR else exit with z=0 + +LAB_11A1 + TSX ; copy stack pointer + INX ; +1 pass return address + INX ; +2 pass return address + INX ; +3 pass calling routine return address + INX ; +4 pass calling routine return address +LAB_11A6 + LDA LAB_STAK+1,X ; get token byte from stack + CMP #TK_FOR ; is it FOR token + BNE LAB_11CE ; exit if not FOR token + + ; was FOR token + LDA Frnxth ; get var pointer for FOR/NEXT high byte + BNE LAB_11BB ; branch if not null + + LDA LAB_STAK+2,X ; get FOR variable pointer low byte + STA Frnxtl ; save var pointer for FOR/NEXT low byte + LDA LAB_STAK+3,X ; get FOR variable pointer high byte + STA Frnxth ; save var pointer for FOR/NEXT high byte +LAB_11BB + CMP LAB_STAK+3,X ; compare var pointer with stacked var pointer (high byte) + BNE LAB_11C7 ; branch if no match + + LDA Frnxtl ; get var pointer for FOR/NEXT low byte + CMP LAB_STAK+2,X ; compare var pointer with stacked var pointer (low byte) + BEQ LAB_11CE ; exit if match found + +LAB_11C7 + TXA ; copy index + CLC ; clear carry for add + ADC #$10 ; add FOR stack use size + TAX ; copy back to index + BNE LAB_11A6 ; loop if not at start of stack + +LAB_11CE + RTS + +; perform NEXT + +LAB_NEXT + BNE LAB_1A46 ; branch if NEXT var + + LDY #$00 ; else clear Y + BEQ LAB_1A49 ; branch always (no variable to search for) + +; NEXT var + +LAB_1A46 + JSR LAB_GVAR ; get variable address +LAB_1A49 + STA Frnxtl ; store variable pointer low byte + STY Frnxth ; store variable pointer high byte + ; (both cleared if no variable defined) + JSR LAB_11A1 ; search the stack for FOR activity + BEQ LAB_1A56 ; branch if found + + LDX #$00 ; else set error $00 ("NEXT without FOR" error) +LAB_1A54 + BEQ LAB_1ABE ; do error #X, then warm start + +LAB_1A56 + TXS ; set stack pointer, X set by search, dumps return addresses + + TXA ; copy stack pointer + SEC ; set carry for subtract + SBC #$F7 ; point to TO var + STA ut2_pl ; save pointer to TO var for compare + ADC #$FB ; point to STEP var + + LDY #>LAB_STAK ; point to stack page high byte + JSR LAB_UFAC ; unpack memory (STEP value) into FAC1 + TSX ; get stack pointer back + LDA LAB_STAK+8,X ; get step sign + STA FAC1_s ; save FAC1 sign (b7) + LDA Frnxtl ; get FOR variable pointer low byte + LDY Frnxth ; get FOR variable pointer high byte + JSR LAB_246C ; add (FOR variable) to FAC1 + JSR LAB_PFAC ; pack FAC1 into (FOR variable) + LDY #>LAB_STAK ; point to stack page high byte + JSR LAB_27FA ; compare FAC1 with (Y,ut2_pl) (TO value) + TSX ; get stack pointer back + CMP LAB_STAK+8,X ; compare step sign + BEQ LAB_1A9B ; branch if = (loop complete) + + ; loop back and do it all again + LDA LAB_STAK+$0D,X ; get FOR line low byte + STA Clinel ; save current line low byte + LDA LAB_STAK+$0E,X ; get FOR line high byte + STA Clineh ; save current line high byte + LDA LAB_STAK+$10,X ; get BASIC execute pointer low byte + STA Bpntrl ; save BASIC execute pointer low byte + LDA LAB_STAK+$0F,X ; get BASIC execute pointer high byte + STA Bpntrh ; save BASIC execute pointer high byte +LAB_1A98 + JMP LAB_15C2 ; go do interpreter inner loop + + ; loop complete so carry on +LAB_1A9B + TXA ; stack copy to A + ADC #$0F ; add $10 ($0F+carry) to dump FOR structure + TAX ; copy back to index + TXS ; copy to stack pointer + JSR LAB_GBYT ; scan memory + CMP #',' ; compare with "," + BNE LAB_1A98 ; branch if not "," (go do interpreter inner loop) + + ; was "," so another NEXT variable to do + JSR LAB_IGBY ; else increment and scan memory + JSR LAB_1A46 ; do NEXT (var) + +; evaluate expression and check is numeric, else do type mismatch + +LAB_EVNM + JSR LAB_EVEX ; evaluate expression + +; check if source is numeric, else do type mismatch + +LAB_CTNM + CLC ; destination is numeric + .byte $24 ; makes next line BIT $38 + +; check if source is string, else do type mismatch + +LAB_CTST + SEC ; required type is string + +; type match check, set C for string, clear C for numeric + +LAB_CKTM + BIT Dtypef ; test data type flag, $FF=string, $00=numeric + BMI LAB_1ABA ; branch if data type is string + + ; else data type was numeric + BCS LAB_1ABC ; if required type is string do type mismatch error +LAB_1AB9 + RTS + + ; data type was string, now check required type +LAB_1ABA + BCS LAB_1AB9 ; exit if required type is string + + ; else do type mismatch error +LAB_1ABC + LDX #$18 ; error code $18 ("Type mismatch" error) +LAB_1ABE + JMP LAB_XERR ; do error #X, then warm start + +; evaluate expression + +LAB_EVEX + LDX Bpntrl ; get BASIC execute pointer low byte + BNE LAB_1AC7 ; skip next if not zero + + DEC Bpntrh ; else decrement BASIC execute pointer high byte +LAB_1AC7 + DEC Bpntrl ; decrement BASIC execute pointer low byte + +LAB_EVEZ + LDA #$00 ; set null precedence (flag done) +LAB_1ACC + PHA ; push precedence byte + LDA #$02 ; 2 bytes + JSR LAB_1212 ; check room on stack for A bytes + JSR LAB_GVAL ; get value from line + LDA #$00 ; clear A + STA comp_f ; clear compare function flag +LAB_1ADB + JSR LAB_GBYT ; scan memory +LAB_1ADE + SEC ; set carry for subtract + SBC #TK_GT ; subtract token for > (lowest comparison function) + BCC LAB_1AFA ; branch if < TK_GT + + CMP #$03 ; compare with ">" to "<" tokens + BCS LAB_1AFA ; branch if >= TK_SGN (highest evaluation function +1) + + ; was token for > = or < (A = 0, 1 or 2) + CMP #$01 ; compare with token for = + ROL ; *2, b0 = carry (=1 if token was = or <) + ; (A = 0, 3 or 5) + EOR #$01 ; toggle b0 + ; (A = 1, 2 or 4. 1 if >, 2 if =, 4 if <) + EOR comp_f ; EOR with compare function flag bits + CMP comp_f ; compare with compare function flag + BCC LAB_1B53 ; if <(comp_f) do syntax error then warm start + ; was more than one <, = or >) + + STA comp_f ; save new compare function flag + JSR LAB_IGBY ; increment and scan memory + JMP LAB_1ADE ; go do next character + + ; token is < ">" or > "<" tokens +LAB_1AFA + LDX comp_f ; get compare function flag + BNE LAB_1B2A ; branch if compare function + + BCS LAB_1B78 ; go do functions + + ; else was < TK_GT so is operator or lower + ADC #TK_GT-TK_PLUS ; add # of operators (+, -, *, /, ^, AND, OR or EOR) + BCC LAB_1B78 ; branch if < + operator + + ; carry was set so token was +, -, *, /, ^, AND, OR or EOR + BNE LAB_1B0B ; branch if not + token + + BIT Dtypef ; test data type flag, $FF=string, $00=numeric + BPL LAB_1B0B ; branch if not string + + ; will only be $00 if type is string and token was + + JMP LAB_224D ; add strings, string 1 is in descriptor des_pl, string 2 + ; is in line, and return + +LAB_1B0B + STA ut1_pl ; save it + ASL ; *2 + ADC ut1_pl ; *3 + TAY ; copy to index +LAB_1B13 + PLA ; pull previous precedence + CMP LAB_OPPT,Y ; compare with precedence byte + BCS LAB_1B7D ; branch if A >= + + JSR LAB_CTNM ; check if source is numeric, else do type mismatch +LAB_1B1C + PHA ; save precedence +LAB_1B1D + JSR LAB_1B43 ; get vector, execute function then continue evaluation + PLA ; restore precedence + LDY prstk ; get precedence stacked flag + BPL LAB_1B3C ; branch if stacked values + + TAX ; copy precedence (set flags) + BEQ LAB_1B9D ; exit if done + + BNE LAB_1B86 ; else pop FAC2 and return, branch always + +LAB_1B2A + ROL Dtypef ; shift data type flag into Cb + TXA ; copy compare function flag + STA Dtypef ; clear data type flag, X is 0xxx xxxx + ROL ; shift data type into compare function byte b0 + LDX Bpntrl ; get BASIC execute pointer low byte + BNE LAB_1B34 ; branch if no underflow + + DEC Bpntrh ; else decrement BASIC execute pointer high byte +LAB_1B34 + DEC Bpntrl ; decrement BASIC execute pointer low byte +TK_LT_PLUS = TK_LT-TK_PLUS + LDY #TK_LT_PLUS*3 ; set offset to last operator entry + STA comp_f ; save new compare function flag + BNE LAB_1B13 ; branch always + +LAB_1B3C + CMP LAB_OPPT,Y ;.compare with stacked function precedence + BCS LAB_1B86 ; branch if A >=, pop FAC2 and return + + BCC LAB_1B1C ; branch always + +;.get vector, execute function then continue evaluation + +LAB_1B43 + LDA LAB_OPPT+2,Y ; get function vector high byte + PHA ; onto stack + LDA LAB_OPPT+1,Y ; get function vector low byte + PHA ; onto stack + ; now push sign, round FAC1 and put on stack + JSR LAB_1B5B ; function will return here, then the next RTS will call + ; the function + LDA comp_f ; get compare function flag + PHA ; push compare evaluation byte + LDA LAB_OPPT,Y ; get precedence byte + JMP LAB_1ACC ; continue evaluating expression + +LAB_1B53 + JMP LAB_SNER ; do syntax error then warm start + +; push sign, round FAC1 and put on stack + +LAB_1B5B + PLA ; get return addr low byte + STA ut1_pl ; save it + INC ut1_pl ; increment it (was ret-1 pushed? yes!) + ; note! no check is made on the high byte! if the calling + ; routine assembles to a page edge then this all goes + ; horribly wrong !!! + PLA ; get return addr high byte + STA ut1_ph ; save it + LDA FAC1_s ; get FAC1 sign (b7) + PHA ; push sign + +; round FAC1 and put on stack + +LAB_1B66 + JSR LAB_27BA ; round FAC1 + LDA FAC1_3 ; get FAC1 mantissa3 + PHA ; push on stack + LDA FAC1_2 ; get FAC1 mantissa2 + PHA ; push on stack + LDA FAC1_1 ; get FAC1 mantissa1 + PHA ; push on stack + LDA FAC1_e ; get FAC1 exponent + PHA ; push on stack + JMP (ut1_pl) ; return, sort of + +; do functions + +LAB_1B78 + LDY #$FF ; flag function + PLA ; pull precedence byte +LAB_1B7B + BEQ LAB_1B9D ; exit if done + +LAB_1B7D + CMP #$64 ; compare previous precedence with $64 + BEQ LAB_1B84 ; branch if was $64 (< function) + + JSR LAB_CTNM ; check if source is numeric, else do type mismatch +LAB_1B84 + STY prstk ; save precedence stacked flag + + ; pop FAC2 and return +LAB_1B86 + PLA ; pop byte + LSR ; shift out comparison evaluation lowest bit + STA Cflag ; save comparison evaluation flag + PLA ; pop exponent + STA FAC2_e ; save FAC2 exponent + PLA ; pop mantissa1 + STA FAC2_1 ; save FAC2 mantissa1 + PLA ; pop mantissa2 + STA FAC2_2 ; save FAC2 mantissa2 + PLA ; pop mantissa3 + STA FAC2_3 ; save FAC2 mantissa3 + PLA ; pop sign + STA FAC2_s ; save FAC2 sign (b7) + EOR FAC1_s ; EOR FAC1 sign (b7) + STA FAC_sc ; save sign compare (FAC1 EOR FAC2) +LAB_1B9D + LDA FAC1_e ; get FAC1 exponent + RTS + +; print "..." string to string util area + +LAB_1BC1 + LDA Bpntrl ; get BASIC execute pointer low byte + LDY Bpntrh ; get BASIC execute pointer high byte + ADC #$00 ; add carry to low byte + BCC LAB_1BCA ; branch if no overflow + + INY ; increment high byte +LAB_1BCA + JSR LAB_20AE ; print " terminated string to Sutill/Sutilh + JMP LAB_23F3 ; restore BASIC execute pointer from temp and return + +; get value from line + +LAB_GVAL + JSR LAB_IGBY ; increment and scan memory + BCS LAB_1BAC ; branch if not numeric character + + ; else numeric string found (e.g. 123) +LAB_1BA9 + JMP LAB_2887 ; get FAC1 from string and return + +; get value from line .. continued + + ; wasn't a number so .. +LAB_1BAC + TAX ; set the flags + BMI LAB_1BD0 ; if -ve go test token values + + ; else it is either a string, number, variable or () + CMP #'$' ; compare with "$" + BEQ LAB_1BA9 ; branch if "$", hex number + + CMP #'%' ; else compare with "%" + BEQ LAB_1BA9 ; branch if "%", binary number + + CMP #'.' ; compare with "." + BEQ LAB_1BA9 ; if so get FAC1 from string and return (e.g. was .123) + + ; it wasn't any sort of number so .. + CMP #$22 ; compare with " + BEQ LAB_1BC1 ; branch if open quote + + ; wasn't any sort of number so .. + +; evaluate expression within parentheses + + CMP #'(' ; compare with "(" + BNE LAB_1C18 ; if not "(" get (var), return value in FAC1 and $ flag + +LAB_1BF7 + JSR LAB_EVEZ ; evaluate expression, no decrement + +; all the 'scan for' routines return the character after the sought character + +; scan for ")" , else do syntax error then warm start + +LAB_1BFB + LDA #$29 ; load A with ")" + +; scan for CHR$(A) , else do syntax error then warm start + +LAB_SCCA + LDY #$00 ; clear index + CMP (Bpntrl),Y ; check next byte is = A + BNE LAB_SNER ; if not do syntax error then warm start + + JMP LAB_IGBY ; increment and scan memory then return + +; scan for "(" , else do syntax error then warm start + +LAB_1BFE + LDA #$28 ; load A with "(" + BNE LAB_SCCA ; scan for CHR$(A), else do syntax error then warm start + ; (branch always) + +; scan for "," , else do syntax error then warm start + +LAB_1C01 + LDA #$2C ; load A with "," + BNE LAB_SCCA ; scan for CHR$(A), else do syntax error then warm start + ; (branch always) + +; syntax error then warm start + +LAB_SNER + LDX #$02 ; error code $02 ("Syntax" error) + JMP LAB_XERR ; do error #X, then warm start + +; get value from line .. continued +; do tokens + +LAB_1BD0 + CMP #TK_MINUS ; compare with token for - + BEQ LAB_1C11 ; branch if - token (do set-up for functions) + + ; wasn't -n so .. + CMP #TK_PLUS ; compare with token for + + BEQ LAB_GVAL ; branch if + token (+n = n so ignore leading +) + + CMP #TK_NOT ; compare with token for NOT + BNE LAB_1BE7 ; branch if not token for NOT + + ; was NOT token +TK_EQUAL_PLUS = TK_EQUAL-TK_PLUS + LDY #TK_EQUAL_PLUS*3 ; offset to NOT function + BNE LAB_1C13 ; do set-up for function then execute (branch always) + +; do = compare + +LAB_EQUAL + JSR LAB_EVIR ; evaluate integer expression (no sign check) + LDA FAC1_3 ; get FAC1 mantissa3 + EOR #$FF ; invert it + TAY ; copy it + LDA FAC1_2 ; get FAC1 mantissa2 + EOR #$FF ; invert it + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; get value from line .. continued + + ; wasn't +, -, or NOT so .. +LAB_1BE7 + CMP #TK_FN ; compare with token for FN + BNE LAB_1BEE ; branch if not token for FN + + JMP LAB_201E ; go evaluate FNx + +; get value from line .. continued + + ; wasn't +, -, NOT or FN so .. +LAB_1BEE + SBC #TK_SGN ; subtract with token for SGN + BCS LAB_1C27 ; if a function token go do it + + JMP LAB_SNER ; else do syntax error + +; set-up for functions + +LAB_1C11 +TK_GT_PLUS = TK_GT-TK_PLUS + LDY #TK_GT_PLUS*3 ; set offset from base to > operator +LAB_1C13 + PLA ; dump return address low byte + PLA ; dump return address high byte + JMP LAB_1B1D ; execute function then continue evaluation + +; variable name set-up +; get (var), return value in FAC_1 and $ flag + +LAB_1C18 + JSR LAB_GVAR ; get (var) address + STA FAC1_2 ; save address low byte in FAC1 mantissa2 + STY FAC1_3 ; save address high byte in FAC1 mantissa3 + LDX Dtypef ; get data type flag, $FF=string, $00=numeric + BMI LAB_1C25 ; if string then return (does RTS) + +LAB_1C24 + JMP LAB_UFAC ; unpack memory (AY) into FAC1 + +LAB_1C25 + RTS + +; get value from line .. continued +; only functions left so .. + +; set up function references + +; new for V2.0+ this replaces a lot of IF .. THEN .. ELSEIF .. THEN .. that was needed +; to process function calls. now the function vector is computed and pushed on the stack +; and the preprocess offset is read. if the preprocess offset is non zero then the vector +; is calculated and the routine called, if not this routine just does RTS. whichever +; happens the RTS at the end of this routine, or the end of the preprocess routine, calls +; the function code + +; this also removes some less than elegant code that was used to bypass type checking +; for functions that returned strings + +LAB_1C27 + ASL ; *2 (2 bytes per function address) + TAY ; copy to index + + LDA LAB_FTBM,Y ; get function jump vector high byte + PHA ; push functions jump vector high byte + LDA LAB_FTBL,Y ; get function jump vector low byte + PHA ; push functions jump vector low byte + + LDA LAB_FTPM,Y ; get function pre process vector high byte + BEQ LAB_1C56 ; skip pre process if null vector + + PHA ; push functions pre process vector high byte + LDA LAB_FTPL,Y ; get function pre process vector low byte + PHA ; push functions pre process vector low byte + +LAB_1C56 + RTS ; do function, or pre process, call + +; process string expression in parenthesis + +LAB_PPFS + JSR LAB_1BF7 ; process expression in parenthesis + JMP LAB_CTST ; check if source is string then do function, + ; else do type mismatch + +; process numeric expression in parenthesis + +LAB_PPFN + JSR LAB_1BF7 ; process expression in parenthesis + JMP LAB_CTNM ; check if source is numeric then do function, + ; else do type mismatch + +; set numeric data type and increment BASIC execute pointer + +LAB_PPBI + LSR Dtypef ; clear data type flag, $FF=string, $00=numeric + JMP LAB_IGBY ; increment and scan memory then do function + +; process string for LEFT$, RIGHT$ or MID$ + +LAB_LRMS + JSR LAB_EVEZ ; evaluate (should be string) expression + JSR LAB_1C01 ; scan for ",", else do syntax error then warm start + JSR LAB_CTST ; check if source is string, else do type mismatch + + PLA ; get function jump vector low byte + TAX ; save functions jump vector low byte + PLA ; get function jump vector high byte + TAY ; save functions jump vector high byte + LDA des_ph ; get descriptor pointer high byte + PHA ; push string pointer high byte + LDA des_pl ; get descriptor pointer low byte + PHA ; push string pointer low byte + TYA ; get function jump vector high byte back + PHA ; save functions jump vector high byte + TXA ; get function jump vector low byte back + PHA ; save functions jump vector low byte + JSR LAB_GTBY ; get byte parameter + TXA ; copy byte parameter to A + RTS ; go do function + +; process numeric expression(s) for BIN$ or HEX$ + +LAB_BHSS + JSR LAB_EVEZ ; process expression + JSR LAB_CTNM ; check if source is numeric, else do type mismatch + LDA FAC1_e ; get FAC1 exponent + CMP #$98 ; compare with exponent = 2^24 + BCS LAB_BHER ; branch if n>=2^24 (is too big) + + JSR LAB_2831 ; convert FAC1 floating-to-fixed + LDX #$02 ; 3 bytes to do +LAB_CFAC + LDA FAC1_1,X ; get byte from FAC1 + STA nums_1,X ; save byte to temp + DEX ; decrement index + BPL LAB_CFAC ; copy FAC1 mantissa to temp + + JSR LAB_GBYT ; get next BASIC byte + LDX #$00 ; set default to no leading "0"s + CMP #')' ; compare with close bracket + BEQ LAB_1C54 ; if ")" go do rest of function + + JSR LAB_SCGB ; scan for "," and get byte + JSR LAB_GBYT ; get last byte back + CMP #')' ; is next character ) + BNE LAB_BHER ; if not ")" go do error + +LAB_1C54 + RTS ; else do function + +LAB_BHER + JMP LAB_FCER ; do function call error then warm start + +; perform EOR + +; added operator format is the same as AND or OR, precedence is the same as OR + +; this bit worked first time but it took a while to sort out the operator table +; pointers and offsets afterwards! + +LAB_EOR + JSR GetFirst ; get first integer expression (no sign check) + EOR XOAw_l ; EOR with expression 1 low byte + TAY ; save in Y + LDA FAC1_2 ; get FAC1 mantissa2 + EOR XOAw_h ; EOR with expression 1 high byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform OR + +LAB_OR + JSR GetFirst ; get first integer expression (no sign check) + ORA XOAw_l ; OR with expression 1 low byte + TAY ; save in Y + LDA FAC1_2 ; get FAC1 mantissa2 + ORA XOAw_h ; OR with expression 1 high byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform AND + +LAB_AND + JSR GetFirst ; get first integer expression (no sign check) + AND XOAw_l ; AND with expression 1 low byte + TAY ; save in Y + LDA FAC1_2 ; get FAC1 mantissa2 + AND XOAw_h ; AND with expression 1 high byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; get first value for OR, AND or EOR + +GetFirst + JSR LAB_EVIR ; evaluate integer expression (no sign check) + LDA FAC1_2 ; get FAC1 mantissa2 + STA XOAw_h ; save it + LDA FAC1_3 ; get FAC1 mantissa3 + STA XOAw_l ; save it + JSR LAB_279B ; copy FAC2 to FAC1 (get 2nd value in expression) + JSR LAB_EVIR ; evaluate integer expression (no sign check) + LDA FAC1_3 ; get FAC1 mantissa3 +LAB_1C95 + RTS + +; perform comparisons + +; do < compare + +LAB_LTHAN + JSR LAB_CKTM ; type match check, set C for string + BCS LAB_1CAE ; branch if string + + ; do numeric < compare + LDA FAC2_s ; get FAC2 sign (b7) + ORA #$7F ; set all non sign bits + AND FAC2_1 ; and FAC2 mantissa1 (AND in sign bit) + STA FAC2_1 ; save FAC2 mantissa1 + LDA #FAC2_e ; set pointer high byte to FAC2 + JSR LAB_27F8 ; compare FAC1 with FAC2 (AY) + TAX ; copy result + JMP LAB_1CE1 ; go evaluate result + + ; do string < compare +LAB_1CAE + LSR Dtypef ; clear data type flag, $FF=string, $00=numeric + DEC comp_f ; clear < bit in compare function flag + JSR LAB_22B6 ; pop string off descriptor stack, or from top of string + ; space returns with A = length, X=pointer low byte, + ; Y=pointer high byte + STA str_ln ; save length + STX str_pl ; save string pointer low byte + STY str_ph ; save string pointer high byte + LDA FAC2_2 ; get descriptor pointer low byte + LDY FAC2_3 ; get descriptor pointer high byte + JSR LAB_22BA ; pop (YA) descriptor off stack or from top of string space + ; returns with A = length, X=pointer low byte, + ; Y=pointer high byte + STX FAC2_2 ; save string pointer low byte + STY FAC2_3 ; save string pointer high byte + TAX ; copy length + SEC ; set carry for subtract + SBC str_ln ; subtract string 1 length + BEQ LAB_1CD6 ; branch if str 1 length = string 2 length + + LDA #$01 ; set str 1 length > string 2 length + BCC LAB_1CD6 ; branch if so + + LDX str_ln ; get string 1 length + LDA #$FF ; set str 1 length < string 2 length +LAB_1CD6 + STA FAC1_s ; save length compare + LDY #$FF ; set index + INX ; adjust for loop +LAB_1CDB + INY ; increment index + DEX ; decrement count + BNE LAB_1CE6 ; branch if still bytes to do + + LDX FAC1_s ; get length compare back +LAB_1CE1 + BMI LAB_1CF2 ; branch if str 1 < str 2 + + CLC ; flag str 1 <= str 2 + BCC LAB_1CF2 ; go evaluate result + +LAB_1CE6 + LDA (FAC2_2),Y ; get string 2 byte + CMP (FAC1_1),Y ; compare with string 1 byte + BEQ LAB_1CDB ; loop if bytes = + + LDX #$FF ; set str 1 < string 2 + BCS LAB_1CF2 ; branch if so + + LDX #$01 ; set str 1 > string 2 +LAB_1CF2 + INX ; x = 0, 1 or 2 + TXA ; copy to A + ROL ; *2 (1, 2 or 4) + AND Cflag ; AND with comparison evaluation flag + BEQ LAB_1CFB ; branch if 0 (compare is false) + + LDA #$FF ; else set result true +LAB_1CFB + JMP LAB_27DB ; save A as integer byte and return + +LAB_1CFE + JSR LAB_1C01 ; scan for ",", else do syntax error then warm start + +; perform DIM + +LAB_DIM + TAX ; copy "DIM" flag to X + JSR LAB_1D10 ; search for variable + JSR LAB_GBYT ; scan memory + BNE LAB_1CFE ; scan for "," and loop if not null + + RTS + +; perform << (left shift) + +LAB_LSHIFT + JSR GetPair ; get integer expression and byte (no sign check) + LDA FAC1_2 ; get expression high byte + LDX TempB ; get shift count + BEQ NoShift ; branch if zero + + CPX #$10 ; compare bit count with 16d + BCS TooBig ; branch if >= + +Ls_loop + ASL FAC1_3 ; shift low byte + ROL ; shift high byte + DEX ; decrement bit count + BNE Ls_loop ; loop if shift not complete + + LDY FAC1_3 ; get expression low byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform >> (right shift) + +LAB_RSHIFT + JSR GetPair ; get integer expression and byte (no sign check) + LDA FAC1_2 ; get expression high byte + LDX TempB ; get shift count + BEQ NoShift ; branch if zero + + CPX #$10 ; compare bit count with 16d + BCS TooBig ; branch if >= + +Rs_loop + LSR ; shift high byte + ROR FAC1_3 ; shift low byte + DEX ; decrement bit count + BNE Rs_loop ; loop if shift not complete + +NoShift + LDY FAC1_3 ; get expression low byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +TooBig + LDA #$00 ; clear high byte + TAY ; copy to low byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +GetPair + JSR LAB_EVBY ; evaluate byte expression, result in X + STX TempB ; save it + JSR LAB_279B ; copy FAC2 to FAC1 (get 2nd value in expression) + JMP LAB_EVIR ; evaluate integer expression (no sign check) + +; search for variable + +; return pointer to variable in Cvaral/Cvarah + +LAB_GVAR + LDX #$00 ; set DIM flag = $00 + JSR LAB_GBYT ; scan memory (1st character) +LAB_1D10 + STX Defdim ; save DIM flag +LAB_1D12 + STA Varnm1 ; save 1st character + AND #$7F ; clear FN flag bit + JSR LAB_CASC ; check byte, return C=0 if<"A" or >"Z" + BCS LAB_1D1F ; branch if ok + + JMP LAB_SNER ; else syntax error then warm start + + ; was variable name so .. +LAB_1D1F + LDX #$00 ; clear 2nd character temp + STX Dtypef ; clear data type flag, $FF=string, $00=numeric + JSR LAB_IGBY ; increment and scan memory (2nd character) + BCC LAB_1D2D ; branch if character = "0"-"9" (ok) + + ; 2nd character wasn't "0" to "9" so .. + JSR LAB_CASC ; check byte, return C=0 if<"A" or >"Z" + BCC LAB_1D38 ; branch if <"A" or >"Z" (go check if string) + +LAB_1D2D + TAX ; copy 2nd character + + ; ignore further (valid) characters in the variable name +LAB_1D2E + JSR LAB_IGBY ; increment and scan memory (3rd character) + BCC LAB_1D2E ; loop if character = "0"-"9" (ignore) + + JSR LAB_CASC ; check byte, return C=0 if<"A" or >"Z" + BCS LAB_1D2E ; loop if character = "A"-"Z" (ignore) + + ; check if string variable +LAB_1D38 + CMP #'$' ; compare with "$" + BNE LAB_1D47 ; branch if not string + +; to introduce a new variable type (% suffix for integers say) then this branch +; will need to go to that check and then that branch, if it fails, go to LAB_1D47 + + ; type is string + LDA #$FF ; set data type = string + STA Dtypef ; set data type flag, $FF=string, $00=numeric + TXA ; get 2nd character back + ORA #$80 ; set top bit (indicate string var) + TAX ; copy back to 2nd character temp + JSR LAB_IGBY ; increment and scan memory + +; after we have determined the variable type we need to come back here to determine +; if it's an array of type. this would plug in a%(b[,c[,d]])) integer arrays nicely + + +LAB_1D47 ; gets here with character after var name in A + STX Varnm2 ; save 2nd character + ORA Sufnxf ; or with subscript/FNX flag (or FN name) + CMP #'(' ; compare with "(" + BNE LAB_1D53 ; branch if not "(" + + JMP LAB_1E17 ; go find, or make, array + +; either find or create var +; var name (1st two characters only!) is in Varnm1,Varnm2 + + ; variable name wasn't var(... so look for plain var +LAB_1D53 + LDA #$00 ; clear A + STA Sufnxf ; clear subscript/FNX flag + LDA Svarl ; get start of vars low byte + LDX Svarh ; get start of vars high byte + LDY #$00 ; clear index +LAB_1D5D + STX Vrschh ; save search address high byte +LAB_1D5F + STA Vrschl ; save search address low byte + CPX Sarryh ; compare high address with var space end + BNE LAB_1D69 ; skip next compare if <> + + ; high addresses were = so compare low addresses + CMP Sarryl ; compare low address with var space end + BEQ LAB_1D8B ; if not found go make new var + +LAB_1D69 + LDA Varnm1 ; get 1st character of var to find + CMP (Vrschl),Y ; compare with variable name 1st character + BNE LAB_1D77 ; branch if no match + + ; 1st characters match so compare 2nd characters + LDA Varnm2 ; get 2nd character of var to find + INY ; index to point to variable name 2nd character + CMP (Vrschl),Y ; compare with variable name 2nd character + BEQ LAB_1DD7 ; branch if match (found var) + + DEY ; else decrement index (now = $00) +LAB_1D77 + CLC ; clear carry for add + LDA Vrschl ; get search address low byte + ADC #$06 ; +6 (offset to next var name) + BCC LAB_1D5F ; loop if no overflow to high byte + + INX ; else increment high byte + BNE LAB_1D5D ; loop always (RAM doesn't extend to $FFFF !) + +; check byte, return C=0 if<"A" or >"Z" or "a" to "z" + +LAB_CASC + CMP #'a' ; compare with "a" + BCS LAB_1D83 ; go check <"z"+1 + +; check byte, return C=0 if<"A" or >"Z" + +LAB_1D82 + CMP #'A' ; compare with "A" + BCC LAB_1D8A ; exit if less + + ; carry is set + SBC #$5B ; subtract "Z"+1 + SEC ; set carry + SBC #$A5 ; subtract $A5 (restore byte) + ; carry clear if byte>$5A +LAB_1D8A + RTS + +LAB_1D83 + SBC #$7B ; subtract "z"+1 + SEC ; set carry + SBC #$85 ; subtract $85 (restore byte) + ; carry clear if byte>$7A + RTS + + ; reached end of variable mem without match + ; .. so create new variable +LAB_1D8B + PLA ; pop return address low byte + PHA ; push return address low byte +LAB_1C18p2 = LAB_1C18+2 + CMP #LAB_1D96 ; high byte point to $00,$00 + RTS + + ; create new numeric variable +LAB_1D98 + LDA Sarryl ; get var mem end low byte + LDY Sarryh ; get var mem end high byte + STA Ostrtl ; save old block start low byte + STY Ostrth ; save old block start high byte + LDA Earryl ; get array mem end low byte + LDY Earryh ; get array mem end high byte + STA Obendl ; save old block end low byte + STY Obendh ; save old block end high byte + CLC ; clear carry for add + ADC #$06 ; +6 (space for one var) + BCC LAB_1DAE ; branch if no overflow to high byte + + INY ; else increment high byte +LAB_1DAE + STA Nbendl ; set new block end low byte + STY Nbendh ; set new block end high byte + JSR LAB_11CF ; open up space in memory + LDA Nbendl ; get new start low byte + LDY Nbendh ; get new start high byte (-$100) + INY ; correct high byte + STA Sarryl ; save new var mem end low byte + STY Sarryh ; save new var mem end high byte + LDY #$00 ; clear index + LDA Varnm1 ; get var name 1st character + STA (Vrschl),Y ; save var name 1st character + INY ; increment index + LDA Varnm2 ; get var name 2nd character + STA (Vrschl),Y ; save var name 2nd character + LDA #$00 ; clear A + INY ; increment index + STA (Vrschl),Y ; initialise var byte + INY ; increment index + STA (Vrschl),Y ; initialise var byte + INY ; increment index + STA (Vrschl),Y ; initialise var byte + INY ; increment index + STA (Vrschl),Y ; initialise var byte + + ; found a match for var ((Vrschl) = ptr) +LAB_1DD7 + LDA Vrschl ; get var address low byte + CLC ; clear carry for add + ADC #$02 ; +2 (offset past var name bytes) + LDY Vrschh ; get var address high byte + BCC LAB_1DE1 ; branch if no overflow from add + + INY ; else increment high byte +LAB_1DE1 + STA Cvaral ; save current var address low byte + STY Cvarah ; save current var address high byte + RTS + +; set-up array pointer (Adatal/h) to first element in array +; set Adatal,Adatah to Astrtl,Astrth+2*Dimcnt+#$05 + +LAB_1DE6 + LDA Dimcnt ; get # of dimensions (1, 2 or 3) + ASL ; *2 (also clears the carry !) + ADC #$05 ; +5 (result is 7, 9 or 11 here) + ADC Astrtl ; add array start pointer low byte + LDY Astrth ; get array pointer high byte + BCC LAB_1DF2 ; branch if no overflow + + INY ; else increment high byte +LAB_1DF2 + STA Adatal ; save array data pointer low byte + STY Adatah ; save array data pointer high byte + RTS + +; evaluate integer expression + +LAB_EVIN + JSR LAB_IGBY ; increment and scan memory + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + +; evaluate integer expression (no check) + +LAB_EVPI + LDA FAC1_s ; get FAC1 sign (b7) + BMI LAB_1E12 ; do function call error if -ve + +; evaluate integer expression (no sign check) + +LAB_EVIR + LDA FAC1_e ; get FAC1 exponent + CMP #$90 ; compare with exponent = 2^16 (n>2^15) + BCC LAB_1E14 ; branch if n<2^16 (is ok) + + LDA #LAB_1DF7 ; set pointer high byte to -32768 + JSR LAB_27F8 ; compare FAC1 with (AY) +LAB_1E12 + BNE LAB_FCER ; if <> do function call error then warm start + +LAB_1E14 + JMP LAB_2831 ; convert FAC1 floating-to-fixed and return + +; find or make array + +LAB_1E17 + LDA Defdim ; get DIM flag + PHA ; push it + LDA Dtypef ; get data type flag, $FF=string, $00=numeric + PHA ; push it + LDY #$00 ; clear dimensions count + +; now get the array dimension(s) and stack it (them) before the data type and DIM flag + +LAB_1E1F + TYA ; copy dimensions count + PHA ; save it + LDA Varnm2 ; get array name 2nd byte + PHA ; save it + LDA Varnm1 ; get array name 1st byte + PHA ; save it + JSR LAB_EVIN ; evaluate integer expression + PLA ; pull array name 1st byte + STA Varnm1 ; restore array name 1st byte + PLA ; pull array name 2nd byte + STA Varnm2 ; restore array name 2nd byte + PLA ; pull dimensions count + TAY ; restore it + TSX ; copy stack pointer + LDA LAB_STAK+2,X ; get DIM flag + PHA ; push it + LDA LAB_STAK+1,X ; get data type flag + PHA ; push it + LDA FAC1_2 ; get this dimension size high byte + STA LAB_STAK+2,X ; stack before flag bytes + LDA FAC1_3 ; get this dimension size low byte + STA LAB_STAK+1,X ; stack before flag bytes + INY ; increment dimensions count + JSR LAB_GBYT ; scan memory + CMP #',' ; compare with "," + BEQ LAB_1E1F ; if found go do next dimension + + STY Dimcnt ; store dimensions count + JSR LAB_1BFB ; scan for ")" , else do syntax error then warm start + PLA ; pull data type flag + STA Dtypef ; restore data type flag, $FF=string, $00=numeric + PLA ; pull DIM flag + STA Defdim ; restore DIM flag + LDX Sarryl ; get array mem start low byte + LDA Sarryh ; get array mem start high byte + +; now check to see if we are at the end of array memory (we would be if there were +; no arrays). + +LAB_1E5C + STX Astrtl ; save as array start pointer low byte + STA Astrth ; save as array start pointer high byte + CMP Earryh ; compare with array mem end high byte + BNE LAB_1E68 ; branch if not reached array mem end + + CPX Earryl ; else compare with array mem end low byte + BEQ LAB_1EA1 ; go build array if not found + + ; search for array +LAB_1E68 + LDY #$00 ; clear index + LDA (Astrtl),Y ; get array name first byte + INY ; increment index to second name byte + CMP Varnm1 ; compare with this array name first byte + BNE LAB_1E77 ; branch if no match + + LDA Varnm2 ; else get this array name second byte + CMP (Astrtl),Y ; compare with array name second byte + BEQ LAB_1E8D ; array found so branch + + ; no match +LAB_1E77 + INY ; increment index + LDA (Astrtl),Y ; get array size low byte + CLC ; clear carry for add + ADC Astrtl ; add array start pointer low byte + TAX ; copy low byte to X + INY ; increment index + LDA (Astrtl),Y ; get array size high byte + ADC Astrth ; add array mem pointer high byte + BCC LAB_1E5C ; if no overflow go check next array + +; do array bounds error + +LAB_1E85 + LDX #$10 ; error code $10 ("Array bounds" error) + .byte $2C ; makes next bit BIT LAB_08A2 + +; do function call error + +LAB_FCER + LDX #$08 ; error code $08 ("Function call" error) +LAB_1E8A + JMP LAB_XERR ; do error #X, then warm start + + ; found array, are we trying to dimension it? +LAB_1E8D + LDX #$12 ; set error $12 ("Double dimension" error) + LDA Defdim ; get DIM flag + BNE LAB_1E8A ; if we are trying to dimension it do error #X, then warm + ; start + +; found the array and we're not dimensioning it so we must find an element in it + + JSR LAB_1DE6 ; set-up array pointer (Adatal/h) to first element in array + ; (Astrtl,Astrth points to start of array) + LDA Dimcnt ; get dimensions count + LDY #$04 ; set index to array's # of dimensions + CMP (Astrtl),Y ; compare with no of dimensions + BNE LAB_1E85 ; if wrong do array bounds error, could do "Wrong + ; dimensions" error here .. if we want a different + ; error message + + JMP LAB_1F28 ; found array so go get element + ; (could jump to LAB_1F28 as all LAB_1F24 does is take + ; Dimcnt and save it at (Astrtl),Y which is already the + ; same or we would have taken the BNE) + + ; array not found, so build it +LAB_1EA1 + JSR LAB_1DE6 ; set-up array pointer (Adatal/h) to first element in array + ; (Astrtl,Astrth points to start of array) + JSR LAB_121F ; check available memory, "Out of memory" error if no room + ; addr to check is in AY (low/high) + LDY #$00 ; clear Y (don't need to clear A) + STY Aspth ; clear array data size high byte + LDA Varnm1 ; get variable name 1st byte + STA (Astrtl),Y ; save array name 1st byte + INY ; increment index + LDA Varnm2 ; get variable name 2nd byte + STA (Astrtl),Y ; save array name 2nd byte + LDA Dimcnt ; get dimensions count + LDY #$04 ; index to dimension count + STY Asptl ; set array data size low byte (four bytes per element) + STA (Astrtl),Y ; set array's dimensions count + + ; now calculate the size of the data space for the array + CLC ; clear carry for add (clear on subsequent loops) +LAB_1EC0 + LDX #$0B ; set default dimension value low byte + LDA #$00 ; set default dimension value high byte + BIT Defdim ; test default DIM flag + BVC LAB_1ED0 ; branch if b6 of Defdim is clear + + PLA ; else pull dimension value low byte + ADC #$01 ; +1 (allow for zeroeth element) + TAX ; copy low byte to X + PLA ; pull dimension value high byte + ADC #$00 ; add carry from low byte + +LAB_1ED0 + INY ; index to dimension value high byte + STA (Astrtl),Y ; save dimension value high byte + INY ; index to dimension value high byte + TXA ; get dimension value low byte + STA (Astrtl),Y ; save dimension value low byte + JSR LAB_1F7C ; does XY = (Astrtl),Y * (Asptl) + STX Asptl ; save array data size low byte + STA Aspth ; save array data size high byte + LDY ut1_pl ; restore index (saved by subroutine) + DEC Dimcnt ; decrement dimensions count + BNE LAB_1EC0 ; loop while not = 0 + + ADC Adatah ; add size high byte to first element high byte + ; (carry is always clear here) + BCS LAB_1F45 ; if overflow go do "Out of memory" error + + STA Adatah ; save end of array high byte + TAY ; copy end high byte to Y + TXA ; get array size low byte + ADC Adatal ; add array start low byte + BCC LAB_1EF3 ; branch if no carry + + INY ; else increment end of array high byte + BEQ LAB_1F45 ; if overflow go do "Out of memory" error + + ; set-up mostly complete, now zero the array +LAB_1EF3 + JSR LAB_121F ; check available memory, "Out of memory" error if no room + ; addr to check is in AY (low/high) + STA Earryl ; save array mem end low byte + STY Earryh ; save array mem end high byte + LDA #$00 ; clear byte for array clear + INC Aspth ; increment array size high byte (now block count) + LDY Asptl ; get array size low byte (now index to block) + BEQ LAB_1F07 ; branch if low byte = $00 + +LAB_1F02 + DEY ; decrement index (do 0 to n-1) + STA (Adatal),Y ; zero byte + BNE LAB_1F02 ; loop until this block done + +LAB_1F07 + DEC Adatah ; decrement array pointer high byte + DEC Aspth ; decrement block count high byte + BNE LAB_1F02 ; loop until all blocks done + + INC Adatah ; correct for last loop + SEC ; set carry for subtract + LDY #$02 ; index to array size low byte + LDA Earryl ; get array mem end low byte + SBC Astrtl ; subtract array start low byte + STA (Astrtl),Y ; save array size low byte + INY ; index to array size high byte + LDA Earryh ; get array mem end high byte + SBC Astrth ; subtract array start high byte + STA (Astrtl),Y ; save array size high byte + LDA Defdim ; get default DIM flag + BNE LAB_1F7B ; exit (RET) if this was a DIM command + + ; else, find element + INY ; index to # of dimensions + +LAB_1F24 + LDA (Astrtl),Y ; get array's dimension count + STA Dimcnt ; save it + +; we have found, or built, the array. now we need to find the element + +LAB_1F28 + LDA #$00 ; clear byte + STA Asptl ; clear array data pointer low byte +LAB_1F2C + STA Aspth ; save array data pointer high byte + INY ; increment index (point to array bound high byte) + PLA ; pull array index low byte + TAX ; copy to X + STA FAC1_2 ; save index low byte to FAC1 mantissa2 + PLA ; pull array index high byte + STA FAC1_3 ; save index high byte to FAC1 mantissa3 + CMP (Astrtl),Y ; compare with array bound high byte + BCC LAB_1F48 ; branch if within bounds + + BNE LAB_1F42 ; if outside bounds do array bounds error + + ; else high byte was = so test low bytes + INY ; index to array bound low byte + TXA ; get array index low byte + CMP (Astrtl),Y ; compare with array bound low byte + BCC LAB_1F49 ; branch if within bounds + +LAB_1F42 + JMP LAB_1E85 ; else do array bounds error + +LAB_1F45 + JMP LAB_OMER ; do "Out of memory" error then warm start + +LAB_1F48 + INY ; index to array bound low byte +LAB_1F49 + LDA Aspth ; get array data pointer high byte + ORA Asptl ; OR with array data pointer low byte + BEQ LAB_1F5A ; branch if array data pointer = null (skip multiply) + + JSR LAB_1F7C ; does XY = (Astrtl),Y * (Asptl) + TXA ; get result low byte + ADC FAC1_2 ; add index low byte from FAC1 mantissa2 + TAX ; save result low byte + TYA ; get result high byte + LDY ut1_pl ; restore index +LAB_1F5A + ADC FAC1_3 ; add index high byte from FAC1 mantissa3 + STX Asptl ; save array data pointer low byte + DEC Dimcnt ; decrement dimensions count + BNE LAB_1F2C ; loop if dimensions still to do + + ASL Asptl ; array data pointer low byte * 2 + ROL ; array data pointer high byte * 2 + ASL Asptl ; array data pointer low byte * 4 + ROL ; array data pointer high byte * 4 + TAY ; copy high byte + LDA Asptl ; get low byte + ADC Adatal ; add array data start pointer low byte + STA Cvaral ; save as current var address low byte + TYA ; get high byte back + ADC Adatah ; add array data start pointer high byte + STA Cvarah ; save as current var address high byte + TAY ; copy high byte to Y + LDA Cvaral ; get current var address low byte +LAB_1F7B + RTS + +; does XY = (Astrtl),Y * (Asptl) + +LAB_1F7C + STY ut1_pl ; save index + LDA (Astrtl),Y ; get dimension size low byte + STA dims_l ; save dimension size low byte + DEY ; decrement index + LDA (Astrtl),Y ; get dimension size high byte + STA dims_h ; save dimension size high byte + + LDA #$10 ; count = $10 (16 bit multiply) + STA numbit ; save bit count + LDX #$00 ; clear result low byte + LDY #$00 ; clear result high byte +LAB_1F8F + TXA ; get result low byte + ASL ; *2 + TAX ; save result low byte + TYA ; get result high byte + ROL ; *2 + TAY ; save result high byte + BCS LAB_1F45 ; if overflow go do "Out of memory" error + + ASL Asptl ; shift multiplier low byte + ROL Aspth ; shift multiplier high byte + BCC LAB_1FA8 ; skip add if no carry + + CLC ; else clear carry for add + TXA ; get result low byte + ADC dims_l ; add dimension size low byte + TAX ; save result low byte + TYA ; get result high byte + ADC dims_h ; add dimension size high byte + TAY ; save result high byte + BCS LAB_1F45 ; if overflow go do "Out of memory" error + +LAB_1FA8 + DEC numbit ; decrement bit count + BNE LAB_1F8F ; loop until all done + + RTS + +; perform FRE() + +LAB_FRE + LDA Dtypef ; get data type flag, $FF=string, $00=numeric + BPL LAB_1FB4 ; branch if numeric + + JSR LAB_22B6 ; pop string off descriptor stack, or from top of string + ; space returns with A = length, X=$71=pointer low byte, + ; Y=$72=pointer high byte + + ; FRE(n) was numeric so do this +LAB_1FB4 + JSR LAB_GARB ; go do garbage collection + SEC ; set carry for subtract + LDA Sstorl ; get bottom of string space low byte + SBC Earryl ; subtract array mem end low byte + TAY ; copy result to Y + LDA Sstorh ; get bottom of string space high byte + SBC Earryh ; subtract array mem end high byte + +; save and convert integer AY to FAC1 + +LAB_AYFC + LSR Dtypef ; clear data type flag, $FF=string, $00=numeric + STA FAC1_1 ; save FAC1 mantissa1 + STY FAC1_2 ; save FAC1 mantissa2 + LDX #$90 ; set exponent=2^16 (integer) + JMP LAB_27E3 ; set exp=X, clear FAC1_3, normalise and return + +; perform POS() + +LAB_POS + LDY TPos ; get terminal position + +; convert Y to byte in FAC1 + +LAB_1FD0 + LDA #$00 ; clear high byte + BEQ LAB_AYFC ; always save and convert integer AY to FAC1 and return + +; check not Direct (used by DEF and INPUT) + +LAB_CKRN + LDX Clineh ; get current line high byte + INX ; increment it + BNE LAB_1F7B ; return if can continue not direct mode + + ; else do illegal direct error +LAB_1FD9 + LDX #$16 ; error code $16 ("Illegal direct" error) +LAB_1FDB + JMP LAB_XERR ; go do error #X, then warm start + +; perform DEF + +LAB_DEF + JSR LAB_200B ; check FNx syntax + STA func_l ; save function pointer low byte + STY func_h ; save function pointer high byte + JSR LAB_CKRN ; check not Direct (back here if ok) + JSR LAB_1BFE ; scan for "(" , else do syntax error then warm start + LDA #$80 ; set flag for FNx + STA Sufnxf ; save subscript/FNx flag + JSR LAB_GVAR ; get (var) address + JSR LAB_CTNM ; check if source is numeric, else do type mismatch + JSR LAB_1BFB ; scan for ")" , else do syntax error then warm start + LDA #TK_EQUAL ; get = token + JSR LAB_SCCA ; scan for CHR$(A), else do syntax error then warm start + LDA Cvarah ; get current var address high byte + PHA ; push it + LDA Cvaral ; get current var address low byte + PHA ; push it + LDA Bpntrh ; get BASIC execute pointer high byte + PHA ; push it + LDA Bpntrl ; get BASIC execute pointer low byte + PHA ; push it + JSR LAB_DATA ; go perform DATA + JMP LAB_207A ; put execute pointer and variable pointer into function + ; and return + +; check FNx syntax + +LAB_200B + LDA #TK_FN ; get FN" token + JSR LAB_SCCA ; scan for CHR$(A) , else do syntax error then warm start + ; return character after A + ORA #$80 ; set FN flag bit + STA Sufnxf ; save FN flag so array variable test fails + JSR LAB_1D12 ; search for FN variable + JMP LAB_CTNM ; check if source is numeric and return, else do type + ; mismatch + + ; Evaluate FNx +LAB_201E + JSR LAB_200B ; check FNx syntax + PHA ; push function pointer low byte + TYA ; copy function pointer high byte + PHA ; push function pointer high byte + JSR LAB_1BFE ; scan for "(", else do syntax error then warm start + JSR LAB_EVEX ; evaluate expression + JSR LAB_1BFB ; scan for ")", else do syntax error then warm start + JSR LAB_CTNM ; check if source is numeric, else do type mismatch + PLA ; pop function pointer high byte + STA func_h ; restore it + PLA ; pop function pointer low byte + STA func_l ; restore it + LDX #$20 ; error code $20 ("Undefined function" error) + LDY #$03 ; index to variable pointer high byte + LDA (func_l),Y ; get variable pointer high byte + BEQ LAB_1FDB ; if zero go do undefined function error + + STA Cvarah ; save variable address high byte + DEY ; index to variable address low byte + LDA (func_l),Y ; get variable address low byte + STA Cvaral ; save variable address low byte + TAX ; copy address low byte + + ; now stack the function variable value before use + INY ; index to mantissa_3 +LAB_2043 + LDA (Cvaral),Y ; get byte from variable + PHA ; stack it + DEY ; decrement index + BPL LAB_2043 ; loop until variable stacked + + LDY Cvarah ; get variable address high byte + JSR LAB_2778 ; pack FAC1 (function expression value) into (XY) + ; (function variable), return Y=0, always + LDA Bpntrh ; get BASIC execute pointer high byte + PHA ; push it + LDA Bpntrl ; get BASIC execute pointer low byte + PHA ; push it + LDA (func_l),Y ; get function execute pointer low byte + STA Bpntrl ; save as BASIC execute pointer low byte + INY ; index to high byte + LDA (func_l),Y ; get function execute pointer high byte + STA Bpntrh ; save as BASIC execute pointer high byte + LDA Cvarah ; get variable address high byte + PHA ; push it + LDA Cvaral ; get variable address low byte + PHA ; push it + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + PLA ; pull variable address low byte + STA func_l ; save variable address low byte + PLA ; pull variable address high byte + STA func_h ; save variable address high byte + JSR LAB_GBYT ; scan memory + BEQ LAB_2074 ; branch if null (should be [EOL] marker) + + JMP LAB_SNER ; else syntax error then warm start + +; restore Bpntrl,Bpntrh and function variable from stack + +LAB_2074 + PLA ; pull BASIC execute pointer low byte + STA Bpntrl ; restore BASIC execute pointer low byte + PLA ; pull BASIC execute pointer high byte + STA Bpntrh ; restore BASIC execute pointer high byte + +; put execute pointer and variable pointer into function + +LAB_207A + LDY #$00 ; clear index + PLA ; pull BASIC execute pointer low byte + STA (func_l),Y ; save to function + INY ; increment index + PLA ; pull BASIC execute pointer high byte + STA (func_l),Y ; save to function + INY ; increment index + PLA ; pull current var address low byte + STA (func_l),Y ; save to function + INY ; increment index + PLA ; pull current var address high byte + STA (func_l),Y ; save to function + RTS + +; perform STR$() + +LAB_STRS + JSR LAB_CTNM ; check if source is numeric, else do type mismatch + JSR LAB_296E ; convert FAC1 to string + LDA #Decssp1 ; set result string high pointer + BEQ LAB_20AE ; print null terminated string to Sutill/Sutilh + +; Do string vector +; copy des_pl/h to des_2l/h and make string space A bytes long + +LAB_209C + LDX des_pl ; get descriptor pointer low byte + LDY des_ph ; get descriptor pointer high byte + STX des_2l ; save descriptor pointer low byte + STY des_2h ; save descriptor pointer high byte + +; make string space A bytes long +; A=length, X=Sutill=ptr low byte, Y=Sutilh=ptr high byte + +LAB_MSSP + JSR LAB_2115 ; make space in string memory for string A long + ; return X=Sutill=ptr low byte, Y=Sutilh=ptr high byte + STX str_pl ; save string pointer low byte + STY str_ph ; save string pointer high byte + STA str_ln ; save length + RTS + +; Scan, set up string +; print " terminated string to Sutill/Sutilh + +LAB_20AE + LDX #$22 ; set terminator to " + STX Srchc ; set search character (terminator 1) + STX Asrch ; set terminator 2 + +; print [Srchc] or [Asrch] terminated string to Sutill/Sutilh +; source is AY + +LAB_20B4 + STA ssptr_l ; store string start low byte + STY ssptr_h ; store string start high byte + STA str_pl ; save string pointer low byte + STY str_ph ; save string pointer high byte + LDY #$FF ; set length to -1 +LAB_20BE + INY ; increment length + LDA (ssptr_l),Y ; get byte from string + BEQ LAB_20CF ; exit loop if null byte [EOS] + + CMP Srchc ; compare with search character (terminator 1) + BEQ LAB_20CB ; branch if terminator + + CMP Asrch ; compare with terminator 2 + BNE LAB_20BE ; loop if not terminator 2 + +LAB_20CB + CMP #$22 ; compare with " + BEQ LAB_20D0 ; branch if " (carry set if = !) + +LAB_20CF + CLC ; clear carry for add (only if [EOL] terminated string) +LAB_20D0 + STY str_ln ; save length in FAC1 exponent + TYA ; copy length to A + ADC ssptr_l ; add string start low byte + STA Sendl ; save string end low byte + LDX ssptr_h ; get string start high byte + BCC LAB_20DC ; branch if no low byte overflow + + INX ; else increment high byte +LAB_20DC + STX Sendh ; save string end high byte + LDA ssptr_h ; get string start high byte + CMP #>Ram_base ; compare with start of program memory + BCS LAB_RTST ; branch if not in utility area + + ; string in utility area, move to string memory + TYA ; copy length to A + JSR LAB_209C ; copy des_pl/h to des_2l/h and make string space A bytes + ; long + LDX ssptr_l ; get string start low byte + LDY ssptr_h ; get string start high byte + JSR LAB_2298 ; store string A bytes long from XY to (Sutill) + +; check for space on descriptor stack then .. +; put string address and length on descriptor stack and update stack pointers + +LAB_RTST + LDX next_s ; get string stack pointer + CPX #des_sk+$09 ; compare with max+1 + BNE LAB_20F8 ; branch if space on string stack + + ; else do string too complex error + LDX #$1C ; error code $1C ("String too complex" error) +LAB_20F5 + JMP LAB_XERR ; do error #X, then warm start + +; put string address and length on descriptor stack and update stack pointers + +LAB_20F8 + LDA str_ln ; get string length + STA PLUS_0,X ; put on string stack + LDA str_pl ; get string pointer low byte + STA PLUS_1,X ; put on string stack + LDA str_ph ; get string pointer high byte + STA PLUS_2,X ; put on string stack + LDY #$00 ; clear Y + STX des_pl ; save string descriptor pointer low byte + STY des_ph ; save string descriptor pointer high byte (always $00) + DEY ; Y = $FF + STY Dtypef ; save data type flag, $FF=string + STX last_sl ; save old stack pointer (current top item) + INX ; update stack pointer + INX ; update stack pointer + INX ; update stack pointer + STX next_s ; save new top item value + RTS + +; Build descriptor +; make space in string memory for string A long +; return X=Sutill=ptr low byte, Y=Sutill=ptr high byte + +LAB_2115 + LSR Gclctd ; clear garbage collected flag (b7) + + ; make space for string A long +LAB_2117 + PHA ; save string length + EOR #$FF ; complement it + SEC ; set carry for subtract (twos comp add) + ADC Sstorl ; add bottom of string space low byte (subtract length) + LDY Sstorh ; get bottom of string space high byte + BCS LAB_2122 ; skip decrement if no underflow + + DEY ; decrement bottom of string space high byte +LAB_2122 + CPY Earryh ; compare with array mem end high byte + BCC LAB_2137 ; do out of memory error if less + + BNE LAB_212C ; if not = skip next test + + CMP Earryl ; compare with array mem end low byte + BCC LAB_2137 ; do out of memory error if less + +LAB_212C + STA Sstorl ; save bottom of string space low byte + STY Sstorh ; save bottom of string space high byte + STA Sutill ; save string utility ptr low byte + STY Sutilh ; save string utility ptr high byte + TAX ; copy low byte to X + PLA ; get string length back + RTS + +LAB_2137 + LDX #$0C ; error code $0C ("Out of memory" error) + LDA Gclctd ; get garbage collected flag + BMI LAB_20F5 ; if set then do error code X + + JSR LAB_GARB ; else go do garbage collection + LDA #$80 ; flag for garbage collected + STA Gclctd ; set garbage collected flag + PLA ; pull length + BNE LAB_2117 ; go try again (loop always, length should never be = $00) + +; garbage collection routine + +LAB_GARB + LDX Ememl ; get end of mem low byte + LDA Ememh ; get end of mem high byte + +; re-run routine from last ending + +LAB_214B + STX Sstorl ; set string storage low byte + STA Sstorh ; set string storage high byte + LDY #$00 ; clear index + STY garb_h ; clear working pointer high byte (flag no strings to move) + LDA Earryl ; get array mem end low byte + LDX Earryh ; get array mem end high byte + STA Histrl ; save as highest string low byte + STX Histrh ; save as highest string high byte + LDA #des_sk ; set descriptor stack pointer + STA ut1_pl ; save descriptor stack pointer low byte + STY ut1_ph ; save descriptor stack pointer high byte ($00) +LAB_2161 + CMP next_s ; compare with descriptor stack pointer + BEQ LAB_216A ; branch if = + + JSR LAB_21D7 ; go garbage collect descriptor stack + BEQ LAB_2161 ; loop always + + ; done stacked strings, now do string vars +LAB_216A + ASL g_step ; set step size = $06 + LDA Svarl ; get start of vars low byte + LDX Svarh ; get start of vars high byte + STA ut1_pl ; save as pointer low byte + STX ut1_ph ; save as pointer high byte +LAB_2176 + CPX Sarryh ; compare start of arrays high byte + BNE LAB_217E ; branch if no high byte match + + CMP Sarryl ; else compare start of arrays low byte + BEQ LAB_2183 ; branch if = var mem end + +LAB_217E + JSR LAB_21D1 ; go garbage collect strings + BEQ LAB_2176 ; loop always + + ; done string vars, now do string arrays +LAB_2183 + STA Nbendl ; save start of arrays low byte as working pointer + STX Nbendh ; save start of arrays high byte as working pointer + LDA #$04 ; set step size + STA g_step ; save step size +LAB_218B + LDA Nbendl ; get pointer low byte + LDX Nbendh ; get pointer high byte +LAB_218F + CPX Earryh ; compare with array mem end high byte + BNE LAB_219A ; branch if not at end + + CMP Earryl ; else compare with array mem end low byte + BEQ LAB_2216 ; tidy up and exit if at end + +LAB_219A + STA ut1_pl ; save pointer low byte + STX ut1_ph ; save pointer high byte + LDY #$02 ; set index + LDA (ut1_pl),Y ; get array size low byte + ADC Nbendl ; add start of this array low byte + STA Nbendl ; save start of next array low byte + INY ; increment index + LDA (ut1_pl),Y ; get array size high byte + ADC Nbendh ; add start of this array high byte + STA Nbendh ; save start of next array high byte + LDY #$01 ; set index + LDA (ut1_pl),Y ; get name second byte + BPL LAB_218B ; skip if not string array + +; was string array so .. + + LDY #$04 ; set index + LDA (ut1_pl),Y ; get # of dimensions + ASL ; *2 + ADC #$05 ; +5 (array header size) + JSR LAB_2208 ; go set up for first element +LAB_21C4 + CPX Nbendh ; compare with start of next array high byte + BNE LAB_21CC ; branch if <> (go do this array) + + CMP Nbendl ; else compare element pointer low byte with next array + ; low byte + BEQ LAB_218F ; if equal then go do next array + +LAB_21CC + JSR LAB_21D7 ; go defrag array strings + BEQ LAB_21C4 ; go do next array string (loop always) + +; defrag string variables +; enter with XA = variable pointer +; return with XA = next variable pointer + +LAB_21D1 + INY ; increment index (Y was $00) + LDA (ut1_pl),Y ; get var name byte 2 + BPL LAB_2206 ; if not string, step pointer to next var and return + + INY ; else increment index +LAB_21D7 + LDA (ut1_pl),Y ; get string length + BEQ LAB_2206 ; if null, step pointer to next string and return + + INY ; else increment index + LDA (ut1_pl),Y ; get string pointer low byte + TAX ; copy to X + INY ; increment index + LDA (ut1_pl),Y ; get string pointer high byte + CMP Sstorh ; compare bottom of string space high byte + BCC LAB_21EC ; branch if less + + BNE LAB_2206 ; if greater, step pointer to next string and return + + ; high bytes were = so compare low bytes + CPX Sstorl ; compare bottom of string space low byte + BCS LAB_2206 ; if >=, step pointer to next string and return + + ; string pointer is < string storage pointer (pos in mem) +LAB_21EC + CMP Histrh ; compare to highest string high byte + BCC LAB_2207 ; if <, step pointer to next string and return + + BNE LAB_21F6 ; if > update pointers, step to next and return + + ; high bytes were = so compare low bytes + CPX Histrl ; compare to highest string low byte + BCC LAB_2207 ; if <, step pointer to next string and return + + ; string is in string memory space +LAB_21F6 + STX Histrl ; save as new highest string low byte + STA Histrh ; save as new highest string high byte + LDA ut1_pl ; get start of vars(descriptors) low byte + LDX ut1_ph ; get start of vars(descriptors) high byte + STA garb_l ; save as working pointer low byte + STX garb_h ; save as working pointer high byte + DEY ; decrement index DIFFERS + DEY ; decrement index (should point to descriptor start) + STY g_indx ; save index pointer + + ; step pointer to next string +LAB_2206 + CLC ; clear carry for add +LAB_2207 + LDA g_step ; get step size +LAB_2208 + ADC ut1_pl ; add pointer low byte + STA ut1_pl ; save pointer low byte + BCC LAB_2211 ; branch if no overflow + + INC ut1_ph ; else increment high byte +LAB_2211 + LDX ut1_ph ; get pointer high byte + LDY #$00 ; clear Y + RTS + +; search complete, now either exit or set-up and move string + +LAB_2216 + DEC g_step ; decrement step size (now $03 for descriptor stack) + LDX garb_h ; get string to move high byte + BEQ LAB_2211 ; exit if nothing to move + + LDY g_indx ; get index byte back (points to descriptor) + CLC ; clear carry for add + LDA (garb_l),Y ; get string length + ADC Histrl ; add highest string low byte + STA Obendl ; save old block end low pointer + LDA Histrh ; get highest string high byte + ADC #$00 ; add any carry + STA Obendh ; save old block end high byte + LDA Sstorl ; get bottom of string space low byte + LDX Sstorh ; get bottom of string space high byte + STA Nbendl ; save new block end low byte + STX Nbendh ; save new block end high byte + JSR LAB_11D6 ; open up space in memory, don't set array end + LDY g_indx ; get index byte + INY ; point to descriptor low byte + LDA Nbendl ; get string pointer low byte + STA (garb_l),Y ; save new string pointer low byte + TAX ; copy string pointer low byte + INC Nbendh ; correct high byte (move sets high byte -1) + LDA Nbendh ; get new string pointer high byte + INY ; point to descriptor high byte + STA (garb_l),Y ; save new string pointer high byte + JMP LAB_214B ; re-run routine from last ending + ; (but don't collect this string) + +; concatenate +; add strings, string 1 is in descriptor des_pl, string 2 is in line + +LAB_224D + LDA des_ph ; get descriptor pointer high byte + PHA ; put on stack + LDA des_pl ; get descriptor pointer low byte + PHA ; put on stack + JSR LAB_GVAL ; get value from line + JSR LAB_CTST ; check if source is string, else do type mismatch + PLA ; get descriptor pointer low byte back + STA ssptr_l ; set pointer low byte + PLA ; get descriptor pointer high byte back + STA ssptr_h ; set pointer high byte + LDY #$00 ; clear index + LDA (ssptr_l),Y ; get length_1 from descriptor + CLC ; clear carry for add + ADC (des_pl),Y ; add length_2 + BCC LAB_226D ; branch if no overflow + + LDX #$1A ; else set error code $1A ("String too long" error) + JMP LAB_XERR ; do error #X, then warm start + +LAB_226D + JSR LAB_209C ; copy des_pl/h to des_2l/h and make string space A bytes + ; long + JSR LAB_228A ; copy string from descriptor (sdescr) to (Sutill) + LDA des_2l ; get descriptor pointer low byte + LDY des_2h ; get descriptor pointer high byte + JSR LAB_22BA ; pop (YA) descriptor off stack or from top of string space + ; returns with A = length, ut1_pl = pointer low byte, + ; ut1_ph = pointer high byte + JSR LAB_229C ; store string A bytes long from (ut1_pl) to (Sutill) + LDA ssptr_l ;.set descriptor pointer low byte + LDY ssptr_h ;.set descriptor pointer high byte + JSR LAB_22BA ; pop (YA) descriptor off stack or from top of string space + ; returns with A = length, X=ut1_pl=pointer low byte, + ; Y=ut1_ph=pointer high byte + JSR LAB_RTST ; check for space on descriptor stack then put string + ; address and length on descriptor stack and update stack + ; pointers + JMP LAB_1ADB ;.continue evaluation + +; copy string from descriptor (sdescr) to (Sutill) + +LAB_228A + LDY #$00 ; clear index + LDA (sdescr),Y ; get string length + PHA ; save on stack + INY ; increment index + LDA (sdescr),Y ; get source string pointer low byte + TAX ; copy to X + INY ; increment index + LDA (sdescr),Y ; get source string pointer high byte + TAY ; copy to Y + PLA ; get length back + +; store string A bytes long from YX to (Sutill) + +LAB_2298 + STX ut1_pl ; save source string pointer low byte + STY ut1_ph ; save source string pointer high byte + +; store string A bytes long from (ut1_pl) to (Sutill) + +LAB_229C + TAX ; copy length to index (don't count with Y) + BEQ LAB_22B2 ; branch if = $0 (null string) no need to add zero length + + LDY #$00 ; zero pointer (copy forward) +LAB_22A0 + LDA (ut1_pl),Y ; get source byte + STA (Sutill),Y ; save destination byte + + INY ; increment index + DEX ; decrement counter + BNE LAB_22A0 ; loop while <> 0 + + TYA ; restore length from Y +LAB_22A9 + CLC ; clear carry for add + ADC Sutill ; add string utility ptr low byte + STA Sutill ; save string utility ptr low byte + BCC LAB_22B2 ; branch if no carry + + INC Sutilh ; else increment string utility ptr high byte +LAB_22B2 + RTS + +; evaluate string + +LAB_EVST + JSR LAB_CTST ; check if source is string, else do type mismatch + +; pop string off descriptor stack, or from top of string space +; returns with A = length, X=pointer low byte, Y=pointer high byte + +LAB_22B6 + LDA des_pl ; get descriptor pointer low byte + LDY des_ph ; get descriptor pointer high byte + +; pop (YA) descriptor off stack or from top of string space +; returns with A = length, X=ut1_pl=pointer low byte, Y=ut1_ph=pointer high byte + +LAB_22BA + STA ut1_pl ; save descriptor pointer low byte + STY ut1_ph ; save descriptor pointer high byte + JSR LAB_22EB ; clean descriptor stack, YA = pointer + PHP ; save status flags + LDY #$00 ; clear index + LDA (ut1_pl),Y ; get length from string descriptor + PHA ; put on stack + INY ; increment index + LDA (ut1_pl),Y ; get string pointer low byte from descriptor + TAX ; copy to X + INY ; increment index + LDA (ut1_pl),Y ; get string pointer high byte from descriptor + TAY ; copy to Y + PLA ; get string length back + PLP ; restore status + BNE LAB_22E6 ; branch if pointer <> last_sl,last_sh + + CPY Sstorh ; compare bottom of string space high byte + BNE LAB_22E6 ; branch if <> + + CPX Sstorl ; else compare bottom of string space low byte + BNE LAB_22E6 ; branch if <> + + PHA ; save string length + CLC ; clear carry for add + ADC Sstorl ; add bottom of string space low byte + STA Sstorl ; save bottom of string space low byte + BCC LAB_22E5 ; skip increment if no overflow + + INC Sstorh ; increment bottom of string space high byte +LAB_22E5 + PLA ; restore string length +LAB_22E6 + STX ut1_pl ; save string pointer low byte + STY ut1_ph ; save string pointer high byte + RTS + +; clean descriptor stack, YA = pointer +; checks if AY is on the descriptor stack, if so does a stack discard + +LAB_22EB + CPY last_sh ; compare pointer high byte + BNE LAB_22FB ; exit if <> + + CMP last_sl ; compare pointer low byte + BNE LAB_22FB ; exit if <> + + STA next_s ; save descriptor stack pointer + SBC #$03 ; -3 + STA last_sl ; save low byte -3 + LDY #$00 ; clear high byte +LAB_22FB + RTS + +; perform CHR$() + +LAB_CHRS + JSR LAB_EVBY ; evaluate byte expression, result in X + TXA ; copy to A + PHA ; save character + LDA #$01 ; string is single byte + JSR LAB_MSSP ; make string space A bytes long A=$AC=length, + ; X=$AD=Sutill=ptr low byte, Y=$AE=Sutilh=ptr high byte + PLA ; get character back + LDY #$00 ; clear index + STA (str_pl),Y ; save byte in string (byte IS string!) + JMP LAB_RTST ; check for space on descriptor stack then put string + ; address and length on descriptor stack and update stack + ; pointers + +; perform LEFT$() + +LAB_LEFT + PHA ; push byte parameter + JSR LAB_236F ; pull string data and byte parameter from stack + ; return pointer in des_2l/h, byte in A (and X), Y=0 + CMP (des_2l),Y ; compare byte parameter with string length + TYA ; clear A + BEQ LAB_2316 ; go do string copy (branch always) + +; perform RIGHT$() + +LAB_RIGHT + PHA ; push byte parameter + JSR LAB_236F ; pull string data and byte parameter from stack + ; return pointer in des_2l/h, byte in A (and X), Y=0 + CLC ; clear carry for add-1 + SBC (des_2l),Y ; subtract string length + EOR #$FF ; invert it (A=LEN(expression$)-l) + +LAB_2316 + BCC LAB_231C ; branch if string length > byte parameter + + LDA (des_2l),Y ; else make parameter = length + TAX ; copy to byte parameter copy + TYA ; clear string start offset +LAB_231C + PHA ; save string start offset +LAB_231D + TXA ; copy byte parameter (or string length if <) +LAB_231E + PHA ; save string length + JSR LAB_MSSP ; make string space A bytes long A=$AC=length, + ; X=$AD=Sutill=ptr low byte, Y=$AE=Sutilh=ptr high byte + LDA des_2l ; get descriptor pointer low byte + LDY des_2h ; get descriptor pointer high byte + JSR LAB_22BA ; pop (YA) descriptor off stack or from top of string space + ; returns with A = length, X=ut1_pl=pointer low byte, + ; Y=ut1_ph=pointer high byte + PLA ; get string length back + TAY ; copy length to Y + PLA ; get string start offset back + CLC ; clear carry for add + ADC ut1_pl ; add start offset to string start pointer low byte + STA ut1_pl ; save string start pointer low byte + BCC LAB_2335 ; branch if no overflow + + INC ut1_ph ; else increment string start pointer high byte +LAB_2335 + TYA ; copy length to A + JSR LAB_229C ; store string A bytes long from (ut1_pl) to (Sutill) + JMP LAB_RTST ; check for space on descriptor stack then put string + ; address and length on descriptor stack and update stack + ; pointers + +; perform MID$() + +LAB_MIDS + PHA ; push byte parameter + LDA #$FF ; set default length = 255 + STA mids_l ; save default length + JSR LAB_GBYT ; scan memory + CMP #')' ; compare with ")" + BEQ LAB_2358 ; branch if = ")" (skip second byte get) + + JSR LAB_1C01 ; scan for "," , else do syntax error then warm start + JSR LAB_GTBY ; get byte parameter (use copy in mids_l) +LAB_2358 + JSR LAB_236F ; pull string data and byte parameter from stack + ; return pointer in des_2l/h, byte in A (and X), Y=0 + DEX ; decrement start index + TXA ; copy to A + PHA ; save string start offset + CLC ; clear carry for sub-1 + LDX #$00 ; clear output string length + SBC (des_2l),Y ; subtract string length + BCS LAB_231D ; if start>string length go do null string + + EOR #$FF ; complement -length + CMP mids_l ; compare byte parameter + BCC LAB_231E ; if length>remaining string go do RIGHT$ + + LDA mids_l ; get length byte + BCS LAB_231E ; go do string copy (branch always) + +; pull string data and byte parameter from stack +; return pointer in des_2l/h, byte in A (and X), Y=0 + +LAB_236F + JSR LAB_1BFB ; scan for ")" , else do syntax error then warm start + PLA ; pull return address low byte (return address) + STA Fnxjpl ; save functions jump vector low byte + PLA ; pull return address high byte (return address) + STA Fnxjph ; save functions jump vector high byte + PLA ; pull byte parameter + TAX ; copy byte parameter to X + PLA ; pull string pointer low byte + STA des_2l ; save it + PLA ; pull string pointer high byte + STA des_2h ; save it + LDY #$00 ; clear index + TXA ; copy byte parameter + BEQ LAB_23A8 ; if null do function call error then warm start + + INC Fnxjpl ; increment function jump vector low byte + ; (JSR pushes return addr-1. this is all very nice + ; but will go tits up if either call is on a page + ; boundary!) + JMP (Fnxjpl) ; in effect, RTS + +; perform LCASE$() + +LAB_LCASE + JSR LAB_EVST ; evaluate string + STA str_ln ; set string length + TAY ; copy length to Y + BEQ NoString ; branch if null string + + JSR LAB_MSSP ; make string space A bytes long A=length, + ; X=Sutill=ptr low byte, Y=Sutilh=ptr high byte + STX str_pl ; save string pointer low byte + STY str_ph ; save string pointer high byte + TAY ; get string length back + +LC_loop + DEY ; decrement index + LDA (ut1_pl),Y ; get byte from string + JSR LAB_1D82 ; is character "A" to "Z" + BCC NoUcase ; branch if not upper case alpha + + ORA #$20 ; convert upper to lower case +NoUcase + STA (Sutill),Y ; save byte back to string + TYA ; test index + BNE LC_loop ; loop if not all done + + BEQ NoString ; tidy up and exit, branch always + +; perform UCASE$() + +LAB_UCASE + JSR LAB_EVST ; evaluate string + STA str_ln ; set string length + TAY ; copy length to Y + BEQ NoString ; branch if null string + + JSR LAB_MSSP ; make string space A bytes long A=length, + ; X=Sutill=ptr low byte, Y=Sutilh=ptr high byte + STX str_pl ; save string pointer low byte + STY str_ph ; save string pointer high byte + TAY ; get string length back + +UC_loop + DEY ; decrement index + LDA (ut1_pl),Y ; get byte from string + JSR LAB_CASC ; is character "a" to "z" (or "A" to "Z") + BCC NoLcase ; branch if not alpha + + AND #$DF ; convert lower to upper case +NoLcase + STA (Sutill),Y ; save byte back to string + TYA ; test index + BNE UC_loop ; loop if not all done + +NoString + JMP LAB_RTST ; check for space on descriptor stack then put string + ; address and length on descriptor stack and update stack + ; pointers + +; perform SADD() + +LAB_SADD + JSR LAB_IGBY ; increment and scan memory + JSR LAB_GVAR ; get var address + + JSR LAB_1BFB ; scan for ")", else do syntax error then warm start + JSR LAB_CTST ; check if source is string, else do type mismatch + + LDY #$02 ; index to string pointer high byte + LDA (Cvaral),Y ; get string pointer high byte + TAX ; copy string pointer high byte to X + DEY ; index to string pointer low byte + LDA (Cvaral),Y ; get string pointer low byte + TAY ; copy string pointer low byte to Y + TXA ; copy string pointer high byte to A + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform LEN() + +LAB_LENS + JSR LAB_ESGL ; evaluate string, get length in A (and Y) + JMP LAB_1FD0 ; convert Y to byte in FAC1 and return + +; evaluate string, get length in Y + +LAB_ESGL + JSR LAB_EVST ; evaluate string + TAY ; copy length to Y + RTS + +; perform ASC() + +LAB_ASC + JSR LAB_ESGL ; evaluate string, get length in A (and Y) + BEQ LAB_23A8 ; if null do function call error then warm start + + LDY #$00 ; set index to first character + LDA (ut1_pl),Y ; get byte + TAY ; copy to Y + JMP LAB_1FD0 ; convert Y to byte in FAC1 and return + +; do function call error then warm start + +LAB_23A8 + JMP LAB_FCER ; do function call error then warm start + +; scan and get byte parameter + +LAB_SGBY + JSR LAB_IGBY ; increment and scan memory + +; get byte parameter + +LAB_GTBY + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + +; evaluate byte expression, result in X + +LAB_EVBY + JSR LAB_EVPI ; evaluate integer expression (no check) + + LDY FAC1_2 ; get FAC1 mantissa2 + BNE LAB_23A8 ; if top byte <> 0 do function call error then warm start + + LDX FAC1_3 ; get FAC1 mantissa3 + JMP LAB_GBYT ; scan memory and return + +; perform VAL() + +LAB_VAL + JSR LAB_ESGL ; evaluate string, get length in A (and Y) + BNE LAB_23C5 ; branch if not null string + + ; string was null so set result = $00 + JMP LAB_24F1 ; clear FAC1 exponent and sign and return + +LAB_23C5 + LDX Bpntrl ; get BASIC execute pointer low byte + LDY Bpntrh ; get BASIC execute pointer high byte + STX Btmpl ; save BASIC execute pointer low byte + STY Btmph ; save BASIC execute pointer high byte + LDX ut1_pl ; get string pointer low byte + STX Bpntrl ; save as BASIC execute pointer low byte + CLC ; clear carry + ADC ut1_pl ; add string length + STA ut2_pl ; save string end low byte + LDA ut1_ph ; get string pointer high byte + STA Bpntrh ; save as BASIC execute pointer high byte + ADC #$00 ; add carry to high byte + STA ut2_ph ; save string end high byte + LDY #$00 ; set index to $00 + LDA (ut2_pl),Y ; get string end +1 byte + PHA ; push it + TYA ; clear A + STA (ut2_pl),Y ; terminate string with $00 + JSR LAB_GBYT ; scan memory + JSR LAB_2887 ; get FAC1 from string + PLA ; restore string end +1 byte + LDY #$00 ; set index to zero + STA (ut2_pl),Y ; put string end byte back + +; restore BASIC execute pointer from temp (Btmpl/Btmph) + +LAB_23F3 + LDX Btmpl ; get BASIC execute pointer low byte back + LDY Btmph ; get BASIC execute pointer high byte back + STX Bpntrl ; save BASIC execute pointer low byte + STY Bpntrh ; save BASIC execute pointer high byte + RTS + +; get two parameters for POKE or WAIT + +LAB_GADB + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + JSR LAB_F2FX ; save integer part of FAC1 in temporary integer + +; scan for "," and get byte, else do Syntax error then warm start + +LAB_SCGB + JSR LAB_1C01 ; scan for "," , else do syntax error then warm start + LDA Itemph ; save temporary integer high byte + PHA ; on stack + LDA Itempl ; save temporary integer low byte + PHA ; on stack + JSR LAB_GTBY ; get byte parameter + PLA ; pull low byte + STA Itempl ; restore temporary integer low byte + PLA ; pull high byte + STA Itemph ; restore temporary integer high byte + RTS + +; convert float to fixed routine. accepts any value that fits in 24 bits, +ve or +; -ve and converts it into a right truncated integer in Itempl and Itemph + +; save unsigned 16 bit integer part of FAC1 in temporary integer + +LAB_F2FX + LDA FAC1_e ; get FAC1 exponent + CMP #$98 ; compare with exponent = 2^24 + BCS LAB_23A8 ; if >= do function call error then warm start + +LAB_F2FU + JSR LAB_2831 ; convert FAC1 floating-to-fixed + LDA FAC1_2 ; get FAC1 mantissa2 + LDY FAC1_3 ; get FAC1 mantissa3 + STY Itempl ; save temporary integer low byte + STA Itemph ; save temporary integer high byte + RTS + +; perform PEEK() + +LAB_PEEK + JSR LAB_F2FX ; save integer part of FAC1 in temporary integer + LDX #$00 ; clear index + LDA (Itempl,X) ; get byte via temporary integer (addr) + TAY ; copy byte to Y + JMP LAB_1FD0 ; convert Y to byte in FAC1 and return + +; perform POKE + +LAB_POKE + JSR LAB_GADB ; get two parameters for POKE or WAIT + TXA ; copy byte argument to A + LDX #$00 ; clear index + STA (Itempl,X) ; save byte via temporary integer (addr) + RTS + +; perform DEEK() + +LAB_DEEK + JSR LAB_F2FX ; save integer part of FAC1 in temporary integer + LDX #$00 ; clear index + LDA (Itempl,X) ; PEEK low byte + TAY ; copy to Y + INC Itempl ; increment pointer low byte + BNE Deekh ; skip high increment if no rollover + + INC Itemph ; increment pointer high byte +Deekh + LDA (Itempl,X) ; PEEK high byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform DOKE + +LAB_DOKE + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + JSR LAB_F2FX ; convert floating-to-fixed + + STY Frnxtl ; save pointer low byte (float to fixed returns word in AY) + STA Frnxth ; save pointer high byte + + JSR LAB_1C01 ; scan for "," , else do syntax error then warm start + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + JSR LAB_F2FX ; convert floating-to-fixed + + TYA ; copy value low byte (float to fixed returns word in AY) + LDX #$00 ; clear index + STA (Frnxtl,X) ; POKE low byte + INC Frnxtl ; increment pointer low byte + BNE Dokeh ; skip high increment if no rollover + + INC Frnxth ; increment pointer high byte +Dokeh + LDA Itemph ; get value high byte + STA (Frnxtl,X) ; POKE high byte + JMP LAB_GBYT ; scan memory and return + +; perform SWAP + +LAB_SWAP + JSR LAB_GVAR ; get var1 address + STA Lvarpl ; save var1 address low byte + STY Lvarph ; save var1 address high byte + LDA Dtypef ; get data type flag, $FF=string, $00=numeric + PHA ; save data type flag + + JSR LAB_1C01 ; scan for "," , else do syntax error then warm start + JSR LAB_GVAR ; get var2 address (pointer in Cvaral/h) + PLA ; pull var1 data type flag + EOR Dtypef ; compare with var2 data type + BPL SwapErr ; exit if not both the same type + + LDY #$03 ; four bytes to swap (either value or descriptor+1) +SwapLp + LDA (Lvarpl),Y ; get byte from var1 + TAX ; save var1 byte + LDA (Cvaral),Y ; get byte from var2 + STA (Lvarpl),Y ; save byte to var1 + TXA ; restore var1 byte + STA (Cvaral),Y ; save byte to var2 + DEY ; decrement index + BPL SwapLp ; loop until done + + RTS + +SwapErr + JMP LAB_1ABC ; do "Type mismatch" error then warm start + +; perform CALL + +LAB_CALL + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + JSR LAB_F2FX ; convert floating-to-fixed + LDA #>CallExit ; set return address high byte + PHA ; put on stack + LDA #8 shifts) + BCC LAB_24A8 ;.go subtract mantissas + +; add 0.5 to FAC1 + +LAB_244E + LDA #LAB_2A96 ; set 0.5 pointer high byte + +; add (AY) to FAC1 + +LAB_246C + JSR LAB_264D ; unpack memory (AY) into FAC2 + +; add FAC2 to FAC1 + +LAB_ADD + BNE LAB_2474 ; branch if FAC1 was not zero + +; copy FAC2 to FAC1 + +LAB_279B + LDA FAC2_s ; get FAC2 sign (b7) + +; save FAC1 sign and copy ABS(FAC2) to FAC1 + +LAB_279D + STA FAC1_s ; save FAC1 sign (b7) + LDX #$04 ; 4 bytes to copy +LAB_27A1 + LDA FAC1_o,X ; get byte from FAC2,X + STA FAC1_e-1,X ; save byte at FAC1,X + DEX ; decrement count + BNE LAB_27A1 ; loop if not all done + + STX FAC1_r ; clear FAC1 rounding byte + RTS + + ; FAC1 is non zero +LAB_2474 + LDX FAC1_r ; get FAC1 rounding byte + STX FAC2_r ; save as FAC2 rounding byte + LDX #FAC2_e ; set index to FAC2 exponent addr + LDA FAC2_e ; get FAC2 exponent +LAB_247C + TAY ; copy exponent + BEQ LAB_244D ; exit if zero + + SEC ; set carry for subtract + SBC FAC1_e ; subtract FAC1 exponent + BEQ LAB_24A8 ; branch if = (go add mantissa) + + BCC LAB_2498 ; branch if < + + ; FAC2>FAC1 + STY FAC1_e ; save FAC1 exponent + LDY FAC2_s ; get FAC2 sign (b7) + STY FAC1_s ; save FAC1 sign (b7) + EOR #$FF ; complement A + ADC #$00 ; +1 (twos complement, carry is set) + LDY #$00 ; clear Y + STY FAC2_r ; clear FAC2 rounding byte + LDX #FAC1_e ; set index to FAC1 exponent addr + BNE LAB_249C ; branch always + +LAB_2498 + LDY #$00 ; clear Y + STY FAC1_r ; clear FAC1 rounding byte +LAB_249C + CMP #$F9 ; compare exponent diff with $F9 + BMI LAB_2467 ; branch if range $79-$F8 + + TAY ; copy exponent difference to Y + LDA FAC1_r ; get FAC1 rounding byte + LSR PLUS_1,X ; shift FAC? mantissa1 + JSR LAB_2592 ; shift FACX Y times right + + ; exponents are equal now do mantissa subtract +LAB_24A8 + BIT FAC_sc ; test sign compare (FAC1 EOR FAC2) + BPL LAB_24F8 ; if = add FAC2 mantissa to FAC1 mantissa and return + + LDY #FAC1_e ; set index to FAC1 exponent addr + CPX #FAC2_e ; compare X to FAC2 exponent addr + BEQ LAB_24B4 ; branch if = + + LDY #FAC2_e ; else set index to FAC2 exponent addr + + ; subtract smaller from bigger (take sign of bigger) +LAB_24B4 + SEC ; set carry for subtract + EOR #$FF ; ones complement A + ADC FAC2_r ; add FAC2 rounding byte + STA FAC1_r ; save FAC1 rounding byte + LDA PLUS_3,Y ; get FACY mantissa3 + SBC PLUS_3,X ; subtract FACX mantissa3 + STA FAC1_3 ; save FAC1 mantissa3 + LDA PLUS_2,Y ; get FACY mantissa2 + SBC PLUS_2,X ; subtract FACX mantissa2 + STA FAC1_2 ; save FAC1 mantissa2 + LDA PLUS_1,Y ; get FACY mantissa1 + SBC PLUS_1,X ; subtract FACX mantissa1 + STA FAC1_1 ; save FAC1 mantissa1 + +; do ABS and normalise FAC1 + +LAB_24D0 + BCS LAB_24D5 ; branch if number is +ve + + JSR LAB_2537 ; negate FAC1 + +; normalise FAC1 + +LAB_24D5 + LDY #$00 ; clear Y + TYA ; clear A + CLC ; clear carry for add +LAB_24D9 + LDX FAC1_1 ; get FAC1 mantissa1 + BNE LAB_251B ; if not zero normalise FAC1 + + LDX FAC1_2 ; get FAC1 mantissa2 + STX FAC1_1 ; save FAC1 mantissa1 + LDX FAC1_3 ; get FAC1 mantissa3 + STX FAC1_2 ; save FAC1 mantissa2 + LDX FAC1_r ; get FAC1 rounding byte + STX FAC1_3 ; save FAC1 mantissa3 + STY FAC1_r ; clear FAC1 rounding byte + ADC #$08 ; add x to exponent offset + CMP #$18 ; compare with $18 (max offset, all bits would be =0) + BNE LAB_24D9 ; loop if not max + +; clear FAC1 exponent and sign + +LAB_24F1 + LDA #$00 ; clear A +LAB_24F3 + STA FAC1_e ; set FAC1 exponent + +; save FAC1 sign + +LAB_24F5 + STA FAC1_s ; save FAC1 sign (b7) + RTS + +; add FAC2 mantissa to FAC1 mantissa + +LAB_24F8 + ADC FAC2_r ; add FAC2 rounding byte + STA FAC1_r ; save FAC1 rounding byte + LDA FAC1_3 ; get FAC1 mantissa3 + ADC FAC2_3 ; add FAC2 mantissa3 + STA FAC1_3 ; save FAC1 mantissa3 + LDA FAC1_2 ; get FAC1 mantissa2 + ADC FAC2_2 ; add FAC2 mantissa2 + STA FAC1_2 ; save FAC1 mantissa2 + LDA FAC1_1 ; get FAC1 mantissa1 + ADC FAC2_1 ; add FAC2 mantissa1 + STA FAC1_1 ; save FAC1 mantissa1 + BCS LAB_252A ; if carry then normalise FAC1 for C=1 + + RTS ; else just exit + +LAB_2511 + ADC #$01 ; add 1 to exponent offset + ASL FAC1_r ; shift FAC1 rounding byte + ROL FAC1_3 ; shift FAC1 mantissa3 + ROL FAC1_2 ; shift FAC1 mantissa2 + ROL FAC1_1 ; shift FAC1 mantissa1 + +; normalise FAC1 + +LAB_251B + BPL LAB_2511 ; loop if not normalised + + SEC ; set carry for subtract + SBC FAC1_e ; subtract FAC1 exponent + BCS LAB_24F1 ; branch if underflow (set result = $0) + + EOR #$FF ; complement exponent + ADC #$01 ; +1 (twos complement) + STA FAC1_e ; save FAC1 exponent + +; test and normalise FAC1 for C=0/1 + +LAB_2528 + BCC LAB_2536 ; exit if no overflow + +; normalise FAC1 for C=1 + +LAB_252A + INC FAC1_e ; increment FAC1 exponent + BEQ LAB_2564 ; if zero do overflow error and warm start + + ROR FAC1_1 ; shift FAC1 mantissa1 + ROR FAC1_2 ; shift FAC1 mantissa2 + ROR FAC1_3 ; shift FAC1 mantissa3 + ROR FAC1_r ; shift FAC1 rounding byte +LAB_2536 + RTS + +; negate FAC1 + +LAB_2537 + LDA FAC1_s ; get FAC1 sign (b7) + EOR #$FF ; complement it + STA FAC1_s ; save FAC1 sign (b7) + +; twos complement FAC1 mantissa + +LAB_253D + LDA FAC1_1 ; get FAC1 mantissa1 + EOR #$FF ; complement it + STA FAC1_1 ; save FAC1 mantissa1 + LDA FAC1_2 ; get FAC1 mantissa2 + EOR #$FF ; complement it + STA FAC1_2 ; save FAC1 mantissa2 + LDA FAC1_3 ; get FAC1 mantissa3 + EOR #$FF ; complement it + STA FAC1_3 ; save FAC1 mantissa3 + LDA FAC1_r ; get FAC1 rounding byte + EOR #$FF ; complement it + STA FAC1_r ; save FAC1 rounding byte + INC FAC1_r ; increment FAC1 rounding byte + BNE LAB_2563 ; exit if no overflow + +; increment FAC1 mantissa + +LAB_2559 + INC FAC1_3 ; increment FAC1 mantissa3 + BNE LAB_2563 ; finished if no rollover + + INC FAC1_2 ; increment FAC1 mantissa2 + BNE LAB_2563 ; finished if no rollover + + INC FAC1_1 ; increment FAC1 mantissa1 +LAB_2563 + RTS + +; do overflow error (overflow exit) + +LAB_2564 + LDX #$0A ; error code $0A ("Overflow" error) + JMP LAB_XERR ; do error #X, then warm start + +; shift FCAtemp << A+8 times + +LAB_2569 + LDX #FACt_1-1 ; set offset to FACtemp +LAB_256B + LDY PLUS_3,X ; get FACX mantissa3 + STY FAC1_r ; save as FAC1 rounding byte + LDY PLUS_2,X ; get FACX mantissa2 + STY PLUS_3,X ; save FACX mantissa3 + LDY PLUS_1,X ; get FACX mantissa1 + STY PLUS_2,X ; save FACX mantissa2 + LDY FAC1_o ; get FAC1 overflow byte + STY PLUS_1,X ; save FACX mantissa1 + +; shift FACX -A times right (> 8 shifts) + +LAB_257B + ADC #$08 ; add 8 to shift count + BMI LAB_256B ; go do 8 shift if still -ve + + BEQ LAB_256B ; go do 8 shift if zero + + SBC #$08 ; else subtract 8 again + TAY ; save count to Y + LDA FAC1_r ; get FAC1 rounding byte + BCS LAB_259A ;. + +LAB_2588 + ASL PLUS_1,X ; shift FACX mantissa1 + BCC LAB_258E ; branch if +ve + + INC PLUS_1,X ; this sets b7 eventually +LAB_258E + ROR PLUS_1,X ; shift FACX mantissa1 (correct for ASL) + ROR PLUS_1,X ; shift FACX mantissa1 (put carry in b7) + +; shift FACX Y times right + +LAB_2592 + ROR PLUS_2,X ; shift FACX mantissa2 + ROR PLUS_3,X ; shift FACX mantissa3 + ROR ; shift FACX rounding byte + INY ; increment exponent diff + BNE LAB_2588 ; branch if range adjust not complete + +LAB_259A + CLC ; just clear it + RTS + +; perform LOG() + +LAB_LOG + JSR LAB_27CA ; test sign and zero + BEQ LAB_25C4 ; if zero do function call error then warm start + + BPL LAB_25C7 ; skip error if +ve + +LAB_25C4 + JMP LAB_FCER ; do function call error then warm start (-ve) + +LAB_25C7 + LDA FAC1_e ; get FAC1 exponent + SBC #$7F ; normalise it + PHA ; save it + LDA #$80 ; set exponent to zero + STA FAC1_e ; save FAC1 exponent + LDA #LAB_25AD ; set 1/root2 pointer high byte + JSR LAB_246C ; add (AY) to FAC1 (1/root2) + LDA #LAB_25B1 ; set root2 pointer high byte + JSR LAB_26CA ; convert AY and do (AY)/FAC1 (root2/(x+(1/root2))) + LDA #LAB_259C ; set 1 pointer high byte + JSR LAB_2455 ; subtract (AY) from FAC1 ((root2/(x+(1/root2)))-1) + LDA #LAB_25A0 ; set pointer high byte to counter + JSR LAB_2B6E ; ^2 then series evaluation + LDA #LAB_25B5 ; set -0.5 pointer high byte + JSR LAB_246C ; add (AY) to FAC1 + PLA ; restore FAC1 exponent + JSR LAB_2912 ; evaluate new ASCII digit + LDA #LAB_25B9 ; set LOG(2) pointer high byte + +; do convert AY, FCA1*(AY) + +LAB_25FB + JSR LAB_264D ; unpack memory (AY) into FAC2 +LAB_MULTIPLY + BEQ LAB_264C ; exit if zero + + JSR LAB_2673 ; test and adjust accumulators + LDA #$00 ; clear A + STA FACt_1 ; clear temp mantissa1 + STA FACt_2 ; clear temp mantissa2 + STA FACt_3 ; clear temp mantissa3 + LDA FAC1_r ; get FAC1 rounding byte + JSR LAB_2622 ; go do shift/add FAC2 + LDA FAC1_3 ; get FAC1 mantissa3 + JSR LAB_2622 ; go do shift/add FAC2 + LDA FAC1_2 ; get FAC1 mantissa2 + JSR LAB_2622 ; go do shift/add FAC2 + LDA FAC1_1 ; get FAC1 mantissa1 + JSR LAB_2627 ; go do shift/add FAC2 + JMP LAB_273C ; copy temp to FAC1, normalise and return + +LAB_2622 + BNE LAB_2627 ; branch if byte <> zero + + JMP LAB_2569 ; shift FCAtemp << A+8 times + + ; else do shift and add +LAB_2627 + LSR ; shift byte + ORA #$80 ; set top bit (mark for 8 times) +LAB_262A + TAY ; copy result + BCC LAB_2640 ; skip next if bit was zero + + CLC ; clear carry for add + LDA FACt_3 ; get temp mantissa3 + ADC FAC2_3 ; add FAC2 mantissa3 + STA FACt_3 ; save temp mantissa3 + LDA FACt_2 ; get temp mantissa2 + ADC FAC2_2 ; add FAC2 mantissa2 + STA FACt_2 ; save temp mantissa2 + LDA FACt_1 ; get temp mantissa1 + ADC FAC2_1 ; add FAC2 mantissa1 + STA FACt_1 ; save temp mantissa1 +LAB_2640 + ROR FACt_1 ; shift temp mantissa1 + ROR FACt_2 ; shift temp mantissa2 + ROR FACt_3 ; shift temp mantissa3 + ROR FAC1_r ; shift temp rounding byte + TYA ; get byte back + LSR ; shift byte + BNE LAB_262A ; loop if all bits not done + +LAB_264C + RTS + +; unpack memory (AY) into FAC2 + +LAB_264D + STA ut1_pl ; save pointer low byte + STY ut1_ph ; save pointer high byte + LDY #$03 ; 4 bytes to get (0-3) + LDA (ut1_pl),Y ; get mantissa3 + STA FAC2_3 ; save FAC2 mantissa3 + DEY ; decrement index + LDA (ut1_pl),Y ; get mantissa2 + STA FAC2_2 ; save FAC2 mantissa2 + DEY ; decrement index + LDA (ut1_pl),Y ; get mantissa1+sign + STA FAC2_s ; save FAC2 sign (b7) + EOR FAC1_s ; EOR with FAC1 sign (b7) + STA FAC_sc ; save sign compare (FAC1 EOR FAC2) + LDA FAC2_s ; recover FAC2 sign (b7) + ORA #$80 ; set 1xxx xxx (set normal bit) + STA FAC2_1 ; save FAC2 mantissa1 + DEY ; decrement index + LDA (ut1_pl),Y ; get exponent byte + STA FAC2_e ; save FAC2 exponent + LDA FAC1_e ; get FAC1 exponent + RTS + +; test and adjust accumulators + +LAB_2673 + LDA FAC2_e ; get FAC2 exponent +LAB_2675 + BEQ LAB_2696 ; branch if FAC2 = $00 (handle underflow) + + CLC ; clear carry for add + ADC FAC1_e ; add FAC1 exponent + BCC LAB_2680 ; branch if sum of exponents <$0100 + + BMI LAB_269B ; do overflow error + + CLC ; clear carry for the add + .byte $2C ; makes next line BIT $1410 +LAB_2680 + BPL LAB_2696 ; if +ve go handle underflow + + ADC #$80 ; adjust exponent + STA FAC1_e ; save FAC1 exponent + BNE LAB_268B ; branch if not zero + + JMP LAB_24F5 ; save FAC1 sign and return + +LAB_268B + LDA FAC_sc ; get sign compare (FAC1 EOR FAC2) + STA FAC1_s ; save FAC1 sign (b7) +LAB_268F + RTS + +; handle overflow and underflow + +LAB_2690 + LDA FAC1_s ; get FAC1 sign (b7) + BPL LAB_269B ; do overflow error + + ; handle underflow +LAB_2696 + PLA ; pop return address low byte + PLA ; pop return address high byte + JMP LAB_24F1 ; clear FAC1 exponent and sign and return + +; multiply by 10 + +LAB_269E + JSR LAB_27AB ; round and copy FAC1 to FAC2 + TAX ; copy exponent (set the flags) + BEQ LAB_268F ; exit if zero + + CLC ; clear carry for add + ADC #$02 ; add two to exponent (*4) + BCS LAB_269B ; do overflow error if > $FF + + LDX #$00 ; clear byte + STX FAC_sc ; clear sign compare (FAC1 EOR FAC2) + JSR LAB_247C ; add FAC2 to FAC1 (*5) + INC FAC1_e ; increment FAC1 exponent (*10) + BNE LAB_268F ; if non zero just do RTS + +LAB_269B + JMP LAB_2564 ; do overflow error and warm start + +; divide by 10 + +LAB_26B9 + JSR LAB_27AB ; round and copy FAC1 to FAC2 + LDA #LAB_26B5 ; set pointer to 10d high addr + LDX #$00 ; clear sign + +; divide by (AY) (X=sign) + +LAB_26C2 + STX FAC_sc ; save sign compare (FAC1 EOR FAC2) + JSR LAB_UFAC ; unpack memory (AY) into FAC1 + JMP LAB_DIVIDE ; do FAC2/FAC1 + + ; Perform divide-by +; convert AY and do (AY)/FAC1 + +LAB_26CA + JSR LAB_264D ; unpack memory (AY) into FAC2 + + ; Perform divide-into +LAB_DIVIDE + BEQ LAB_2737 ; if zero go do /0 error + + JSR LAB_27BA ; round FAC1 + LDA #$00 ; clear A + SEC ; set carry for subtract + SBC FAC1_e ; subtract FAC1 exponent (2s complement) + STA FAC1_e ; save FAC1 exponent + JSR LAB_2673 ; test and adjust accumulators + INC FAC1_e ; increment FAC1 exponent + BEQ LAB_269B ; if zero do overflow error + + LDX #$FF ; set index for pre increment + LDA #$01 ; set bit to flag byte save +LAB_26E4 + LDY FAC2_1 ; get FAC2 mantissa1 + CPY FAC1_1 ; compare FAC1 mantissa1 + BNE LAB_26F4 ; branch if <> + + LDY FAC2_2 ; get FAC2 mantissa2 + CPY FAC1_2 ; compare FAC1 mantissa2 + BNE LAB_26F4 ; branch if <> + + LDY FAC2_3 ; get FAC2 mantissa3 + CPY FAC1_3 ; compare FAC1 mantissa3 +LAB_26F4 + PHP ; save FAC2-FAC1 compare status + ROL ; shift the result byte + BCC LAB_2702 ; if no carry skip the byte save + + LDY #$01 ; set bit to flag byte save + INX ; else increment the index to FACt + CPX #$02 ; compare with the index to FACt_3 + BMI LAB_2701 ; if not last byte just go save it + + BNE LAB_272B ; if all done go save FAC1 rounding byte, normalise and + ; return + + LDY #$40 ; set bit to flag byte save for the rounding byte +LAB_2701 + STA FACt_1,X ; write result byte to FACt_1 + index + TYA ; copy the next save byte flag +LAB_2702 + PLP ; restore FAC2-FAC1 compare status + BCC LAB_2704 ; if FAC2 < FAC1 then skip the subtract + + TAY ; save FAC2-FAC1 compare status + LDA FAC2_3 ; get FAC2 mantissa3 + SBC FAC1_3 ; subtract FAC1 mantissa3 + STA FAC2_3 ; save FAC2 mantissa3 + LDA FAC2_2 ; get FAC2 mantissa2 + SBC FAC1_2 ; subtract FAC1 mantissa2 + STA FAC2_2 ; save FAC2 mantissa2 + LDA FAC2_1 ; get FAC2 mantissa1 + SBC FAC1_1 ; subtract FAC1 mantissa1 + STA FAC2_1 ; save FAC2 mantissa1 + TYA ; restore FAC2-FAC1 compare status + + ; FAC2 = FAC2*2 +LAB_2704 + ASL FAC2_3 ; shift FAC2 mantissa3 + ROL FAC2_2 ; shift FAC2 mantissa2 + ROL FAC2_1 ; shift FAC2 mantissa1 + BCS LAB_26F4 ; loop with no compare + + BMI LAB_26E4 ; loop with compare + + BPL LAB_26F4 ; loop always with no compare + +; do A<<6, save as FAC1 rounding byte, normalise and return + +LAB_272B + LSR ; shift b1 - b0 .. + ROR ; .. + ROR ; .. to b7 - b6 + STA FAC1_r ; save FAC1 rounding byte + PLP ; dump FAC2-FAC1 compare status + JMP LAB_273C ; copy temp to FAC1, normalise and return + +; do "Divide by zero" error + +LAB_2737 + LDX #$14 ; error code $14 ("Divide by zero" error) + JMP LAB_XERR ; do error #X, then warm start + +; copy temp to FAC1 and normalise + +LAB_273C + LDA FACt_1 ; get temp mantissa1 + STA FAC1_1 ; save FAC1 mantissa1 + LDA FACt_2 ; get temp mantissa2 + STA FAC1_2 ; save FAC1 mantissa2 + LDA FACt_3 ; get temp mantissa3 + STA FAC1_3 ; save FAC1 mantissa3 + JMP LAB_24D5 ; normalise FAC1 and return + +; unpack memory (AY) into FAC1 + +LAB_UFAC + STA ut1_pl ; save pointer low byte + STY ut1_ph ; save pointer high byte + LDY #$03 ; 4 bytes to do + LDA (ut1_pl),Y ; get last byte + STA FAC1_3 ; save FAC1 mantissa3 + DEY ; decrement index + LDA (ut1_pl),Y ; get last-1 byte + STA FAC1_2 ; save FAC1 mantissa2 + DEY ; decrement index + LDA (ut1_pl),Y ; get second byte + STA FAC1_s ; save FAC1 sign (b7) + ORA #$80 ; set 1xxx xxxx (add normal bit) + STA FAC1_1 ; save FAC1 mantissa1 + DEY ; decrement index + LDA (ut1_pl),Y ; get first byte (exponent) + STA FAC1_e ; save FAC1 exponent + STY FAC1_r ; clear FAC1 rounding byte + RTS + +; pack FAC1 into Adatal + +LAB_276E + LDX #Adatal ; set pointer high byte + BEQ LAB_2778 ; pack FAC1 into (XY) and return + +; pack FAC1 into (Lvarpl) + +LAB_PFAC + LDX Lvarpl ; get destination pointer low byte + LDY Lvarph ; get destination pointer high byte + +; pack FAC1 into (XY) + +LAB_2778 + JSR LAB_27BA ; round FAC1 + STX ut1_pl ; save pointer low byte + STY ut1_ph ; save pointer high byte + LDY #$03 ; set index + LDA FAC1_3 ; get FAC1 mantissa3 + STA (ut1_pl),Y ; store in destination + DEY ; decrement index + LDA FAC1_2 ; get FAC1 mantissa2 + STA (ut1_pl),Y ; store in destination + DEY ; decrement index + LDA FAC1_s ; get FAC1 sign (b7) + ORA #$7F ; set bits x111 1111 + AND FAC1_1 ; AND in FAC1 mantissa1 + STA (ut1_pl),Y ; store in destination + DEY ; decrement index + LDA FAC1_e ; get FAC1 exponent + STA (ut1_pl),Y ; store in destination + STY FAC1_r ; clear FAC1 rounding byte + RTS + +; round and copy FAC1 to FAC2 + +LAB_27AB + JSR LAB_27BA ; round FAC1 + +; copy FAC1 to FAC2 + +LAB_27AE + LDX #$05 ; 5 bytes to copy +LAB_27B0 + LDA FAC1_e-1,X ; get byte from FAC1,X + STA FAC1_o,X ; save byte at FAC2,X + DEX ; decrement count + BNE LAB_27B0 ; loop if not all done + + STX FAC1_r ; clear FAC1 rounding byte +LAB_27B9 + RTS + +; round FAC1 + +LAB_27BA + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_27B9 ; exit if zero + + ASL FAC1_r ; shift FAC1 rounding byte + BCC LAB_27B9 ; exit if no overflow + +; round FAC1 (no check) + +LAB_27C2 + JSR LAB_2559 ; increment FAC1 mantissa + BNE LAB_27B9 ; branch if no overflow + + JMP LAB_252A ; normalise FAC1 for C=1 and return + +; get FAC1 sign +; return A=FF,C=1/-ve A=01,C=0/+ve + +LAB_27CA + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_27D7 ; exit if zero (already correct SGN(0)=0) + +; return A=FF,C=1/-ve A=01,C=0/+ve +; no = 0 check + +LAB_27CE + LDA FAC1_s ; else get FAC1 sign (b7) + +; return A=FF,C=1/-ve A=01,C=0/+ve +; no = 0 check, sign in A + +LAB_27D0 + ROL ; move sign bit to carry + LDA #$FF ; set byte for -ve result + BCS LAB_27D7 ; return if sign was set (-ve) + + LDA #$01 ; else set byte for +ve result +LAB_27D7 + RTS + +; perform SGN() + +LAB_SGN + JSR LAB_27CA ; get FAC1 sign + ; return A=$FF/-ve A=$01/+ve +; save A as integer byte + +LAB_27DB + STA FAC1_1 ; save FAC1 mantissa1 + LDA #$00 ; clear A + STA FAC1_2 ; clear FAC1 mantissa2 + LDX #$88 ; set exponent + +; set exp=X, clearFAC1 mantissa3 and normalise + +LAB_27E3 + LDA FAC1_1 ; get FAC1 mantissa1 + EOR #$FF ; complement it + ROL ; sign bit into carry + +; set exp=X, clearFAC1 mantissa3 and normalise + +LAB_STFA + LDA #$00 ; clear A + STA FAC1_3 ; clear FAC1 mantissa3 + STX FAC1_e ; set FAC1 exponent + STA FAC1_r ; clear FAC1 rounding byte + STA FAC1_s ; clear FAC1 sign (b7) + JMP LAB_24D0 ; do ABS and normalise FAC1 + +; perform ABS() + +LAB_ABS + LSR FAC1_s ; clear FAC1 sign (put zero in b7) + RTS + +; compare FAC1 with (AY) +; returns A=$00 if FAC1 = (AY) +; returns A=$01 if FAC1 > (AY) +; returns A=$FF if FAC1 < (AY) + +LAB_27F8 + STA ut2_pl ; save pointer low byte +LAB_27FA + STY ut2_ph ; save pointer high byte + LDY #$00 ; clear index + LDA (ut2_pl),Y ; get exponent + INY ; increment index + TAX ; copy (AY) exponent to X + BEQ LAB_27CA ; branch if (AY) exponent=0 and get FAC1 sign + ; A=FF,C=1/-ve A=01,C=0/+ve + + LDA (ut2_pl),Y ; get (AY) mantissa1 (with sign) + EOR FAC1_s ; EOR FAC1 sign (b7) + BMI LAB_27CE ; if signs <> do return A=FF,C=1/-ve + ; A=01,C=0/+ve and return + + CPX FAC1_e ; compare (AY) exponent with FAC1 exponent + BNE LAB_2828 ; branch if different + + LDA (ut2_pl),Y ; get (AY) mantissa1 (with sign) + ORA #$80 ; normalise top bit + CMP FAC1_1 ; compare with FAC1 mantissa1 + BNE LAB_2828 ; branch if different + + INY ; increment index + LDA (ut2_pl),Y ; get mantissa2 + CMP FAC1_2 ; compare with FAC1 mantissa2 + BNE LAB_2828 ; branch if different + + INY ; increment index + LDA #$7F ; set for 1/2 value rounding byte + CMP FAC1_r ; compare with FAC1 rounding byte (set carry) + LDA (ut2_pl),Y ; get mantissa3 + SBC FAC1_3 ; subtract FAC1 mantissa3 + BEQ LAB_2850 ; exit if mantissa3 equal + +; gets here if number <> FAC1 + +LAB_2828 + LDA FAC1_s ; get FAC1 sign (b7) + BCC LAB_282E ; branch if FAC1 > (AY) + + EOR #$FF ; else toggle FAC1 sign +LAB_282E + JMP LAB_27D0 ; return A=FF,C=1/-ve A=01,C=0/+ve + +; convert FAC1 floating-to-fixed + +LAB_2831 + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_287F ; if zero go clear FAC1 and return + + SEC ; set carry for subtract + SBC #$98 ; subtract maximum integer range exponent + BIT FAC1_s ; test FAC1 sign (b7) + BPL LAB_2845 ; branch if FAC1 +ve + + ; FAC1 was -ve + TAX ; copy subtracted exponent + LDA #$FF ; overflow for -ve number + STA FAC1_o ; set FAC1 overflow byte + JSR LAB_253D ; twos complement FAC1 mantissa + TXA ; restore subtracted exponent +LAB_2845 + LDX #FAC1_e ; set index to FAC1 + CMP #$F9 ; compare exponent result + BPL LAB_2851 ; if < 8 shifts shift FAC1 A times right and return + + JSR LAB_257B ; shift FAC1 A times right (> 8 shifts) + STY FAC1_o ; clear FAC1 overflow byte +LAB_2850 + RTS + +; shift FAC1 A times right + +LAB_2851 + TAY ; copy shift count + LDA FAC1_s ; get FAC1 sign (b7) + AND #$80 ; mask sign bit only (x000 0000) + LSR FAC1_1 ; shift FAC1 mantissa1 + ORA FAC1_1 ; OR sign in b7 FAC1 mantissa1 + STA FAC1_1 ; save FAC1 mantissa1 + JSR LAB_2592 ; shift FAC1 Y times right + STY FAC1_o ; clear FAC1 overflow byte + RTS + +; perform INT() + +LAB_INT + LDA FAC1_e ; get FAC1 exponent + CMP #$98 ; compare with max int + BCS LAB_2886 ; exit if >= (already int, too big for fractional part!) + + JSR LAB_2831 ; convert FAC1 floating-to-fixed + STY FAC1_r ; save FAC1 rounding byte + LDA FAC1_s ; get FAC1 sign (b7) + STY FAC1_s ; save FAC1 sign (b7) + EOR #$80 ; toggle FAC1 sign + ROL ; shift into carry + LDA #$98 ; set new exponent + STA FAC1_e ; save FAC1 exponent + LDA FAC1_3 ; get FAC1 mantissa3 + STA Temp3 ; save for EXP() function + JMP LAB_24D0 ; do ABS and normalise FAC1 + +; clear FAC1 and return + +LAB_287F + STA FAC1_1 ; clear FAC1 mantissa1 + STA FAC1_2 ; clear FAC1 mantissa2 + STA FAC1_3 ; clear FAC1 mantissa3 + TAY ; clear Y +LAB_2886 + RTS + +; get FAC1 from string +; this routine now handles hex and binary values from strings +; starting with "$" and "%" respectively + +LAB_2887 + LDY #$00 ; clear Y + STY Dtypef ; clear data type flag, $FF=string, $00=numeric + LDX #$09 ; set index +LAB_288B + STY numexp,X ; clear byte + DEX ; decrement index + BPL LAB_288B ; loop until numexp to negnum (and FAC1) = $00 + + BCC LAB_28FE ; branch if 1st character numeric + +; get FAC1 from string .. first character wasn't numeric + + CMP #'-' ; else compare with "-" + BNE LAB_289A ; branch if not "-" + + STX negnum ; set flag for -ve number (X = $FF) + BEQ LAB_289C ; branch always (go scan and check for hex/bin) + +; get FAC1 from string .. first character wasn't numeric or - + +LAB_289A + CMP #'+' ; else compare with "+" + BNE LAB_289D ; branch if not "+" (go check for hex/bin) + +; was "+" or "-" to start, so get next character + +LAB_289C + JSR LAB_IGBY ; increment and scan memory + BCC LAB_28FE ; branch if numeric character + +; code here for hex and binary numbers + +LAB_289D + CMP #'$' ; else compare with "$" + BNE LAB_NHEX ; branch if not "$" + + JMP LAB_CHEX ; branch if "$" + +LAB_NHEX + CMP #'%' ; else compare with "%" + BNE LAB_28A3 ; branch if not "%" (continue original code) + + JMP LAB_CBIN ; branch if "%" + +LAB_289E + JSR LAB_IGBY ; increment and scan memory (ignore + or get next number) +LAB_28A1 + BCC LAB_28FE ; branch if numeric character + +; get FAC1 from string .. character wasn't numeric, -, +, hex or binary + +LAB_28A3 + CMP #'.' ; else compare with "." + BEQ LAB_28D5 ; branch if "." + +; get FAC1 from string .. character wasn't numeric, -, + or . + + CMP #'E' ; else compare with "E" + BNE LAB_28DB ; branch if not "E" + + ; was "E" so evaluate exponential part + JSR LAB_IGBY ; increment and scan memory + BCC LAB_28C7 ; branch if numeric character + + CMP #TK_MINUS ; else compare with token for - + BEQ LAB_28C2 ; branch if token for - + + CMP #'-' ; else compare with "-" + BEQ LAB_28C2 ; branch if "-" + + CMP #TK_PLUS ; else compare with token for + + BEQ LAB_28C4 ; branch if token for + + + CMP #'+' ; else compare with "+" + BEQ LAB_28C4 ; branch if "+" + + BNE LAB_28C9 ; branch always + +LAB_28C2 + ROR expneg ; set exponent -ve flag (C, which=1, into b7) +LAB_28C4 + JSR LAB_IGBY ; increment and scan memory +LAB_28C7 + BCC LAB_2925 ; branch if numeric character + +LAB_28C9 + BIT expneg ; test exponent -ve flag + BPL LAB_28DB ; if +ve go evaluate exponent + + ; else do exponent = -exponent + LDA #$00 ; clear result + SEC ; set carry for subtract + SBC expcnt ; subtract exponent byte + JMP LAB_28DD ; go evaluate exponent + +LAB_28D5 + ROR numdpf ; set decimal point flag + BIT numdpf ; test decimal point flag + BVC LAB_289E ; branch if only one decimal point so far + + ; evaluate exponent +LAB_28DB + LDA expcnt ; get exponent count byte +LAB_28DD + SEC ; set carry for subtract + SBC numexp ; subtract numerator exponent + STA expcnt ; save exponent count byte + BEQ LAB_28F6 ; branch if no adjustment + + BPL LAB_28EF ; else if +ve go do FAC1*10^expcnt + + ; else go do FAC1/10^(0-expcnt) +LAB_28E6 + JSR LAB_26B9 ; divide by 10 + INC expcnt ; increment exponent count byte + BNE LAB_28E6 ; loop until all done + + BEQ LAB_28F6 ; branch always + +LAB_28EF + JSR LAB_269E ; multiply by 10 + DEC expcnt ; decrement exponent count byte + BNE LAB_28EF ; loop until all done + +LAB_28F6 + LDA negnum ; get -ve flag + BMI LAB_28FB ; if -ve do - FAC1 and return + + RTS + +; do - FAC1 and return + +LAB_28FB + JMP LAB_GTHAN ; do - FAC1 and return + +; do unsigned FAC1*10+number + +LAB_28FE + PHA ; save character + BIT numdpf ; test decimal point flag + BPL LAB_2905 ; skip exponent increment if not set + + INC numexp ; else increment number exponent +LAB_2905 + JSR LAB_269E ; multiply FAC1 by 10 + PLA ; restore character + AND #$0F ; convert to binary + JSR LAB_2912 ; evaluate new ASCII digit + JMP LAB_289E ; go do next character + +; evaluate new ASCII digit + +LAB_2912 + PHA ; save digit + JSR LAB_27AB ; round and copy FAC1 to FAC2 + PLA ; restore digit + JSR LAB_27DB ; save A as integer byte + LDA FAC2_s ; get FAC2 sign (b7) + EOR FAC1_s ; toggle with FAC1 sign (b7) + STA FAC_sc ; save sign compare (FAC1 EOR FAC2) + LDX FAC1_e ; get FAC1 exponent + JMP LAB_ADD ; add FAC2 to FAC1 and return + +; evaluate next character of exponential part of number + +LAB_2925 + LDA expcnt ; get exponent count byte + CMP #$0A ; compare with 10 decimal + BCC LAB_2934 ; branch if less + + LDA #$64 ; make all -ve exponents = -100 decimal (causes underflow) + BIT expneg ; test exponent -ve flag + BMI LAB_2942 ; branch if -ve + + JMP LAB_2564 ; else do overflow error + +LAB_2934 + ASL ; * 2 + ASL ; * 4 + ADC expcnt ; * 5 + ASL ; * 10 + LDY #$00 ; set index + ADC (Bpntrl),Y ; add character (will be $30 too much!) + SBC #'0'-1 ; convert character to binary +LAB_2942 + STA expcnt ; save exponent count byte + JMP LAB_28C4 ; go get next character + +; print " in line [LINE #]" + +LAB_2953 + LDA #LAB_LMSG ; point to " in line " message high byte + JSR LAB_18C3 ; print null terminated string from memory + + ; print Basic line # + LDA Clineh ; get current line high byte + LDX Clinel ; get current line low byte + +; print XA as unsigned integer + +LAB_295E + STA FAC1_1 ; save low byte as FAC1 mantissa1 + STX FAC1_2 ; save high byte as FAC1 mantissa2 + LDX #$90 ; set exponent to 16d bits + SEC ; set integer is +ve flag + JSR LAB_STFA ; set exp=X, clearFAC1 mantissa3 and normalise + LDY #$00 ; clear index + TYA ; clear A + JSR LAB_297B ; convert FAC1 to string, skip sign character save + JMP LAB_18C3 ; print null terminated string from memory and return + +; convert FAC1 to ASCII string result in (AY) +; not any more, moved scratchpad to page 0 + +LAB_296E + LDY #$01 ; set index = 1 + LDA #$20 ; character = " " (assume +ve) + BIT FAC1_s ; test FAC1 sign (b7) + BPL LAB_2978 ; branch if +ve + + LDA #$2D ; else character = "-" +LAB_2978 + STA Decss,Y ; save leading character (" " or "-") +LAB_297B + STA FAC1_s ; clear FAC1 sign (b7) + STY Sendl ; save index + INY ; increment index + LDX FAC1_e ; get FAC1 exponent + BNE LAB_2989 ; branch if FAC1<>0 + + ; exponent was $00 so FAC1 is 0 + LDA #'0' ; set character = "0" + JMP LAB_2A89 ; save last character, [EOT] and exit + + ; FAC1 is some non zero value +LAB_2989 + LDA #$00 ; clear (number exponent count) + CPX #$81 ; compare FAC1 exponent with $81 (>1.00000) + + BCS LAB_299A ; branch if FAC1=>1 + + ; FAC1<1 + LDA #LAB_294F ; set pointer high byte to 1,000,000 + JSR LAB_25FB ; do convert AY, FCA1*(AY) + LDA #$FA ; set number exponent count (-6) +LAB_299A + STA numexp ; save number exponent count +LAB_299C + LDA #LAB_294B ; set pointer high byte to 999999.4375 + JSR LAB_27F8 ; compare FAC1 with (AY) + BEQ LAB_29C3 ; exit if FAC1 = (AY) + + BPL LAB_29B9 ; go do /10 if FAC1 > (AY) + + ; FAC1 < (AY) +LAB_29A7 + LDA #LAB_2947 ; set pointer high byte to 99999.9375 + JSR LAB_27F8 ; compare FAC1 with (AY) + BEQ LAB_29B2 ; branch if FAC1 = (AY) (allow decimal places) + + BPL LAB_29C0 ; branch if FAC1 > (AY) (no decimal places) + + ; FAC1 <= (AY) +LAB_29B2 + JSR LAB_269E ; multiply by 10 + DEC numexp ; decrement number exponent count + BNE LAB_29A7 ; go test again (branch always) + +LAB_29B9 + JSR LAB_26B9 ; divide by 10 + INC numexp ; increment number exponent count + BNE LAB_299C ; go test again (branch always) + +; now we have just the digits to do + +LAB_29C0 + JSR LAB_244E ; add 0.5 to FAC1 (round FAC1) +LAB_29C3 + JSR LAB_2831 ; convert FAC1 floating-to-fixed + LDX #$01 ; set default digits before dp = 1 + LDA numexp ; get number exponent count + CLC ; clear carry for add + ADC #$07 ; up to 6 digits before point + BMI LAB_29D8 ; if -ve then 1 digit before dp + + CMP #$08 ; A>=8 if n>=1E6 + BCS LAB_29D9 ; branch if >= $08 + + ; carry is clear + ADC #$FF ; take 1 from digit count + TAX ; copy to A + LDA #$02 ;.set exponent adjust +LAB_29D8 + SEC ; set carry for subtract +LAB_29D9 + SBC #$02 ; -2 + STA expcnt ;.save exponent adjust + STX numexp ; save digits before dp count + TXA ; copy to A + BEQ LAB_29E4 ; branch if no digits before dp + + BPL LAB_29F7 ; branch if digits before dp + +LAB_29E4 + LDY Sendl ; get output string index + LDA #$2E ; character "." + INY ; increment index + STA Decss,Y ; save to output string + TXA ;. + BEQ LAB_29F5 ;. + + LDA #'0' ; character "0" + INY ; increment index + STA Decss,Y ; save to output string +LAB_29F5 + STY Sendl ; save output string index +LAB_29F7 + LDY #$00 ; clear index (point to 100,000) + LDX #$80 ; +LAB_29FB + LDA FAC1_3 ; get FAC1 mantissa3 + CLC ; clear carry for add + ADC LAB_2A9C,Y ; add -ve LSB + STA FAC1_3 ; save FAC1 mantissa3 + LDA FAC1_2 ; get FAC1 mantissa2 + ADC LAB_2A9B,Y ; add -ve NMSB + STA FAC1_2 ; save FAC1 mantissa2 + LDA FAC1_1 ; get FAC1 mantissa1 + ADC LAB_2A9A,Y ; add -ve MSB + STA FAC1_1 ; save FAC1 mantissa1 + INX ; + BCS LAB_2A18 ; + + BPL LAB_29FB ; not -ve so try again + + BMI LAB_2A1A ; + +LAB_2A18 + BMI LAB_29FB ; + +LAB_2A1A + TXA ; + BCC LAB_2A21 ; + + EOR #$FF ; + ADC #$0A ; +LAB_2A21 + ADC #'0'-1 ; add "0"-1 to result + INY ; increment index .. + INY ; .. to next less .. + INY ; .. power of ten + STY Cvaral ; save as current var address low byte + LDY Sendl ; get output string index + INY ; increment output string index + TAX ; copy character to X + AND #$7F ; mask out top bit + STA Decss,Y ; save to output string + DEC numexp ; decrement # of characters before the dp + BNE LAB_2A3B ; branch if still characters to do + + ; else output the point + LDA #$2E ; character "." + INY ; increment output string index + STA Decss,Y ; save to output string +LAB_2A3B + STY Sendl ; save output string index + LDY Cvaral ; get current var address low byte + TXA ; get character back + EOR #$FF ; + AND #$80 ; + TAX ; + CPY #$12 ; compare index with max + BNE LAB_29FB ; loop if not max + + ; now remove trailing zeroes + LDY Sendl ; get output string index +LAB_2A4B + LDA Decss,Y ; get character from output string + DEY ; decrement output string index + CMP #'0' ; compare with "0" + BEQ LAB_2A4B ; loop until non "0" character found + + CMP #'.' ; compare with "." + BEQ LAB_2A58 ; branch if was dp + + ; restore last character + INY ; increment output string index +LAB_2A58 + LDA #$2B ; character "+" + LDX expcnt ; get exponent count + BEQ LAB_2A8C ; if zero go set null terminator and exit + + ; exponent isn't zero so write exponent + BPL LAB_2A68 ; branch if exponent count +ve + + LDA #$00 ; clear A + SEC ; set carry for subtract + SBC expcnt ; subtract exponent count adjust (convert -ve to +ve) + TAX ; copy exponent count to X + LDA #'-' ; character "-" +LAB_2A68 + STA Decss+2,Y ; save to output string + LDA #$45 ; character "E" + STA Decss+1,Y ; save exponent sign to output string + TXA ; get exponent count back + LDX #'0'-1 ; one less than "0" character + SEC ; set carry for subtract +LAB_2A74 + INX ; increment 10's character + SBC #$0A ;.subtract 10 from exponent count + BCS LAB_2A74 ; loop while still >= 0 + + ADC #':' ; add character ":" ($30+$0A, result is 10 less that value) + STA Decss+4,Y ; save to output string + TXA ; copy 10's character + STA Decss+3,Y ; save to output string + LDA #$00 ; set null terminator + STA Decss+5,Y ; save to output string + BEQ LAB_2A91 ; go set string pointer (AY) and exit (branch always) + + ; save last character, [EOT] and exit +LAB_2A89 + STA Decss,Y ; save last character to output string + + ; set null terminator and exit +LAB_2A8C + LDA #$00 ; set null terminator + STA Decss+1,Y ; save after last character + + ; set string pointer (AY) and exit +LAB_2A91 + LDA #Decssp1 ; set result string high pointer + RTS + +; perform power function + +LAB_POWER + BEQ LAB_EXP ; go do EXP() + + LDA FAC2_e ; get FAC2 exponent + BNE LAB_2ABF ; branch if FAC2<>0 + + JMP LAB_24F3 ; clear FAC1 exponent and sign and return + +LAB_2ABF + LDX #func_l ; set destination pointer high byte + JSR LAB_2778 ; pack FAC1 into (XY) + LDA FAC2_s ; get FAC2 sign (b7) + BPL LAB_2AD9 ; branch if FAC2>0 + + ; else FAC2 is -ve and can only be raised to an + ; integer power which gives an x +j0 result + JSR LAB_INT ; perform INT + LDA #func_l ; set source pointer high byte + JSR LAB_27F8 ; compare FAC1 with (AY) + BNE LAB_2AD9 ; branch if FAC1 <> (AY) to allow Function Call error + ; this will leave FAC1 -ve and cause a Function Call + ; error when LOG() is called + + TYA ; clear sign b7 + LDY Temp3 ; save mantissa 3 from INT() function as sign in Y + ; for possible later negation, b0 +LAB_2AD9 + JSR LAB_279D ; save FAC1 sign and copy ABS(FAC2) to FAC1 + TYA ; copy sign back .. + PHA ; .. and save it + JSR LAB_LOG ; do LOG(n) + LDA #garb_l ; set pointer high byte + JSR LAB_25FB ; do convert AY, FCA1*(AY) (square the value) + JSR LAB_EXP ; go do EXP(n) + PLA ; pull sign from stack + LSR ; b0 is to be tested, shift to Cb + BCC LAB_2AF9 ; if no bit then exit + + ; Perform negation +; do - FAC1 + +LAB_GTHAN + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_2AF9 ; exit if FAC1_e = $00 + + LDA FAC1_s ; get FAC1 sign (b7) + EOR #$FF ; complement it + STA FAC1_s ; save FAC1 sign (b7) +LAB_2AF9 + RTS + +; perform EXP() (x^e) + +LAB_EXP + LDA #LAB_2AFA ; set 1.443 pointer high byte + JSR LAB_25FB ; do convert AY, FCA1*(AY) + LDA FAC1_r ; get FAC1 rounding byte + ADC #$50 ; +$50/$100 + BCC LAB_2B2B ; skip rounding if no carry + + JSR LAB_27C2 ; round FAC1 (no check) +LAB_2B2B + STA FAC2_r ; save FAC2 rounding byte + JSR LAB_27AE ; copy FAC1 to FAC2 + LDA FAC1_e ; get FAC1 exponent + CMP #$88 ; compare with EXP limit (256d) + BCC LAB_2B39 ; branch if less + +LAB_2B36 + JSR LAB_2690 ; handle overflow and underflow +LAB_2B39 + JSR LAB_INT ; perform INT + LDA Temp3 ; get mantissa 3 from INT() function + CLC ; clear carry for add + ADC #$81 ; normalise +1 + BEQ LAB_2B36 ; if $00 go handle overflow + + SEC ; set carry for subtract + SBC #$01 ; now correct for exponent + PHA ; save FAC2 exponent + + ; swap FAC1 and FAC2 + LDX #$04 ; 4 bytes to do +LAB_2B49 + LDA FAC2_e,X ; get FAC2,X + LDY FAC1_e,X ; get FAC1,X + STA FAC1_e,X ; save FAC1,X + STY FAC2_e,X ; save FAC2,X + DEX ; decrement count/index + BPL LAB_2B49 ; loop if not all done + + LDA FAC2_r ; get FAC2 rounding byte + STA FAC1_r ; save as FAC1 rounding byte + JSR LAB_SUBTRACT ; perform subtraction, FAC2 from FAC1 + JSR LAB_GTHAN ; do - FAC1 + LDA #LAB_2AFE ; set counter pointer high byte + JSR LAB_2B84 ; go do series evaluation + LDA #$00 ; clear A + STA FAC_sc ; clear sign compare (FAC1 EOR FAC2) + PLA ;.get saved FAC2 exponent + JMP LAB_2675 ; test and adjust accumulators and return + +; ^2 then series evaluation + +LAB_2B6E + STA Cptrl ; save count pointer low byte + STY Cptrh ; save count pointer high byte + JSR LAB_276E ; pack FAC1 into Adatal + LDA #Adatal ; pointer to original # high byte + JMP LAB_25FB ; do convert AY, FCA1*(AY) and return + +; series evaluation + +LAB_2B84 + STA Cptrl ; save count pointer low byte + STY Cptrh ; save count pointer high byte +LAB_2B88 + LDX #numexp ; set pointer high byte to partial @ numexp + DEC numcon ; decrement constants count + BNE LAB_2B9B ; loop until all done + + RTS + +; RND(n), 32 bit Galoise version. make n=0 for 19th next number in sequence or n<>0 +; to get 19th next number in sequence after seed n. This version of the PRNG uses +; the Galois method and a sample of 65536 bytes produced gives the following values. + +; Entropy = 7.997442 bits per byte +; Optimum compression would reduce these 65536 bytes by 0 percent + +; Chi square distribution for 65536 samples is 232.01, and +; randomly would exceed this value 75.00 percent of the time + +; Arithmetic mean value of data bytes is 127.6724, 127.5 would be random +; Monte Carlo value for Pi is 3.122871269, error 0.60 percent +; Serial correlation coefficient is -0.000370, totally uncorrelated would be 0.0 + +LAB_RND + LDA FAC1_e ; get FAC1 exponent + BEQ NextPRN ; do next random # if zero + + ; else get seed into random number store + LDX #Rbyte4 ; set PRNG pointer low byte + LDY #$00 ; set PRNG pointer high byte + JSR LAB_2778 ; pack FAC1 into (XY) +NextPRN + LDX #$AF ; set EOR byte + LDY #$13 ; do this nineteen times +LoopPRN + ASL Rbyte1 ; shift PRNG most significant byte + ROL Rbyte2 ; shift PRNG middle byte + ROL Rbyte3 ; shift PRNG least significant byte + ROL Rbyte4 ; shift PRNG extra byte + BCC Ninc1 ; branch if bit 32 clear + + TXA ; set EOR byte + EOR Rbyte1 ; EOR PRNG extra byte + STA Rbyte1 ; save new PRNG extra byte +Ninc1 + DEY ; decrement loop count + BNE LoopPRN ; loop if not all done + + LDX #$02 ; three bytes to copy +CopyPRNG + LDA Rbyte1,X ; get PRNG byte + STA FAC1_1,X ; save FAC1 byte + DEX + BPL CopyPRNG ; loop if not complete + + LDA #$80 ; set the exponent + STA FAC1_e ; save FAC1 exponent + + ASL ; clear A + STA FAC1_s ; save FAC1 sign + + JMP LAB_24D5 ; normalise FAC1 and return + +; perform COS() + +LAB_COS + LDA #LAB_2C78 ; set (pi/2) pointer high byte + JSR LAB_246C ; add (AY) to FAC1 + +; perform SIN() + +LAB_SIN + JSR LAB_27AB ; round and copy FAC1 to FAC2 + LDA #LAB_2C7C ; set (2*pi) pointer high byte + LDX FAC2_s ; get FAC2 sign (b7) + JSR LAB_26C2 ; divide by (AY) (X=sign) + JSR LAB_27AB ; round and copy FAC1 to FAC2 + JSR LAB_INT ; perform INT + LDA #$00 ; clear byte + STA FAC_sc ; clear sign compare (FAC1 EOR FAC2) + JSR LAB_SUBTRACT ; perform subtraction, FAC2 from FAC1 + LDA #LAB_2C80 ; set 0.25 pointer high byte + JSR LAB_2455 ; perform subtraction, (AY) from FAC1 + LDA FAC1_s ; get FAC1 sign (b7) + PHA ; save FAC1 sign + BPL LAB_2C35 ; branch if +ve + + ; FAC1 sign was -ve + JSR LAB_244E ; add 0.5 to FAC1 + LDA FAC1_s ; get FAC1 sign (b7) + BMI LAB_2C38 ; branch if -ve + + LDA Cflag ; get comparison evaluation flag + EOR #$FF ; toggle flag + STA Cflag ; save comparison evaluation flag +LAB_2C35 + JSR LAB_GTHAN ; do - FAC1 +LAB_2C38 + LDA #LAB_2C80 ; set 0.25 pointer high byte + JSR LAB_246C ; add (AY) to FAC1 + PLA ; restore FAC1 sign + BPL LAB_2C45 ; branch if was +ve + + ; else correct FAC1 + JSR LAB_GTHAN ; do - FAC1 +LAB_2C45 + LDA #LAB_2C84 ; set pointer high byte to counter + JMP LAB_2B6E ; ^2 then series evaluation and return + +; perform TAN() + +LAB_TAN + JSR LAB_276E ; pack FAC1 into Adatal + LDA #$00 ; clear byte + STA Cflag ; clear comparison evaluation flag + JSR LAB_SIN ; go do SIN(n) + LDX #func_l ; set sin(n) pointer high byte + JSR LAB_2778 ; pack FAC1 into (XY) + LDA #Adatal ; set n pointer high addr + JSR LAB_UFAC ; unpack memory (AY) into FAC1 + LDA #$00 ; clear byte + STA FAC1_s ; clear FAC1 sign (b7) + LDA Cflag ; get comparison evaluation flag + JSR LAB_2C74 ; save flag and go do series evaluation + + LDA #func_l ; set sin(n) pointer high byte + JMP LAB_26CA ; convert AY and do (AY)/FAC1 + +LAB_2C74 + PHA ; save comparison evaluation flag + JMP LAB_2C35 ; go do series evaluation + +; perform USR() + +LAB_USR + JSR Usrjmp ; call user code + JMP LAB_1BFB ; scan for ")", else do syntax error then warm start + +; perform ATN() + +LAB_ATN + LDA FAC1_s ; get FAC1 sign (b7) + PHA ; save sign + BPL LAB_2CA1 ; branch if +ve + + JSR LAB_GTHAN ; else do - FAC1 +LAB_2CA1 + LDA FAC1_e ; get FAC1 exponent + PHA ; push exponent + CMP #$81 ; compare with 1 + BCC LAB_2CAF ; branch if FAC1<1 + + LDA #LAB_259C ; set 1 pointer high byte + JSR LAB_26CA ; convert AY and do (AY)/FAC1 +LAB_2CAF + LDA #LAB_2CC9 ; set pointer high byte to counter + JSR LAB_2B6E ; ^2 then series evaluation + PLA ; restore old FAC1 exponent + CMP #$81 ; compare with 1 + BCC LAB_2CC2 ; branch if FAC1<1 + + LDA #LAB_2C78 ; set (pi/2) pointer high byte + JSR LAB_2455 ; perform subtraction, (AY) from FAC1 +LAB_2CC2 + PLA ; restore FAC1 sign + BPL LAB_2D04 ; exit if was +ve + + JMP LAB_GTHAN ; else do - FAC1 and return + +; perform BITSET + +LAB_BITSET + JSR LAB_GADB ; get two parameters for POKE or WAIT + CPX #$08 ; only 0 to 7 are allowed + BCS FCError ; branch if > 7 + + LDA #$00 ; clear A + SEC ; set the carry +S_Bits + ROL ; shift bit + DEX ; decrement bit number + BPL S_Bits ; loop if still +ve + + INX ; make X = $00 + ORA (Itempl,X) ; or with byte via temporary integer (addr) + STA (Itempl,X) ; save byte via temporary integer (addr) +LAB_2D04 + RTS + +; perform BITCLR + +LAB_BITCLR + JSR LAB_GADB ; get two parameters for POKE or WAIT + CPX #$08 ; only 0 to 7 are allowed + BCS FCError ; branch if > 7 + + LDA #$FF ; set A +S_Bitc + ROL ; shift bit + DEX ; decrement bit number + BPL S_Bitc ; loop if still +ve + + INX ; make X = $00 + AND (Itempl,X) ; and with byte via temporary integer (addr) + STA (Itempl,X) ; save byte via temporary integer (addr) + RTS + +FCError + JMP LAB_FCER ; do function call error then warm start + +; perform BITTST() + +LAB_BTST + JSR LAB_IGBY ; increment BASIC pointer + JSR LAB_GADB ; get two parameters for POKE or WAIT + CPX #$08 ; only 0 to 7 are allowed + BCS FCError ; branch if > 7 + + JSR LAB_GBYT ; get next BASIC byte + CMP #')' ; is next character ")" + BEQ TST_OK ; if ")" go do rest of function + + JMP LAB_SNER ; do syntax error then warm start + +TST_OK + JSR LAB_IGBY ; update BASIC execute pointer (to character past ")") + LDA #$00 ; clear A + SEC ; set the carry +T_Bits + ROL ; shift bit + DEX ; decrement bit number + BPL T_Bits ; loop if still +ve + + INX ; make X = $00 + AND (Itempl,X) ; AND with byte via temporary integer (addr) + BEQ LAB_NOTT ; branch if zero (already correct) + + LDA #$FF ; set for -1 result +LAB_NOTT + JMP LAB_27DB ; go do SGN tail + +; perform BIN$() + +LAB_BINS + CPX #$19 ; max + 1 + BCS BinFErr ; exit if too big ( > or = ) + + STX TempB ; save # of characters ($00 = leading zero remove) + LDA #$18 ; need A byte long space + JSR LAB_MSSP ; make string space A bytes long + LDY #$17 ; set index + LDX #$18 ; character count +NextB1 + LSR nums_1 ; shift highest byte + ROR nums_2 ; shift middle byte + ROR nums_3 ; shift lowest byte bit 0 to carry + TXA ; load with "0"/2 + ROL ; shift in carry + STA (str_pl),Y ; save to temp string + index + DEY ; decrement index + BPL NextB1 ; loop if not done + + LDA TempB ; get # of characters + BEQ EndBHS ; branch if truncate + + TAX ; copy length to X + SEC ; set carry for add ! + EOR #$FF ; 1's complement + ADC #$18 ; add 24d + BEQ GoPr2 ; if zero print whole string + + BNE GoPr1 ; else go make output string + +; this is the exit code and is also used by HEX$() +; truncate string to remove leading "0"s + +EndBHS + TAY ; clear index (A=0, X=length here) +NextB2 + LDA (str_pl),Y ; get character from string + CMP #'0' ; compare with "0" + BNE GoPr ; if not "0" then go print string from here + + DEX ; decrement character count + BEQ GoPr3 ; if zero then end of string so go print it + + INY ; else increment index + BPL NextB2 ; loop always + +; make fixed length output string - ignore overflows! + +GoPr3 + INX ; need at least 1 character +GoPr + TYA ; copy result +GoPr1 + CLC ; clear carry for add + ADC str_pl ; add low address + STA str_pl ; save low address + LDA #$00 ; do high byte + ADC str_ph ; add high address + STA str_ph ; save high address +GoPr2 + STX str_ln ; X holds string length + JSR LAB_IGBY ; update BASIC execute pointer (to character past ")") + JMP LAB_RTST ; check for space on descriptor stack then put address + ; and length on descriptor stack and update stack pointers + +BinFErr + JMP LAB_FCER ; do function call error then warm start + +; perform HEX$() + +LAB_HEXS + CPX #$07 ; max + 1 + BCS BinFErr ; exit if too big ( > or = ) + + STX TempB ; save # of characters + + LDA #$06 ; need 6 bytes for string + JSR LAB_MSSP ; make string space A bytes long + LDY #$05 ; set string index + + SED ; need decimal mode for nibble convert + LDA nums_3 ; get lowest byte + JSR LAB_A2HX ; convert A to ASCII hex byte and output + LDA nums_2 ; get middle byte + JSR LAB_A2HX ; convert A to ASCII hex byte and output + LDA nums_1 ; get highest byte + JSR LAB_A2HX ; convert A to ASCII hex byte and output + CLD ; back to binary + + LDX #$06 ; character count + LDA TempB ; get # of characters + BEQ EndBHS ; branch if truncate + + TAX ; copy length to X + SEC ; set carry for add ! + EOR #$FF ; 1's complement + ADC #$06 ; add 6d + BEQ GoPr2 ; if zero print whole string + + BNE GoPr1 ; else go make output string (branch always) + +; convert A to ASCII hex byte and output .. note set decimal mode before calling + +LAB_A2HX + TAX ; save byte + AND #$0F ; mask off top bits + JSR LAB_AL2X ; convert low nibble to ASCII and output + TXA ; get byte back + LSR ; /2 shift high nibble to low nibble + LSR ; /4 + LSR ; /8 + LSR ; /16 +LAB_AL2X + CMP #$0A ; set carry for +1 if >9 + ADC #'0' ; add ASCII "0" + STA (str_pl),Y ; save to temp string + DEY ; decrement counter + RTS + +LAB_NLTO + STA FAC1_e ; save FAC1 exponent + LDA #$00 ; clear sign compare +LAB_MLTE + STA FAC_sc ; save sign compare (FAC1 EOR FAC2) + TXA ; restore character + JSR LAB_2912 ; evaluate new ASCII digit + +; gets here if the first character was "$" for hex +; get hex number + +LAB_CHEX + JSR LAB_IGBY ; increment and scan memory + BCC LAB_ISHN ; branch if numeric character + + ORA #$20 ; case convert, allow "A" to "F" and "a" to "f" + SBC #'a' ; subtract "a" (carry set here) + CMP #$06 ; compare normalised with $06 (max+1) + BCS LAB_EXCH ; exit if >"f" or <"0" + + ADC #$0A ; convert to nibble +LAB_ISHN + AND #$0F ; convert to binary + TAX ; save nibble + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_MLTE ; skip multiply if zero + + ADC #$04 ; add four to exponent (*16 - carry clear here) + BCC LAB_NLTO ; if no overflow do evaluate digit + +LAB_MLTO + JMP LAB_2564 ; do overflow error and warm start + +LAB_NXCH + TAX ; save bit + LDA FAC1_e ; get FAC1 exponent + BEQ LAB_MLBT ; skip multiply if zero + + INC FAC1_e ; increment FAC1 exponent (*2) + BEQ LAB_MLTO ; do overflow error if = $00 + + LDA #$00 ; clear sign compare +LAB_MLBT + STA FAC_sc ; save sign compare (FAC1 EOR FAC2) + TXA ; restore bit + JSR LAB_2912 ; evaluate new ASCII digit + +; gets here if the first character was "%" for binary +; get binary number + +LAB_CBIN + JSR LAB_IGBY ; increment and scan memory + EOR #'0' ; convert "0" to 0 etc. + CMP #$02 ; compare with max+1 + BCC LAB_NXCH ; branch exit if < 2 + +LAB_EXCH + JMP LAB_28F6 ; evaluate -ve flag and return + +; ctrl-c check routine. includes limited "life" byte save for INGET routine +; now also the code that checks to see if an interrupt has occurred + +CTRLC + LDA ccflag ; get [CTRL-C] check flag + BNE LAB_FBA2 ; exit if inhibited + + JSR V_INPT ; scan input device + BCC LAB_FBA0 ; exit if buffer empty + + STA ccbyte ; save received byte + LDX #$20 ; "life" timer for bytes + STX ccnull ; set countdown + JMP LAB_1636 ; return to BASIC + +LAB_FBA0 + LDX ccnull ; get countdown byte + BEQ LAB_FBA2 ; exit if finished + + DEC ccnull ; else decrement countdown +LAB_FBA2 + LDX #NmiBase ; set pointer to NMI values + JSR LAB_CKIN ; go check interrupt + LDX #IrqBase ; set pointer to IRQ values + JSR LAB_CKIN ; go check interrupt +LAB_CRTS + RTS + +; check whichever interrupt is indexed by X + +LAB_CKIN + LDA PLUS_0,X ; get interrupt flag byte + BPL LAB_CRTS ; branch if interrupt not enabled + +; we disable the interrupt here and make two new commands RETIRQ and RETNMI to +; automatically enable the interrupt when we exit + + ASL ; move happened bit to setup bit + AND #$40 ; mask happened bits + BEQ LAB_CRTS ; if no interrupt then exit + + STA PLUS_0,X ; save interrupt flag byte + + TXA ; copy index .. + TAY ; .. to Y + + PLA ; dump return address low byte, call from CTRL-C + PLA ; dump return address high byte + + LDA #$05 ; need 5 bytes for GOSUB + JSR LAB_1212 ; check room on stack for A bytes + LDA Bpntrh ; get BASIC execute pointer high byte + PHA ; push on stack + LDA Bpntrl ; get BASIC execute pointer low byte + PHA ; push on stack + LDA Clineh ; get current line high byte + PHA ; push on stack + LDA Clinel ; get current line low byte + PHA ; push on stack + LDA #TK_GOSUB ; token for GOSUB + PHA ; push on stack + + LDA PLUS_1,Y ; get interrupt code pointer low byte + STA Bpntrl ; save as BASIC execute pointer low byte + LDA PLUS_2,Y ; get interrupt code pointer high byte + STA Bpntrh ; save as BASIC execute pointer high byte + + JMP LAB_15C2 ; go do interpreter inner loop + ; can't RTS, we used the stack! the RTS from the ctrl-c + ; check will be taken when the RETIRQ/RETNMI/RETURN is + ; executed at the end of the subroutine + +; get byte from input device, no waiting +; returns with carry set if byte in A + +INGET + JSR V_INPT ; call scan input device + BCS LAB_FB95 ; if byte go reset timer + + LDA ccnull ; get countdown + BEQ LAB_FB96 ; exit if empty + + LDA ccbyte ; get last received byte + SEC ; flag we got a byte +LAB_FB95 + LDX #$00 ; clear X + STX ccnull ; clear timer because we got a byte +LAB_FB96 + RTS + +; these routines only enable the interrupts if the set-up flag is set +; if not they have no effect + +; perform IRQ {ON|OFF|CLEAR} + +LAB_IRQ + LDX #IrqBase ; set pointer to IRQ values + .byte $2C ; make next line BIT abs. + +; perform NMI {ON|OFF|CLEAR} + +LAB_NMI + LDX #NmiBase ; set pointer to NMI values + CMP #TK_ON ; compare with token for ON + BEQ LAB_INON ; go turn on interrupt + + CMP #TK_OFF ; compare with token for OFF + BEQ LAB_IOFF ; go turn off interrupt + + EOR #TK_CLEAR ; compare with token for CLEAR, A = $00 if = TK_CLEAR + BEQ LAB_INEX ; go clear interrupt flags and return + + JMP LAB_SNER ; do syntax error then warm start + +LAB_IOFF + LDA #$7F ; clear A + AND PLUS_0,X ; AND with interrupt setup flag + BPL LAB_INEX ; go clear interrupt enabled flag and return + +LAB_INON + LDA PLUS_0,X ; get interrupt setup flag + ASL ; Shift bit to enabled flag + ORA PLUS_0,X ; OR with flag byte +LAB_INEX + STA PLUS_0,X ; save interrupt flag byte + JMP LAB_IGBY ; update BASIC execute pointer and return + +; these routines set up the pointers and flags for the interrupt routines +; note that the interrupts are also enabled by these commands + +; perform ON IRQ + +LAB_SIRQ + CLI ; enable interrupts + LDX #IrqBase ; set pointer to IRQ values + .byte $2C ; make next line BIT abs. + +; perform ON NMI + +LAB_SNMI + LDX #NmiBase ; set pointer to NMI values + + STX TempB ; save interrupt pointer + JSR LAB_IGBY ; increment and scan memory (past token) + JSR LAB_GFPN ; get fixed-point number into temp integer + LDA Smeml ; get start of mem low byte + LDX Smemh ; get start of mem high byte + JSR LAB_SHLN ; search Basic for temp integer line number from AX + BCS LAB_LFND ; if carry set go set-up interrupt + + JMP LAB_16F7 ; else go do "Undefined statement" error and warm start + +LAB_LFND + LDX TempB ; get interrupt pointer + LDA Baslnl ; get pointer low byte + SBC #$01 ; -1 (carry already set for subtract) + STA PLUS_1,X ; save as interrupt pointer low byte + LDA Baslnh ; get pointer high byte + SBC #$00 ; subtract carry + STA PLUS_2,X ; save as interrupt pointer high byte + + LDA #$C0 ; set interrupt enabled/setup bits + STA PLUS_0,X ; set interrupt flags +LAB_IRTS + RTS + +; return from IRQ service, restores the enabled flag. + +; perform RETIRQ + +LAB_RETIRQ + BNE LAB_IRTS ; exit if following token (to allow syntax error) + + LDA IrqBase ; get interrupt flags + ASL ; copy setup to enabled (b7) + ORA IrqBase ; OR in setup flag + STA IrqBase ; save enabled flag + JMP LAB_16E8 ; go do rest of RETURN + +; return from NMI service, restores the enabled flag. + +; perform RETNMI + +LAB_RETNMI + BNE LAB_IRTS ; exit if following token (to allow syntax error) + + LDA NmiBase ; get set-up flag + ASL ; copy setup to enabled (b7) + ORA NmiBase ; OR in setup flag + STA NmiBase ; save enabled flag + JMP LAB_16E8 ; go do rest of RETURN + +; MAX() MIN() pre process + +LAB_MMPP + JSR LAB_EVEZ ; process expression + JMP LAB_CTNM ; check if source is numeric, else do type mismatch + +; perform MAX() + +LAB_MAX + JSR LAB_PHFA ; push FAC1, evaluate expression, + ; pull FAC2 and compare with FAC1 + BPL LAB_MAX ; branch if no swap to do + + LDA FAC2_1 ; get FAC2 mantissa1 + ORA #$80 ; set top bit (clear sign from compare) + STA FAC2_1 ; save FAC2 mantissa1 + JSR LAB_279B ; copy FAC2 to FAC1 + BEQ LAB_MAX ; go do next (branch always) + +; perform MIN() + +LAB_MIN + JSR LAB_PHFA ; push FAC1, evaluate expression, + ; pull FAC2 and compare with FAC1 + BMI LAB_MIN ; branch if no swap to do + + BEQ LAB_MIN ; branch if no swap to do + + LDA FAC2_1 ; get FAC2 mantissa1 + ORA #$80 ; set top bit (clear sign from compare) + STA FAC2_1 ; save FAC2 mantissa1 + JSR LAB_279B ; copy FAC2 to FAC1 + BEQ LAB_MIN ; go do next (branch always) + +; exit routine. don't bother returning to the loop code +; check for correct exit, else so syntax error + +LAB_MMEC + CMP #')' ; is it end of function? + BNE LAB_MMSE ; if not do MAX MIN syntax error + + PLA ; dump return address low byte + PLA ; dump return address high byte + JMP LAB_IGBY ; update BASIC execute pointer (to chr past ")") + +LAB_MMSE + JMP LAB_SNER ; do syntax error then warm start + +; check for next, evaluate and return or exit +; this is the routine that does most of the work + +LAB_PHFA + JSR LAB_GBYT ; get next BASIC byte + CMP #',' ; is there more ? + BNE LAB_MMEC ; if not go do end check + + ; push FAC1 + JSR LAB_27BA ; round FAC1 + LDA FAC1_s ; get FAC1 sign + ORA #$7F ; set all non sign bits + AND FAC1_1 ; AND FAC1 mantissa1 (AND in sign bit) + PHA ; push on stack + LDA FAC1_2 ; get FAC1 mantissa2 + PHA ; push on stack + LDA FAC1_3 ; get FAC1 mantissa3 + PHA ; push on stack + LDA FAC1_e ; get FAC1 exponent + PHA ; push on stack + + JSR LAB_IGBY ; scan and get next BASIC byte (after ",") + JSR LAB_EVNM ; evaluate expression and check is numeric, + ; else do type mismatch + + ; pop FAC2 (MAX/MIN expression so far) + PLA ; pop exponent + STA FAC2_e ; save FAC2 exponent + PLA ; pop mantissa3 + STA FAC2_3 ; save FAC2 mantissa3 + PLA ; pop mantissa1 + STA FAC2_2 ; save FAC2 mantissa2 + PLA ; pop sign/mantissa1 + STA FAC2_1 ; save FAC2 sign/mantissa1 + STA FAC2_s ; save FAC2 sign + + ; compare FAC1 with (packed) FAC2 + LDA #FAC2_e ; set pointer high byte to FAC2 + JMP LAB_27F8 ; compare FAC1 with FAC2 (AY) and return + ; returns A=$00 if FAC1 = (AY) + ; returns A=$01 if FAC1 > (AY) + ; returns A=$FF if FAC1 < (AY) + +; perform WIDTH + +LAB_WDTH + CMP #',' ; is next byte "," + BEQ LAB_TBSZ ; if so do tab size + + JSR LAB_GTBY ; get byte parameter + TXA ; copy width to A + BEQ LAB_NSTT ; branch if set for infinite line + + CPX #$10 ; else make min width = 16d + BCC TabErr ; if less do function call error and exit + +; this next compare ensures that we can't exit WIDTH via an error leaving the +; tab size greater than the line length. + + CPX TabSiz ; compare with tab size + BCS LAB_NSTT ; branch if >= tab size + + STX TabSiz ; else make tab size = terminal width +LAB_NSTT + STX TWidth ; set the terminal width + JSR LAB_GBYT ; get BASIC byte back + BEQ WExit ; exit if no following + + CMP #',' ; else is it "," + BNE LAB_MMSE ; if not do syntax error + +LAB_TBSZ + JSR LAB_SGBY ; scan and get byte parameter + TXA ; copy TAB size + BMI TabErr ; if >127 do function call error and exit + + CPX #$01 ; compare with min-1 + BCC TabErr ; if <=1 do function call error and exit + + LDA TWidth ; set flags for width + BEQ LAB_SVTB ; skip check if infinite line + + CPX TWidth ; compare TAB with width + BEQ LAB_SVTB ; ok if = + + BCS TabErr ; branch if too big + +LAB_SVTB + STX TabSiz ; save TAB size + +; calculate tab column limit from TAB size. The Iclim is set to the last tab +; position on a line that still has at least one whole tab width between it +; and the end of the line. + +WExit + LDA TWidth ; get width + BEQ LAB_SULP ; branch if infinite line + + CMP TabSiz ; compare with tab size + BCS LAB_WDLP ; branch if >= tab size + + STA TabSiz ; else make tab size = terminal width +LAB_SULP + SEC ; set carry for subtract +LAB_WDLP + SBC TabSiz ; subtract tab size + BCS LAB_WDLP ; loop while no borrow + + ADC TabSiz ; add tab size back + CLC ; clear carry for add + ADC TabSiz ; add tab size back again + STA Iclim ; save for now + LDA TWidth ; get width back + SEC ; set carry for subtract + SBC Iclim ; subtract remainder + STA Iclim ; save tab column limit +LAB_NOSQ + RTS + +TabErr + JMP LAB_FCER ; do function call error then warm start + +; perform SQR() + +LAB_SQR + LDA FAC1_s ; get FAC1 sign + BMI TabErr ; if -ve do function call error + + LDA FAC1_e ; get exponent + BEQ LAB_NOSQ ; if zero just return + + ; else do root + JSR LAB_27AB ; round and copy FAC1 to FAC2 + LDA #$00 ; clear A + + STA FACt_3 ; clear remainder + STA FACt_2 ; .. + STA FACt_1 ; .. + STA TempB ; .. + + STA FAC1_3 ; clear root + STA FAC1_2 ; .. + STA FAC1_1 ; .. + + LDX #$18 ; 24 pairs of bits to do + LDA FAC2_e ; get exponent + LSR ; check odd/even + BCS LAB_SQE2 ; if odd only 1 shift first time + +LAB_SQE1 + ASL FAC2_3 ; shift highest bit of number .. + ROL FAC2_2 ; .. + ROL FAC2_1 ; .. + ROL FACt_3 ; .. into remainder + ROL FACt_2 ; .. + ROL FACt_1 ; .. + ROL TempB ; .. never overflows +LAB_SQE2 + ASL FAC2_3 ; shift highest bit of number .. + ROL FAC2_2 ; .. + ROL FAC2_1 ; .. + ROL FACt_3 ; .. into remainder + ROL FACt_2 ; .. + ROL FACt_1 ; .. + ROL TempB ; .. never overflows + + ASL FAC1_3 ; root = root * 2 + ROL FAC1_2 ; .. + ROL FAC1_1 ; .. never overflows + + LDA FAC1_3 ; get root low byte + ROL ; *2 + STA Temp3 ; save partial low byte + LDA FAC1_2 ; get root low mid byte + ROL ; *2 + STA Temp3+1 ; save partial low mid byte + LDA FAC1_1 ; get root high mid byte + ROL ; *2 + STA Temp3+2 ; save partial high mid byte + LDA #$00 ; get root high byte (always $00) + ROL ; *2 + STA Temp3+3 ; save partial high byte + + ; carry clear for subtract +1 + LDA FACt_3 ; get remainder low byte + SBC Temp3 ; subtract partial low byte + STA Temp3 ; save partial low byte + + LDA FACt_2 ; get remainder low mid byte + SBC Temp3+1 ; subtract partial low mid byte + STA Temp3+1 ; save partial low mid byte + + LDA FACt_1 ; get remainder high mid byte + SBC Temp3+2 ; subtract partial high mid byte + TAY ; copy partial high mid byte + + LDA TempB ; get remainder high byte + SBC Temp3+3 ; subtract partial high byte + BCC LAB_SQNS ; skip sub if remainder smaller + + STA TempB ; save remainder high byte + + STY FACt_1 ; save remainder high mid byte + + LDA Temp3+1 ; get remainder low mid byte + STA FACt_2 ; save remainder low mid byte + + LDA Temp3 ; get partial low byte + STA FACt_3 ; save remainder low byte + + INC FAC1_3 ; increment root low byte (never any rollover) +LAB_SQNS + DEX ; decrement bit pair count + BNE LAB_SQE1 ; loop if not all done + + SEC ; set carry for subtract + LDA FAC2_e ; get exponent + SBC #$80 ; normalise + ROR ; /2 and re-bias to $80 + ADC #$00 ; add bit zero back in (allow for half shift) + STA FAC1_e ; save it + JMP LAB_24D5 ; normalise FAC1 and return + +; perform VARPTR() + +LAB_VARPTR + JSR LAB_IGBY ; increment and scan memory + JSR LAB_GVAR ; get var address + JSR LAB_1BFB ; scan for ")" , else do syntax error then warm start + LDY Cvaral ; get var address low byte + LDA Cvarah ; get var address high byte + JMP LAB_AYFC ; save and convert integer AY to FAC1 and return + +; perform PI + +LAB_PI + LDA #LAB_2C7C ; set (2*pi) pointer high byte + JSR LAB_UFAC ; unpack memory (AY) into FAC1 + DEC FAC1_e ; make result = PI + RTS + +; perform TWOPI + +LAB_TWOPI + LDA #LAB_2C7C ; set (2*pi) pointer high byte + JMP LAB_UFAC ; unpack memory (AY) into FAC1 and return + +; system dependant i/o vectors +; these are in RAM and are set by the monitor at start-up + +V_INPT + JMP (VEC_IN) ; non halting scan input device +V_OUTP + JMP (VEC_OUT) ; send byte to output device +V_LOAD + JMP (VEC_LD) ; load BASIC program +V_SAVE + JMP (VEC_SV) ; save BASIC program + +; The rest are tables messages and code for RAM + +; the rest of the code is tables and BASIC start-up code + +PG2_TABS + .byte $00 ; ctrl-c flag - $00 = enabled + .byte $00 ; ctrl-c byte - GET needs this + .byte $00 ; ctrl-c byte timeout - GET needs this + .word CTRLC ; ctrl c check vector +; .word xxxx ; non halting key input - monitor to set this +; .word xxxx ; output vector - monitor to set this +; .word xxxx ; load vector - monitor to set this +; .word xxxx ; save vector - monitor to set this +PG2_TABE + +; character get subroutine for zero page + +; For a 1.8432MHz 6502 including the JSR and RTS +; fastest (>=":") = 29 cycles = 15.7uS +; slowest (<":") = 40 cycles = 21.7uS +; space skip = +21 cycles = +11.4uS +; inc across page = +4 cycles = +2.2uS + +; the target address for the LDA at LAB_2CF4 becomes the BASIC execute pointer once the +; block is copied to it's destination, any non zero page address will do at assembly +; time, to assemble a three byte instruction. + +; page 0 initialisation table from $BC +; increment and scan memory + +LAB_2CEE + INC Bpntrl ; increment BASIC execute pointer low byte + BNE LAB_2CF4 ; branch if no carry + ; else + INC Bpntrh ; increment BASIC execute pointer high byte + +; page 0 initialisation table from $C2 +; scan memory + +LAB_2CF4 + LDA $FFFF ; get byte to scan (addr set by call routine) + CMP #TK_ELSE ; compare with the token for ELSE + BEQ LAB_2D05 ; exit if ELSE, not numeric, carry set + + CMP #':' ; compare with ":" + BCS LAB_2D05 ; exit if >= ":", not numeric, carry set + + CMP #' ' ; compare with " " + BEQ LAB_2CEE ; if " " go do next + + SEC ; set carry for SBC + SBC #'0' ; subtract "0" + SEC ; set carry for SBC + SBC #$D0 ; subtract -"0" + ; clear carry if byte = "0"-"9" +LAB_2D05 + RTS + +; page zero initialisation table $00-$12 inclusive + +StrTab + .byte $4C ; JMP opcode + .word LAB_COLD ; initial warm start vector (cold start) + + .byte $00 ; these bytes are not used by BASIC + .word $0000 ; + .word $0000 ; + .word $0000 ; + + .byte $4C ; JMP opcode + .word LAB_FCER ; initial user function vector ("Function call" error) + .byte $00 ; default NULL count + .byte $00 ; clear terminal position + .byte $00 ; default terminal width byte + .byte $F2 ; default limit for TAB = 14 + .word Ram_base ; start of user RAM +EndTab + +LAB_MSZM + .byte $0D,$0A,"Memory size ",$00 + +LAB_SMSG + .byte " Bytes free",$0D,$0A,$0A + .byte "Enhanced BASIC 2.22",$0A,$00 + +; numeric constants and series + + ; constants and series for LOG(n) +LAB_25A0 + .byte $02 ; counter + .byte $80,$19,$56,$62 ; 0.59898 + .byte $80,$76,$22,$F3 ; 0.96147 +;## .byte $80,$76,$22,$F1 ; 0.96147 + .byte $82,$38,$AA,$40 ; 2.88539 +;## .byte $82,$38,$AA,$45 ; 2.88539 + +LAB_25AD + .byte $80,$35,$04,$F3 ; 0.70711 1/root 2 +LAB_25B1 + .byte $81,$35,$04,$F3 ; 1.41421 root 2 +LAB_25B5 + .byte $80,$80,$00,$00 ; -0.5 +LAB_25B9 + .byte $80,$31,$72,$18 ; 0.69315 LOG(2) + + ; numeric PRINT constants +LAB_2947 + .byte $91,$43,$4F,$F8 ; 99999.9375 (max value with at least one decimal) +LAB_294B + .byte $94,$74,$23,$F7 ; 999999.4375 (max value before scientific notation) +LAB_294F + .byte $94,$74,$24,$00 ; 1000000 + + ; EXP(n) constants and series +LAB_2AFA + .byte $81,$38,$AA,$3B ; 1.4427 (1/LOG base 2 e) +LAB_2AFE + .byte $06 ; counter + .byte $74,$63,$90,$8C ; 2.17023e-4 + .byte $77,$23,$0C,$AB ; 0.00124 + .byte $7A,$1E,$94,$00 ; 0.00968 + .byte $7C,$63,$42,$80 ; 0.05548 + .byte $7E,$75,$FE,$D0 ; 0.24023 + .byte $80,$31,$72,$15 ; 0.69315 + .byte $81,$00,$00,$00 ; 1.00000 + +;## .byte $07 ; counter +;## .byte $74,$94,$2E,$40 ; -1/7! (-1/5040) +;## .byte $77,$2E,$4F,$70 ; 1/6! ( 1/720) +;## .byte $7A,$88,$02,$6E ; -1/5! (-1/120) +;## .byte $7C,$2A,$A0,$E6 ; 1/4! ( 1/24) +;## .byte $7E,$AA,$AA,$50 ; -1/3! (-1/6) +;## .byte $7F,$7F,$FF,$FF ; 1/2! ( 1/2) +;## .byte $81,$80,$00,$00 ; -1/1! (-1/1) +;## .byte $81,$00,$00,$00 ; 1/0! ( 1/1) + + ; trigonometric constants and series +LAB_2C78 + .byte $81,$49,$0F,$DB ; 1.570796371 (pi/2) as floating # +LAB_2C84 + .byte $04 ; counter + .byte $86,$1E,$D7,$FB ; 39.7109 +;## .byte $86,$1E,$D7,$BA ; 39.7109 + .byte $87,$99,$26,$65 ;-76.575 +;## .byte $87,$99,$26,$64 ;-76.575 + .byte $87,$23,$34,$58 ; 81.6022 + .byte $86,$A5,$5D,$E1 ;-41.3417 +;## .byte $86,$A5,$5D,$E0 ;-41.3417 +LAB_2C7C + .byte $83,$49,$0F,$DB ; 6.28319 (2*pi) as floating # +;## .byte $83,$49,$0F,$DA ; 6.28319 (2*pi) as floating # + +LAB_2CC9 + .byte $08 ; counter + .byte $78,$3A,$C5,$37 ; 0.00285 + .byte $7B,$83,$A2,$5C ;-0.0160686 + .byte $7C,$2E,$DD,$4D ; 0.0426915 + .byte $7D,$99,$B0,$1E ;-0.0750429 + .byte $7D,$59,$ED,$24 ; 0.106409 + .byte $7E,$91,$72,$00 ;-0.142036 + .byte $7E,$4C,$B9,$73 ; 0.199926 + .byte $7F,$AA,$AA,$53 ;-0.333331 + +;## .byte $08 ; counter +;## .byte $78,$3B,$D7,$4A ; 1/17 +;## .byte $7B,$84,$6E,$02 ;-1/15 +;## .byte $7C,$2F,$C1,$FE ; 1/13 +;## .byte $7D,$9A,$31,$74 ;-1/11 +;## .byte $7D,$5A,$3D,$84 ; 1/9 +;## .byte $7E,$91,$7F,$C8 ;-1/7 +;## .byte $7E,$4C,$BB,$E4 ; 1/5 +;## .byte $7F,$AA,$AA,$6C ;-1/3 + +LAB_1D96 = *+1 ; $00,$00 used for undefined variables +LAB_259C + .byte $81,$00,$00,$00 ; 1.000000, used for INC +LAB_2AFD + .byte $81,$80,$00,$00 ; -1.00000, used for DEC. must be on the same page as +1.00 + + ; misc constants +LAB_1DF7 + .byte $90 ;-32768 (uses first three bytes from 0.5) +LAB_2A96 + .byte $80,$00,$00,$00 ; 0.5 +LAB_2C80 + .byte $7F,$00,$00,$00 ; 0.25 +LAB_26B5 + .byte $84,$20,$00,$00 ; 10.0000 divide by 10 constant + +; This table is used in converting numbers to ASCII. + +LAB_2A9A +LAB_2A9B = LAB_2A9A+1 +LAB_2A9C = LAB_2A9B+1 + .byte $FE,$79,$60 ; -100000 + .byte $00,$27,$10 ; 10000 + .byte $FF,$FC,$18 ; -1000 + .byte $00,$00,$64 ; 100 + .byte $FF,$FF,$F6 ; -10 + .byte $00,$00,$01 ; 1 + +LAB_CTBL + .word LAB_END-1 ; END + .word LAB_FOR-1 ; FOR + .word LAB_NEXT-1 ; NEXT + .word LAB_DATA-1 ; DATA + .word LAB_INPUT-1 ; INPUT + .word LAB_DIM-1 ; DIM + .word LAB_READ-1 ; READ + .word LAB_LET-1 ; LET + .word LAB_DEC-1 ; DEC new command + .word LAB_GOTO-1 ; GOTO + .word LAB_RUN-1 ; RUN + .word LAB_IF-1 ; IF + .word LAB_RESTORE-1 ; RESTORE modified command + .word LAB_GOSUB-1 ; GOSUB + .word LAB_RETIRQ-1 ; RETIRQ new command + .word LAB_RETNMI-1 ; RETNMI new command + .word LAB_RETURN-1 ; RETURN + .word LAB_REM-1 ; REM + .word LAB_STOP-1 ; STOP + .word LAB_ON-1 ; ON modified command + .word LAB_NULL-1 ; NULL modified command + .word LAB_INC-1 ; INC new command + .word LAB_WAIT-1 ; WAIT + .word V_LOAD-1 ; LOAD + .word V_SAVE-1 ; SAVE + .word LAB_DEF-1 ; DEF + .word LAB_POKE-1 ; POKE + .word LAB_DOKE-1 ; DOKE new command + .word LAB_CALL-1 ; CALL new command + .word LAB_DO-1 ; DO new command + .word LAB_LOOP-1 ; LOOP new command + .word LAB_PRINT-1 ; PRINT + .word LAB_CONT-1 ; CONT + .word LAB_LIST-1 ; LIST + .word LAB_CLEAR-1 ; CLEAR + .word LAB_NEW-1 ; NEW + .word LAB_WDTH-1 ; WIDTH new command + .word LAB_GET-1 ; GET new command + .word LAB_SWAP-1 ; SWAP new command + .word LAB_BITSET-1 ; BITSET new command + .word LAB_BITCLR-1 ; BITCLR new command + .word LAB_IRQ-1 ; IRQ new command + .word LAB_NMI-1 ; NMI new command + +; function pre process routine table + +LAB_FTPL +LAB_FTPM = LAB_FTPL+$01 + .word LAB_PPFN-1 ; SGN(n) process numeric expression in () + .word LAB_PPFN-1 ; INT(n) " + .word LAB_PPFN-1 ; ABS(n) " + .word LAB_EVEZ-1 ; USR(x) process any expression + .word LAB_1BF7-1 ; FRE(x) " + .word LAB_1BF7-1 ; POS(x) " + .word LAB_PPFN-1 ; SQR(n) process numeric expression in () + .word LAB_PPFN-1 ; RND(n) " + .word LAB_PPFN-1 ; LOG(n) " + .word LAB_PPFN-1 ; EXP(n) " + .word LAB_PPFN-1 ; COS(n) " + .word LAB_PPFN-1 ; SIN(n) " + .word LAB_PPFN-1 ; TAN(n) " + .word LAB_PPFN-1 ; ATN(n) " + .word LAB_PPFN-1 ; PEEK(n) " + .word LAB_PPFN-1 ; DEEK(n) " + .word $0000 ; SADD() none + .word LAB_PPFS-1 ; LEN($) process string expression in () + .word LAB_PPFN-1 ; STR$(n) process numeric expression in () + .word LAB_PPFS-1 ; VAL($) process string expression in () + .word LAB_PPFS-1 ; ASC($) " + .word LAB_PPFS-1 ; UCASE$($) " + .word LAB_PPFS-1 ; LCASE$($) " + .word LAB_PPFN-1 ; CHR$(n) process numeric expression in () + .word LAB_BHSS-1 ; HEX$(n) " + .word LAB_BHSS-1 ; BIN$(n) " + .word $0000 ; BITTST() none + .word LAB_MMPP-1 ; MAX() process numeric expression + .word LAB_MMPP-1 ; MIN() " + .word LAB_PPBI-1 ; PI advance pointer + .word LAB_PPBI-1 ; TWOPI " + .word $0000 ; VARPTR() none + .word LAB_LRMS-1 ; LEFT$() process string expression + .word LAB_LRMS-1 ; RIGHT$() " + .word LAB_LRMS-1 ; MID$() " + +; action addresses for functions + +LAB_FTBL +LAB_FTBM = LAB_FTBL+$01 + .word LAB_SGN-1 ; SGN() + .word LAB_INT-1 ; INT() + .word LAB_ABS-1 ; ABS() + .word LAB_USR-1 ; USR() + .word LAB_FRE-1 ; FRE() + .word LAB_POS-1 ; POS() + .word LAB_SQR-1 ; SQR() + .word LAB_RND-1 ; RND() modified function + .word LAB_LOG-1 ; LOG() + .word LAB_EXP-1 ; EXP() + .word LAB_COS-1 ; COS() + .word LAB_SIN-1 ; SIN() + .word LAB_TAN-1 ; TAN() + .word LAB_ATN-1 ; ATN() + .word LAB_PEEK-1 ; PEEK() + .word LAB_DEEK-1 ; DEEK() new function + .word LAB_SADD-1 ; SADD() new function + .word LAB_LENS-1 ; LEN() + .word LAB_STRS-1 ; STR$() + .word LAB_VAL-1 ; VAL() + .word LAB_ASC-1 ; ASC() + .word LAB_UCASE-1 ; UCASE$() new function + .word LAB_LCASE-1 ; LCASE$() new function + .word LAB_CHRS-1 ; CHR$() + .word LAB_HEXS-1 ; HEX$() new function + .word LAB_BINS-1 ; BIN$() new function + .word LAB_BTST-1 ; BITTST() new function + .word LAB_MAX-1 ; MAX() new function + .word LAB_MIN-1 ; MIN() new function + .word LAB_PI-1 ; PI new function + .word LAB_TWOPI-1 ; TWOPI new function + .word LAB_VARPTR-1 ; VARPTR() new function + .word LAB_LEFT-1 ; LEFT$() + .word LAB_RIGHT-1 ; RIGHT$() + .word LAB_MIDS-1 ; MID$() + +; hierarchy and action addresses for operator + +LAB_OPPT + .byte $79 ; + + .word LAB_ADD-1 + .byte $79 ; - + .word LAB_SUBTRACT-1 + .byte $7B ; * + .word LAB_MULTIPLY-1 + .byte $7B ; / + .word LAB_DIVIDE-1 + .byte $7F ; ^ + .word LAB_POWER-1 + .byte $50 ; AND + .word LAB_AND-1 + .byte $46 ; EOR new operator + .word LAB_EOR-1 + .byte $46 ; OR + .word LAB_OR-1 + .byte $56 ; >> new operator + .word LAB_RSHIFT-1 + .byte $56 ; << new operator + .word LAB_LSHIFT-1 + .byte $7D ; > + .word LAB_GTHAN-1 + .byte $5A ; = + .word LAB_EQUAL-1 + .byte $64 ; < + .word LAB_LTHAN-1 + +; keywords start with .. +; this is the first character table and must be in alphabetic order + +TAB_1STC + .byte "*" + .byte "+" + .byte "-" + .byte "/" + .byte "<" + .byte "=" + .byte ">" + .byte "?" + .byte "A" + .byte "B" + .byte "C" + .byte "D" + .byte "E" + .byte "F" + .byte "G" + .byte "H" + .byte "I" + .byte "L" + .byte "M" + .byte "N" + .byte "O" + .byte "P" + .byte "R" + .byte "S" + .byte "T" + .byte "U" + .byte "V" + .byte "W" + .byte "^" + .byte $00 ; table terminator + +; pointers to keyword tables + +TAB_CHRT + .word TAB_STAR ; table for "*" + .word TAB_PLUS ; table for "+" + .word TAB_MNUS ; table for "-" + .word TAB_SLAS ; table for "/" + .word TAB_LESS ; table for "<" + .word TAB_EQUL ; table for "=" + .word TAB_MORE ; table for ">" + .word TAB_QEST ; table for "?" + .word TAB_ASCA ; table for "A" + .word TAB_ASCB ; table for "B" + .word TAB_ASCC ; table for "C" + .word TAB_ASCD ; table for "D" + .word TAB_ASCE ; table for "E" + .word TAB_ASCF ; table for "F" + .word TAB_ASCG ; table for "G" + .word TAB_ASCH ; table for "H" + .word TAB_ASCI ; table for "I" + .word TAB_ASCL ; table for "L" + .word TAB_ASCM ; table for "M" + .word TAB_ASCN ; table for "N" + .word TAB_ASCO ; table for "O" + .word TAB_ASCP ; table for "P" + .word TAB_ASCR ; table for "R" + .word TAB_ASCS ; table for "S" + .word TAB_ASCT ; table for "T" + .word TAB_ASCU ; table for "U" + .word TAB_ASCV ; table for "V" + .word TAB_ASCW ; table for "W" + .word TAB_POWR ; table for "^" + +; tables for each start character, note if a longer keyword with the same start +; letters as a shorter one exists then it must come first, else the list is in +; alphabetical order as follows .. + +; [keyword,token +; [keyword,token]] +; end marker (#$00) + +TAB_STAR + .byte TK_MUL,$00 ; * +TAB_PLUS + .byte TK_PLUS,$00 ; + +TAB_MNUS + .byte TK_MINUS,$00 ; - +TAB_SLAS + .byte TK_DIV,$00 ; / +TAB_LESS +LBB_LSHIFT + .byte "<",TK_LSHIFT ; << note - "<<" must come before "<" + .byte TK_LT ; < + .byte $00 +TAB_EQUL + .byte TK_EQUAL,$00 ; = +TAB_MORE +LBB_RSHIFT + .byte ">",TK_RSHIFT ; >> note - ">>" must come before ">" + .byte TK_GT ; > + .byte $00 +TAB_QEST + .byte TK_PRINT,$00 ; ? +TAB_ASCA +LBB_ABS + .byte "BS(",TK_ABS ; ABS( +LBB_AND + .byte "ND",TK_AND ; AND +LBB_ASC + .byte "SC(",TK_ASC ; ASC( +LBB_ATN + .byte "TN(",TK_ATN ; ATN( + .byte $00 +TAB_ASCB +LBB_BINS + .byte "IN$(",TK_BINS ; BIN$( +LBB_BITCLR + .byte "ITCLR",TK_BITCLR ; BITCLR +LBB_BITSET + .byte "ITSET",TK_BITSET ; BITSET +LBB_BITTST + .byte "ITTST(",TK_BITTST + ; BITTST( + .byte $00 +TAB_ASCC +LBB_CALL + .byte "ALL",TK_CALL ; CALL +LBB_CHRS + .byte "HR$(",TK_CHRS ; CHR$( +LBB_CLEAR + .byte "LEAR",TK_CLEAR ; CLEAR +LBB_CONT + .byte "ONT",TK_CONT ; CONT +LBB_COS + .byte "OS(",TK_COS ; COS( + .byte $00 +TAB_ASCD +LBB_DATA + .byte "ATA",TK_DATA ; DATA +LBB_DEC + .byte "EC",TK_DEC ; DEC +LBB_DEEK + .byte "EEK(",TK_DEEK ; DEEK( +LBB_DEF + .byte "EF",TK_DEF ; DEF +LBB_DIM + .byte "IM",TK_DIM ; DIM +LBB_DOKE + .byte "OKE",TK_DOKE ; DOKE note - "DOKE" must come before "DO" +LBB_DO + .byte "O",TK_DO ; DO + .byte $00 +TAB_ASCE +LBB_ELSE + .byte "LSE",TK_ELSE ; ELSE +LBB_END + .byte "ND",TK_END ; END +LBB_EOR + .byte "OR",TK_EOR ; EOR +LBB_EXP + .byte "XP(",TK_EXP ; EXP( + .byte $00 +TAB_ASCF +LBB_FN + .byte "N",TK_FN ; FN +LBB_FOR + .byte "OR",TK_FOR ; FOR +LBB_FRE + .byte "RE(",TK_FRE ; FRE( + .byte $00 +TAB_ASCG +LBB_GET + .byte "ET",TK_GET ; GET +LBB_GOSUB + .byte "OSUB",TK_GOSUB ; GOSUB +LBB_GOTO + .byte "OTO",TK_GOTO ; GOTO + .byte $00 +TAB_ASCH +LBB_HEXS + .byte "EX$(",TK_HEXS ; HEX$( + .byte $00 +TAB_ASCI +LBB_IF + .byte "F",TK_IF ; IF +LBB_INC + .byte "NC",TK_INC ; INC +LBB_INPUT + .byte "NPUT",TK_INPUT ; INPUT +LBB_INT + .byte "NT(",TK_INT ; INT( +LBB_IRQ + .byte "RQ",TK_IRQ ; IRQ + .byte $00 +TAB_ASCL +LBB_LCASES + .byte "CASE$(",TK_LCASES + ; LCASE$( +LBB_LEFTS + .byte "EFT$(",TK_LEFTS ; LEFT$( +LBB_LEN + .byte "EN(",TK_LEN ; LEN( +LBB_LET + .byte "ET",TK_LET ; LET +LBB_LIST + .byte "IST",TK_LIST ; LIST +LBB_LOAD + .byte "OAD",TK_LOAD ; LOAD +LBB_LOG + .byte "OG(",TK_LOG ; LOG( +LBB_LOOP + .byte "OOP",TK_LOOP ; LOOP + .byte $00 +TAB_ASCM +LBB_MAX + .byte "AX(",TK_MAX ; MAX( +LBB_MIDS + .byte "ID$(",TK_MIDS ; MID$( +LBB_MIN + .byte "IN(",TK_MIN ; MIN( + .byte $00 +TAB_ASCN +LBB_NEW + .byte "EW",TK_NEW ; NEW +LBB_NEXT + .byte "EXT",TK_NEXT ; NEXT +LBB_NMI + .byte "MI",TK_NMI ; NMI +LBB_NOT + .byte "OT",TK_NOT ; NOT +LBB_NULL + .byte "ULL",TK_NULL ; NULL + .byte $00 +TAB_ASCO +LBB_OFF + .byte "FF",TK_OFF ; OFF +LBB_ON + .byte "N",TK_ON ; ON +LBB_OR + .byte "R",TK_OR ; OR + .byte $00 +TAB_ASCP +LBB_PEEK + .byte "EEK(",TK_PEEK ; PEEK( +LBB_PI + .byte "I",TK_PI ; PI +LBB_POKE + .byte "OKE",TK_POKE ; POKE +LBB_POS + .byte "OS(",TK_POS ; POS( +LBB_PRINT + .byte "RINT",TK_PRINT ; PRINT + .byte $00 +TAB_ASCR +LBB_READ + .byte "EAD",TK_READ ; READ +LBB_REM + .byte "EM",TK_REM ; REM +LBB_RESTORE + .byte "ESTORE",TK_RESTORE + ; RESTORE +LBB_RETIRQ + .byte "ETIRQ",TK_RETIRQ ; RETIRQ +LBB_RETNMI + .byte "ETNMI",TK_RETNMI ; RETNMI +LBB_RETURN + .byte "ETURN",TK_RETURN ; RETURN +LBB_RIGHTS + .byte "IGHT$(",TK_RIGHTS + ; RIGHT$( +LBB_RND + .byte "ND(",TK_RND ; RND( +LBB_RUN + .byte "UN",TK_RUN ; RUN + .byte $00 +TAB_ASCS +LBB_SADD + .byte "ADD(",TK_SADD ; SADD( +LBB_SAVE + .byte "AVE",TK_SAVE ; SAVE +LBB_SGN + .byte "GN(",TK_SGN ; SGN( +LBB_SIN + .byte "IN(",TK_SIN ; SIN( +LBB_SPC + .byte "PC(",TK_SPC ; SPC( +LBB_SQR + .byte "QR(",TK_SQR ; SQR( +LBB_STEP + .byte "TEP",TK_STEP ; STEP +LBB_STOP + .byte "TOP",TK_STOP ; STOP +LBB_STRS + .byte "TR$(",TK_STRS ; STR$( +LBB_SWAP + .byte "WAP",TK_SWAP ; SWAP + .byte $00 +TAB_ASCT +LBB_TAB + .byte "AB(",TK_TAB ; TAB( +LBB_TAN + .byte "AN(",TK_TAN ; TAN( +LBB_THEN + .byte "HEN",TK_THEN ; THEN +LBB_TO + .byte "O",TK_TO ; TO +LBB_TWOPI + .byte "WOPI",TK_TWOPI ; TWOPI + .byte $00 +TAB_ASCU +LBB_UCASES + .byte "CASE$(",TK_UCASES + ; UCASE$( +LBB_UNTIL + .byte "NTIL",TK_UNTIL ; UNTIL +LBB_USR + .byte "SR(",TK_USR ; USR( + .byte $00 +TAB_ASCV +LBB_VAL + .byte "AL(",TK_VAL ; VAL( +LBB_VPTR + .byte "ARPTR(",TK_VPTR ; VARPTR( + .byte $00 +TAB_ASCW +LBB_WAIT + .byte "AIT",TK_WAIT ; WAIT +LBB_WHILE + .byte "HILE",TK_WHILE ; WHILE +LBB_WIDTH + .byte "IDTH",TK_WIDTH ; WIDTH + .byte $00 +TAB_POWR + .byte TK_POWER,$00 ; ^ + +; new decode table for LIST +; Table is .. +; byte - keyword length, keyword first character +; word - pointer to rest of keyword from dictionary + +; note if length is 1 then the pointer is ignored + +LAB_KEYT + .byte 3,'E' + .word LBB_END ; END + .byte 3,'F' + .word LBB_FOR ; FOR + .byte 4,'N' + .word LBB_NEXT ; NEXT + .byte 4,'D' + .word LBB_DATA ; DATA + .byte 5,'I' + .word LBB_INPUT ; INPUT + .byte 3,'D' + .word LBB_DIM ; DIM + .byte 4,'R' + .word LBB_READ ; READ + .byte 3,'L' + .word LBB_LET ; LET + .byte 3,'D' + .word LBB_DEC ; DEC + .byte 4,'G' + .word LBB_GOTO ; GOTO + .byte 3,'R' + .word LBB_RUN ; RUN + .byte 2,'I' + .word LBB_IF ; IF + .byte 7,'R' + .word LBB_RESTORE ; RESTORE + .byte 5,'G' + .word LBB_GOSUB ; GOSUB + .byte 6,'R' + .word LBB_RETIRQ ; RETIRQ + .byte 6,'R' + .word LBB_RETNMI ; RETNMI + .byte 6,'R' + .word LBB_RETURN ; RETURN + .byte 3,'R' + .word LBB_REM ; REM + .byte 4,'S' + .word LBB_STOP ; STOP + .byte 2,'O' + .word LBB_ON ; ON + .byte 4,'N' + .word LBB_NULL ; NULL + .byte 3,'I' + .word LBB_INC ; INC + .byte 4,'W' + .word LBB_WAIT ; WAIT + .byte 4,'L' + .word LBB_LOAD ; LOAD + .byte 4,'S' + .word LBB_SAVE ; SAVE + .byte 3,'D' + .word LBB_DEF ; DEF + .byte 4,'P' + .word LBB_POKE ; POKE + .byte 4,'D' + .word LBB_DOKE ; DOKE + .byte 4,'C' + .word LBB_CALL ; CALL + .byte 2,'D' + .word LBB_DO ; DO + .byte 4,'L' + .word LBB_LOOP ; LOOP + .byte 5,'P' + .word LBB_PRINT ; PRINT + .byte 4,'C' + .word LBB_CONT ; CONT + .byte 4,'L' + .word LBB_LIST ; LIST + .byte 5,'C' + .word LBB_CLEAR ; CLEAR + .byte 3,'N' + .word LBB_NEW ; NEW + .byte 5,'W' + .word LBB_WIDTH ; WIDTH + .byte 3,'G' + .word LBB_GET ; GET + .byte 4,'S' + .word LBB_SWAP ; SWAP + .byte 6,'B' + .word LBB_BITSET ; BITSET + .byte 6,'B' + .word LBB_BITCLR ; BITCLR + .byte 3,'I' + .word LBB_IRQ ; IRQ + .byte 3,'N' + .word LBB_NMI ; NMI + +; secondary commands (can't start a statement) + + .byte 4,'T' + .word LBB_TAB ; TAB + .byte 4,'E' + .word LBB_ELSE ; ELSE + .byte 2,'T' + .word LBB_TO ; TO + .byte 2,'F' + .word LBB_FN ; FN + .byte 4,'S' + .word LBB_SPC ; SPC + .byte 4,'T' + .word LBB_THEN ; THEN + .byte 3,'N' + .word LBB_NOT ; NOT + .byte 4,'S' + .word LBB_STEP ; STEP + .byte 5,'U' + .word LBB_UNTIL ; UNTIL + .byte 5,'W' + .word LBB_WHILE ; WHILE + .byte 3,'O' + .word LBB_OFF ; OFF + +; opperators + + .byte 1,'+' + .word $0000 ; + + .byte 1,'-' + .word $0000 ; - + .byte 1,'*' + .word $0000 ; * + .byte 1,'/' + .word $0000 ; / + .byte 1,'^' + .word $0000 ; ^ + .byte 3,'A' + .word LBB_AND ; AND + .byte 3,'E' + .word LBB_EOR ; EOR + .byte 2,'O' + .word LBB_OR ; OR + .byte 2,'>' + .word LBB_RSHIFT ; >> + .byte 2,'<' + .word LBB_LSHIFT ; << + .byte 1,'>' + .word $0000 ; > + .byte 1,'=' + .word $0000 ; = + .byte 1,'<' + .word $0000 ; < + +; functions + + .byte 4,'S' ; + .word LBB_SGN ; SGN + .byte 4,'I' ; + .word LBB_INT ; INT + .byte 4,'A' ; + .word LBB_ABS ; ABS + .byte 4,'U' ; + .word LBB_USR ; USR + .byte 4,'F' ; + .word LBB_FRE ; FRE + .byte 4,'P' ; + .word LBB_POS ; POS + .byte 4,'S' ; + .word LBB_SQR ; SQR + .byte 4,'R' ; + .word LBB_RND ; RND + .byte 4,'L' ; + .word LBB_LOG ; LOG + .byte 4,'E' ; + .word LBB_EXP ; EXP + .byte 4,'C' ; + .word LBB_COS ; COS + .byte 4,'S' ; + .word LBB_SIN ; SIN + .byte 4,'T' ; + .word LBB_TAN ; TAN + .byte 4,'A' ; + .word LBB_ATN ; ATN + .byte 5,'P' ; + .word LBB_PEEK ; PEEK + .byte 5,'D' ; + .word LBB_DEEK ; DEEK + .byte 5,'S' ; + .word LBB_SADD ; SADD + .byte 4,'L' ; + .word LBB_LEN ; LEN + .byte 5,'S' ; + .word LBB_STRS ; STR$ + .byte 4,'V' ; + .word LBB_VAL ; VAL + .byte 4,'A' ; + .word LBB_ASC ; ASC + .byte 7,'U' ; + .word LBB_UCASES ; UCASE$ + .byte 7,'L' ; + .word LBB_LCASES ; LCASE$ + .byte 5,'C' ; + .word LBB_CHRS ; CHR$ + .byte 5,'H' ; + .word LBB_HEXS ; HEX$ + .byte 5,'B' ; + .word LBB_BINS ; BIN$ + .byte 7,'B' ; + .word LBB_BITTST ; BITTST + .byte 4,'M' ; + .word LBB_MAX ; MAX + .byte 4,'M' ; + .word LBB_MIN ; MIN + .byte 2,'P' ; + .word LBB_PI ; PI + .byte 5,'T' ; + .word LBB_TWOPI ; TWOPI + .byte 7,'V' ; + .word LBB_VPTR ; VARPTR + .byte 6,'L' ; + .word LBB_LEFTS ; LEFT$ + .byte 7,'R' ; + .word LBB_RIGHTS ; RIGHT$ + .byte 5,'M' ; + .word LBB_MIDS ; MID$ + +; BASIC messages, mostly error messages + +LAB_BAER + .word ERR_NF ;$00 NEXT without FOR + .word ERR_SN ;$02 syntax + .word ERR_RG ;$04 RETURN without GOSUB + .word ERR_OD ;$06 out of data + .word ERR_FC ;$08 function call + .word ERR_OV ;$0A overflow + .word ERR_OM ;$0C out of memory + .word ERR_US ;$0E undefined statement + .word ERR_BS ;$10 array bounds + .word ERR_DD ;$12 double dimension array + .word ERR_D0 ;$14 divide by 0 + .word ERR_ID ;$16 illegal direct + .word ERR_TM ;$18 type mismatch + .word ERR_LS ;$1A long string + .word ERR_ST ;$1C string too complex + .word ERR_CN ;$1E continue error + .word ERR_UF ;$20 undefined function + .word ERR_LD ;$22 LOOP without DO + +; I may implement these two errors to force definition of variables and +; dimensioning of arrays before use. + +; .word ERR_UV ;$24 undefined variable + +; the above error has been tested and works (see code and comments below LAB_1D8B) + +; .word ERR_UA ;$26 undimensioned array + +ERR_NF .byte "NEXT without FOR",$00 +ERR_SN .byte "Syntax",$00 +ERR_RG .byte "RETURN without GOSUB",$00 +ERR_OD .byte "Out of DATA",$00 +ERR_FC .byte "Function call",$00 +ERR_OV .byte "Overflow",$00 +ERR_OM .byte "Out of memory",$00 +ERR_US .byte "Undefined statement",$00 +ERR_BS .byte "Array bounds",$00 +ERR_DD .byte "Double dimension",$00 +ERR_D0 .byte "Divide by zero",$00 +ERR_ID .byte "Illegal direct",$00 +ERR_TM .byte "Type mismatch",$00 +ERR_LS .byte "String too long",$00 +ERR_ST .byte "String too complex",$00 +ERR_CN .byte "Can't continue",$00 +ERR_UF .byte "Undefined function",$00 +ERR_LD .byte "LOOP without DO",$00 + +;ERR_UV .byte "Undefined variable",$00 + +; the above error has been tested and works (see code and comments below LAB_1D8B) + +;ERR_UA .byte "Undimensioned array",$00 + +LAB_BMSG .byte $0D,$0A,"Break",$00 +LAB_EMSG .byte " Error",$00 +LAB_LMSG .byte " in line ",$00 +LAB_RMSG .byte $0D,$0A,"Ready",$0D,$0A,$00 + +LAB_IMSG .byte " Extra ignored",$0D,$0A,$00 +LAB_REDO .byte " Redo from start",$0D,$0A,$00 + +AA_end_basic diff --git a/EhBASIC/original/min_mon.asm b/EhBASIC/original/min_mon.asm new file mode 100644 index 0000000..2b71dc1 --- /dev/null +++ b/EhBASIC/original/min_mon.asm @@ -0,0 +1,134 @@ + +; minimal monitor for EhBASIC and 6502 simulator V1.05 + +; To run EhBASIC on the simulator load and assemble [F7] this file, start the simulator +; running [F6] then start the code with the RESET [CTRL][SHIFT]R. Just selecting RUN +; will do nothing, you'll still have to do a reset to run the code. + + .include "basic.asm" + +; put the IRQ and MNI code in RAM so that it can be changed + +IRQ_vec = VEC_SV+2 ; IRQ code vector +NMI_vec = IRQ_vec+$0A ; NMI code vector + +; setup for the 6502 simulator environment + +IO_AREA = $F000 ; set I/O area for this monitor + +ACIAsimwr = IO_AREA+$01 ; simulated ACIA write port +ACIAsimrd = IO_AREA+$04 ; simulated ACIA read port + +; now the code. all this does is set up the vectors and interrupt code +; and wait for the user to select [C]old or [W]arm start. nothing else +; fits in less than 128 bytes + + *= $FF80 ; pretend this is in a 1/8K ROM + +; reset vector points here + +RES_vec + CLD ; clear decimal mode + LDX #$FF ; empty stack + TXS ; set the stack + +; set up vectors and interrupt code, copy them to page 2 + + LDY #END_CODE-LAB_vec ; set index/count +LAB_stlp + LDA LAB_vec-1,Y ; get byte from interrupt code + STA VEC_IN-1,Y ; save to RAM + DEY ; decrement index/count + BNE LAB_stlp ; loop if more to do + +; now do the signon message, Y = $00 here + +LAB_signon + LDA LAB_mess,Y ; get byte from sign on message + BEQ LAB_nokey ; exit loop if done + + JSR V_OUTP ; output character + INY ; increment index + BNE LAB_signon ; loop, branch always + +LAB_nokey + JSR V_INPT ; call scan input device + BCC LAB_nokey ; loop if no key + + AND #$DF ; mask xx0x xxxx, ensure upper case + CMP #'W' ; compare with [W]arm start + BEQ LAB_dowarm ; branch if [W]arm start + + CMP #'C' ; compare with [C]old start + BNE RES_vec ; loop if not [C]old start + + JMP LAB_COLD ; do EhBASIC cold start + +LAB_dowarm + JMP LAB_WARM ; do EhBASIC warm start + +; byte out to simulated ACIA + +ACIAout + STA ACIAsimwr ; save byte to simulated ACIA + RTS + +; byte in from simulated ACIA + +ACIAin + LDA ACIAsimrd ; get byte from simulated ACIA + BEQ LAB_nobyw ; branch if no byte waiting + + SEC ; flag byte received + RTS + +LAB_nobyw + CLC ; flag no byte received +no_load ; empty load vector for EhBASIC +no_save ; empty save vector for EhBASIC + RTS + +; vector tables + +LAB_vec + .word ACIAin ; byte in from simulated ACIA + .word ACIAout ; byte out to simulated ACIA + .word no_load ; null load vector for EhBASIC + .word no_save ; null save vector for EhBASIC + +; EhBASIC IRQ support + +IRQ_CODE + PHA ; save A + LDA IrqBase ; get the IRQ flag byte + LSR ; shift the set b7 to b6, and on down ... + ORA IrqBase ; OR the original back in + STA IrqBase ; save the new IRQ flag byte + PLA ; restore A + RTI + +; EhBASIC NMI support + +NMI_CODE + PHA ; save A + LDA NmiBase ; get the NMI flag byte + LSR ; shift the set b7 to b6, and on down ... + ORA NmiBase ; OR the original back in + STA NmiBase ; save the new NMI flag byte + PLA ; restore A + RTI + +END_CODE + +LAB_mess + .byte $0D,$0A,"6502 EhBASIC [C]old/[W]arm ?",$00 + ; sign on string + +; system vectors + + *= $FFFA + + .word NMI_vec ; NMI vector + .word RES_vec ; RESET vector + .word IRQ_vec ; IRQ vector + diff --git a/EhBASIC/original/readme.txt b/EhBASIC/original/readme.txt new file mode 100644 index 0000000..d1e864a --- /dev/null +++ b/EhBASIC/original/readme.txt @@ -0,0 +1,26 @@ + + Enhanced BASIC is a BASIC interpreter for the 6502 family microprocessors. It + is constructed to be quick and powerful and easily ported between 6502 systems. + It requires few resources to run and includes instructions to facilitate easy + low level handling of hardware devices. It also retains most of the powerful + high level instructions from similar BASICs. + + EhBASIC is free but not copyright free. For non commercial use there is only one + restriction, any derivative work should include, in any binary image distributed, + the string "Derived from EhBASIC" and in any distribution that includes human + readable files a file that includes the above string in a human readable form + e.g. not as a comment in an HTML file. + + For commercial use please contact me, Lee Davison, at leeedavison@googlemail.com + for conditions. + + For more information on EhBASIC, other versions of EhBASIC and other projects + please visit my site at .. + + http://members.multimania.co.uk/leeedavison/index.html + + + P.S. c't magazin, henceforth refered to as "those thieving german bastards", are + prohibited from using this or any version of EhBASIC for any of their projects + or products. The excuse "we don't charge people for it" doesn't wash, it adds + value to your product so you owe me. diff --git a/Krusader/Krusader 1.3 65C02.asm b/Krusader/Krusader 1.3 65C02.asm new file mode 100644 index 0000000..da26a33 --- /dev/null +++ b/Krusader/Krusader 1.3 65C02.asm @@ -0,0 +1,3023 @@ +; KRUSADER - An Editor/Assembler/Disassembler for the Replica 1 + +; 65C02 version 1.3 - 24 December, 2007 +; (c) Ken Wessen (ken.wessen@gmail.com) + +; Notes: +; - entry points: +; SHELL = $711C($F01C) +; MOVEDN = $7304($F204) +; DEBUG = -($FE03) +; SHOW = -($FE16) +; DSMBL = $7BEA($FAEA) +; **************************************** +; - Does not support the single bit operations BBR, BBS, SMB, RMB, or STP and WAI +; - minimonitor does not include tracing +; - 49 bytes free + +APPLE1 =1 +INROM =1 +TABTOSPACE = 1 +UNK_ERR_CHECK = 0 + +MINIMONITOR = INROM & 1 +BRKAS2 = 1 ; if 1, BRK will assemble to two $00 bytes + ; if set, then minimonitor will work unchanged + ; for both hardware and software interrupts + + .if INROM + .org $F000 + .else + .org $7100 + .endif + .start MAIN + + .if INROM +MONTOR =ESCAPE + .ELSE +MONTOR =$FF1F + .endif + +; Constants + +BS =$08 ; backspace +SP =$20 ; space +CR =$0D ; carriage return +LF =$0A ; line feed +ESC =$1B ; escape +INMASK =$7F + +LNMSZ =$03 +LBLSZ =$06 ; labels are up to 6 characters +MNESZ =$03 ; mnemonics are always 3 characters +ARGSZ =$0E ; arguments are up to 14 characters +COMSZ =$0A ; comments fill the rest - up to 10 characters + +ENDLBL =LNMSZ+LBLSZ+1 +ENDMNE =ENDLBL+MNESZ+1 +ENDARG =ENDMNE+ARGSZ+1 +ENDLN =ENDARG+COMSZ+1 + +SYMSZ =$06 ; size of labels + +LINESZ =$27 ; size of a line +USESZ =LINESZ-LNMSZ-1 ; usable size of a line +CNTSZ =COMM-LABEL-1 ; size of content in a line + +MAXSYM =$20 ; at most 32 local symbols (256B) and +MAXFRF =$55 ; 85 forward references (896B) + ; globals are limited by 1 byte index => max of 256 (2K) + ; global symbol table grows downwards + +; Symbols used in source code + +IMV ='#' ; Indicates immediate mode value +HEX ='$' ; Indicates a hex value +OPEN ='(' ; Open bracket for indirect addressing +CLOSE =')' ; Close bracket for indirect addressing +PC ='*' ; Indicates PC relative addressing +LOBYTE ='<' ; Indicates lo-byte of following word +HIBYTE ='>' ; Indicates hi-byte of following word +PLUS ='+' ; Plus in simple expressions +MINUS ='-' ; Minus in simple expressions +DOT ='.' ; Indicates a local label +QUOTE =''' ; delimits a string +COMMA =',' +CMNT =';' ; indicates a full line comment + +PROMPT ='?' + +EOL =$00 ; end of line marker +EOFLD =$01 ; end of field in tokenised source line +BLANK =$02 ; used to mark a blank line + +PRGEND =$FE ; used to flag end of program parsing +FAIL =$FF ; used to flag failure in various searches + +; Zero page storage +IOBUF =$00 ; I/O buffer for source code input and analysis +LABEL =$04 ; label starts here +MNE =$0B ; mnemonic starts here +ARGS =$0F ; arguments start here +COMM =$1D ; comments start here +FREFTL =$29 ; address of forward reference table +FREFTH =$2A +NFREF =$2B ; number of forward symbols +RECNM =$2C ; number of table entries +RECSZ =$2D ; size of table entries +RECSIG =$2E ; significant characters in table entries +XSAV =$2F +YSAV =$30 +CURMNE =$3C ; Holds the current mne index +CURADM =$3D ; Holds the current addressing mode +LVALL =$3E ; Storage for a label value +LVALH =$3F +TBLL =$40 ; address of search table +TBLH =$41 +STRL =$42 ; address of search string +STRH =$43 +SCRTCH =$44 ; scratch location +NPTCH =$45 ; counts frefs when patching +PTCHTL =$46 ; address of forward reference being patched +PTCHTH =$47 +FLGSAV =$48 + +MISCL =$50 ; Miscellaneous address pointer +MISCH =$51 +MISC2L =$52 ; And another +MISC2H =$53 +TEMP1 =$54 ; general purpose storage +TEMP2 =$55 +TEMP3 =$56 +TEMP4 =$57 +LMNE =TEMP3 ; alias for compression and expansion routines +RMNE =TEMP4 +FRFLAG =$58 ; if nonzero, disallow forward references +ERFLAG =$59 ; if nonzero, do not report error line +HADFRF =$5A ; if nonzero, handled a forward reference +PRFLAG =$5B + +; want these to persist if possible when going into the monitor +; to test code etc, so put them right up high +; these 6 locations must be contiguous +GSYMTL =$E9 ; address of the global symbol table +GSYMTH =$EA +NGSYM =$EB ; number of global symbols +LSYMTL =$EC ; address of the local symbol table +LSYMTH =$ED +NLSYM =$EE ; number of local symbols + + .if MINIMONITOR +; these 7 locations must be contiguous +REGS =$F0 +SAVP =REGS +SAVS =$F1 +SAVY =$F2 +SAVX =$F3 +SAVA =$F4 + .endif +CURPCL =$F5 ; Current PC +CURPCH =$F6 + +CODEH =$F8 ; hi byte of code storage area (low is $00) +TABLEH =$F9 ; hi byte of symbol table area + +; these 4 locations must be contiguous +LINEL =$FA ; Current source line number (starts at 0) +LINEH =$FB +CURLNL =$FC ; Current source line address +CURLNH =$FD + +SRCSTL =$FE ; source code start address +SRCSTH =$FF + +; for disassembler +FORMAT =FREFTL ; re-use spare locations +LENGTH =FREFTH +COUNT =NFREF +PCL =CURPCL +PCH =CURPCH + +; **************************************** +; COMMAND SHELL/EDITOR CODE +; **************************************** + +MAIN + .if INROM + LDA #$03 + STA CODEH + LDA #$20 + STA SRCSTH + LDA #$7C + STA TABLEH + .else + LDA #$03 + STA CODEH + LDA #$1D + STA SRCSTH + LDA #$6D + STA TABLEH + .endif + LDX #MSGSZ +.NEXT LDA MSG-1,X + JSR OUTCH + DEX + BNE .NEXT + DEX + TXS ; reset stack pointer on startup + JSR SHINIT ; default source line and address data +; JMP SHELL +; falls through to SHELL + +; **************************************** + +SHELL ; Loops forever + ; also the re-entry point + CLD ; just incase + LDA #$00 + STA PRFLAG + JSR FILBUF + LDX #ARGS + STX FRFLAG ; set flags in SHELL + STX ERFLAG + JSR CRLF + LDA #PROMPT + JSR OUTCH ; prompt + JSR OUTSP ; can drop this if desperate for 3 more bytes :-) +.KEY JSR GETCH + CMP #BS + BEQ SHELL ; start again + CMP #CR + BEQ .RUN + JSR OUTCH + STA IOBUF,X + INX + BNE .KEY ; always branches +.RUN LDA ARGS + BEQ SHELL ; empty command line + LDA ARGS+1 ; ensure command is just a single letter + BEQ .OK + CMP #SP + BNE SHLERR +.OK LDX #NUMCMD +.NEXT LDA CMDS-1,X ; find the typed command + CMP ARGS + BEQ GOTCMD + DEX + BNE .NEXT + PHA ; put dummy data on the stack + PHA +SHLERR + LDY #SYNTAX +ERR2 PLA ; need to clean up the stack + PLA + JSR SHWERR + BNE SHELL +GOTCMD JSR RUNCMD + JMP SHELL ; ready for next command + +; **************************************** + +SHINIT + LDA #$00 + TAY + STA SRCSTL ; low byte zero for storage area + STA (SRCSTL),Y ; and put a zero in it for EOP +TOSTRT ; set LINEL,H and CURLNL,H to the start + LDA SRCSTH + STA CURLNH + LDA #$00 + STA LINEL + STA LINEH ; 0 lines + STA CURLNL + RTS ; leaves $00 in A + +; **************************************** + +PANIC + JSR SHINIT + LDA ARGS+2 + BNE .SKIP + LDA #$01 +.SKIP STA (SRCSTL),Y ; Y is $00 from SHINIT + RTS + +; **************************************** + +VALUE + JSR ADDARG + BEQ SHLERR + JSR CRLF + LDA LVALH + LDX LVALL + JMP PRNTAX + +; **************************************** + +RUN + JSR ADDARG + BEQ SHLERR + JSR CRLF + JMP (LVALL) ; jump to the address + +; **************************************** + +ADDARG ; convert argument to address + LDX #$02 + LDA ARGS,X + BEQ .NOARG + PHA + JSR EVAL + PLA + ;CPX #FAIL + INX + BEQ ERR2;SHLERR +.NOARG RTS + +; **************************************** + +PCTOLV + LDA CURPCL + STA LVALL + LDA CURPCH + STA LVALH + RTS + +; **************************************** + +LVTOPC + LDA LVALL + STA CURPCL + LDA LVALH + STA CURPCH + RTS + +; **************************************** + +FILLSP + LDA #SP +FILBUF ; fill the buffer with the contents of A + LDX #LINESZ +.CLR STA IOBUF-1,X + DEX + BNE .CLR + RTS + +; **************************************** + +RUNCMD + LDA CMDH-1,X + PHA + LDA CMDL-1,X + PHA + RTS + +; **************************************** + +NEW + JSR SHINIT + JMP INSERT + +; **************************************** + +LIST ; L - list all + ; L nnn - list from line nnn + JSR TOSTRT + JSR GETARG + BEQ .NEXT ; no args, list from start + JSR GOTOLN ; deal with arguments if necessary +.NEXT LDY #$00 + LDA (CURLNL),Y + BEQ .RET + JSR PRNTLN + JSR UPDTCL + LDA KBDRDY + .if APPLE1 + BPL .NEXT + .else + BEQ .NEXT + .endif + LDA KBD +.RET RTS + +; **************************************** + +MEM + JSR TOEND ; set CURLNL,H to the end + JSR CRLF + LDX #$04 +.LOOP LDA CURLNL-1,X + JSR OUTHEX + CPX #$03 + BNE .SKIP + JSR PRDASH +.SKIP DEX + BNE .LOOP +RET RTS + +; **************************************** + +GETARG ; get the one or two numeric arguments + ; to the list, edit, delete and insert commands + ; store them in TEMP1-4 as found + ; arg count in Y or X has FAIL + LDY #$00 + STY YSAV + LDX #$01 +.NEXT LDA ARGS,X + BEQ .DONE ; null terminator + CMP #SP ; find the space + BEQ .CVT + CMP #HEX ; or $ symbol + BEQ .CVT + INX + BNE .NEXT +.CVT INC YSAV ; count args + LDA #HEX + STA ARGS,X ; replace the space with '$' and convert + JSR CONVRT + ;CPX #FAIL + INX + BEQ LCLERR + ;INX + LDA LVALL + STA TEMP1,Y + INY + LDA LVALH + STA TEMP1,Y + INY + BNE .NEXT ; always branches +.DONE LDY YSAV + RTS ; m in TEMP1,2, n in TEMP3,4 + +; **************************************** + +EDIT + JSR GETARG + ;CPY #$01 + DEY + BNE LCLERR + JSR DELETE ; must not overwrite the command input buffer +; JMP INSERT +; falls through to INSERT + +; **************************************** + +INSERT + JSR GETARG ; deal with arguments if necessary + ;CPX #FAIL + INX + BEQ LCLERR + ;CPY #$00 ; no args + TYA + BNE .ARGS + JSR TOEND ; insert at the end + CLC + BCC .IN +.ARGS JSR GOTOLN ; if no such line will insert at end +.IN JSR INPUT ; Get one line + CPX #FAIL ; Was there an error? + BEQ RET; + ; Save the tokenised line and update pointers + ; tokenised line is in IOBUF, size X + ; move up from CURLNL,H to make space + STX XSAV ; save X (data size) + LDA CURLNH + STA MISCH + STA MISC2H + LDA CURLNL + STA MISCL ; src in MISCL,H now + CLC + ADC XSAV + STA MISC2L + BCC .READY + INC MISC2H ; MISC2L,H is destination +.READY JSR GETSZ + JSR MOVEUP ; do the move + LDY #$00 + ; now move the line to the source storage area + ; Y bytes, from IOBUF to CURLN +.MOVE LDA IOBUF,Y + STA (CURLNL),Y + INY + CPY XSAV + BNE .MOVE + JSR UPDTCL ; update CURLNL,H + BNE .IN ; always branches + +; **************************************** + +LCLERR ; local error wrapper + ; shared by the routines around it + JMP SHLERR + +; **************************************** + +GETSZ ; SIZE = TEMP1,2 = lastlnL,H - MISCL,H + 1 + LDX #-$04 +.LOOP LDA CURLNH+1,X + PHA ; save CURLN and LINEN on the stack + INX + BNE .LOOP + JSR TOEND + SEC + LDA CURLNL + SBC MISCL + STA TEMP1 + LDA CURLNH + SBC MISCH + STA TEMP2 + INC TEMP1 + BNE .SKIP + INC TEMP2 +.SKIP LDX #$04 +.LOOP2 PLA ; get CURLN and LINEN from the stack + STA LINEL-1,X + DEX + BNE .LOOP2 + RTS + +; **************************************** + +DELETE ; Delete the specified range + ; Moves from address of line arg2 (MISCL,H) + ; to address of line arg1 (MISC2L,H) + JSR GETARG +; CPY #$00 + BEQ LCLERR + STY YSAV +.DOIT JSR GOTOLN ; this leaves TEMP1 in Y and TEMP2 in X + CPX #FAIL + BEQ LCLERR + LDA CURLNL + STA MISC2L + LDA CURLNH + STA MISC2H ; destination address is set in MISC2L,H + LDA YSAV + ;CMP #$01 + LSR + BEQ .INC + LDX TEMP4 + LDY TEMP3 ; Validate the range arguments + CPX TEMP2 ; First compare high bytes + BNE .CHK ; If TEMP4 != TEMP2, we just need to check carry + CPY TEMP1 ; Compare low bytes when needed +.CHK BCC LCLERR ; If carry clear, 2nd argument is too low +.INC INY ; Now increment the second argument + BNE .CONT + INX +.CONT STX TEMP2 + STY TEMP1 + JSR GOTOLN + LDA CURLNL + STA MISCL + LDA CURLNH + STA MISCH + JSR GETSZ +; JMP MOVEDN +; falls through + +; **************************************** +; Memory moving routines +; From http://www.6502.org/source/general/memory_move.html +; **************************************** + +; Some aliases for the following two memory move routines + +FROM =MISCL ; move from MISCL,H +TO =MISC2L ; to MISCL2,H +SIZEL =TEMP1 +SIZEH =TEMP2 + +MOVEDN ; Move memory down + LDY #$00 + LDX SIZEH + BEQ .MD2 +.MD1 LDA (FROM),Y ; move a page at a time + STA (TO),Y + INY + BNE .MD1 + INC FROM+1 + INC TO+1 + DEX + BNE .MD1 +.MD2 LDX SIZEL + BEQ .MD4 +.MD3 LDA (FROM),Y ; move the remaining bytes + STA (TO),Y + INY + DEX + BNE .MD3 +.MD4 RTS + +MOVEUP ; Move memory up + LDX SIZEH ; the last byte must be moved first + CLC ; start at the final pages of FROM and TO + TXA + ADC FROM+1 + STA FROM+1 + CLC + TXA + ADC TO+1 + STA TO+1 + INX ; allows the use of BNE after the DEX below + LDY SIZEL + BEQ .MU3 + DEY ; move bytes on the last page first + BEQ .MU2 +.MU1 LDA (FROM),Y + STA (TO),Y + DEY + BNE .MU1 +.MU2 LDA (FROM),Y ; handle Y = 0 separately + STA (TO),Y +.MU3 DEY + DEC FROM+1 ; move the next page (if any) + DEC TO+1 + DEX + BNE .MU1 + RTS + +; **************************************** + +TOEND + LDA #$FF + STA TEMP2 ; makes illegal line number +; JMP GOTOLN ; so CURLNL,H will be set to the end +; falls through + +; **************************************** + +GOTOLN ; go to line number given in TEMP1,2 + ; sets CURLNL,H to the appropriate address + ; and leaves TEMP1 in Y and TEMP2 in X + ; if not present, return #FAIL in X + ; and LINEL,H will be set to the next available line number +EOP = LFAIL +GOTIT = LRET + JSR TOSTRT +.NXTLN ; is the current line number the same + ; as specified in TEMP1,2? + ; Z set if equal + ; C set if TEMP1,2 >= LINEL,H + LDY TEMP1 + CPY LINEL + BNE .NO + LDX TEMP2 + CPX LINEH + BEQ GOTIT +.NO LDY #$FF +.NXTBT INY ; find EOL + LDA (CURLNL),Y + BNE .NXTBT + TYA + ;CPY #$00 + BEQ EOP ; null at start of line => end of program + INY + JSR UPDTCL ; increment CURLNL,H by Y bytes + BNE .NXTLN ; always branches +;.EOP LDX #FAIL +;.GOTIT RTS ; address is now in CURLNL,H + +; **************************************** + +PRNTLN ; print out the current line (preserve X) + JSR CRLF + STX XSAV + JSR DETKN + INY + JSR PRLNNM + LDX #$00 +.PRINT LDA LABEL,X + BEQ .DONE ; null terminator + JSR OUTCH + INX + ;CPX #USESZ + BNE .PRINT +.DONE LDX XSAV + RTS + +; **************************************** + +NEXTCH ; Check for valid character in A + ; Also allows direct entry to appropriate location + ; Flag success with C flag + JSR GETCH + .if TABTOSPACE + CMP #$09 ; is it a tab? + BNE .SKIP + LDA #SP + .endif +.SKIP CMP #SP ; valid ASCII range is $20 to $5D + BPL CHANM ; check alpha numeric entries + TAY + PLA + PLA + PLA + PLA ; wipe out return addresses + CPY #BS + BEQ INPUT ; just do it all again +.NOBS CPY #CR + BNE LFAIL + CPX #LABEL ; CR at start of LABEL means a blank line + BEQ DOBLNK + LDA #EOL + STA IOBUF,X + BEQ GOTEOL +LFAIL LDX #FAIL ; may flag error or just end +LRET RTS +CHANM CPX #LINESZ ; ignore any characters over the end of the line + BPL CHNO +; CMP #']'+1 ; is character is in range $20-$5D? +; BPL CHNO ; branch to NO... +CHOK SEC ; C flag on indicates success + RTS +CHKLBL CMP #DOT ; here are more specific checks + BEQ CHOK +CHKALN CMP #'0' ; check alpha-numeric + BMI CHNO ; less than 0 + CMP #'9'+1 + BMI CHOK ; between 0 and 9 +CHKLET CMP #'A' + BMI CHNO ; less than A + CMP #'Z'+1 + BMI CHOK ; between A and Z +CHNO CLC + RTS ; C flag off indicates failure + +; **************************************** + +DOBLNK + LDA #BLANK + TAX ; BLANK = #$02, and that is also the + STA IOBUF ; tokenised size of a blank line + LDA #EOL ; (and only a blank line) + STA IOBUF+1 +ENDIN RTS + +INPUT + JSR FILLSP + LDA #EOL ; need this marker at the start of the comments + STA COMM ; for when return hit in args field + JSR CRLF + JSR PRLNNM + LDX #LABEL ; point to LABEL area + LDA #ENDLBL + JSR ONEFLD + JSR INSSPC ; Move to mnemonic field + LDA LABEL + CMP #CMNT + BEQ .CMNT + LDA #ENDMNE + JSR ONEFLD + JSR INSSPC ; Move to args field + LDA #ENDARG + JSR ONEFLD +.CMNT LDA #EOL + JSR ONEFLD +GOTEOL ;JMP TOTKN +; falls through + +; **************************************** + +TOTKN ; tokenise to IOBUF to calc size + ; then move memory to make space + ; then copy from IOBUF into the space + LDX #$00 + STX MISCH + LDA #SP + STA TEMP2 + LDA #LABEL + STA MISCL + LDA #EOFLD + STA TEMP1 + JSR TKNISE + LDY LABEL + CPY #CMNT + BNE .CONT + LDA #MNE + BNE ISCMNT ; always branches +.CONT TXA ; save X + PHA + +; JSR SRCHMN ; is it a mnemonic? + +SRCHMN ; Search the table of mnemonics for the mnemonic in MNE + ; Return the index in A + +CMPMNE ; compress the 3 char mnemonic + ; at MNE to MNE+2 into 2 chars + ; at LMNE and RMNE + CLC + ROR LMNE + LDX #$03 +.NEXT2 SEC + LDA MNE-1,X + SBC #'A'-1 + LDY #$05 +.LOOP2 LSR + ROR LMNE + ROR RMNE + DEY + BNE .LOOP2 + DEX + BNE .NEXT2 + + LDX #NUMMN ; Number of mnemonics +.LOOP LDA LMNETB-1,X + CMP LMNE + BNE .NXT + LDA RMNETB-1,X + CMP RMNE + BEQ .FND +.NXT DEX + BNE .LOOP +.FND DEX ; X = $FF for failure +; RTS + + TXA + CMP #FAIL + BNE .FOUND + LDA MNE ; or a directive? + CMP #DOT + BNE .ERR + LDX #NUMDIR + LDA MNE+1 +.NEXT CMP DIRS-1,X + BEQ .FDIR + DEX + BNE .NEXT +.ERR PLA + LDY #INVMNE + JMP SHWERR +.FDIR DEX + ASL ; double directive code to avoid collisions +.FOUND TAY ; put mnemonic/directive code in Y + INY ; offset by 1 so no code $00 + PLA ; restore Y + TAX + STY IOBUF,X + INX + LDA #ARGS + STA MISCL + LDA #EOFLD + STA TEMP1 + JSR TKNISE + STX XSAV + INC XSAV + LDA #COMM +ISCMNT STA MISCL + LDA #EOL + STA TEMP1 + STA TEMP2 + JSR TKNISE + CPX XSAV + BNE .RET + DEX ; no args or comments, so stop early + STA IOBUF-1,X ; A already holds $00 +.RET RTS + +ONEFLD ; do one entry field + ; A holds the end point for the field + STA TEMP1 ; last position +.NEXT JSR NEXTCH ; catches ESC, CR and BS + BCC .NEXT ; only allow legal keys + JSR OUTCH ; echo + STA IOBUF,X + INX + CMP #SP + BEQ .FILL + CPX TEMP1 + BNE .NEXT +.RET RTS +.FILL LDA TEMP1 + BEQ .NEXT ; just treat a space normally + CPX TEMP1 ; fill spaces + BEQ .RET + LDA #SP + STA IOBUF,X + JSR OUTCH +.CONT INX + BNE .FILL ; always branches + +; **************************************** + +INSSPC + LDA IOBUF-1,X ; was previous character a space? + CMP #SP + BEQ .JUMP +.GET JSR NEXTCH ; handles BS, CR and ESC + CMP #SP + BNE .GET ; only let SP through +.JUMP STA IOBUF,X ; insert the space + INX + JMP OUTCH + +TKNISE + LDY #$00 +.NEXT LDA (MISCL),Y + BEQ .EOF + CMP TEMP2 + BEQ .EOF ; null terminator + STA IOBUF,X + INX + INC MISCL + BNE .NEXT +.EOF LDA TEMP1 + STA IOBUF,X + INX + RTS + +; **************************************** + +DETKN ; Load a line to the IOBUF + ; (detokenising as necessary) + ; On return, Y holds tokenised size + JSR FILLSP + LDY #$00 + LDX #LABEL +.LBL LDA (CURLNL),Y + BEQ .EOP ; indicates end of program + CMP #BLANK + BNE .SKIP + INY + LDA #EOL + BEQ .EOL +.SKIP CMP #EOFLD + BEQ .CHK + STA IOBUF,X + INX + INY + BNE .LBL +.CHK LDA LABEL + CMP #CMNT + BNE .NEXT + LDX #MNE + BNE .CMNT ; always branches +.NEXT INY + LDA (CURLNL),Y ; get mnemonic code + TAX + DEX ; correct for offset in tokenise + STX CURMNE ; store mnemonic for assembler + CPX #NUMMN + BPL .DIR + TYA ; save Y + PHA + JSR EXPMNE + PLA ; restore Y + TAY + BNE .REST +.DIR ;STX MNE+1 + TXA + LSR ; halve the directive codes + STA MNE+1 + LDA #DOT + STA MNE +.REST INY + LDX #ARGS ; point to ARGS area +.LOOP LDA (CURLNL),Y + BEQ .EOL ; indicates end of line + CMP #EOFLD + BNE .CONT + INY + LDX #COMM ; point to COMM area + BNE .LOOP +.CONT STA IOBUF,X + INX +.CMNT INY + BNE .LOOP +.EOP LDX #PRGEND +.EOL STA IOBUF,X + RTS + +; **************************************** +; ASSEMBLER CODE +; **************************************** + +ASSEM ; Run an assembly + JSR INIT ; Set the default values + JSR CRLF + JSR MYPRPC +.NEXT JSR DO1LN ; line is in the buffer - parse it + ;CPX #FAIL + INX + BEQ SHWERR + CPX #PRGEND+1 ; +1 because of INX above + BNE .NEXT + INC FRFLAG ; have to resolve them all now - this ensures FRFLAG nonzero + JSR PATCH ; back patch any remaining forward references + ;CPX #FAIL + INX + BEQ SHWERR + JMP MYPRPC ; output finishing module end address +;.ERR JMP SHWERR + +; **************************************** + +SHWERR ; Show message for error with id in Y + ; Also display line if appropriate + JSR CRLF + LDX #ERPRSZ +.NEXT LDA ERRPRE-1,X + JSR OUTCH + DEX + BNE .NEXT + TYA + .if UNK_ERR_CHECK + BEQ .SKIP + CPY #MAXERR+1 + BCC .SHOW ; If error code valid, show req'd string +.UNKWN LDY #UNKERR ; else show unknown error + BEQ .SKIP + .endif +.SHOW CLC + TXA ; sets A to zero +.ADD ADC #EMSGSZ + DEY + BNE .ADD + TAY +.SKIP LDX #EMSGSZ + .if UNK_ERR_CHECK +.LOOP LDA ERRMSG,Y + .else +.LOOP LDA ERRMSG-EMSGSZ,Y + .endif + JSR OUTCH + INY + DEX + BNE .LOOP + ;LDX #FAIL + DEX ; sets X = #FAIL + LDA ERFLAG + BNE RET1 + JMP PRNTLN + +; **************************************** + +INIT + JSR TOSTRT ; leaves $00 in A + STA FRFLAG + STA NGSYM + STA GSYMTL + STA CURPCL ; Initial value of PC for the assembled code + LDA CODEH + STA CURPCH + JSR CLRLCL ; set local and FREF table pointers + STX GSYMTH ; global table high byte - in X from CLRLCL +; JMP INITFR +; falls through + +; **************************************** + +INITFR ; initialise the FREF table and related pointers + LDA #$00 + STA NFREF + STA FREFTL + STA PTCHTL + LDY TABLEH + INY + STY FREFTH + STY PTCHTH +RET1 RTS + +; **************************************** + +DO1LN + JSR DETKN + CPX #PRGEND + BEQ .ENDPR + CPX #LABEL ; means we are still at the first field => blank line + BEQ .DONE + LDA #$00 + STA ERFLAG + STA FRFLAG + STA HADFRF + JSR PARSE + CPX #FAIL + BEQ DORTS +.CONT LDY #$00 +.LOOP LDA (CURLNL),Y + BEQ .DONE + INY + BNE .LOOP +.DONE INY ; one more to skip the null +.ENDPR ;JMP UPDTCL +; falls through + +; **************************************** + +UPDTCL ; update the current line pointer + ; by the number of bytes in Y + LDA CURLNL + STY SCRTCH + CLC + ADC SCRTCH ; move the current line pointer forward by 'Y' bytes + STA CURLNL + BCC INCLN + INC CURLNH ; increment the high byte if necessary +INCLN + INC LINEL + BNE DORTS + INC LINEH +DORTS RTS ; global label so can be shared + +; **************************************** + +MKOBJC ; MNE is in CURMNE, addr mode is in CURADM + ; and the args are in LVALL,H + ; calculate the object code, and update PC + LDY CURMNE + LDA BASE,Y ; get base value for current mnemonic + LDX CURADM + CLC + ADC OFFSET,X ; add in the offset +.NOSTZ CPX #ABY ; handle exceptions + BEQ .CHABY + CPX #IMM + BNE .CONT + CPY #$22 ; check if BIT first + BNE .NOBIT + ADC #ADJBIT +.NOBIT CPY #$28 ; immediate mode need to adjust a range + BMI .CONT + CPY #$2F+1 + BCS .CONT + ADC #ADJIMM ; carry is clear +; BNE .CONT +.CHABY CPY #$35 ; LDX check + BNE .CONT + CLC + ADC #ADJABY +.CONT CPY #$23 ; STZ needs special handling + BNE .DONE + CPX #ABS + BMI .DONE + BEQ .SKIP + ADC #$1-$10 ; carry is set +.SKIP ADC #$30-1 ; carry is set +.DONE JSR DOBYTE ; we have the object code + .IF BRKAS2 + CMP #$00 + BNE .MKARG + JSR DOBYTE + .ENDIF +.MKARG ; where appropriate, the arg value is in LVALL,H + ; copy to ARGS and null terminate + TXA ; quick check for X=0 + BEQ DORTS ; IMP - no args + DEX + BEQ DORTS ; ACC - no args + LDA LVALL ; needed for .BYT handling + ; word arg if X is greater than or equal to ABS + CPX #ABS-1 + BMI DOBYTE ; X < #ABS +DOWORD JSR DOBYTE + LDA LVALH +DOBYTE LDY #$00 + STA (CURPCL),Y +; JMP INCPC +; falls through + +; **************************************** + +INCPC ; increment the PC + INC CURPCL + BNE .DONE ; any carry? + INC CURPCH ; yes +.DONE RTS + +; **************************************** + +CALCAM ; work out the addressing mode + JSR ADDMOD + CPX #FAIL + BNE MKOBJC + LDY #ILLADM ; Illegal address mode error + RTS + +PARSE ; Parse one line and validate + LDA LABEL + CMP #CMNT + BEQ DORTS ; ignore comment lines + LDX MNE ; first need to check for an equate + CPX #DOT + BNE .NOEQU + LDX MNE+1 + CPX #MOD ; Do we have a new module? + BNE .NOMOD + JMP DOMOD +.NOMOD CPX #EQU + BEQ DOEQU +.NOEQU CMP #SP ; Is there a label? + BEQ .NOLABL + JSR PCSYM ; save the symbol value - in this case it is the PC +.NOLABL LDA MNE + CMP #DOT ; do we have a directive? + BNE CALCAM ; no + +; **************************************** + +DODIR + LDX #$00 ; handle directives (except equate and module) + LDA MNE+1 + CMP #STR + BEQ DOSTR + STA FRFLAG ; Disallows forward references + JSR QTEVAL + ;CPX #FAIL + INX + BEQ DIRERR + LDA LVALL + LDX MNE+1 + CPX #WORD + BEQ DOWORD + LDX LVALH + BEQ DOBYTE +DIRERR LDY #SYNTAX + LDX #FAIL + RTS +DOSTR LDA ARGS,X + CMP #QUOTE + BNE DIRERR ; String invalid +.LOOP INX + LDA ARGS,X + BEQ DIRERR ; end found before string closed - error + CMP #QUOTE + BEQ DIROK + JSR DOBYTE ; just copy over the bytes + CPX #ARGSZ ; can't go over the size limit + BNE .LOOP + BEQ DIRERR ; hit the limit without a closing quote - error +DIROK RTS + +; **************************************** + +DOEQU + ;LDA LABEL + STA FRFLAG + JSR CHKALN ; label must be global + BCC DIRERR ; MUST have a label for an equate + LDX #$00 + JSR QTEVAL ; work out the associated value + ;CPX #FAIL + INX + BEQ DIRERR + JMP STRSYM + +; **************************************** + +DOMOD ; Do we have a new module? + ;LDA LABEL + JSR CHKALN ; must have a global label + BCC DIRERR + LDY #$00 + LDA ARGS + BEQ .STORE + CMP #SP + BEQ .STORE +.SETPC JSR ATOFR ; output finishing module end address (+1) + LDX #$00 ; set a new value for the PC from the args + LDA ARGS + JSR CONVRT + ;CPX #FAIL + INX + BEQ DIRERR + JSR LVTOPC +.STORE JSR PCSYM + CPX #FAIL + BEQ DIROK + JSR PATCH + CPX #FAIL + BEQ DIROK + JSR SHWMOD + LDA #$00 ; reset patch flag + JSR ATOFR ; output new module start address +; JMP CLRLCL +; falls through + +; **************************************** + +CLRLCL ; clear the local symbol table + LDX #$00 ; this also clears any errors + STX NLSYM ; to their starting values + STX LSYMTL + LDX TABLEH ; and then the high bytes + STX LSYMTH + RTS + +; **************************************** + +ATOFR STA FRFLAG +; JMP MYPRPC +; falls through + +; **************************************** + +MYPRPC + LDA CURPCH + LDX CURPCL + LDY FRFLAG ; flag set => print dash and minus 1 + BEQ .NODEC + PHA + JSR PRDASH + PLA + CPX #$00 + BNE .SKIP ; is X zero? + SEC + SBC #$01 +.SKIP DEX +.NODEC JMP PRNTAX + +; **************************************** + +PATCH ; back patch in the forward reference symbols + ; all are words + LDX NFREF + BEQ .RET ; nothing to do + STX ERFLAG ; set flag +.STRPC STX NPTCH + LDA CURPCL ; save the PC on the stack + PHA + LDA CURPCH + PHA + JSR INITFR +.NEXT LDY #$00 + LDA FRFLAG + STA FLGSAV ; so I can restore the FREF flag + STY HADFRF + LDA (PTCHTL),Y + CMP #DOT + BNE .LOOP + STA FRFLAG ; nonzero means must resolve local symbols +.LOOP LDA (PTCHTL),Y ; copy symbol to COMM + STA COMM,Y + INY + CPY #SYMSZ + BNE .LOOP + LDA (PTCHTL),Y ; get the PC for this symbol + STA CURPCL + INY + LDA (PTCHTL),Y + STA CURPCH + INY + LDA (PTCHTL),Y + STA TEMP1 ; save any offset value + JSR DOLVAL ; get the symbols true value + CPX #FAIL ; value now in LVALL,H or error + BEQ .ERR + LDA HADFRF ; if we have a persistent FREF + BEQ .CONT ; need to copy its offset as well + LDA TEMP1 + STA (MISCL),Y ; falls through to some meaningless patching... + ;SEC ; unless I put these two in + ;BCS .MORE +.CONT JSR ADD16X + LDY #$00 + LDA (CURPCL),Y ; get the opcode + CMP #$80 + BEQ .BRA + AND #$1F ; check for branch opcode - format XXY10000 + CMP #$10 + BEQ .BRA + JSR INCPC ; skip the opcode +.SKIP LDA LVALL + JSR DOWORD +.MORE CLC + LDA PTCHTL ; move to the next symbol + ADC #SYMSZ+3 + STA PTCHTL + BCC .DECN + INC PTCHTH +.DECN LDA FLGSAV + STA FRFLAG + DEC NPTCH + BNE .NEXT +.DONE PLA + STA CURPCH ; restore the PC from the stack + PLA + STA CURPCL +.RET RTS +.BRA JSR ADDOFF ; BRA instructions have a 1 byte offset argument only + CPX #FAIL + BEQ .ERR + LDY #$01 ; save the offset at PC + 1 + LDA LVALL + STA (CURPCL),Y + JMP .MORE +.ERR LDY #$00 + JSR OUTSP +.LOOP2 LDA (PTCHTL),Y ; Show symbol that failed + JSR OUTCH + INY + CPY #SYMSZ + BNE .LOOP2 + DEY ; Since #UNKSYM = #SYMSZ - 1 + BNE .DONE ; always branches + +; **************************************** + +ADDMOD ; Check the arguments and work out the + ; addressing mode + ; return mode in X + LDX #$FF ; default error value for mode + STX CURADM ; save it + LDA CURMNE + LDX ARGS ; Start checking the format... + BEQ .EOL + CPX #SP + BNE .NOTSP +.EOL CMP #$12 ; check exception first - JSR + BEQ .RET + LDX #IMP ; implied mode - space + JSR CHKMOD ; check command is ok with this mode + CPX #FAIL ; not ok + BEQ .NOTIMP ; may still be accumulator mode though +.RET RTS +.NOTIMP LDX #ACC ; accumulator mode - space + JMP CHKMOD ; check command is ok with this mode +.NOTSP CPX #IMV ; immediate mode - '#' + BEQ .DOIMM + LDX #REL + JSR CHKMOD ; check if command is a branch + CPX #FAIL + BEQ .NOTREL + LDA ARGS + JMP DOREL +.DOIMM CMP #$2C ; check exception first - STA + BEQ BAD + LDX #IMM + CMP #$35 ; check inclusion - STX + BEQ .IMMOK + CMP #$22 ; check inclusion - BIT + BEQ .IMMOK + JSR CHKMOD ; check command is ok with this mode + CPX #FAIL + BEQ .RET +.IMMOK STX CURADM ; handle immediate mode + ;LDX #01 ; skip the '#' + DEX ; X == IMM == 2 + JSR QTEVAL + INX + BEQ BAD + LDA LVALH + BNE BAD + ;LDX #IMM + RTS +.NOTREL LDX #0 ; check the more complicated modes + LDA ARGS + CMP #OPEN ; indirection? + BNE .CONT ; no + INX ; skip the '(' +.CONT JSR EVAL + CPX #FAIL + BEQ .RET + JSR FMT2AM ; calculate the addressing mode from the format + CPX #FAIL + BEQ .RET + STX CURADM +; JMP CHKEXS +; falls through + +; **************************************** + +CHKEXS ; Current addressing mode is in X + CPX #ZPY ; for MNE indices 28 to 2F, ZPY is illegal + BNE .CONT ; but ABY is ok, so promote byte argument to word + LDA CURMNE + CMP #$28 + BCC .CONT + CMP #$2F+1 + BCS .CONT + LDX #ABY ; updated addressing mode + BNE OK +.CONT LDY #SPCNT ; check special includes +.LOOP LDA SPINC1-1,Y ; load mnemonic code + CMP CURMNE + BNE .NEXT + LDX SPINC2-1,Y ; load addressing mode + CPX CURADM + BEQ OK ; match - so ok + LDX SPINC3-1,Y ; load addressing mode + CPX CURADM + BEQ OK ; match - so ok +.NEXT DEY + BNE .LOOP + LDX CURADM +; BNE CHKMOD ; wasn't in the exceptions table - check normally +; falls through + +; **************************************** + +CHKMOD LDA CURMNE ; always > 0 + CMP MIN,X ; mode index in X + BCC BAD ; mnemonic < MIN + CMP MAX,X ; MAX,X holds actually MAX + 1 + BCS BAD ; mnemonic > MAX +OK STX CURADM ; save mode + RTS + +; **************************************** + +BAD LDX #FAIL ; Illegal addressing mode error + RTS +DOREL + LDX #$00 + STX LVALL + STX LVALH + CMP #PC ; PC relative mode - '*' + BNE DOLBL + JSR PCTOLV + JSR XCONT +; JMP ADDOFF ; just do an unnecessary EVAL and save 3 bytes +DOLBL JSR EVAL ; we have a label +ADDOFF SEC ; calculate relative offset as LVALL,H - PC + LDA LVALL + SBC CURPCL + STA LVALL + LDA LVALH + SBC CURPCH + STA LVALH + BEQ DECLV ; error if high byte nonzero + INC LVALH + BNE BAD ; need either $00 or $FF +DECLV DEC LVALL + DEC LVALL +RELOK RTS ; need to end up with offset value in LVALL +;ERROFF LDX #FAIL +; RTS + +; **************************************** + +QTEVAL ; evaluate an expression possibly with a quote + LDA ARGS,X + BEQ BAD + CMP #QUOTE + BEQ QCHAR + JMP EVAL +QCHAR INX + LDA #$0 + STA LVALH ; quoted char must be a single byte + LDA ARGS,X ; get the character + STA LVALL + INX ; check and skip the closing quote + LDA ARGS,X + CMP #QUOTE + BNE BAD + INX + LDA ARGS,X + BEQ XDONE + CMP #SP + BEQ XDONE +; JMP DOPLMN +; falls through + +; **************************************** + +DOPLMN ; handle a plus/minus expression + ; on entry, A holds the operator, and X the location + ; store the result in LVALL,H + PHA ; save the operator + INX ; move forward + LDA ARGS,X ; first calculate the value of the byte + JSR BYT2HX + CPX #FAIL + BNE .CONT + PLA +; LDX #FAIL ; X is already $FF +.RET RTS +.CONT STA TEMP1 ; store the value of the byte in TEMP1 + PLA + CMP #PLUS + BEQ .NONEG + LDA TEMP1 + CLC ; for minus, need to negate it + EOR #$FF + ADC #$1 + STA TEMP1 +.NONEG LDA HADFRF + BEQ .SKIP + LDA TEMP1 ; save the offset for use when patching + STA (MISCL),Y +.SKIP ;JMP ADD16X +; falls through + +; **************************************** + +ADD16X ; Add a signed 8 bit number in TEMP1 + ; to a 16 bit number in LVALL,H + ; preserve X (thanks leeeeee, www.6502.org/forum) + LDA TEMP1 ; signed 8 bit number + BPL .CONT + DEC LVALH ; bit 7 was set, so it's a negative +.CONT CLC + ADC LVALL + STA LVALL ; update the stored number low byte + BCC .EXIT + INC LVALH ; update the stored number high byte +.EXIT RTS + +; **************************************** + +EVAL ; Evaluate an argument expression + ; X points to offset from ARGS of the start + ; on exit we have the expression replaced + ; by the required constant + STX TEMP3 ; store start of the expression + LDA ARGS,X + CMP #LOBYTE + BEQ .HASOP + CMP #HIBYTE + BNE .DOLBL +.HASOP STA FRFLAG ; disables forward references when there + INX ; is a '<' or a '>' in the expression + LDA ARGS,X +.DOLBL JSR CHKLBL ; is there a label? + BCS .LBL ; yes - get its value + JSR CONVRT ; convert the ASCII + CPX #FAIL + BEQ XERR + BNE XCONT +.LBL STX XSAV ; move X to Y + JSR LB2VAL ; yes - get its value + CPX #FAIL + BEQ XDONE + LDX XSAV +XCONT INX ; skip the '$' + LDA ARGS,X ; Value now in LVALL,H for ASCII or LABEL + JSR CHKLBL + BCS XCONT ; Continue until end of label or digits + ;STX TEMP4 ; Store end index + CMP #PLUS + BEQ .DOOP + CMP #MINUS + BNE XCHKOP +.DOOP JSR DOPLMN + CPX #FAIL + BNE XCONT +XERR LDY #SYNTAX ; argument syntax error +XDONE RTS +XCHKOP LDY #$00 + LDA FRFLAG + CMP #LOBYTE + BEQ .GETLO + CMP #HIBYTE + BNE .STORE + LDA LVALH ; move LVALH to LVALL + STA LVALL +.GETLO STY LVALH ; keep LVALL, and zero LVALH +.STORE LDA ARGS,X ; copy rest of args to COMM + STA COMM,Y + BEQ .DOVAL + CMP #SP + BEQ .DOVAL + INX + INY + CPX #ARGSZ + BNE .STORE +.DOVAL LDA #$00 + STA COMM,Y + LDY TEMP3 ; get start index + LDA #HEX ; put the '$" back in so subsequent code + STA ARGS,Y ; manages the value properly + INY + LDA LVALH + BEQ .DOLO + JSR HX2ASC +.DOLO LDA LVALL + JSR HX2ASC + LDX #$00 ; bring back the rest from IOBUF +.COPY LDA COMM,X + STA ARGS,Y ; store at offset Y from ARGS + BEQ XDONE + INX + INY + BNE .COPY + +; **************************************** + +LB2VAL ; label to be evaluated is in ARGS + X (X = 0 or 1) + LDY #$00 +.NEXT CPY #LBLSZ ; all chars done + BEQ DOLVAL + JSR CHKLBL ; has the label finished early? + BCC .STOP + STA COMM,Y ; copy because we need exactly 6 chars for the search + INX ; COMM isn't used in parsing, so it + LDA ARGS,X ; can be treated as scratch space + INY + BNE .NEXT +.STOP LDA #SP ; label is in COMM - ensure filled with spaces +.LOOP STA COMM,Y ; Y still points to next byte to process + INY + CPY #LBLSZ + BNE .LOOP +DOLVAL LDA #COMM) + STX STRH + LDA #SYMSZ + STA RECSIG + LDA #SYMSZ+2 + STA RECSZ ; size includes additional two bytes for value + LDA COMM + CMP #DOT + BEQ .LOCAL ; local symbol + JSR SYMSCH + BEQ .FREF ; if not there, handle as a forward reference +.FOUND LDY #SYMSZ + LDA (TBLL),Y ; save value + STA LVALL + INY + LDA (TBLL),Y + STA LVALH + RTS +.LOCAL ; locals much the same + LDX #$03 ; select local table + JSR SYMSCH + BNE .FOUND ; if not there, handle as a forward reference +.FREF LDA FRFLAG ; set when patching + BNE SYMERR ; can't add FREFs when patching + JSR PCTOLV ; default value to PC + LDA FREFTH ; store it in the table + STA MISCH + LDA FREFTL ; Calculate storage address + LDX NFREF + BEQ .CONT ; no symbols to skip +.LOOP CLC + ADC #SYMSZ+3 ; skip over existing symbols + BCC .SKIP + INC MISCH ; carry bit set - increase high pointer +.SKIP DEX + BNE .LOOP +.CONT STA MISCL ; Reqd address is now in MISCL,H + INC NFREF ; Update FREF count + LDA NFREF + CMP #MAXFRF ; Check for table full + BPL OVFERR + LDA #COMM + STA HADFRF ; non-zero value tells that FREF was encountered + STA MISC2L + JSR STORE ; Store the symbol + INY + TXA ; X is zero after STORE + STA (MISCL),Y + RTS ; No error + +; **************************************** + +PCSYM + JSR PCTOLV +; JMP STRSYM + +; **************************************** + +STRSYM ; Store symbol - name at LABEL, value in MISC2L,H + LDA #LABEL + STA MISC2L + STA STRL + LDX #$00 + STX STRH + LDA #SYMSZ + STA RECSIG + LDA LABEL ; Global or local? + CMP #DOT + BNE .SRCH ; Starts with a dot, so local + LDX #$03 +.SRCH JSR SYMSCH + BEQ STCONT ; Not there yet, so ok +.ERR PLA + PLA +SYMERR LDY #UNKSYM ; missing symbol error + BNE SBAD + ;LDX #FAIL + ;RTS +OVFERR LDY #OVRFLW ; Symbol table overflow error +SBAD LDX #FAIL + RTS +STCONT LDA #LABEL + LDX LABEL ; Global or local? + CPX #DOT + BEQ .LSYM ; Starts with a dot, so local + SEC ; Store symbol in global symbol table + LDA GSYMTL ; Make space for next symbol + SBC #SYMSZ+2 ; skip over existing symbols + BCS .CONTG ; Reqd address is now in GSYMTL,H +.DWNHI DEC GSYMTH ; carry bit clear - decrease high pointer +.CONTG STA GSYMTL + INC NGSYM ; Update Symbol count - overflow on 256 symbols + BEQ OVFERR ; Check for table full + STA MISCL ; put addres into MISCH,L for saving + LDA GSYMTH + STA MISCH + BNE STORE ; Always branches - symbol tables cannot be on page zero +.LSYM LDA LSYMTH ; Store symbol in local symbol table + STA MISCH + LDA LSYMTL ; Calculate storage address + LDX NLSYM + BEQ .CONTL ; no symbols to skip +.LOOP CLC + ADC #SYMSZ+2 ; skip over existing symbols + BCC .SKIP + INC MISCH +.SKIP DEX + BNE .LOOP +.CONTL STA MISCL ; Reqd address is now in MISCL,H + INC NLSYM ; Update Symbol count + LDA NLSYM + CMP #MAXSYM ; Check for table full + BPL OVFERR +STORE LDY #0 ; First store the symbol string + STY MISC2H + LDX #SYMSZ +.MV LDA (MISC2L),Y ; move bytes + STA (MISCL),Y + INY + DEX + BNE .MV + LDA LVALL ; Now store the value WORD + STA (MISCL),Y + INY + LDA LVALH + STA (MISCL),Y + RTS ; No error + + +; **************************************** + +CONVRT ; convert an ASCII string at ARGS,X + ; of the form $nnnn (1 to 4 digits) + ; return the result in LVALL,H, and preserves X and Y + ; uses COMM area for scratch space + CMP #HEX ; syntax for hex constant + BNE SBAD ; syntax error + STY COMM+1 + JSR NBYTS + CPX #FAIL + BEQ SBAD + STA COMM + LDY #$00 + STY LVALH +.BACK DEX + DEX + LDA ARGS,X + CMP #HEX + BEQ .1DIG + JSR BYT2HX + SEC + BCS .SKIP +.1DIG JSR AHARGS1 ; one digit +.SKIP STA LVALL,Y + INY + CPY COMM + BNE .BACK +.RET LDY COMM+1 + RTS + +; **************************************** + +SYMSCH ; X = 0 for globals + ; X = 3 for locals + LDA GSYMTL,X ; get global symbol value + STA TBLL + LDA GSYMTH,X + STA TBLH + LDA NGSYM,X ; Number of global symbols + STA RECNM + JSR SEARCH + CPX #FAIL ; Z set if search failed + RTS ; caller to check + +; **************************************** + +FMT2AM ; calculate the addressing given + ; the format of the arguments; + ; return format in X, and + ; location to CHKEXT from in A + ; $FF invalid + ; #ZPG $nn + ; #ZPX $nn,X + ; #ZPY $nn,Y + ; #ABS $nnnn + ; #ABX $nnnn,X + ; #ABY $nnnn,Y + ; #IND ($nnnn) + ; #IDX ($nn,X) + ; #IDY ($nn),Y + ; #INZ ($nn) + ; #IAX ($nnnn,X) +; +; Addressing modes are organised as follows: +; +; IMP (0) ZPG (4) INZ (7) ABS (A) IND (D) +; ACC (1) ZPX (5) INX (8) ABX (B) IAX (E) +; IMM (2) ZPY (6) INY (9) ABY (C) --- +; REL (3) --- --- --- --- +; +; so algorithm below starts with 4, adds 3 if indirect +; and adds 6 if absolute (i.e. 2 byte address), then adds 1 or 2 +; if ,X or ,Y format +; + LDX #$00 + LDA #$04 ; start with mode index of 4 + LDY ARGS,X + CPY #OPEN + BNE .SKIP + CLC ; add 3 for indirect modes + ADC #$03 + INX +.SKIP PHA + JSR NBYTS ; count bytes (1 or 2 only) + TAY ; byte count in Y + DEX + LDA CURMNE + CMP #$12 ; is it JSR? + BEQ .JSR + CMP #$38 ; is it JMP? + BNE .NOJMP +.JSR ;LDY #$2 ; force 2 bytes for these two situations + INY ; following code treats Y = 3 the same as Y = 2 +.NOJMP PLA ; mode base back in A + INX ; check for NBYTS failure + BEQ FERR + DEY + BEQ .1BYT +.2BYT CLC + ADC #$06 ; add 6 to base index for 2 byte modes +.1BYT TAY ; mode index now in Y +.CHECK LDA ARGS,X + BEQ .DONE + CMP #SP + BNE .CONT +.DONE LDA ARGS + CMP #OPEN ; brackets must match + BEQ FERR +.RET CPY #$0F + BPL FERR ; no indirect absolute Y mode + TYA + TAX + RTS +.CONT CMP #CLOSE + BNE .MORE + LDA #SP + STA ARGS ; erase brackets now they have + INX + LDA ARGS,X + CMP #COMMA + BNE .CHECK +.MORE LDA ARGS,X + CMP #COMMA + BNE FERR + INX + LDA ARGS,X + CMP #'X' + BEQ .ISX +.ISY CMP #'Y' + BNE FERR + LDA ARGS + CMP #OPEN + BEQ FERR + STA ARGS-2,X ; to avoid ,X check below + INY +.ISX INY + LDA ARGS-2,X + CMP #CLOSE + BEQ FERR + INX + BNE .CHECK ; always +FERR LDX #FAIL ; error message generated upstream +FRET RTS +NBYTS LDY #$00 ; count bytes using Y +.LOOP INX + INY + JSR AHARGS + CMP #FAIL + BNE .LOOP +.NEXT TYA + LSR ; divide number by 2 + BEQ FERR ; zero is an error + CMP #$03 ; 3 or more is an error + BCS FERR +.RET RTS + +; **************************************** +; * Utility Routines * +; **************************************** + +SEARCH ; TBLL,H has the address of the table to search + ; and address of record on successful return + ; STRL,H has the address of the search string + ; Search through RECNM records + ; Each of size RECSZ with RECSIG significant chars + LDA RECNM + BEQ FERR ; empty table + LDX #$00 ; Record number +.CHK1 LDY #$FF ; Index into entry +.CHMTCH INY + CPY RECSIG ; Have we checked all significant chars? + BEQ FRET ; Yes + LDA (TBLL),Y ; Load the bytes to compare + CMP (STRL),Y + BEQ .CHMTCH ; Check next if these match + INX ; Else move to next record + CPX RECNM + BEQ FERR + LDA TBLL ; Update address + CLC + ADC RECSZ + STA TBLL + BCC .CHK1 + INC TBLH ; Including high byte if necessary + BCS .CHK1 ; will always branch +;.FAIL LDX #FAIL ; X = $FF indicates failure +;.MATCH RTS ; got it - index is in X, address is in A and TBLL,H + +; **************************************** + +BYT2HX ; convert the ASCII byte (1 or 2 chars) at offset X in + ; the args field to Hex + ; result in A ($FF for fail) + + JSR AHARGS + CMP #FAIL ; indicates conversion error + BEQ FERR + PHA + JSR AHARGS1 + DEX + CMP #FAIL + BNE .CONT + PLA ; just ignore 2nd character + RTS +.CONT STA SCRTCH + PLA + ASL ; shift + ASL + ASL + ASL + ADC SCRTCH + RTS + +; **************************************** + +AHARGS1 INX ; caller needs to DEX +AHARGS LDA ARGS,X +ASC2HX ; convert ASCII code in A to a HEX digit + EOR #$30 + CMP #$0A + BCC .VALID + ADC #$88 ; $89 - CLC + CMP #$FA + BCC .ERR + AND #$0F +.VALID RTS +.ERR LDA #FAIL ; this value can never be from a single digit, + RTS ; so ok to indicate error + +; **************************************** + +HX2ASC ; convert a byte in A into two ASCII characters + ; store in ARGS,Y and ARGS+1,Y + PHA ; 1st byte. + LSR + LSR + LSR + LSR + JSR DO1DIG + PLA +DO1DIG AND #$0F ; Print 1 hex digit + ORA #$30 + CMP #$3A + BCC .DONE + ADC #$06 +.DONE STA ARGS,Y + INY + RTS + +; **************************************** + +EXPMNE ; copy the 2 chars at R/LMNETB,X + ; into LMNE and RMNE, and expand + ; into 3 chars at MNE to MNE+2 + LDA LMNETB,X + STA LMNE + LDA RMNETB,X + STA RMNE + LDX #$00 +.NEXT LDA #$00 + LDY #$05 +.LOOP ASL RMNE + ROL LMNE + ROL + DEY + BNE .LOOP + ADC #'A'-1 + STA MNE,X + LDY PRFLAG + BEQ .SKIP + JSR OUTCH ; print the mnemonic as well +.SKIP INX + CPX #$03 + BNE .NEXT + RTS + +; **************************************** +; DISASSEMBLER +; Adapted from code in a Dr Dobbs article +; by Steve Wozniak and Allen Baum (Sep '76) +; **************************************** + +DISASM + JSR ADDARG + ;BEQ .DODIS + BEQ DSMBL +.COPY JSR LVTOPC +;.DODIS JMP DSMBL +; fall through + +; **************************************** + +DSMBL + ;LDA #$13 ; Count for 20 instruction dsmbly + ;STA COUNT +.DSMBL2 JSR INSTDSP ; Disassemble and display instr. + JSR PCADJ + STA PCL ; Update PCL,H to next instr. + STY PCH + ;DEC COUNT ; Done first 19 instrs + ;BNE .DSMBL2 ; * Yes, loop. Else DSMBL 20th + + LDA KBDRDY ; Now disassemble until key press + .if APPLE1 + BPL .DSMBL2 + .else + BEQ .DSMBL2 + .endif + LDA KBD + +INSTDSP JSR PRPC ; Print PCL,H + LDA (PCL,X) ; Get op code + TAY + LSR ; * Even/odd test + BCC .IEVEN + ROR ; * Test B1 + BCS .ERR ; XXXXXX11 instr invalid + ;CMP #$A2 + ;BEQ ERR ; 10001001 instr invalid + AND #$87 ; Mask 3 bits for address mode + ;ORA #$80 ; * add indexing offset +.IEVEN LSR ; * LSB into carry for + TAX ; Left/right test below + LDA MODE,X ; Index into address mode table + BCC .RTMODE ; If carry set use LSD for + LSR ; * print format index + LSR + LSR ; If carry clear use MSD + LSR +.RTMODE AND #$0F ; Mask for 4-bit index + BNE .GETFMT ; $0 for invalid opcodes +.ERR LDY #$FC ; Substitute $FC for invalid op, + LDA #$00 ; set print format index to 0 +.GETFMT TAX + LDA MODE2,X ; Index into print format table + STA FORMAT ; Save for address field format + AND #$03 ; Mask 2-bit length. 0=1-byte + STA LENGTH ; * 1=2-byte, 2=3 byte + TYA ; * op code + JSR GETMNE + LDY #$00 + PHA ; Save mnemonic table index +.PROP LDA (PCL),Y + JSR OUTHEX + LDX #$01 +.PROPBL JSR PRBL2 + CPY LENGTH ; Print instr (1 to 3 bytes) + INY ; * in a 12-character field + BCC .PROP + LDX #$03 ; char count for mnemonic print + STX PRFLAG ; So EXPMNE prints the mnemonic + CPY #$04 + BCC .PROPBL + PLA ; Recover mnemonic index + TAX + JSR EXPMNE + JSR PRBLNK ; Output 3 blanks + LDY LENGTH + LDX #$06 ; Count for 6 print format bits +.PPADR1 CPX #$03 + BEQ .PPADR5 ; If X=3 then print address val +.PPADR2 ASL FORMAT ; Test next print format bit + BCC .PPADR3 ; If 0 don't print + LDA CHAR1-1,X ; * corresponding chars + JSR OUTCH ; Output 1 or 2 chars + LDA CHAR2-1,X ; * (If char from char2 is 0, + BEQ .PPADR3 ; * don't output it) + JSR OUTCH +.PPADR3 DEX + BNE .PPADR1 + STX PRFLAG ; reset flag to 0 + RTS ; Return if done 6 format bits +.PPADR4 DEY + BMI .PPADR2 + JSR OUTHEX ; Output 1- or 2-byte address +.PPADR5 LDA FORMAT + CMP #$E8 ; Handle rel addressing mode + LDA (PCL),Y ; Special print target adr + BCC .PPADR4 ; * (not displacement) +.RELADR JSR PCADJ3 ; PCL,H + DISPL + 1 to A,Y + TAX + INX + BNE PRNTYX ; * +1 to X,Y + INY +PRNTYX TYA +PRNTAX JSR OUTHEX ; Print target adr of branch +PRNTX TXA ; * and return + JMP OUTHEX +PRPC JSR CRLF ; Output carriage return + LDA PCH + LDX PCL + JSR PRNTAX ; Output PCL and PCH +PRBLNK LDX #$03 ; Blank count +PRBL2 JSR OUTSP ; Output a blank + DEX + BNE PRBL2 ; Loop until count = 0 + RTS +PCADJ SEC +PCADJ2 LDA LENGTH ; 0=1-byte, 1=2-byte, 2=3-byte +PCADJ3 LDY PCH + TAX ; * test displ sign (for rel + BPL .PCADJ4 ; * branch). Extend neg + DEY ; * by decrementing PCH +.PCADJ4 ADC PCL + BCC .RTS ; PCL+LENGTH (or displ) + 1 to A + INY ; * carry into Y (PCH) +.RTS RTS + +; lookup table for disassembly special cases +TBLSZ = $1A +DISTBL .byte $80, $41, $4C, $38, $6C, $38, $7C, $38 + .byte $0A, $30, $2A, $31, $4A, $32, $6A, $33 + .byte $9C, $23, $9E, $23, $04, $20, $0C, $20 + .byte $89, $22 + +; Get the MNE index using the following rules: +; - lookup awkward cases in a lookup table (DISTBL) +; - consider opcodes by category: +; 1: nnnn1000 -> nnnn +; 2: nnn10000 -> nnn + BPL +; 3: nnnn1010 or 0nn00000 -> BRK + nnnn(0nn0) +; 4: change nnnX0010 to nnnX0001 +; 5: nnnXXXab -> 001abnnn if >= 23 +; 6: 001abnnn + 1 otherwise + +GETMNE ; get mnemonic index for opcode in A + ; on completion, A holds the index + ; into the mnemonic table + STA TEMP1 ; will need it later + LDX #TBLSZ ; check lookup table first +.LOOP LDA DISTBL-2,X + CMP TEMP1 + BNE .SKIP + LDA DISTBL-1,X ; got it + RTS +.SKIP DEX + DEX + BNE .LOOP + LDA TEMP1 + LSR + LSR + LSR + LSR + STA TEMP2 ; save the high nibble + LDA TEMP1 + AND #$0F + CMP #$08 + BNE .NOTC1 + LDA TEMP2 ; high nibble is our index + RTS +.NOTC1 LDA TEMP1 + AND #$1F + CMP #$10 + BNE .NOTC2 + LDA TEMP2 + LSR + ADC #$39-1 ; since carry is set + RTS +.NOTC2 LDA TEMP1 + AND #$9F + BEQ .DOC3 + AND #$0F + CMP #$0A + BNE .NOTC3 +.DOC3 LDA TEMP2 + CLC + ADC #$10 + RTS +.NOTC3 LDX TEMP1 ; does this code end in 10010? + TXA + AND #$1F + CMP #$12 + BNE .1 + DEX +.1 TXA ; ? ABCD EFGH - thanks bogax, www.6502.org/forum + ASL ; A BCDE FGH0 + ADC #$80 ; B ?CDE FGHA + ROL ; ? CDEF GHAB + ASL ; C DEFG HAB0 + AND #$1F ; C 000G HAB0 + ADC #$20 ; 0 001G HABC + CMP #$23 + BMI .NOTC5 + RTS +.NOTC5 TAX + INX + TXA + RTS + +; Data and related constants + +MODES ; Addressing mode constants +IMP = $00 +ACC = $01 +IMM = $02 ; #$nn or #'' or #LABEL +REL = $03 ; *+nn or LABEL +ZPG = $04 ; $nn or LABEL +ZPX = $05 ; $nn,X or LABEL,X +ZPY = $06 ; $nn,Y or LABEL,Y +IDZ = $07 ; ($nn) or (LABEL) +IDX = $08 ; ($nn,X) or (LABEL,X) +IDY = $09 ; ($nn),Y or (LABEL),Y +ABS = $0A ; $nnnn or LABEL +ABX = $0B ; $nnnn,X or LABEL,X +ABY = $0C ; $nnnn or LABEL +IND = $0D ; ($nnnn) or (LABEL) +IAX = $0E ; ($nnnn,X) or (LABEL,X) + +NUMMN =$42 ; number of mnemonics + +; Tables + +LMNETB + .byte $82 ; PHP + .byte $1B ; CLC + .byte $83 ; PLP + .byte $99 ; SEC + .byte $82 ; PHA + .byte $1B ; CLI + .byte $83 ; PLA + .byte $99 ; SEI + .byte $21 ; DEY + .byte $A6 ; TYA + .byte $A0 ; TAY + .byte $1B ; CLV + .byte $4B ; INY + .byte $1B ; CLD + .byte $4B ; INX + .byte $99 ; SED + .byte $14 ; BRK + .byte $21 ; DEA + .byte $54 ; JSR + .byte $4B ; INA + .byte $95 ; RTI + .byte $82 ; PHY + .byte $95 ; RTS + .byte $83 ; PLY + .byte $A6 ; TXA + .byte $A6 ; TXS + .byte $A0 ; TAX + .byte $A4 ; TSX + .byte $21 ; DEX + .byte $82 ; PHX + .byte $73 ; NOP + .byte $83 ; PLX + .byte $A4 ; TSB + .byte $A4 ; TRB + .byte $12 ; BIT + .byte $9D ; STZ + .byte $9D ; STY + .byte $61 ; LDY + .byte $1C ; CPY + .byte $1C ; CPX + .byte $7C ; ORA + .byte $0B ; AND + .byte $2B ; EOR + .byte $9 ; ADC + .byte $9D ; STA + .byte $61 ; LDA + .byte $1B ; CMP + .byte $98 ; SBC + .byte $0C ; ASL + .byte $93 ; ROL + .byte $64 ; LSR + .byte $93 ; ROR + .byte $9D ; STX + .byte $61 ; LDX + .byte $21 ; DEC + .byte $4B ; INC + .byte $53 ; JMP + .byte $14 ; BPL + .byte $13 ; BMI + .byte $15 ; BVC + .byte $15 ; BVS + .byte $10 ; BCC + .byte $10 ; BCS + .byte $13 ; BNE + .byte $11 ; BEQ + .byte $14 ; BRA +RMNETB + .byte $20 ; PHP + .byte $06 ; CLC + .byte $20 ; PLP + .byte $46 ; SEC + .byte $02 ; PHA + .byte $12 ; CLI + .byte $02 ; PLA + .byte $52 ; SEI + .byte $72 ; DEY + .byte $42 ; TYA + .byte $72 ; TAY + .byte $2C ; CLV + .byte $B2 ; INY + .byte $08 ; CLD + .byte $B0 ; INX + .byte $48 ; SED + .byte $96 ; BRK + .byte $42 ; DEA + .byte $E4 ; JSR + .byte $82 ; INA + .byte $12 ; RTI + .byte $32 ; PHY + .byte $26 ; RTS + .byte $32 ; PLY + .byte $02 ; TXA + .byte $26 ; TXS + .byte $70 ; TAX + .byte $F0 ; TSX + .byte $70 ; DEX + .byte $30 ; PHX + .byte $E0 ; NOP + .byte $30 ; PLX + .byte $C4 ; TSB + .byte $84 ; TRB + .byte $68 ; BIT + .byte $34 ; STZ + .byte $32 ; STY + .byte $32 ; LDY + .byte $32 ; CPY + .byte $30 ; CPX + .byte $82 ; ORA + .byte $88 ; AND + .byte $E4 ; EOR + .byte $06 ; ADC + .byte $02 ; STA + .byte $02 ; LDA + .byte $60 ; CMP + .byte $86 ; SBC + .byte $D8 ; ASL + .byte $D8 ; ROL + .byte $E4 ; LSR + .byte $E4 ; ROR + .byte $30 ; STX + .byte $30 ; LDX + .byte $46 ; DEC + .byte $86 ; INC + .byte $60 ; JMP + .byte $18 ; BPL + .byte $52 ; BMI + .byte $86 ; BVC + .byte $A6 ; BVS + .byte $C6 ; BCC + .byte $E6 ; BCS + .byte $8A ; BNE + .byte $62 ; BEQ + .byte $82 ; BRA + +MIN ; Minimum legal value for MNE for each mode. + .byte $00, $30, $25, $39 + .byte $20, $28, $34 + .byte $28, $28, $28 + .byte $20, $28, $28 + .byte $38, $38 +MAX ; Maximum +1 legal value of MNE for each mode. + .byte $1F+1, $33+1, $2F+1, $41+1 + .byte $37+1, $33+1, $35+1 + .byte $2F+1, $2F+1, $2F+1 + .byte $38+1, $33+1, $2F+1 + .byte $38+1, $38+1 +BASE ; Base value for each opcode + .byte $08, $18, $28, $38 + .byte $48, $58, $68, $78 + .byte $88, $98, $A8, $B8 + .byte $C8, $D8, $E8, $F8 + .byte $00, $1A, $14, $3A + .byte $40, $5A, $60, $7A + .byte $8A, $9A, $AA, $BA + .byte $CA, $DA, $EA, $FA + .byte $00, $10, $20, $60 + .byte $80, $A0, $C0, $E0 + .byte $01, $21, $41, $61 + .byte $81, $A1, $C1, $E1 + .byte $02, $22, $42, $62 + .byte $82, $A2, $C2, $E2 + .byte $40, $10, $30, $50 + .byte $70, $90, $B0, $D0 + .byte $F0, $80 +OFFSET ; Default offset values for each mode, + ; added to BASE to get the opcode + .byte $00, $08, $00, $00 + .byte $04, $14, $14 + .byte $11, $00, $10 + .byte $0C, $1C, $18 + .byte $2C, $3C + + +; offset adjustments for the mnemonic exceptions +ADJABY =$04 +ADJIMM =$08 +ADJBIT =$68 +ADJSTZ =$D0 + +; disassembler data + +; XXXXXXZ0 instrs +; * Z=0, right half-byte +; * Z=1, left half-byte +MODE .byte $0F, $22, $FF, $33, $CB + .byte $62, $FF, $73, $03, $22 + .byte $FF, $33, $CB, $66, $FF + .byte $77, $0F, $20, $FF, $33 + .byte $CB, $60, $FF, $70, $0F + .byte $22, $FF, $39, $CB, $66 + .byte $FF, $7D, $0B, $22, $FF + .byte $33, $CB, $A6, $FF, $73 + .byte $11, $22, $FF, $33, $CB + .byte $A6, $FF, $87, $01, $22 + .byte $FF, $33, $CB, $60, $FF + .byte $70, $01, $22, $FF, $33 + .byte $CB, $60, $FF, $70 +; YYXXXZ01 instrs + .byte $24, $31, $65, $78 + +MODE2 .byte $00 ; ERR + .byte $21 ; IMM + .byte $81 ; Z-PAG + .byte $82 ; ABS + .byte $59 ; (Z-PAG,X) + .byte $4D ; (Z-PAG),Y + .byte $91 ; Z-PAG,X + .byte $92 ; ABS,X + .byte $86 ; ABS,Y + .byte $4A ; (ABS) + .byte $85 ; Z-PAG,Y + .byte $9D ; REL + .byte $49 ; (Z-PAG) + .byte $5A ; (ABS,X) +CHAR2 .byte 'Y' + .byte $00 + .byte 'X' + .byte '$' + .byte '$' + .byte $00 +CHAR1 .byte ',' + .byte ')' + .byte ',' + .byte '#' + .byte '(' + .byte '$' +; Special case mnemonics +SPCNT = $08 ; duplicate some checks so I can use the same loop above +; Opcodes +SPINC1 .byte $12, $22, $23, $24, $25, $35, $36, $37 +; 1st address mode to check +SPINC2 .byte $0A, $0B, $0B, $05, $0B, $0C, $0B, $0B +; 2nd address mode to check +SPINC3 .byte $0A, $05, $05, $05, $05, $0C, $05, $05 + +; commands + +NUMCMD =$0D +CMDS .ASCII "NLXEMRDI!$AVP" + +N1 = NEW-1 +L1 = LIST-1 +D1 = DELETE-1 +E1 = EDIT-1 +M1 = MEM-1 +R1 = RUN-1 +DIS1 = DISASM-1 +I1 = INSERT-1 +GL1 = GETLINE-1 +MON1 = MONTOR-1 +A1 = ASSEM-1 +V1 = VALUE-1 +P1 = PANIC-1 + +CMDH .byte >N1 + .byte >L1 + .byte >D1 + .byte >E1 + .byte >M1 + .byte >R1 + .byte >DIS1 + .byte >I1 + .byte >GL1 + .byte >MON1 + .byte >A1 + .byte >V1 + .byte >P1 + +CMDL .byte MODE (XAM mode). + LDA XAML ; See if there's more to print + CMP L + LDA XAMH + SBC H + BCS TONEXTITEM ; Not less! No more data to output + INC XAML ; Increment 'examine index' + BNE MOD8CHK ; No carry! + INC XAMH +MOD8CHK LDA XAML ; If address MOD 8 = 0 start new line + AND #$07 + BPL NXTPRNT ; Always taken. + + .if APPLE1 +; Apple 1 I/O values +KBD =$D010 ; Apple 1 Keyboard character read. +KBDRDY =$D011 ; Apple 1 Keyboard data waiting when negative. + + .ORG $FFDC +OUTHEX PHA ; Print 1 hex byte. + LSR + LSR + LSR + LSR + JSR PRHEX + PLA +PRHEX AND #$0F ; Print 1 hex digit + ORA #$30 + CMP #$3A + BCC OUTCH + ADC #$06 +OUTCH BIT DSP ; DA bit (B7) cleared yet? + BMI OUTCH ; No! Wait for display ready + STA DSP ; Output character. Sets DA + RTS + .else +IOMEM =$E000 +PUTCH =IOMEM+1 +KBD =IOMEM+4 +KBDRDY =IOMEM+4 + + .ORG $FFDC +OUTHEX PHA ; Print 1 hex byte. + LSR + LSR + LSR + LSR + JSR PRHEX + PLA +PRHEX AND #$0F ; Print 1 hex digit + ORA #$30 + CMP #$3A + BCC OUTCH + ADC #$06 +OUTCH STA PUTCH + RTS + .ENDIF + + .if MINIMONITOR + .ORG $FFFA ; INTERRUPT VECTORS + .WORD $0F00 + .WORD RESET + .WORD DEBUG + .ENDIF + .ELSE +; Apple 1 I/O values +OUTCH =$FFEF ; Apple 1 Echo +PRHEX =$FFE5 ; Apple 1 Echo +OUTHEX =$FFDC ; Apple 1 Print Hex Byte Routine +KBD =$D010 ; Apple 1 Keyboard character read. +KBDRDY =$D011 ; Apple 1 Keyboard data waiting when negative. + .ENDIF ; inrom + \ No newline at end of file diff --git a/Krusader/krusader 1.3.asm b/Krusader/krusader 1.3.asm new file mode 100644 index 0000000..b8a39be --- /dev/null +++ b/Krusader/krusader 1.3.asm @@ -0,0 +1,3038 @@ +; KRUSADER - An Assembly Language IDE for the Replica 1 + +; 6502 Version 1.3 - 23 December, 2007 +; (c) Ken Wessen (ken.wessen@gmail.com) + +; Notes: +; - 11 bytes free (17 free if TABTOSPACE = 0) +; - Entry points: +; SHELL = $711C($F01C) +; MOVEDN = $7304($F204) +; DEBUG = -($FE14) +; XBRK = -($FE1E) - debugger re-entry point +; DSMBL = $7BCA($FACA) + +APPLE1 =1 +INROM =1 +TABTOSPACE = 1 +UNK_ERR_CHECK = 0 + +MINIMONITOR = INROM & 1 +DOTRACE = MINIMONITOR & 1 +BRKAS2 = 1 ; if 1, BRK will assemble to two $00 bytes + ; if set, then minimonitor will work unchanged + ; for both hardware and software interrupts + + .if INROM + .org $F000 + .else + .org $7100 + .endif + .start MAIN + + .if INROM +MONTOR =ESCAPE + .ELSE +MONTOR =$FF1F +GETLINE =MONTOR ; doesn't work in RAM version because needs adjusted monitor code + .endif + +; Constants + +BS =$08 ; backspace +SP =$20 ; space +CR =$0D ; carriage return +LF =$0A ; line feed +ESC =$1B ; escape +INMASK =$7F + +LNMSZ =$03 +LBLSZ =$06 ; labels are up to 6 characters +MNESZ =$03 ; mnemonics are always 3 characters +ARGSZ =$0E ; arguments are up to 14 characters +COMSZ =$0A ; comments fill the rest - up to 10 characters + +ENDLBL =LNMSZ+LBLSZ+1 +ENDMNE =ENDLBL+MNESZ+1 +ENDARG =ENDMNE+ARGSZ+1 +ENDLN =ENDARG+COMSZ+1 + +SYMSZ =$06 ; size of labels + +LINESZ =$27 ; size of a line +USESZ =LINESZ-LNMSZ-1 ; usable size of a line +CNTSZ =COMM-LABEL-1 ; size of content in a line + +MAXSYM =$20 ; at most 32 local symbols (256B) and +MAXFRF =$55 ; 85 forward references (896B) + ; globals are limited by 1 byte index => max of 256 (2K) + ; global symbol table grows downwards + +; Symbols used in source code + +IMV ='#' ; Indicates immediate mode value +HEX ='$' ; Indicates a hex value +OPEN ='(' ; Open bracket for indirect addressing +CLOSE =')' ; Close bracket for indirect addressing +PC ='*' ; Indicates PC relative addressing +LOBYTE ='<' ; Indicates lo-byte of following word +HIBYTE ='>' ; Indicates hi-byte of following word +PLUS ='+' ; Plus in simple expressions +MINUS ='-' ; Minus in simple expressions +DOT ='.' ; Indicates a local label +QUOTE =''' ; delimits a string +COMMA =',' +CMNT =';' ; indicates a full line comment + +PROMPT ='?' + +EOL =$00 ; end of line marker +EOFLD =$01 ; end of field in tokenised source line +BLANK =$02 ; used to mark a blank line + +PRGEND =$FE ; used to flag end of program parsing +FAIL =$FF ; used to flag failure in various searches + +; Zero page storage +IOBUF =$00 ; I/O buffer for source code input and analysis +LABEL =$04 ; label starts here +MNE =$0B ; mnemonic starts here +ARGS =$0F ; arguments start here +COMM =$1D ; comments start here +FREFTL =$29 ; address of forward reference table +FREFTH =$2A +NFREF =$2B ; number of forward symbols +RECNM =$2C ; number of table entries +RECSZ =$2D ; size of table entries +RECSIG =$2E ; significant characters in table entries +XSAV =$2F +YSAV =$30 +CURMNE =$3C ; Holds the current mne index +CURADM =$3D ; Holds the current addressing mode +LVALL =$3E ; Storage for a label value +LVALH =$3F +TBLL =$40 ; address of search table +TBLH =$41 +STRL =$42 ; address of search string +STRH =$43 +SCRTCH =$44 ; scratch location +NPTCH =$45 ; counts frefs when patching +PTCHTL =$46 ; address of forward reference being patched +PTCHTH =$47 +FLGSAV =$48 + +MISCL =$50 ; Miscellaneous address pointer +MISCH =$51 +MISC2L =$52 ; And another +MISC2H =$53 +TEMP1 =$54 ; general purpose storage +TEMP2 =$55 +TEMP3 =$56 +TEMP4 =$57 +LMNE =TEMP3 ; alias for compression and expansion routines +RMNE =TEMP4 +FRFLAG =$58 ; if nonzero, disallow forward references +ERFLAG =$59 ; if nonzero, do not report error line +HADFRF =$5A ; if nonzero, handled a forward reference +PRFLAG =$5B + +; want these to persist if possible when going into the monitor +; to test code etc, so put them right up high +XQT =$E0 + +; these 6 locations must be contiguous +GSYMTL =$E9 ; address of the global symbol table +GSYMTH =$EA +NGSYM =$EB ; number of global symbols +LSYMTL =$EC ; address of the local symbol table +LSYMTH =$ED +NLSYM =$EE ; number of local symbols + +; these 7 locations must be contiguous +REGS =$F0 +SAVP =REGS +SAVS =$F1 +SAVY =$F2 +SAVX =$F3 +SAVA =$F4 +CURPCL =$F5 ; Current PC +CURPCH =$F6 + +CODEH =$F8 ; hi byte of code storage area (low is $00) +TABLEH =$F9 ; hi byte of symbol table area + +; these 4 locations must be contiguous +LINEL =$FA ; Current source line number (starts at 0) +LINEH =$FB +CURLNL =$FC ; Current source line address +CURLNH =$FD + +SRCSTL =$FE ; source code start address +SRCSTH =$FF + +; for disassembler +FORMAT =FREFTL ; re-use spare locations +LENGTH =FREFTH +COUNT =NFREF +PCL =CURPCL +PCH =CURPCH + +; **************************************** +; COMMAND SHELL/EDITOR CODE +; **************************************** + +MAIN + .if INROM + LDA #$03 + STA CODEH + LDA #$20 + STA SRCSTH + LDA #$7C + STA TABLEH + .else + LDA #$03 + STA CODEH + LDA #$1D + STA SRCSTH + LDA #$6D + STA TABLEH + .endif + LDX #MSGSZ +.NEXT LDA MSG-1,X + JSR OUTCH + DEX + BNE .NEXT + DEX + TXS ; reset stack pointer on startup + JSR SHINIT ; default source line and address data +; JMP SHELL +; falls through to SHELL + +; **************************************** + +SHELL ; Loops forever + ; also the re-entry point + CLD ; just incase + LDA #$00 + STA PRFLAG + JSR FILBUF + LDX #ARGS + STX FRFLAG ; set flags in SHELL + STX ERFLAG + JSR CRLF + LDA #PROMPT + JSR OUTCH ; prompt + JSR OUTSP ; can drop this if desperate for 3 more bytes :-) +.KEY JSR GETCH + CMP #BS + BEQ SHELL ; start again + CMP #CR + BEQ .RUN + JSR OUTCH + STA IOBUF,X + INX + BNE .KEY ; always branches +.RUN LDA ARGS + BEQ SHELL ; empty command line + LDA ARGS+1 ; ensure command is just a single letter + BEQ .OK + CMP #SP + BNE .ERR;SHLERR +.OK LDX #NUMCMD +.NEXT LDA CMDS-1,X ; find the typed command + CMP ARGS + BEQ GOTCMD + DEX + BNE .NEXT +.ERR PHA ; put dummy data on the stack + PHA +SHLERR + LDY #SYNTAX +ERR2 PLA ; need to clean up the stack + PLA + JSR SHWERR + BNE SHELL +GOTCMD JSR RUNCMD + JMP SHELL ; ready for next command + +; **************************************** + +SHINIT + LDA #$00 + TAY + STA SRCSTL ; low byte zero for storage area + STA (SRCSTL),Y ; and put a zero in it for EOP +TOSTRT ; set LINEL,H and CURLNL,H to the start + LDA SRCSTH + STA CURLNH + LDA #$00 + STA LINEL + STA LINEH ; 0 lines + STA CURLNL + RTS ; leaves $00 in A + +; **************************************** + +PANIC + JSR SHINIT + LDA ARGS+2 + BNE .SKIP + LDA #$01 +.SKIP STA (SRCSTL),Y ; Y is $00 from SHINIT + RTS + +; **************************************** + +VALUE + JSR ADDARG + BEQ SHLERR + JSR CRLF + LDA LVALH + LDX LVALL + JMP PRNTAX + +; **************************************** + +RUN + JSR ADDARG + BEQ SHLERR + JSR CRLF + JMP (LVALL) ; jump to the address + +; **************************************** + +ADDARG ; convert argument to address + LDX #$02 + LDA ARGS,X + BEQ .NOARG + PHA + JSR EVAL + PLA + ;CPX #FAIL + INX + BEQ ERR2;SHLERR +.NOARG RTS + +; **************************************** + +PCTOLV + LDA CURPCL + STA LVALL + LDA CURPCH + STA LVALH + RTS + +; **************************************** + +LVTOPC + LDA LVALL + STA CURPCL + LDA LVALH + STA CURPCH + RTS + +; **************************************** + +FILLSP + LDA #SP +FILBUF ; fill the buffer with the contents of A + LDX #LINESZ +.CLR STA IOBUF-1,X + DEX + BNE .CLR + RTS + +; **************************************** + +RUNCMD + LDA CMDH-1,X + PHA + LDA CMDL-1,X + PHA + RTS + +; **************************************** + +NEW + JSR SHINIT + JMP INSERT + +; **************************************** + +LIST ; L - list all + ; L nnn - list from line nnn + JSR TOSTRT + JSR GETARG + BEQ .NEXT ; no args, list from start + JSR GOTOLN ; deal with arguments if necessary +.NEXT LDY #$00 + LDA (CURLNL),Y + BEQ .RET + JSR PRNTLN + JSR UPDTCL + LDA KBDRDY + .if APPLE1 + BPL .NEXT + .else + BEQ .NEXT + .endif + LDA KBD +.RET RTS + +; **************************************** + +MEM + JSR TOEND ; set CURLNL,H to the end + JSR CRLF + LDX #$04 +.LOOP LDA CURLNL-1,X + JSR OUTHEX + CPX #$03 + BNE .SKIP + JSR PRDASH +.SKIP DEX + BNE .LOOP +RET RTS + +; **************************************** + +GETARG ; get the one or two numeric arguments + ; to the list, edit, delete and insert commands + ; store them in TEMP1-4 as found + ; arg count in Y or X has FAIL + LDY #$00 + STY YSAV + LDX #$01 +.NEXT LDA ARGS,X + BEQ .DONE ; null terminator + CMP #SP ; find the space + BEQ .CVT + CMP #HEX ; or $ symbol + BEQ .CVT + INX + BNE .NEXT +.CVT INC YSAV ; count args + LDA #HEX + STA ARGS,X ; replace the space with '$' and convert + JSR CONVRT + ;CPX #FAIL + INX + BEQ LCLERR + ;INX + LDA LVALL + STA TEMP1,Y + INY + LDA LVALH + STA TEMP1,Y + INY + BNE .NEXT ; always branches +.DONE LDY YSAV + RTS ; m in TEMP1,2, n in TEMP3,4 + +; **************************************** + +EDIT + JSR GETARG + ;CPY #$01 + DEY + BNE LCLERR + JSR DELETE ; must not overwrite the command input buffer +; JMP INSERT +; falls through to INSERT + +; **************************************** + +INSERT + JSR GETARG ; deal with arguments if necessary + ;CPX #FAIL + INX + BEQ LCLERR + ;CPY #$00 ; no args + TYA + BNE .ARGS + JSR TOEND ; insert at the end + CLC + BCC .IN +.ARGS JSR GOTOLN ; if no such line will insert at end +.IN JSR INPUT ; Get one line + CPX #FAIL ; Was there an error? + BEQ RET; + ; Save the tokenised line and update pointers + ; tokenised line is in IOBUF, size X + ; move up from CURLNL,H to make space + STX XSAV ; save X (data size) + LDA CURLNH + STA MISCH + STA MISC2H + LDA CURLNL + STA MISCL ; src in MISCL,H now + CLC + ADC XSAV + STA MISC2L + BCC .READY + INC MISC2H ; MISC2L,H is destination +.READY JSR GETSZ + JSR MOVEUP ; do the move + LDY #$00 + ; now move the line to the source storage area + ; Y bytes, from IOBUF to CURLN +.MOVE LDA IOBUF,Y + STA (CURLNL),Y + INY + CPY XSAV + BNE .MOVE + JSR UPDTCL ; update CURLNL,H + BNE .IN ; always branches + +; **************************************** + +LCLERR ; local error wrapper + ; shared by the routines around it + JMP SHLERR + +; **************************************** + +GETSZ ; SIZE = TEMP1,2 = lastlnL,H - MISCL,H + 1 + LDX #-$04 +.LOOP LDA CURLNH+1,X + PHA ; save CURLN and LINEN on the stack + INX + BNE .LOOP + JSR TOEND + SEC + LDA CURLNL + SBC MISCL + STA TEMP1 + LDA CURLNH + SBC MISCH + STA TEMP2 + INC TEMP1 + BNE .SKIP + INC TEMP2 +.SKIP LDX #$04 +.LOOP2 PLA ; get CURLN and LINEN from the stack + STA LINEL-1,X + DEX + BNE .LOOP2 + RTS + +; **************************************** + +DELETE ; Delete the specified range + ; Moves from address of line arg2 (MISCL,H) + ; to address of line arg1 (MISC2L,H) + JSR GETARG +; CPY #$00 + BEQ LCLERR + STY YSAV +.DOIT JSR GOTOLN ; this leaves TEMP1 in Y and TEMP2 in X + CPX #FAIL + BEQ LCLERR + LDA CURLNL + STA MISC2L + LDA CURLNH + STA MISC2H ; destination address is set in MISC2L,H + LDA YSAV + ;CMP #$01 + LSR + BEQ .INC + LDX TEMP4 + LDY TEMP3 ; Validate the range arguments + CPX TEMP2 ; First compare high bytes + BNE .CHK ; If TEMP4 != TEMP2, we just need to check carry + CPY TEMP1 ; Compare low bytes when needed +.CHK BCC LCLERR ; If carry clear, 2nd argument is too low +.INC INY ; Now increment the second argument + BNE .CONT + INX +.CONT STX TEMP2 + STY TEMP1 + JSR GOTOLN + LDA CURLNL + STA MISCL + LDA CURLNH + STA MISCH + JSR GETSZ +; JMP MOVEDN +; falls through + +; **************************************** +; Memory moving routines +; From http://www.6502.org/source/general/memory_move.html +; **************************************** + +; Some aliases for the following two memory move routines + +FROM =MISCL ; move from MISCL,H +TO =MISC2L ; to MISCL2,H +SIZEL =TEMP1 +SIZEH =TEMP2 + +MOVEDN ; Move memory down + LDY #$00 + LDX SIZEH + BEQ .MD2 +.MD1 LDA (FROM),Y ; move a page at a time + STA (TO),Y + INY + BNE .MD1 + INC FROM+1 + INC TO+1 + DEX + BNE .MD1 +.MD2 LDX SIZEL + BEQ .MD4 +.MD3 LDA (FROM),Y ; move the remaining bytes + STA (TO),Y + INY + DEX + BNE .MD3 +.MD4 RTS + +MOVEUP ; Move memory up + LDX SIZEH ; the last byte must be moved first + CLC ; start at the final pages of FROM and TO + TXA + ADC FROM+1 + STA FROM+1 + CLC + TXA + ADC TO+1 + STA TO+1 + INX ; allows the use of BNE after the DEX below + LDY SIZEL + BEQ .MU3 + DEY ; move bytes on the last page first + BEQ .MU2 +.MU1 LDA (FROM),Y + STA (TO),Y + DEY + BNE .MU1 +.MU2 LDA (FROM),Y ; handle Y = 0 separately + STA (TO),Y +.MU3 DEY + DEC FROM+1 ; move the next page (if any) + DEC TO+1 + DEX + BNE .MU1 + RTS + +; **************************************** + +TOEND + LDA #$FF + STA TEMP2 ; makes illegal line number +; JMP GOTOLN ; so CURLNL,H will be set to the end +; falls through + +; **************************************** + +GOTOLN ; go to line number given in TEMP1,2 + ; sets CURLNL,H to the appropriate address + ; and leaves TEMP1 in Y and TEMP2 in X + ; if not present, return #FAIL in X + ; and LINEL,H will be set to the next available line number +EOP = LFAIL +GOTIT = LRET + JSR TOSTRT +.NXTLN ; is the current line number the same + ; as specified in TEMP1,2? + ; Z set if equal + ; C set if TEMP1,2 >= LINEL,H + LDY TEMP1 + CPY LINEL + BNE .NO + LDX TEMP2 + CPX LINEH + BEQ GOTIT +.NO LDY #$FF +.NXTBT INY ; find EOL + LDA (CURLNL),Y + BNE .NXTBT + TYA + ;CPY #$00 + BEQ EOP ; null at start of line => end of program + INY + JSR UPDTCL ; increment CURLNL,H by Y bytes + BNE .NXTLN ; always branches +;.EOP LDX #FAIL +;.GOTIT RTS ; address is now in CURLNL,H + +; **************************************** + +PRNTLN ; print out the current line (preserve X) + JSR CRLF + STX XSAV + JSR DETKN + INY + JSR PRLNNM + LDX #$00 +.PRINT LDA LABEL,X + BEQ .DONE ; null terminator + JSR OUTCH + INX + ;CPX #USESZ + BNE .PRINT +.DONE LDX XSAV + RTS + +; **************************************** + +NEXTCH ; Check for valid character in A + ; Also allows direct entry to appropriate location + ; Flag success with C flag + JSR GETCH + .if TABTOSPACE + CMP #$09 ; is it a tab? + BNE .SKIP + LDA #SP + .endif +.SKIP CMP #SP ; valid ASCII range is $20 to $5D + BPL CHANM ; check alpha numeric entries + TAY + PLA + PLA + PLA + PLA ; wipe out return addresses + CPY #BS + BEQ INPUT ; just do it all again +.NOBS CPY #CR + BNE LFAIL + CPX #LABEL ; CR at start of LABEL means a blank line + BEQ DOBLNK + LDA #EOL + STA IOBUF,X + BEQ GOTEOL +LFAIL LDX #FAIL ; may flag error or just end +LRET RTS +CHANM CPX #LINESZ ; ignore any characters over the end of the line + BPL CHNO + CMP #']'+1 ; is character is in range $20-$5D? + BPL CHNO ; branch to NO... +CHOK SEC ; C flag on indicates success + RTS +CHKLBL CMP #DOT ; here are more specific checks + BEQ CHOK +CHKALN CMP #'0' ; check alpha-numeric + BMI CHNO ; less than 0 + CMP #'9'+1 + BMI CHOK ; between 0 and 9 +CHKLET CMP #'A' + BMI CHNO ; less than A + CMP #'Z'+1 + BMI CHOK ; between A and Z +CHNO CLC + RTS ; C flag off indicates failure + +; **************************************** + +DOBLNK + LDA #BLANK + TAX ; BLANK = #$02, and that is also the + STA IOBUF ; tokenised size of a blank line + LDA #EOL ; (and only a blank line) + STA IOBUF+1 +ENDIN RTS + +INPUT + JSR FILLSP + LDA #EOL ; need this marker at the start of the comments + STA COMM ; for when return hit in args field + JSR CRLF + JSR PRLNNM + LDX #LABEL ; point to LABEL area + LDA #ENDLBL + JSR ONEFLD + JSR INSSPC ; Move to mnemonic field + LDA LABEL + CMP #CMNT + BEQ .CMNT + LDA #ENDMNE + JSR ONEFLD + JSR INSSPC ; Move to args field + LDA #ENDARG + JSR ONEFLD +.CMNT LDA #EOL + JSR ONEFLD +GOTEOL ;JMP TOTKN +; falls through + +; **************************************** + +TOTKN ; tokenise to IOBUF to calc size + ; then move memory to make space + ; then copy from IOBUF into the space + LDX #$00 + STX MISCH + LDA #SP + STA TEMP2 + LDA #LABEL + STA MISCL + LDA #EOFLD + STA TEMP1 + JSR TKNISE + LDY LABEL + CPY #CMNT + BNE .CONT + LDA #MNE + BNE ISCMNT ; always branches +.CONT TXA ; save X + PHA + +; JSR SRCHMN ; is it a mnemonic? + +SRCHMN ; Search the table of mnemonics for the mnemonic in MNE + ; Return the index in A + +CMPMNE ; compress the 3 char mnemonic + ; at MNE to MNE+2 into 2 chars + ; at LMNE and RMNE + CLC + ROR LMNE + LDX #$03 +.NEXT2 SEC + LDA MNE-1,X + SBC #'A'-1 + LDY #$05 +.LOOP2 LSR + ROR LMNE + ROR RMNE + DEY + BNE .LOOP2 + DEX + BNE .NEXT2 + + LDX #NUMMN ; Number of mnemonics +.LOOP LDA LMNETB-1,X + CMP LMNE + BNE .NXT + LDA RMNETB-1,X + CMP RMNE + BEQ .FND +.NXT DEX + BNE .LOOP +.FND DEX ; X = $FF for failure +; RTS + + TXA + CMP #FAIL + BNE .FOUND + LDA MNE ; or a directive? + CMP #DOT + BNE .ERR + LDX #NUMDIR + LDA MNE+1 +.NEXT CMP DIRS-1,X + BEQ .FDIR + DEX + BNE .NEXT +.ERR PLA + LDY #INVMNE + JMP SHWERR +.FDIR DEX +.FOUND TAY ; put mnemonic/directive code in Y + INY ; offset by 1 so no code $00 + PLA ; restore Y + TAX + STY IOBUF,X + INX + LDA #ARGS + STA MISCL +; LDA #EOFLD +; STA TEMP1 + JSR TKNISE + STX XSAV + INC XSAV + LDA #COMM +ISCMNT STA MISCL + LDA #EOL + STA TEMP1 + STA TEMP2 + JSR TKNISE + CPX XSAV + BNE .RET + DEX ; no args or comments, so stop early + STA IOBUF-1,X ; A already holds $00 +.RET RTS + +ONEFLD ; do one entry field + ; A holds the end point for the field + STA TEMP1 ; last position +.NEXT JSR NEXTCH ; catches ESC, CR and BS + BCC .NEXT ; only allow legal keys + JSR OUTCH ; echo + STA IOBUF,X + INX + CMP #SP + BEQ .FILL + CPX TEMP1 + BNE .NEXT +.RET RTS +.FILL LDA TEMP1 + BEQ .NEXT ; just treat a space normally + CPX TEMP1 ; fill spaces + BEQ .RET + LDA #SP + STA IOBUF,X + JSR OUTCH +.CONT INX + BNE .FILL ; always branches + +; **************************************** + +INSSPC + LDA IOBUF-1,X ; was previous character a space? + CMP #SP + BEQ .JUMP +.GET JSR NEXTCH ; handles BS, CR and ESC + CMP #SP + BNE .GET ; only let SP through +.JUMP STA IOBUF,X ; insert the space + INX + JMP OUTCH + +TKNISE + LDY #$00 +.NEXT LDA (MISCL),Y + BEQ .EOF + CMP TEMP2 + BEQ .EOF ; null terminator + STA IOBUF,X + INX + INC MISCL + BNE .NEXT +.EOF LDA TEMP1 + STA IOBUF,X + INX + RTS + +; **************************************** + +DETKN ; Load a line to the IOBUF + ; (detokenising as necessary) + ; On return, Y holds tokenised size + JSR FILLSP + LDY #$00 + LDX #LABEL +.LBL LDA (CURLNL),Y + BEQ .EOP ; indicates end of program + CMP #BLANK + BNE .SKIP + INY + LDA #EOL + BEQ .EOL +.SKIP CMP #EOFLD + BEQ .CHK + STA IOBUF,X + INX + INY + BNE .LBL +.CHK LDA LABEL + CMP #CMNT + BNE .NEXT + LDX #MNE + BNE .CMNT ; always branches +.NEXT INY + LDA (CURLNL),Y ; get mnemonic code + TAX + DEX ; correct for offset in tokenise + STX CURMNE ; store mnemonic for assembler + CPX #NUMMN + BPL .DIR + TYA ; save Y + PHA + JSR EXPMNE + PLA ; restore Y + TAY + BNE .REST +.DIR STX MNE+1 + LDA #DOT + STA MNE +.REST INY + LDX #ARGS ; point to ARGS area +.LOOP LDA (CURLNL),Y + BEQ .EOL ; indicates end of line + CMP #EOFLD + BNE .CONT + INY + LDX #COMM ; point to COMM area + BNE .LOOP +.CONT STA IOBUF,X + INX +.CMNT INY + BNE .LOOP +.EOP LDX #PRGEND +.EOL STA IOBUF,X + RTS + +; **************************************** +; ASSEMBLER CODE +; **************************************** + +ASSEM ; Run an assembly + JSR INIT ; Set the default values + JSR CRLF + JSR MYPRPC +.NEXT JSR DO1LN ; line is in the buffer - parse it + ;CPX #FAIL + INX + BEQ SHWERR + CPX #PRGEND+1 ; +1 because of INX above + BNE .NEXT + INC FRFLAG ; have to resolve them all now - this ensures FRFLAG nonzero + JSR PATCH ; back patch any remaining forward references + ;CPX #FAIL + INX + BEQ SHWERR + JMP MYPRPC ; output finishing module end address +;.ERR JMP SHWERR + +; **************************************** + +SHWERR ; Show message for error with id in Y + ; Also display line if appropriate + JSR CRLF + LDX #ERPRSZ +.NEXT LDA ERRPRE-1,X + JSR OUTCH + DEX + BNE .NEXT + TYA + .if UNK_ERR_CHECK + BEQ .SKIP + CPY #MAXERR+1 + BCC .SHOW ; If error code valid, show req'd string +.UNKWN LDY #UNKERR ; else show unknown error + BEQ .SKIP + .endif +.SHOW CLC + TXA ; sets A to zero +.ADD ADC #EMSGSZ + DEY + BNE .ADD + TAY +.SKIP LDX #EMSGSZ + .if UNK_ERR_CHECK +.LOOP LDA ERRMSG,Y + .else +.LOOP LDA ERRMSG-EMSGSZ,Y + .endif + JSR OUTCH + INY + DEX + BNE .LOOP + ;LDX #FAIL + DEX ; sets X = #FAIL + LDA ERFLAG + BNE RET1 + JMP PRNTLN + +; **************************************** + +INIT + JSR TOSTRT ; leaves $00 in A + STA FRFLAG + STA NGSYM + STA GSYMTL + STA CURPCL ; Initial value of PC for the assembled code + LDA CODEH + STA CURPCH + JSR CLRLCL ; set local and FREF table pointers + STX GSYMTH ; global table high byte - in X from CLRLCL +; JMP INITFR +; falls through + +; **************************************** + +INITFR ; initialise the FREF table and related pointers + LDA #$00 + STA NFREF + STA FREFTL + STA PTCHTL + LDY TABLEH + INY + STY FREFTH + STY PTCHTH +RET1 RTS + +; **************************************** + +DO1LN + JSR DETKN + CPX #PRGEND + BEQ .ENDPR + CPX #LABEL ; means we are still at the first field => blank line + BEQ .DONE + LDA #$00 + STA ERFLAG + STA FRFLAG + STA HADFRF + JSR PARSE + CPX #FAIL + BEQ DORTS +.CONT LDY #$00 +.LOOP LDA (CURLNL),Y + BEQ .DONE + INY + BNE .LOOP +.DONE INY ; one more to skip the null +.ENDPR ;JMP UPDTCL +; falls through + +; **************************************** + +UPDTCL ; update the current line pointer + ; by the number of bytes in Y + LDA CURLNL + STY SCRTCH + CLC + ADC SCRTCH ; move the current line pointer forward by 'Y' bytes + STA CURLNL + BCC INCLN + INC CURLNH ; increment the high byte if necessary +INCLN + INC LINEL + BNE DORTS + INC LINEH +DORTS RTS ; global label so can be shared + +; **************************************** + +MKOBJC ; MNE is in CURMNE, addr mode is in CURADM + ; and the args are in LVALL,H + ; calculate the object code, and update PC + LDY CURMNE + LDA BASE,Y ; get base value for current mnemonic + LDX CURADM + CLC + ADC OFFSET,X ; add in the offset + CPX #ABY ; handle exception + BEQ .CHABY + CPX #IMM + BNE .CONT + CPY #$28 ; immediate mode need to adjust a range + BMI .CONT + CPY #$2F+1 + BCS .CONT + ADC #ADJIMM ; carry is clear + ;BNE .CONT +.CHABY CPY #$35 + BNE .CONT + CLC + ADC #ADJABY +.CONT JSR DOBYTE ; we have the object code + .IF BRKAS2 + CMP #$00 + BNE .MKARG + JSR DOBYTE + .ENDIF +.MKARG ; where appropriate, the arg value is in LVALL,H + ; copy to ARGS and null terminate + TXA ; quick check for X=0 + BEQ DORTS ; IMP - no args + DEX + BEQ DORTS ; ACC - no args + LDA LVALL ; needed for .BYT handling + ; word arg if X is greater than or equal to ABS + CPX #ABS-1 + BMI DOBYTE ; X < #ABS +DOWORD JSR DOBYTE + LDA LVALH +DOBYTE LDY #$00 + STA (CURPCL),Y +; JMP INCPC +; falls through + +; **************************************** + +INCPC ; increment the PC + INC CURPCL + BNE .DONE ; any carry? + INC CURPCH ; yes +.DONE RTS + +; **************************************** + +CALCAM ; work out the addressing mode + JSR ADDMOD + CPX #FAIL + BNE MKOBJC + LDY #ILLADM ; Illegal address mode error + RTS + +PARSE ; Parse one line and validate + LDA LABEL + CMP #CMNT + BEQ DORTS ; ignore comment lines + LDX MNE ; first need to check for an equate + CPX #DOT + BNE .NOEQU + LDX MNE+1 + CPX #MOD ; Do we have a new module? + BNE .NOMOD + JMP DOMOD +.NOMOD CPX #EQU + BEQ DOEQU +.NOEQU CMP #SP ; Is there a label? + BEQ .NOLABL + JSR PCSYM ; save the symbol value - in this case it is the PC +.NOLABL LDA MNE + CMP #DOT ; do we have a directive? + BNE CALCAM ; no + +; **************************************** + +DODIR + LDX #$00 ; handle directives (except equate and module) + LDA MNE+1 + CMP #STR + BEQ DOSTR + STA FRFLAG ; Disallows forward references + JSR QTEVAL + ;CPX #FAIL + INX + BEQ DIRERR + LDA LVALL + LDX MNE+1 + CPX #WORD + BEQ DOWORD + LDX LVALH + BEQ DOBYTE +DIRERR LDY #SYNTAX + LDX #FAIL + RTS +DOSTR LDA ARGS,X + CMP #QUOTE + BNE DIRERR ; String invalid +.LOOP INX + LDA ARGS,X + BEQ DIRERR ; end found before string closed - error + CMP #QUOTE + BEQ DIROK + JSR DOBYTE ; just copy over the bytes + CPX #ARGSZ ; can't go over the size limit + BNE .LOOP + BEQ DIRERR ; hit the limit without a closing quote - error +DIROK RTS + +; **************************************** + +DOEQU + ;LDA LABEL + STA FRFLAG + JSR CHKALN ; label must be global + BCC DIRERR ; MUST have a label for an equate + LDX #$00 + JSR QTEVAL ; work out the associated value + ;CPX #FAIL + INX + BEQ DIRERR + JMP STRSYM + +; **************************************** + +DOMOD ; Do we have a new module? + ;LDA LABEL + JSR CHKALN ; must have a global label + BCC DIRERR + LDY #$00 + LDA ARGS + BEQ .STORE + CMP #SP + BEQ .STORE +.SETPC JSR ATOFR ; output finishing module end address (+1) + LDX #$00 ; set a new value for the PC from the args + LDA ARGS + JSR CONVRT + ;CPX #FAIL + INX + BEQ DIRERR + JSR LVTOPC +.STORE JSR PCSYM + CPX #FAIL + BEQ DIROK + JSR PATCH + CPX #FAIL + BEQ DIROK + JSR SHWMOD + LDA #$00 ; reset patch flag + JSR ATOFR ; output new module start address +; JMP CLRLCL +; falls through + +; **************************************** + +CLRLCL ; clear the local symbol table + LDX #$00 ; this also clears any errors + STX NLSYM ; to their starting values + STX LSYMTL + LDX TABLEH ; and then the high bytes + STX LSYMTH + RTS + +; **************************************** + +ATOFR STA FRFLAG +; JMP MYPRPC +; falls through + +; **************************************** + +MYPRPC + LDA CURPCH + LDX CURPCL + LDY FRFLAG ; flag set => print dash and minus 1 + BEQ .NODEC + PHA + JSR PRDASH + PLA + CPX #$00 + BNE .SKIP ; is X zero? + SEC + SBC #$01 +.SKIP DEX +.NODEC JMP PRNTAX + +; **************************************** + +PATCH ; back patch in the forward reference symbols + ; all are words + LDX NFREF + BEQ .RET ; nothing to do + STX ERFLAG ; set flag +.STRPC STX NPTCH + LDA CURPCL ; save the PC on the stack + PHA + LDA CURPCH + PHA + JSR INITFR +.NEXT LDY #$00 + LDA FRFLAG + STA FLGSAV ; so I can restore the FREF flag + STY HADFRF + LDA (PTCHTL),Y + CMP #DOT + BNE .LOOP + STA FRFLAG ; nonzero means must resolve local symbols +.LOOP LDA (PTCHTL),Y ; copy symbol to COMM + STA COMM,Y + INY + CPY #SYMSZ + BNE .LOOP + LDA (PTCHTL),Y ; get the PC for this symbol + STA CURPCL + INY + LDA (PTCHTL),Y + STA CURPCH + INY + LDA (PTCHTL),Y + STA TEMP1 ; save any offset value + JSR DOLVAL ; get the symbols true value + CPX #FAIL ; value now in LVALL,H or error + BEQ .ERR + LDA HADFRF ; if we have a persistent FREF + BEQ .CONT ; need to copy its offset as well + LDA TEMP1 + STA (MISCL),Y ; falls through to some meaningless patching... + ;SEC ; unless I put these two in + ;BCS .MORE +.CONT JSR ADD16X + LDY #$00 + LDA (CURPCL),Y ; get the opcode + AND #$1F ; check for branch opcode - format XXY10000 + CMP #$10 + BEQ .BRA + JSR INCPC ; skip the opcode +.SKIP LDA LVALL + JSR DOWORD +.MORE CLC + LDA PTCHTL ; move to the next symbol + ADC #SYMSZ+3 + STA PTCHTL + BCC .DECN + INC PTCHTH +.DECN LDA FLGSAV + STA FRFLAG + DEC NPTCH + BNE .NEXT +.DONE PLA + STA CURPCH ; restore the PC from the stack + PLA + STA CURPCL +.RET RTS +.BRA JSR ADDOFF ; BRA instructions have a 1 byte offset argument only + CPX #FAIL + BEQ .ERR + LDY #$01 ; save the offset at PC + 1 + LDA LVALL + STA (CURPCL),Y + JMP .MORE +.ERR LDY #$00 + JSR OUTSP +.LOOP2 LDA (PTCHTL),Y ; Show symbol that failed + JSR OUTCH + INY + CPY #SYMSZ + BNE .LOOP2 + DEY ; Since #UNKSYM = #SYMSZ - 1 + BNE .DONE ; always branches + +; **************************************** + +ADDMOD ; Check the arguments and work out the + ; addressing mode + ; return mode in X + LDX #$FF ; default error value for mode + STX CURADM ; save it + LDA CURMNE + LDX ARGS ; Start checking the format... + BEQ .EOL + CPX #SP + BNE .NOTSP +.EOL LDX #IMP ; implied mode - space + JSR CHKMOD ; check command is ok with this mode + CPX #FAIL ; not ok + BNE .RET ; may still be accumulator mode though + LDX #ACC ; accumulator mode - space + JMP CHKMOD ; check command is ok with this mode +.NOTSP CPX #IMV ; immediate mode - '#' + BEQ .DOIMM + LDX #REL + JSR CHKMOD ; check if command is a branch + CPX #FAIL + BEQ .NOTREL + LDA ARGS + JMP DOREL +.DOIMM CMP #$2C ; check exception first - STA + BEQ BAD + LDX #IMM + CMP #$35 ; check inclusion first - STX + BEQ .IMMOK + JSR CHKMOD ; check command is ok with this mode + CPX #FAIL + BEQ .RET +.IMMOK STX CURADM ; handle immediate mode + ;LDX #01 ; skip the '#' + DEX ; X == IMM == 2 + JSR QTEVAL + INX + BEQ BAD + LDA LVALH + BNE BAD + ;LDX #IMM +.RET RTS +.NOTREL LDX #0 ; check the more complicated modes + LDA ARGS + CMP #OPEN ; indirection? + BNE .CONT ; no + INX ; skip the '(' +.CONT JSR EVAL + CPX #FAIL + BEQ .RET + JSR FMT2AM ; calculate the addressing mode from the format + CPX #FAIL + BEQ .RET + STX CURADM +; JMP CHKEXS +; falls through + +; **************************************** + +CHKEXS ; Current addressing mode is in X + CPX #ZPY ; for MNE indices 28 to 2F, ZPY is illegal + BNE .CONT ; but ABY is ok, so promote byte argument to word + LDA CURMNE + CMP #$28 + BCC .CONT + CMP #$2F+1 + BCS .CONT + LDX #ABY ; updated addressing mode + BNE OK +.CONT LDY #SPCNT ; check special includes +.LOOP LDA SPINC1-1,Y ; load mnemonic code + CMP CURMNE + BNE .NEXT + LDX SPINC2-1,Y ; load addressing mode + CPX CURADM + BEQ OK ; match - so ok + LDX SPINC3-1,Y ; load addressing mode + CPX CURADM + BEQ OK ; match - so ok +.NEXT DEY + BNE .LOOP + LDX CURADM +; BNE CHKMOD ; wasn't in the exceptions table - check normally +; falls through + +; **************************************** + +CHKMOD LDA CURMNE ; always > 0 + CMP MIN,X ; mode index in X + BCC BAD ; mnemonic < MIN + CMP MAX,X ; MAX,X holds actually MAX + 1 + BCS BAD ; mnemonic > MAX +OK STX CURADM ; save mode + RTS + +; **************************************** + +BAD LDX #FAIL ; Illegal addressing mode error + RTS +DOREL + LDX #$00 + STX LVALL + STX LVALH + CMP #PC ; PC relative mode - '*' + BNE DOLBL + JSR PCTOLV + JSR XCONT +; JMP ADDOFF ; just do an unnecessary EVAL and save 3 bytes +DOLBL JSR EVAL ; we have a label +ADDOFF SEC ; calculate relative offset as LVALL,H - PC + LDA LVALL + SBC CURPCL + STA LVALL + LDA LVALH + SBC CURPCH + STA LVALH + BEQ DECLV ; error if high byte nonzero + INC LVALH + BNE BAD ; need either $00 or $FF +DECLV DEC LVALL + DEC LVALL +RELOK RTS ; need to end up with offset value in LVALL +;ERROFF LDX #FAIL +; RTS + +; **************************************** + +QTEVAL ; evaluate an expression possibly with a quote + LDA ARGS,X + BEQ BAD + CMP #QUOTE + BEQ QCHAR + JMP EVAL +QCHAR INX + LDA #$0 + STA LVALH ; quoted char must be a single byte + LDA ARGS,X ; get the character + STA LVALL + INX ; check and skip the closing quote + LDA ARGS,X + CMP #QUOTE + BNE BAD + INX + LDA ARGS,X + BEQ XDONE + CMP #SP + BEQ XDONE +; JMP DOPLMN +; falls through + +; **************************************** + +DOPLMN ; handle a plus/minus expression + ; on entry, A holds the operator, and X the location + ; store the result in LVALL,H + PHA ; save the operator + INX ; move forward + LDA ARGS,X ; first calculate the value of the byte + JSR BYT2HX + CPX #FAIL + BNE .CONT + PLA +; LDX #FAIL ; X is already $FF +.RET RTS +.CONT STA TEMP1 ; store the value of the byte in TEMP1 + PLA + CMP #PLUS + BEQ .NONEG + LDA TEMP1 + CLC ; for minus, need to negate it + EOR #$FF + ADC #$1 + STA TEMP1 +.NONEG LDA HADFRF + BEQ .SKIP + LDA TEMP1 ; save the offset for use when patching + STA (MISCL),Y +.SKIP ;JMP ADD16X +; falls through + +; **************************************** + +ADD16X ; Add a signed 8 bit number in TEMP1 + ; to a 16 bit number in LVALL,H + ; preserve X (thanks leeeeee, www.6502.org/forum) + LDA TEMP1 ; signed 8 bit number + BPL .CONT + DEC LVALH ; bit 7 was set, so it's a negative +.CONT CLC + ADC LVALL + STA LVALL ; update the stored number low byte + BCC .EXIT + INC LVALH ; update the stored number high byte +.EXIT RTS + +; **************************************** + +EVAL ; Evaluate an argument expression + ; X points to offset from ARGS of the start + ; on exit we have the expression replaced + ; by the required constant + STX TEMP3 ; store start of the expression + LDA ARGS,X + CMP #LOBYTE + BEQ .HASOP + CMP #HIBYTE + BNE .DOLBL +.HASOP STA FRFLAG ; disables forward references when there + INX ; is a '<' or a '>' in the expression + LDA ARGS,X +.DOLBL JSR CHKLBL ; is there a label? + BCS .LBL ; yes - get its value + JSR CONVRT ; convert the ASCII + CPX #FAIL + BEQ XERR + BNE XCONT +.LBL STX XSAV ; move X to Y + JSR LB2VAL ; yes - get its value + CPX #FAIL + BEQ XDONE + LDX XSAV +XCONT INX ; skip the '$' + LDA ARGS,X ; Value now in LVALL,H for ASCII or LABEL + JSR CHKLBL + BCS XCONT ; Continue until end of label or digits + ;STX TEMP4 ; Store end index + CMP #PLUS + BEQ .DOOP + CMP #MINUS + BNE XCHKOP +.DOOP JSR DOPLMN + CPX #FAIL + BNE XCONT +XERR LDY #SYNTAX ; argument syntax error +XDONE RTS +XCHKOP LDY #$00 + LDA FRFLAG + CMP #LOBYTE + BEQ .GETLO + CMP #HIBYTE + BNE .STORE + LDA LVALH ; move LVALH to LVALL + STA LVALL +.GETLO STY LVALH ; keep LVALL, and zero LVALH +.STORE LDA ARGS,X ; copy rest of args to COMM + STA COMM,Y + BEQ .DOVAL + CMP #SP + BEQ .DOVAL + INX + INY + CPX #ARGSZ + BNE .STORE +.DOVAL LDA #$00 + STA COMM,Y + LDY TEMP3 ; get start index + LDA #HEX ; put the '$" back in so subsequent code + STA ARGS,Y ; manages the value properly + INY + LDA LVALH + BEQ .DOLO + JSR HX2ASC +.DOLO LDA LVALL + JSR HX2ASC + LDX #$00 ; bring back the rest from IOBUF +.COPY LDA COMM,X + STA ARGS,Y ; store at offset Y from ARGS + BEQ XDONE + INX + INY + BNE .COPY + +; **************************************** + +LB2VAL ; label to be evaluated is in ARGS + X (X = 0 or 1) + LDY #$00 +.NEXT CPY #LBLSZ ; all chars done + BEQ DOLVAL + JSR CHKLBL ; has the label finished early? + BCC .STOP + STA COMM,Y ; copy because we need exactly 6 chars for the search + INX ; COMM isn't used in parsing, so it + LDA ARGS,X ; can be treated as scratch space + INY + BNE .NEXT +.STOP LDA #SP ; label is in COMM - ensure filled with spaces +.LOOP STA COMM,Y ; Y still points to next byte to process + INY + CPY #LBLSZ + BNE .LOOP +DOLVAL LDA #COMM) + STX STRH + LDA #SYMSZ + STA RECSIG + LDA #SYMSZ+2 + STA RECSZ ; size includes additional two bytes for value + LDA COMM + CMP #DOT + BEQ .LOCAL ; local symbol + JSR SYMSCH + BEQ .FREF ; if not there, handle as a forward reference +.FOUND LDY #SYMSZ + LDA (TBLL),Y ; save value + STA LVALL + INY + LDA (TBLL),Y + STA LVALH + RTS +.LOCAL ; locals much the same + LDX #$03 ; select local table + JSR SYMSCH + BNE .FOUND ; if not there, handle as a forward reference +.FREF LDA FRFLAG ; set when patching + BNE SYMERR ; can't add FREFs when patching + JSR PCTOLV ; default value to PC + LDA FREFTH ; store it in the table + STA MISCH + LDA FREFTL ; Calculate storage address + LDX NFREF + BEQ .CONT ; no symbols to skip +.LOOP CLC + ADC #SYMSZ+3 ; skip over existing symbols + BCC .SKIP + INC MISCH ; carry bit set - increase high pointer +.SKIP DEX + BNE .LOOP +.CONT STA MISCL ; Reqd address is now in MISCL,H + INC NFREF ; Update FREF count + LDA NFREF + CMP #MAXFRF ; Check for table full + BPL OVFERR + LDA #COMM + STA HADFRF ; non-zero value tells that FREF was encountered + STA MISC2L + JSR STORE ; Store the symbol + INY + TXA ; X is zero after STORE + STA (MISCL),Y + RTS ; No error + +; **************************************** + +PCSYM + JSR PCTOLV +; JMP STRSYM + +; **************************************** + +STRSYM ; Store symbol - name at LABEL, value in MISC2L,H + LDA #LABEL + STA MISC2L + STA STRL + LDX #$00 + STX STRH + LDA #SYMSZ + STA RECSIG + LDA LABEL ; Global or local? + CMP #DOT + BNE .SRCH ; Starts with a dot, so local + LDX #$03 +.SRCH JSR SYMSCH + BEQ STCONT ; Not there yet, so ok +.ERR PLA + PLA +SYMERR LDY #UNKSYM ; missing symbol error + BNE SBAD + ;LDX #FAIL + ;RTS +OVFERR LDY #OVRFLW ; Symbol table overflow error +SBAD LDX #FAIL + RTS +STCONT ;LDA #LABEL + LDX LABEL ; Global or local? + CPX #DOT + BEQ .LSYM ; Starts with a dot, so local + SEC ; Store symbol in global symbol table + LDA GSYMTL ; Make space for next symbol + SBC #SYMSZ+2 ; skip over existing symbols + BCS .CONTG ; Reqd address is now in GSYMTL,H +.DWNHI DEC GSYMTH ; carry bit clear - decrease high pointer +.CONTG STA GSYMTL + INC NGSYM ; Update Symbol count - overflow on 256 symbols + BEQ OVFERR ; Check for table full + STA MISCL ; put addres into MISCH,L for saving + LDA GSYMTH + STA MISCH + BNE STORE ; Always branches - symbol tables cannot be on page zero +.LSYM LDA LSYMTH ; Store symbol in local symbol table + STA MISCH + LDA LSYMTL ; Calculate storage address + LDX NLSYM + BEQ .CONTL ; no symbols to skip +.LOOP CLC + ADC #SYMSZ+2 ; skip over existing symbols + BCC .SKIP + INC MISCH +.SKIP DEX + BNE .LOOP +.CONTL STA MISCL ; Reqd address is now in MISCL,H + INC NLSYM ; Update Symbol count + LDA NLSYM + CMP #MAXSYM ; Check for table full + BPL OVFERR +STORE LDY #0 ; First store the symbol string + STY MISC2H + LDX #SYMSZ +.MV LDA (MISC2L),Y ; move bytes + STA (MISCL),Y + INY + DEX + BNE .MV + LDA LVALL ; Now store the value WORD + STA (MISCL),Y + INY + LDA LVALH + STA (MISCL),Y + RTS ; No error + + +; **************************************** + +CONVRT ; convert an ASCII string at ARGS,X + ; of the form $nnnn (1 to 4 digits) + ; return the result in LVALL,H, and preserves X and Y + ; uses COMM area for scratch space + CMP #HEX ; syntax for hex constant + BNE SBAD ; syntax error + STY COMM+1 + JSR NBYTS + CPX #FAIL + BEQ SBAD + STA COMM + LDY #$00 + STY LVALH +.BACK DEX + DEX + LDA ARGS,X + CMP #HEX + BEQ .1DIG + JSR BYT2HX + SEC + BCS .SKIP +.1DIG JSR AHARGS1 ; one digit +.SKIP STA LVALL,Y + INY + CPY COMM + BNE .BACK +.RET LDY COMM+1 + RTS + +; **************************************** + +SYMSCH ; X = 0 for globals + ; X = 3 for locals + LDA GSYMTL,X ; get global symbol value + STA TBLL + LDA GSYMTH,X + STA TBLH + LDA NGSYM,X ; Number of global symbols + STA RECNM + JSR SEARCH + CPX #FAIL ; Z set if search failed + RTS ; caller to check + +; **************************************** + +FMT2AM ; calculate the addressing given + ; the format of the arguments; + ; return format in X, and + ; location to CHKEXT from in A + ; $FF invalid + ; #ZPG $nn + ; #ZPX $nn,X + ; #ZPY $nn,Y + ; #ABS $nnnn + ; #ABX $nnnn,X + ; #ABY $nnnn,Y + ; #IND ($nnnn) + ; #IDX ($nn,X) + ; #IDY ($nn),Y + ; #INZ ($nn) + ; #IAX ($nnnn,X) +; +; Addressing modes are organised as follows: +; +; IMP (0) ZPG (4) INZ (-) ABS (9) IND (C) +; ACC (1) ZPX (5) INX (7) ABX (A) IAX (D) +; IMM (2) ZPY (6) INY (8) ABY (B) --- +; REL (3) --- --- --- --- +; +; so algorithm below starts with 4, adds 3 if indirect +; and adds 6 if absolute (i.e. 2 byte address), then adds 1 or 2 +; if ,X or ,Y format +; + LDX #$00 + LDA #$04 ; start with mode index of 4 + LDY ARGS,X + CPY #OPEN + BNE .SKIP + CLC ; add 3 for indirect modes + ADC #$03 + INX +.SKIP PHA + JSR NBYTS ; count bytes (1 or 2 only) + TAY ; byte count in Y + DEX + LDA CURMNE + CMP #$21 ; is it JSR? + BEQ .JSR + CMP #$23 ; is it JMP? + BNE .NOJMP +.JSR ;LDY #$2 ; force 2 bytes for these two situations + INY ; following code treats Y = 3 the same as Y = 2 +.NOJMP PLA ; mode base back in A + INX ; check for NBYTS failure + BEQ FERR + DEY + BEQ .1BYT +.2BYT CLC + ADC #$06 ; add 6 to base index for 2 byte modes +.1BYT TAY ; mode index now in Y +.CHECK LDA ARGS,X + BEQ .DONE + CMP #SP + BNE .CONT +.DONE LDA ARGS + CMP #OPEN ; brackets must match + BEQ FERR +.RET CPY #$0F + BPL FERR ; no indirect absolute Y mode + CPY #$07 + BEQ FERR ; no indirect zero page mode + BMI .1 ; 6502 has no INZ mode, so reduce + DEY ; so reduce by ifgreater than 7 +.1 TYA + TAX + RTS +.CONT CMP #CLOSE + BNE .MORE + LDA #SP + STA ARGS ; erase brackets now they have + INX + LDA ARGS,X + CMP #COMMA + BNE .CHECK +.MORE LDA ARGS,X + CMP #COMMA + BNE FERR + INX + LDA ARGS,X + CMP #'X' + BEQ .ISX +.ISY CMP #'Y' + BNE FERR + LDA ARGS + CMP #OPEN + BEQ FERR + STA ARGS-2,X ; to avoid ,X check below + INY +.ISX INY + LDA ARGS-2,X + CMP #CLOSE + BEQ FERR + INX + BNE .CHECK ; always +FERR LDX #FAIL ; error message generated upstream +FRET RTS +NBYTS LDY #$00 ; count bytes using Y +.LOOP INX + INY + JSR AHARGS + CMP #FAIL + BNE .LOOP +.NEXT TYA + LSR ; divide number by 2 + BEQ FERR ; zero is an error + CMP #$03 ; 3 or more is an error + BCS FERR +.RET RTS + +; **************************************** +; * Utility Routines * +; **************************************** + +SEARCH ; TBLL,H has the address of the table to search + ; and address of record on successful return + ; STRL,H has the address of the search string + ; Search through RECNM records + ; Each of size RECSZ with RECSIG significant chars + LDA RECNM + BEQ FERR ; empty table + LDX #$00 ; Record number +.CHK1 LDY #$FF ; Index into entry +.CHMTCH INY + CPY RECSIG ; Have we checked all significant chars? + BEQ FRET ; Yes + LDA (TBLL),Y ; Load the bytes to compare + CMP (STRL),Y + BEQ .CHMTCH ; Check next if these match + INX ; Else move to next record + CPX RECNM + BEQ FERR + LDA TBLL ; Update address + CLC + ADC RECSZ + STA TBLL + BCC .CHK1 + INC TBLH ; Including high byte if necessary + BCS .CHK1 ; will always branch +;.FAIL LDX #FAIL ; X = $FF indicates failure +;.MATCH RTS ; got it - index is in X, address is in A and TBLL,H + +; **************************************** + +BYT2HX ; convert the ASCII byte (1 or 2 chars) at offset X in + ; the args field to Hex + ; result in A ($FF for fail) + + JSR AHARGS + CMP #FAIL ; indicates conversion error + BEQ FERR + PHA + JSR AHARGS1 + DEX + CMP #FAIL + BNE .CONT + PLA ; just ignore 2nd character + RTS +.CONT STA SCRTCH + PLA + ASL ; shift + ASL + ASL + ASL + ADC SCRTCH + RTS + +; **************************************** + +AHARGS1 INX ; caller needs to DEX +AHARGS LDA ARGS,X +ASC2HX ; convert ASCII code in A to a HEX digit + EOR #$30 + CMP #$0A + BCC .VALID + ADC #$88 ; $89 - CLC + CMP #$FA + BCC .ERR + AND #$0F +.VALID RTS +.ERR LDA #FAIL ; this value can never be from a single digit, + RTS ; so ok to indicate error + +; **************************************** + +HX2ASC ; convert a byte in A into two ASCII characters + ; store in ARGS,Y and ARGS+1,Y + PHA ; 1st byte. + JSR LSR4 ; slower, but saves a byte and not too crucial + JSR DO1DIG + PLA +DO1DIG AND #$0F ; Print 1 hex digit + ORA #$30 + CMP #$3A + BCC .DONE + ADC #$06 +.DONE STA ARGS,Y + INY + RTS + +; **************************************** + +EXPMNE ; copy the 2 chars at R/LMNETB,X + ; into LMNE and RMNE, and expand + ; into 3 chars at MNE to MNE+2 + LDA LMNETB,X + STA LMNE + LDA RMNETB,X + STA RMNE + LDX #$00 +.NEXT LDA #$00 + LDY #$05 +.LOOP ASL RMNE + ROL LMNE + ROL + DEY + BNE .LOOP + ADC #'A'-1 + STA MNE,X + LDY PRFLAG + BEQ .SKIP + JSR OUTCH ; print the mnemonic as well +.SKIP INX + CPX #$03 + BNE .NEXT + RTS + +; **************************************** +; DISASSEMBLER +; Adapted from code in a Dr Dobbs article +; by Steve Wozniak and Allen Baum (Sep '76) +; **************************************** + +DISASM + JSR ADDARG + ;BEQ .DODIS + BEQ DSMBL +.COPY JSR LVTOPC +;.DODIS JMP DSMBL +; fall through + +; **************************************** + +DSMBL + ;LDA #$13 ; Count for 20 instruction dsmbly + ;STA COUNT +.DSMBL2 JSR INSTDSP ; Disassemble and display instr. + JSR PCADJ + STA PCL ; Update PCL,H to next instr. + STY PCH + ;DEC COUNT ; Done first 19 instrs + ;BNE .DSMBL2 ; * Yes, loop. Else DSMBL 20th + + LDA KBDRDY ; Now disassemble until key press + .if APPLE1 + BPL .DSMBL2 + .else + BEQ .DSMBL2 + .endif + LDA KBD + + +INSTDSP JSR PRPC ; Print PCL,H + LDA (PCL,X) ; Get op code + TAY + LSR ; * Even/odd test + BCC .IEVEN + ROR ; * Test B1 + BCS .ERR ; XXXXXX11 instr invalid + CMP #$A2 + BEQ .ERR ; 10001001 instr invalid + AND #$87 ; Mask 3 bits for address mode + ;ORA #$80 ; * add indexing offset +.IEVEN LSR ; * LSB into carry for + TAX ; Left/right test below + LDA MODE,X ; Index into address mode table + BCC .RTMODE ; If carry set use LSD for + JSR LSR4 + ;LSR ; * print format index + ;LSR + ;LSR ; If carry clear use MSD + ;LSR +.RTMODE AND #$0F ; Mask for 4-bit index + BNE .GETFMT ; $0 for invalid opcodes +.ERR LDY #$80 ; Substitute $80 for invalid op, + LDA #$00 ; set print format index to 0 +.GETFMT TAX + LDA MODE2,X ; Index into print format table + STA FORMAT ; Save for address field format + AND #$03 ; Mask 2-bit length. 0=1-byte + STA LENGTH ; * 1=2-byte, 2=3 byte + TYA ; * op code + JSR GETMNE + LDY #$00 + PHA ; Save mnemonic table index +.PROP LDA (PCL),Y + JSR OUTHEX + LDX #$01 +.PROPBL JSR PRBL2 + CPY LENGTH ; Print instr (1 to 3 bytes) + INY ; * in a 12-character field + BCC .PROP + LDX #$03 ; char count for mnemonic print + STX PRFLAG ; So EXPMNE prints the mnemonic + CPY #$04 + BCC .PROPBL + PLA ; Recover mnemonic index + TAX + JSR EXPMNE + JSR PRBLNK ; Output 3 blanks + LDY LENGTH + LDX #$06 ; Count for 6 print format bits +.PPADR1 CPX #$03 + BEQ .PPADR5 ; If X=3 then print address val +.PPADR2 ASL FORMAT ; Test next print format bit + BCC .PPADR3 ; If 0 don't print + LDA CHAR1-1,X ; * corresponding chars + JSR OUTCH ; Output 1 or 2 chars + LDA CHAR2-1,X ; * (If char from char2 is 0, + BEQ .PPADR3 ; * don't output it) + JSR OUTCH +.PPADR3 DEX + BNE .PPADR1 + STX PRFLAG ; reset flag to 0 + RTS ; Return if done 6 format bits +.PPADR4 DEY + BMI .PPADR2 + JSR OUTHEX ; Output 1- or 2-byte address +.PPADR5 LDA FORMAT + CMP #$E8 ; Handle rel addressing mode + LDA (PCL),Y ; Special print target adr + BCC .PPADR4 ; * (not displacement) +.RELADR JSR PCADJ3 ; PCL,H + DISPL + 1 to A,Y + TAX + INX + BNE PRNTYX ; * +1 to X,Y + INY +PRNTYX TYA +PRNTAX JSR OUTHEX ; Print target adr of branch +PRNTX TXA ; * and return + JMP OUTHEX +PRPC JSR CRLF ; Output carriage return + LDA PCH + LDX PCL + JSR PRNTAX ; Output PCL and PCH +PRBLNK LDX #$03 ; Blank count +PRBL2 JSR OUTSP ; Output a blank + DEX + BNE PRBL2 ; Loop until count = 0 + RTS +PCADJ SEC +PCADJ2 LDA LENGTH ; 0=1-byte, 1=2-byte, 2=3-byte +PCADJ3 LDY PCH + TAX ; * test displ sign (for rel + BPL .PCADJ4 ; * branch). Extend neg + DEY ; * by decrementing PCH +.PCADJ4 ADC PCL + BCC .RTS ; PCL+LENGTH (or displ) + 1 to A + INY ; * carry into Y (PCH) +.RTS RTS + +GETMNE ; get mnemonic index for opcode in A + ; on completion, A holds the index + ; into the mnemonic table + STA TEMP1 ; will need it later + AND #$8F + CMP #$8A + BEQ CAT3 + ASL + CMP #$10 + BEQ CAT2 + LDA TEMP1 ; ? ABCD EFGH - thanks bogax, www.6502.org/forum + ASL ; A BCDE FGH0 + ADC #$80 ; B ?CDE FGHA + ROL ; ? CDEF GHAB + ASL ; C DEFG HAB0 + AND #$1F ; C 000G HAB0 + ADC #$20 ; 0 001G HABC + PHA + LDA TEMP1 ; get the opcode back + AND #$9F + BEQ CAT1 + ASL + CMP #$20 + BEQ CAT4 + AND #$06 + BNE CAT67 +CAT5 ; remaining nnnX XX00 codes + PLA + AND #$07 ; just low 3 bits + CMP #$03 + BPL .3 + ADC #$02 ; correction for 21 and 22 +.3 ADC #$1F ; and add 20 + RTS +CAT4 ; Branch instructions - nnn1 0000 + PLA + AND #$07 ; just low 3 bits + ADC #$18 ; and add 19 (carry is set) + RTS +CAT1 ; 0nn0 0000 - BRK, JSR, RTI, RTS + PLA + TAX + LDA MNEDAT-$20,X + RTS +MNEDAT .byte $16, $21, $17, $18 +CAT2 ; nnnn 1000 - lots of no-arg mnemonics + LDA TEMP1 +LSR4 LSR ; need high 4 bits + LSR + LSR + LSR + RTS +CAT3 ; 1nnn 1010 - TXA,TXS,TAX,TSX,DEX,-,NOP,- + JSR CAT2 ; need high 4 bits + CMP #$0E + BNE .2 + ADC #$FD +.2 ADC #$08 ; then add 8 + RTS +CAT67 ; gets the index for categories 6 and 7 + PLA ; i.e. nnnX XX01 and nnnX XX10 ($28-$2F, $30-$37) + RTS ; it's already done + +; Data and related constants + +MODES ; Addressing mode constants +IMP = $00 +ACC = $01 +IMM = $02 ; #$nn or #'' or #LABEL +REL = $03 ; *+nn or LABEL +ZPG = $04 ; $nn or LABEL +ZPX = $05 ; $nn,X or LABEL,X +ZPY = $06 ; $nn,Y or LABEL,Y +IDX = $07 ; ($nn,X) or (LABEL,X) +IDY = $08 ; ($nn),Y or (LABEL),Y +ABS = $09 ; $nnnn or LABEL +ABX = $0A ; $nnnn,X or LABEL,X +ABY = $0B ; $nnnn or LABEL +IND = $0C ; ($nnnn) or (LABEL) + +NUMMN =$38 ; number of mnemonics + +; Tables + +LMNETB + + .byte $82 ; PHP + .byte $1B ; CLC + .byte $83 ; PLP + .byte $99 ; SEC + .byte $82 ; PHA + .byte $1B ; CLI + .byte $83 ; PLA + .byte $99 ; SEI + .byte $21 ; DEY + .byte $A6 ; TYA + .byte $A0 ; TAY + .byte $1B ; CLV + .byte $4B ; INY + .byte $1B ; CLD + .byte $4B ; INX + .byte $99 ; SED + .byte $A6 ; TXA + .byte $A6 ; TXS + .byte $A0 ; TAX + .byte $A4 ; TSX + .byte $21 ; DEX + .byte $73 ; NOP + .byte $14 ; BRK + .byte $95 ; RTI + .byte $95 ; RTS + .byte $14 ; BPL + .byte $13 ; BMI + .byte $15 ; BVC + .byte $15 ; BVS + .byte $10 ; BCC + .byte $10 ; BCS + .byte $13 ; BNE + .byte $11 ; BEQ + .byte $54 ; JSR + .byte $12 ; BIT + .byte $53 ; JMP + .byte $9D ; STY + .byte $61 ; LDY + .byte $1C ; CPY + .byte $1C ; CPX + .byte $7C ; ORA + .byte $0B ; AND + .byte $2B ; EOR + .byte $09 ; ADC + .byte $9D ; STA + .byte $61 ; LDA + .byte $1B ; CMP + .byte $98 ; SBC + .byte $0C ; ASL + .byte $93 ; ROL + .byte $64 ; LSR + .byte $93 ; ROR + .byte $9D ; STX + .byte $61 ; LDX + .byte $21 ; DEC + .byte $4B ; INC + + +RMNETB + .byte $20 ; PHP + .byte $06 ; CLC + .byte $20 ; PLP + .byte $46 ; SEC + .byte $02 ; PHA + .byte $12 ; CLI + .byte $02 ; PLA + .byte $52 ; SEI + .byte $72 ; DEY + .byte $42 ; TYA + .byte $72 ; TAY + .byte $2C ; CLV + .byte $B2 ; INY + .byte $08 ; CLD + .byte $B0 ; INX + .byte $48 ; SED + .byte $02 ; TXA + .byte $26 ; TXS + .byte $70 ; TAX + .byte $F0 ; TSX + .byte $70 ; DEX + .byte $E0 ; NOP + .byte $96 ; BRK + .byte $12 ; RTI + .byte $26 ; RTS + .byte $18 ; BPL + .byte $52 ; BMI + .byte $86 ; BVC + .byte $A6 ; BVS + .byte $C6 ; BCC + .byte $E6 ; BCS + .byte $8A ; BNE + .byte $62 ; BEQ + .byte $E4 ; JSR + .byte $68 ; BIT + .byte $60 ; JMP + .byte $32 ; STY + .byte $32 ; LDY + .byte $32 ; CPY + .byte $30 ; CPX + .byte $82 ; ORA + .byte $88 ; AND + .byte $E4 ; EOR + .byte $06 ; ADC + .byte $02 ; STA + .byte $02 ; LDA + .byte $60 ; CMP + .byte $86 ; SBC + .byte $D8 ; ASL + .byte $D8 ; ROL + .byte $E4 ; LSR + .byte $E4 ; ROR + .byte $30 ; STX + .byte $30 ; LDX + .byte $46 ; DEC + .byte $86 ; INC + +MIN ; Minimum legal value for MNE for each mode. + .byte $00, $30, $25, $19, $24 + .byte $28, $34, $28, $28, $21, $28 + .byte $28, $23 +MAX ; Maximum +1 legal value of MNE for each mode. + .byte $18+1, $33+1, $2F+1, $20+1, $37+1 + .byte $33+1, $35+1, $2F+1, $2F+1, $37+1, $33+1 + .byte $2F+1, $23+1 +BASE ; Base value for each opcode + .byte $08, $18, $28, $38 + .byte $48, $58, $68, $78 + .byte $88, $98, $A8, $B8 + .byte $C8, $D8, $E8, $F8 + .byte $8A, $9A, $AA, $BA + .byte $CA, $EA, $00, $40 + .byte $60, $10, $30, $50 + .byte $70, $90, $B0, $D0 + .byte $F0, $14, $20, $40 + .byte $80, $A0, $C0, $E0 + .byte $01, $21, $41, $61 + .byte $81, $A1, $C1, $E1 + .byte $02, $22, $42, $62 + .byte $82, $A2, $C2, $E2 +OFFSET ; Default offset values for each mode, + ; added to BASE to get Opcode + .byte $00, $08, $00, $00, $04 + .byte $14, $14, $00, $10, $0C, $1C + .byte $18, $2C + +; offset adjustments for the mnemonic exceptions +ADJABY =$04 ; +ADJIMM =$08 ; + +; disassembler data + +; XXXXXXZ0 instrs +; * Z=0, left half-byte +; * Z=1, right half-byte +MODE .byte $04, $20, $54, $30, $0D + .byte $80, $04, $90, $03, $22 + .byte $54, $33, $0D, $80, $04 + .byte $90, $04, $20, $54, $33 + .byte $0D, $80, $04, $90, $04 + .byte $20, $54, $3B, $0D, $80 + .byte $04, $90, $00, $22, $44 + .byte $33, $0D, $C8, $44, $00 + .byte $11, $22, $44, $33, $0D + .byte $C8, $44, $A9, $01, $22 + .byte $44, $33, $0D, $80, $04 + .byte $90, $01, $22, $44, $33 + .byte $0D, $80, $04, $90 +; YYXXXZ01 instrs + .byte $26, $31, $87, $9A + +MODE2 .byte $00 ; ERR + .byte $21 ; IMM + .byte $81 ; Z-PAG + .byte $82 ; ABS + .byte $00 ; IMPL + .byte $00 ; ACC + .byte $59 ; (Z-PAG,X) + .byte $4D ; (Z-PAG),Y + .byte $91 ; Z-PAG,X + .byte $92 ; ABS,X + .byte $86 ; ABS,Y + .byte $4A ; (ABS) + .byte $85 ; Z-PAG,Y + .byte $9D ; REL +CHAR1 .byte ',' + .byte ')' + .byte ',' + .byte '#' + .byte '(' + .byte '$' +CHAR2 .byte 'Y' + .byte $00 + .byte 'X' + .byte '$' + .byte '$' + .byte $00 + +; Special case mnemonics +SPCNT = $06 ; duplicate some checks so I can use the same loop above +; Opcodes +SPINC1 .byte $22, $24, $25, $35, $36, $37 +; 1st address mode to check +SPINC2 .byte $04, $05, $05, $02, $05, $05 +; 2nd address mode to check +SPINC3 .byte $04, $05, $0A, $0B, $0A, $0A + +; commands + +NUMCMD =$0D +CMDS .ASCII "NLXEMRDI!$AVP" + +N1 = NEW-1 +L1 = LIST-1 +D1 = DELETE-1 +E1 = EDIT-1 +M1 = MEM-1 +R1 = RUN-1 +DIS1 = DISASM-1 +I1 = INSERT-1 +GL1 = GETLINE-1 +MON1 = MONTOR-1 +A1 = ASSEM-1 +V1 = VALUE-1 +P1 = PANIC-1 + +CMDH .byte >N1 + .byte >L1 + .byte >D1 + .byte >E1 + .byte >M1 + .byte >R1 + .byte >DIS1 + .byte >I1 + .byte >GL1 + .byte >MON1 + .byte >A1 + .byte >V1 + .byte >P1 + +CMDL .byte MODE (XAM mode). + LDA XAML ; See if there's more to print + CMP L + LDA XAMH + SBC H + BCS TONEXTITEM ; Not less! No more data to output + INC XAML ; Increment 'examine index' + BNE MOD8CHK ; No carry! + INC XAMH +MOD8CHK LDA XAML ; If address MOD 8 = 0 start new line + AND #$07 + BPL NXTPRNT ; Always taken. + + .if APPLE1 +; Apple 1 I/O values +KBD =$D010 ; Apple 1 Keyboard character read. +KBDRDY =$D011 ; Apple 1 Keyboard data waiting when negative. + + .ORG $FFDC +OUTHEX PHA ; Print 1 hex byte. + LSR + LSR + LSR + LSR + JSR PRHEX + PLA +PRHEX AND #$0F ; Print 1 hex digit + ORA #$30 + CMP #$3A + BCC OUTCH + ADC #$06 +OUTCH BIT DSP ; DA bit (B7) cleared yet? + BMI OUTCH ; No! Wait for display ready + STA DSP ; Output character. Sets DA + RTS + .else +IOMEM =$E000 +PUTCH =IOMEM+1 +KBD =IOMEM+4 +KBDRDY =IOMEM+4 + + .ORG $FFDC +OUTHEX PHA ; Print 1 hex byte. + LSR + LSR + LSR + LSR + JSR PRHEX + PLA +PRHEX AND #$0F ; Print 1 hex digit + ORA #$30 + CMP #$3A + BCC OUTCH + ADC #$06 +OUTCH STA PUTCH + RTS + .ENDIF + + + .if MINIMONITOR + .ORG $FFFA ; INTERRUPT VECTORS + .WORD $0F00 + .WORD RESET + .WORD DEBUG + .ENDIF + .ELSE +; Apple 1 I/O values +OUTCH =$FFEF ; Apple 1 Echo +PRHEX =$FFE5 ; Apple 1 Echo +OUTHEX =$FFDC ; Apple 1 Print Hex Byte Routine +KBD =$D010 ; Apple 1 Keyboard character read. +KBDRDY =$D011 ; Apple 1 Keyboard data waiting when negative. + .endif ; inrom \ No newline at end of file diff --git a/Krusader/krusader13.pdf b/Krusader/krusader13.pdf new file mode 100644 index 0000000000000000000000000000000000000000..07cbbe7775c29367a5530748044bf7975449ce8e GIT binary patch literal 334084 zcmbrG1z1#D_weaP5KtNhL_j2F7zT#!5|HkWp`^P7=}zfJP>?PWX#tTAm2M=YrKG>X zd-dYI?|uLGy`RtHa~L>#opsh)JJxTnJ$DtvBv?SK99VY;KXi;?K>$F2t&t@bA0L2C z8NeoKt6~da69X_h!i=3PY;9OU_W%L{STGxttDB&|-^?g&I{zI0sp|zbA44@2ibc8?fr!)}w{}2cMMI7f(apo{9D}bo2wY8y*iQ^yQxc(1u z9M{FM1AmIMb#^d@!R492e#&A8{hcglqaSwT_|*9Vf9r&jwgdNPBtzkA!e~1JBtrM<``^9wNpPsn#!LQQ( z)(6+6L9RQ3C8;QU44Pe+JZI9$cJ3+$S_zjem%z2R5ge)>V& z!NJzynwY{+tj|AUiacIDd^c$WI>#o0wQ!^Gc;Q9xS`gzc_|OAz3y=U=xC*WY^ccVk1Z z8you5k+Qa~09lv|%nBfFV`}SQZFm(gKP?XZTNnOe5a_QKhyL`V9DI0ka0kd4+WjH# zzj^Q%k$)2U(~Y9$hBjtb#t?&<8ai7!0e*jqtgZ2P)&B@T=s$2K^w;?2{^?BR-v)ja z+ea`P3&-!00m{baFzY|&0q%d`Q0T7?<^JhVIa`;jm-tQjpXTBITPI$(%`XEk2>8>L zDlkVUxX_={K)}EC-tRHNeLW^Xz@MH|f?2@~9bo_|3rBd2xnGm`H~!NG0k2+72>@V| zuy6wCURyPQe$1BuK0YiqS(uHPlR1DL!VLs`|Az%{VC4jJfH$-X{~vuT!}>A9E=D$ijNMJMR8_ z``hO$+g#uTE9(sFuqKTqJ198ac6dVDbK-P9<)ZuOV_#Lc456pmOK5GZtK-Tzqrya| zUb@%=bU#;Rx2!Yj(@Je-Wb1QR_p0)W=7IGNHZI%7!58fXGs8hlBNc$z8p;wsyDx~D z&q8V1dVtEpXj4FiPUzG+o}iD{@p&4ajp#CIUL|8?L#tRJy9LUZ!Uy1WmxR7H^Uce~ zV4>JULFS!!Jo4N4q9IyIjoN45${X_!YD?9S+{ceTAJ+QBz%MJ6Hm;MEq$oQD$2!gp zdE7{ALVO5Ggax0YUJ$qZt5{;ByKc;;*{Dg8TLBg30+A{SC{$f9 zcT{$n4<6qx+|4oSbuYs;_4L>1DD)eO2(zP@@zm%1(xQFwdS+R*L~Zks*_iyTq>&dG zRvgs&0KJj{H^#UGw{uQwLQWVvRao=G~r5-!feNuKJHW_PI~AbBjYo?16R9 z9?r+bs@|iB98c*OCLupo@1q4?4AuaQ&WxJLq!4Hb2NKd>Fm`LWqy?FsdzdCbRSGk$ z+63^)Txh*qbl+@!Es?#U3&AONkq!Dp^T}Bzn7{5G_kLSJ(>G**^Gxy4aeacOq7S{* z$}$IqNe< zkUp_tBTmJk;IUs6TB_Nsyewtn&XX;~)Oy$;1`A!@Fl?PDNI}=f93E0|JbRrO>lV|P z$nb3mH#Bf6a3{d(WvPR}V*AUf9I<;sxt~ASk`^`!5f&CP+q;_OP-)Q`W+gnkTDdDzf$Nn^%ivRM|;j7dL4KOH>LJg-vz^!+Jv z$x??hnp8~9;+)Fmq5K#94D3u5m?0GF8@gh=$q*5wuW072FYRO2tfq;uEsKVG>k}oF ztsus5OLJZae;{|HU$cZItoQi*Z^U*{r zvTEptU3#QAs*r_OZ99?+HUWw^R`|oQ>RlBlh zv17|c_zxk@HpfdxMN}lt=a6wmcUeyc;jAb~`-@GXlC|nJp1B>6nSm@_q>={pKfUdI zpGB4w%Q(YA>np?S7f1?_)I@|3-ytq0S)DbVLUp>)UXPTi@zN9DJhGT2&0sFr$Cnt` zz~~xhWsn#&Ws3jp!LW=XJMvD3Hahw)8&fHpe_sb3`JhtxzXrcLLOxQ ztRye?Xpvqq+oAP+qX>t3XR{7BU1?S*yX1oq(;Bim8Cm0G6K&jdb4r7>==VUmGnQM7 z0@(CK=mD)?-ZofJ3gPce3+KHl?G9D~qN!6^z8bnYK{XIMjwR{PaPP~r=k3qV;*_zH z=>*KSQg27iycRJDa>{6R=(@q+n{#x6;_1;q+0P*wf)h8g zD68XKMQbKT-QRseEuR&QewAM7`zzYL{(X8MNXOdf$%~QrOGR-6 zRQ@k7?pc1a!>?r8dEImWCSS;z4}!?dts6P^{$G1aMU6drU-=&3#f9#%Md4%-_*;^! zQ7fPNPl7vXH011G7<^#dV|)F{MfpB#M3M?;)49Jb7&nda$#O>2QJ-#8$rq+=6JY|n z5-DO|VH_Fhjlulfwcvi|mqz;zZZz6uD1%SUn}=|2-ywClvHEZtG zf3;&BZ?fiU!J8km-Bb0%?rxO*9OUmJ?Af{g!YsQLeGB|VQxZSVv5UUy#@nqo;o)!R zvnM7iOHVvi6Bse-gkPNVcj+s9s&lKnRZ$lfp!^m$&xC$wwkrANYphVNeDBocJqx8p z2curD{P!B-!cM95j4(R3Nx{0`cc%=xKDB8hwb`FxD(F}r%p)tsgJi@ z@9?^cMi@zE+t}_+fHJwf8$V<_YJM?Q&U7Rpe<3D0J2o|8(k8YIoinOT8mmfKf6vZy zc{?yzRn%asjJ|C4qYKSf1wb%z-%u@uk8&H4oMOSdnWIld1>&IyIbZ- z3tbYDb~}<<$*(IOzH=z8N(nd0v5tWZ&BREVI?1$jyz?DEii|{DroEqIr$`?Wik*g! z|Naszau4R}J5K3KAXz4!yRR76P8PSMPQDaZs7RYJgHWKe=-sCD1cwj(MCo%P4bEQC z=Kx$J)&kGbpjEeqcg)FBK3g6Z&e?5?ls4Lsj}BHL18z!2_BpA1(oY_~(|@gPAiQ<^ zmh&vf`KLX4`Shc17;hRtpZgD<*ZldX$legOi(1RCrA+xY_`afPTY%%R zK~8qwp=Susbc_bKT|d-)xTVb>CrO(-0u3-+;yB<*{$Q;aLCz%Upj<-9^-TC3%x*Rp z0nd`6`{EmhOLf~FHjhk_lB9w7o}Qknp{y6slrLBoL0)=X510)uhpDkEN1{=6r~M$p8v_MDX_D4THD{ zD`3C>CSwV8qvH~UqWXRG4a;~dCyXX@3lJFBZqY5~rt55>j-$>sZh^0(=JaDCP%Z)bCtBwbMJ!>;h4EvYg*Hf7NC)TLc zJ*l(KwJ*@68b~S$A3eJn>8aPt5*_7#>^Zqj4z1=%={8ak^!X-SRuh0hf1-=+Dl_1v z(tcR(B-n(M#kiyrl&|o>elblDdX9?dP#-n5TV*M^!NxOsbdCvn(oEl3FrA2{OHeU$4>)_!V@u=)SsMht#}!mn3L zY9)X!e9a5^`3}G50oUH)`vbAqL~I>QU=H7v1M0F#u}QOuYF|AN2-i^wzN!akvvYE@ zazi-*U?`B469@sYb8)hA!Ic7YbFhNBx#0IHI~zHDw~MTW%_I1$z@PFf{gX1awRM8O zAME!e?7C*ZCsqGSGcY?VH$24xbHafIgd4yP=7wv=1psrwHHLuxH_d)a!mnxe-R{5o z{lC}@2v4D}p7@*1KpgC>9B_2S4u-Ho;SK?F!mr#=06Pbim5Y<}56#%FOvWb6$H#UR zS!}}J5h$B7o2ruZ)qh5FCnq}|Ha1sRS5{j%=(BKq#A>F z&2_zh&pH2%ML{4gRxU1X0EiRJ$_^(*Aa)2VC!8sQxVTu^f&a*&KZxaTQ0lr~zo)nV zMlT3E2P+gFdq4Doz&#I#H~&Jd-w@GtwSG@h|BYH4P%gMyS7rjTLZAQ+c#nXoni|J}my3pWIAVGj712p@sq@xTrI-wkYTYYk(y zGc<4mQqK zR=-RF?AJTs-!+C0SYY@J^ykLEbEfZ>`kSU49AH*1DC93qf13(_M1}->(u7Na(-{ya zyx;sbhrvIb9$K2_EEElO6m6 zTpIYd84&(~3;yA^X_HOj>gV64PBsZJyvg@X2&F5a#HQe2Ype`|&vEc6T}%SNrUG+= zv!?5V>5mPA?=Sv4YXXDei0mJ-rVOq1_{rLvms`yu;67P5<#2TTxtAjrk^-j$3=cP^ zbW_a)=jf!2L2bV03dtwEcuGy;KXGbJS18CUtUl+}(athQ`fSmgf{3)gHJYQT;$5W%xkt==hms_%#?$GhE!R`< zn#^MKizl-R4wKcW={MUonDfK}Uw0GB6pEG*PE^h;&{5na4M(GprtbJ@w70qyaI;HQ_@&-e-Du4ayT8?z9j2e{zOZU~fQ#EnzlH(9sO9&x-DrGZQf~N4 z(D-$$vtHKiKKk$r7sR2Kg!6F=W1h`gRv9y7JSSa6k>H@&)6Qc1JeO=bM0HvdH?>J*r?W4AM@LX~jzyV^_Xm zij3WZ;NmAp<9Yv^0ht8*r&WJDk*EHT9`Vp56QU$S>mb3#YZPdmwFiE4Q~riI(0&kK zfMVx^ddJlM>Nm!NrL)aZ*gQ|UPFIRTamhFy|Yd0#G;!zYx>T|lHO5V66M_K)IbS$1O9u!JCYMK|C#F;t>cOFLE z?y?B6n>b?F}(EO8X?ubeuO zIiNl_fBHqCz>GU(gpcLvu2P};4IFwBnmlZ@{M^ZBh0!K=r+MmaSfTx`8}+QGqT+0c zK}7ScWqIyMw>1$ITNcT~=5HV^4V&FFpj@(8Z_?bWvvDb4(&~|9^rGuuIpf&1xl_Z! zwV5852bB$^(lsdFGpM1+jOSGk(mtkO?3rWercW7~C7shetvUqq6Xx@$)=-WyHdeWWh@prN#3T1-&_9tO zd_Ly>Vk4&%01=_+5~O!c4O+WofG9t^5X1D736w4xpePeA+4evvU_D~mWf%@Kr!DIm zcNO2zE0$fGyUCZzeQ)qs9%LGsHX1BH$wxSEVVhKM81L^s0Gsl|P#WCM#tU)pXVSHQ zimFfh;U4}b2nTuQh7}%KsDCjoZexMr#9hXs$^?{;g%;fqZ*^*f=f%gX_}04Z{sCh0 z&sF@KWxdsSR6`Qd1zVA+FY!9?nwL0qsy#i(+E^(~bfjOS()zAVn{c^3*G*gW-SiW3 z`!-ZYjqPPt5M$1)B4wUrzkyBXCFCeR6-Z7WBS*dfr6h&c6(Kli^7>MGq9YG;1hwLH z0?}^S6QQ!b5~C^!d3EtgbZ89GMzTgu^pqhY1vnjRWAv3+@8TZfeW=B|7hle*k93&J zZ;82gK5vOt7f)DWp!3Iblj{M)?2w?=ZcZ^zZr6!M($>TLIBrk*>3S(T#l$GIeu6tUq3vFCR&sRp zN^@&3alcL0+iVP1_keR^4Q+!`5y$U2F61^$mJefP9E6On&T*GZrMApXD7K2vc`U0G zWA^IA`DB*Z=<6x=eYlO%uH1Ky6?q< zj2xSfv}z%rDIH1Q;Q6>XhwxeXa}s(W@grVWX0uHu>SD?u0eOT^UpY5d{Rfz+^9>a= zd+y*of#7m$sqywq?Js7CzhOe@-v#YsbP+8)!5d_Adu3V68u?YhockeNd58a=UrK%r z&QlVR%izQakyspA*4X>D54zu^z0Id0%V#hbOBQXHI~~uhP>&~ZeqNS@0J|#Fw;%Jdd|u&p*iU-+58We`W#=R@;3r~n zmlwHpjDgIARtHsfIp{YEapy5p)G=^SOCEZhP{&FC^6)NVRPq=m>uuGw^Ae$J zA`?%uxfJkImq)e|Ft=R)P_WmcHZ(k0By0Ucjo9i+kgBJKp^>q8d zjHVj=%|O>xi%l(AbaC_mIys*@+26`Va*_-G+!emX8&+=p^k=M|WRkXg%^C4NLn36xen)7%AG0^5z-^ z#z|9|T0+AWM40CXK2D`9KvkYMp%|CvP6(-O2K7%23L-C6S53L|3V}ZIoz$kh8LxSS zQ@c-a2O~LSzwgBs%AWrOGr=0EB{k*9=eMf?nTjbDH`GJ})WU|XWg^HY$04`+!zRCU zzq&|B-DmnRsG>gfqVUF&+z^%IGzMEhc$B62Fmj;1gfI~0punq#i0cj?aBJ)>?<(K+ zCNuLF?X56zKNcVZbW}if&8(G#sVTmsDZQje#QlpJ@b;~b$@@&AJB@f04@^N3=qZosOnW(O#^25ZPa(|M zdCdHTF?!RViaR1%HWic&UQM*7-}8CPqk@X-$3khLP6ogvG?Ecz#azv)L6GBW$JQId zEBAajBoh3-u;rpfQ3XZs{yPoKqr&J_=#{!t#b3X953mj+bbZ zrXIuhR6(zBcSmq*SS++%7Al;Uo}g?*dvjS(%m-%%!Ol>0;+AuI9Udl}HM$*ad^rwZ zXKV60{y19JbbNS_wryjx-E=vMCwQ^HXEH?WDnG;DiX0xepEaD1!p!{H$lL8}PjiP6 zvLim4$Wya$^mpzWU3{(?I2DYkt6}5>)HM8c(e~_!V&!jx8+?r{{lStKJ?ZSM7%Ou{B=L`-(bFi!R9;Li= zaX;SK!7n{JJi<@k9yV}iVAFldFkiZ(kHR|8^gjA-Wcv7;;>8e&O(Qn>Ns5=y9J!iiyi;_-2|4Q2i?0~D zIpI`&{k2K9)H`o%JBq$_jXy<@&5*^RJYD=?fj<&WD+PpU@0nLwtWmSny#)XkDf+w> zZ)aQz2I<(XAOqVC-{(ZTq*IiJOCe}Yv%iW7e#nD4y|jz0bg`nk^L zuJFx&Xv@g~VP%K2l|Pdo(D#1%!-m)J%`aT$I&k?F#ry(kuH&0uD9?3B14ls|{~p*} zCsn@#8*VPpZ@dQZe-{U3sEyl9LW#Tw)G?BgVtrHZ-c#zy1_v_BSo63Lw3897_VT@m zODBmb5t1R^Us+M2YaGTHWYE{mctffCl|(!GcI}M5;Q?=7>CCeL?SSkUM@!iKaL+Dm z-Ja*H4uQ|BFDmw|*JO3RjGrsl+)A{4r7Bph{n^^Z_H)lxVG*U#=+`y<-d$odBsJ9~ zQFUp%syJKK7<6D;t^4L#lDAj9(os1>x9*bX;Ox<%?UW6y7OpomZ$9aDS~eiPc3j?=F7)f2|3b!ZXRjK~zuixxhFtl#bf+ApE9{Nhd}3wV4p{G2kY%sCUt7eMb_}7AZ7)*-+H8o zjRP)OX1dQ`@$qwpV75MNtLo-6Kfc#`7XI3?~GuBOJ6d>rChm9wWn6D;kJyGT# zCg^i{Yx6M!UgojJ6M!sGE^>kmFO@Hgm!%^+1s^dOx+5#R(2n^L^H7T}ypAC(zkk$i zYYCQZqXV3?3`DTBa=*E0gXdfrf_0gQ7(us+u^M1bmh1?`tXU*BVR4Ia*b! z%Gl45szH|mf~{efXNRaU6pAi-90qyg2xQax)GBtB{Fo1$&S_(2-*M@Coil%6FkD^H zdKsVQ%D}T5&&MOJx+Ezzo__XadZXxKpBd{;8D;QW=$wZ8c`3hY;gN7bP{w?#tuTq} zc?+I5Z)T95dbf@)gxzvy+Wv77`}W~J&P8fe$L+plskxbr$*qSd)h+2BFp!G%$R4?| z?b1_iBBt~LM#XOrO?EufDsJbfI-X>uZ3|(W8E(9@Z)3=ZsAjqYc=ol%PNY25-M607 zicmeqcA6WTPO%Td3tg%-Pj-?i!>XC!v<}wJ>`OpRfAT!d#hJeWDSalj)2YK?R?tax za<>=2RbNxf z%1<>DE2p=2#mvXe?9)#T6T?429MAC7A`BcaKE1o?hB528N=a4TNw@b_M=mY4}Al-&sC5D$^_VyA0oE&3b&g zv9K2Z4qh^vh%~j-bE|BUe@oz#o(wUfC?CukLA;tFu@h%o78H?7fM-u!i5sF{-mO}u?X5=t2b_|odO3Vx_&vDl;= z$VbI|yCeI1To_~9OARN*6w>w{L)K>PrvSW=z5N$XIYtpcyfTu?%Odb+g+@t&+-Kbm zLY0-%oVRYtezPjOas1i}!D#3xXuez`L6RWn{tZqv8Oo^1Z^PbmbeNG{S=!mte$PZk zW~M0HnZB~3FTV_+&NgK6f)sFg>nnH=&;@)@%x)|RF|lkFQ^!ZJ@C#`BXkF1i$?KJc z?bTAKN_NT=E$H}^EU2H9c+{_eA|%s}+m6ZDL{>sNT)k4%=Q&}~2=xgugOU5(eGSK) zoXRiV_OqgB8cGoX&-_b_i|-=FBiwUTZH`YZgmp{!oPO%Wh_vv-a*lZJj~(hDC5cK+ z8{(ZaJaf>mfRtdl9a=LZ|Cty8l2U7p=AUyf$OMeS>#kp8JP<$y2|fzMdm3JrQByi> zpB1dos&6E(0-VJOZt-Ufyf@&;f%sPvC`S6fdO^Ej% zSvv(8b$c8BBe85|R-`qo<(+55E$8Nt&r)YnysLF}^(R4&FW2?eda%3B&067mlm?$d z{6WUIv|5AO*p;y_*0$ZAcSxGD>yEkv)TA60JG6ogqG{}TjnP%PV(O*wV`wyA99x*C zv@)|BQu02X--`p@D7b+g)mPA#h<`$66eNsLfpc9gWr9sgMja<#>!v0(F2(4i{bMx`Vm)$sJ|v6opC5;CO#OjY{Gm zm-iyHjFWb<2oSa#@;>=TZIA^QtglN6l^<`GhCD&zv%8%U>p7gA?zeYh@mi;ZXDw

hnroLE8eZa&V86VCcf2}KW4gNzeLcTlDPyP+65Lv z@USM$POnGtw=TJP&*?zm(cTp5S7LSBvVkl;of7)u33>LUCgq83dqnRT$s#{_aL42&J4KR?uvPI=q5L!U{43%L}&ZKPY~~x;}bY|PB*qt-d}T6Bd33`+FI1~e3%h2 zy3R^$5G|+BdQhiL=Y-6a`Hh9}_|#(|wJ-Z+ErN*MQiW12&JUKyeWQuiOooyl60o>p zgvuh9qjuOTKb18PJcmZMr>CqS?%|f(! zoW#G(3TT%}=V#HCmOtr6Et;p}|3V&uClhBGu6R+`K365=oA+>{!J4hJW&>Q0_Sm|% zc0-&yPE?JP*Cj*d#35lg>5iKSF-JPPymvz+DIh$Z#&AQ@jx#)9hp6L8fDfa_H-dF5 z1fjwz!6AEW!lB+CO^X{`8B!zxh(7OI^hIublQU@&*uXljHsz_L%&uhUW(lW5QutOP zS4HV!F2zGcjr66dTtaXfLl8O=SaB}vyQpS9<`YllpAjc-y?$KrvI}(b8Ig-o$%p$% zYWf>O(w7LB!y#Q(_qugMWJ}p{^`t7Z^0~T0=Zc%?85X(%=&V~L z)}}eyu(nR9Ty) zJ~k89`phZ3a#;-g_Z)g-Idd_cR$Cdzk~~ItvC!rIjj)J?7l_F>xji z9{2e&0z^I7^6Dql&))NQ_=>0kBFG}RYRZ|tP}+q+3S_52ueEy!pB4Gh<(b}%VcC}@ zM+iZyvUimfW3_n`$@h>mR5mO$T?$vi4P$NoRoMl{SCE@MxC({gs8{kqSlxIV+ayzd z$*hc;_@z-f@u|4ALy`Yvd*THa$(XHSG9rPCL20NU$R@Q)X@)~^mpI>pBd$xYn-^Wm zuE?Pn`#tP=gM)2Yfmx_yeD3SO0|fv3yLZV=i+iij7%-!rA;$8LAx4a};62Zs+kSZi zad*p+f4&Ne3gEeT?Dx2|O>qvLkYc=U{1*IN6sP)-u`U4(iYXxv84) z4S*5bJAgp*tTJs+XUc2$v6`V^!+%I6;Wb}>q;~(gDENDN^(*3r{BNn`wW45---~>| zpYDYh`+h&g`==t`@Av%382?wa4KMNq!f7WMUib-tmokFkMZVl{3U_sdS6TiOfb3)l zKco8B0^c9!k-uk||D#qQcnu&s{KzOf6kd)BuYF?&!cXJEi@Vsl;pMa(|ER$72iE>e zdGC*N(Emy=cqudxUM34+XXS#IX@cN*8wjsNgO?$5Lar6}{ukK($MH*e$>{e(lh?Sx zkAu|TRlDK_-=poHa)WDu`8RDjpd8;Ty8nWDuY&AH0R2r{Fud}X1AYSY&u#xwnfM=c zW`|>cAiM0MV9 zent-~iPY-X_TSKi(U#~B>3T(vrFF$_3(S$aBqjUt8}z$eoTxYxGxq}B%uKflaXEB9 z9(?YoK*d6a}q+%%5}{zU#9U)mxt$b^3>P& zr2!4aUA7p!yTfTC)Wj1%8A@pBNfCQv4pa@u5ZwUQ5v5vKBhNQj?0sD}x>pjBYN=gT zX*=JU(!~}jzs)M9qMr7Kb4pMBz|MulEK^DQaSk_7Fe0pBUszc5C0IL53^ARNF^JI*ex!*?)uJdy1R(Dlon)vBcIrhOi#l6+vo39jO)oaJ zyK5izF`%O%%P-AF0dT`S&$tfS-B?`-Inmu{k@rjrNb<*PSS>)g?GJ2X0%1N-uTK+e z*F||0zIyvvj6O29;iZz_Ek@AHWxdEvZzf^8VF_xaa#<;qb4`XSf{G2FHVQfl>nzV0 z1B!O0NBt3CfL;kPy&}RWZTa~LkS_g!BLRVg*rl5kH1^0siUb=cf^0G7ZdrgZvEUiW z+bk}RhC49|V8A?)BjIVL5GJn}<}LIDlCQh1$SN-JNp~Ia0RTU_Zc5^vRm9HiA<@2T#dkHSc;x+-5*lEcJfdv|#~cj+LBk%ti0fvW&PL=V>Ca%EM19i2Rkt{0%bS5*<VDe3J!C0DUk9ZD0udMr zHSJ#zPov}n3z@>%h+FU-m|*);lH!yMLuiGB&GzW{^QviR`m7e%TEMi`-P>d_$PZT# za0GPU-85OnHx0L|YJ=E}d@ab5Kpvy&%_V0GC@~9id+|2B+CoIDrdt_jQY^bMqkBhL zpA;}-CCD*us~yL}zb-^Ve`Zv7ffx8LvM(8CIJH8L!J zjm${Z8(1uFTUBvCzhJ{N{%9OSRAH#qX`E`3FoNJf_7V9TXkv}$C_im2@==>NAe!u1 zAztKjfoNRk+yXvMg$Fcg#X|{V>J4vm&v5b-iG@o|? zOfRbraLOq*nNRCzE1Z+lHOYiR0=|ff?mjYBYQ+P$IB@h~CPO2)Ez(Iq7l(-yE#zjv z2P9UNa<@)+Xp59lV#vR&TBTXZ#ow=7tu0!EU7lNNk0P_J=ys)qsCbtdJlr$uDp%%z zw;)VC?UT{{m}aLkARbk}|HTD-fGZM=+(s^<2F-A2N`bMkHoe8!m9hp%%8eELNT(QX z;0;fbA^{o;Ny3a=YbS(|uu%^vb;{ZIFJWBxkm#y$zO2e^j3#MoJ&T1<6`*ET z>n;qPYs3n(DD1+V=^bk|9wZ{EN}wS0)}neKUoNu#_I{TeyDYfllxX>pg!2T|4ULad zcG}fA@7^>;5-Qf~oj&Yx@-8AA2YK6FvLlua9a*>KSM-m@)p-HsvZc)K%+uc=m2<<( zA4IK%AhYQf(zotsXHy2Ib0yR3Hg-hdw3g;NMigP-c`c)k@;rLwy!Mt7Vt01H`N0xu zV^fve5|Pxi$}m@Uk-&AqD84u<^JK7?s)L`vVB5myrX}^wsi?b!of!`A38on~1BW*}8Cj<-h89yD4pJBG?XUYvYucd%*brHLn> zm}sEs?MX6wxM^5-yedOgZ?WcSi8tBwU{Wq=^bU;@!+JKDs;t-BtePOP((IEgrm_~B zNyOgjt3X@R^j&zsxOkDxj=1m{omoS*RP0^T=W^Lkr-CGNeBYUU7{st3^p4?l!c|6l zeryj!{FraBGxv3?G&?7mJ&?MHJ$T~L!*jE)3T44e?9@H^k!Q#*IOrVNH;zU~52)oF zMb4k^dJCP4cItmv&D9EF#PiV$d^6Y|h`5FuOsnG|$70=Any)_NSeHj{KoJ;LbptQFD2IMwe�Gm)h|H@%gte!ap@-)?hS^ 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%0Aj%y*EO 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

O5$57>^PGGkx33++8%H*J+lSX)NG&Mzzk=Qyo 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.|Zl` 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 F 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 IZ(i>FAJU^|6951J literal 0 HcmV?d00001 diff --git a/urls.txt b/urls.txt new file mode 100644 index 0000000..0cc7dc5 --- /dev/null +++ b/urls.txt @@ -0,0 +1,4 @@ + - Assembler http://school.anhb.uwa.edu.au/personalpages/kwessen/apple1/Krusader.htm + - BASIC http://www.themotionstore.com/leeedavison/6502/ehbasic/index.html + - Pascal http://pascal.hansotten.com/ + - DOS http://www.z80.eu/dos65.html diff --git a/vm/ConsoleDummy.js b/vm/ConsoleDummy.js new file mode 100644 index 0000000..78d52fd --- /dev/null +++ b/vm/ConsoleDummy.js @@ -0,0 +1,12 @@ +(function (con) { + // the dummy function + function dummy() {}; + // console methods that may exist + for(var methods = "assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(','), func; func = methods.pop();) { + con[func] = con[func] || dummy; + } +}(window.console = window.console || {})); +// we do this crazy little dance so that the `console` object +// inside the function is a name that can be shortened to a single +// letter by the compressor to make the compressed script as tiny +// as possible. diff --git a/vm/LICENSE.txt b/vm/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/vm/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/vm/README.txt b/vm/README.txt new file mode 100644 index 0000000..4302d52 --- /dev/null +++ b/vm/README.txt @@ -0,0 +1,137 @@ +6502 Based Virtual Computer +Copyright (C) 2011 Scott C. Duensing + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +--- + +This is a simple (and fictional) 6502-based computer written in JavaScript. + +Features: + +- 6502 CPU (lacks interrupt support at this time). +- 16 color, 80x25 text display. +- "Hardware" psuedo-random number generator. +- Keyboard. +- Simple clock. + +Notes: + +- All hardware components are in the "computer" folder. +- A sample "Hello World" is in the "software" folder. +- "machine.html" loads everything and provides a DIV for the display. +- "machine.js" glues all the components together into a computer. + +Memory Map: + + MM_IRQ_VECTOR = 0xfffe + MM_RESET_VECTOR = 0xfffc + MM_NMI_VECTOR = 0xfffa + MM_ROM_END = 0xfff9 + MM_ROM_START = 0xf000 + MM_CLOCK = 0xefa5 + MM_PRNG = 0xefa4 + MM_KEYBOARD_META = 0xefa3 + MM_KEYBOARD_CHARACTER = 0xefa2 + MM_DISPLAY_COMMAND = 0xefa1 + MM_DISPLAY_DATA = 0xefa0 + MM_DISPLAY_ADAPTER = 0xe000 + MM_HEAP_END = 0xdfff + MM_CODE_START = 0x0200 + MM_STACK = 0x0100 + MM_ZERO_PAGE = 0x0000 + +Display Adapter: + +You can either jam bytes right into display memory (beginning at +MM_DISPLAY_ADAPTER) or use the simple BIOS included in the adapter. ("BOS"? +displays don't do input!) + +Each character on the display is comprised of two bytes: The character to +display, and the color attributes (in that order). Colors are stored with the +foreground value in the lower nibble of the attribute byte and the background +in the high nibble. Color values are: + + 0 = Black + 1 = Blue + 2 = Green + 3 = Cyan + 4 = Red + 5 = Magenta + 6 = Brown + 7 = Light Gray + 8 = Gray + 9 = Light Blue + 10 = Light Green + 11 = Light Cyan + 12 = Light Red + 13 = Light Magenta + 14 = Yellow + 15 = White + +Using the BIOS provides several conveniences over writing directly to the +framebuffer, including tab stops, line wrapping, scrolling, and more. Access +to the BIOS is through two memory locations, one for data (MM_DISPLAY_DATA) +and one for issuing commands to operate on that data (MM_DISPLAY_COMMAND). +Available commands are: + + 0 = Store Data LSB (data byte stored for later) + 1 = Clear Display to Current Color (no data byte needed) + 2 = Set Cursor X (position 0-79 stored in data byte) + 3 = Set Cursor Y (position 0-24 stored in data byte) + 4 = Get Cursor X (position returned in data byte) + 5 = Get Cursor Y (position returned in data byte) + 6 = Set Color (same format as above, store in data byte) + 7 = Draw Character in Current Color (character code in data byte) + 8 = Draw String in Current Color (see below) + +For drawing strings, first write the LSB of the address of the string in +memory to MM_DISPLAY_DATA and then write a 0 to MM_DISPLAY_COMMAND to store +the LSB. Then write the MSB to MM_DISPLAY_DATA and 8 to MM_DISPLAY_COMMAND to +draw the string. Since no length information is provided, strings must be +zero terminated. + +Keyboard: + +The current status of the keyboard can be read from two bytes, +MM_KEYBOARD_CHARACTER and MM_KEYBOARD_META. MM_KEYBOARD_CHARACTER always +contains the value of the most recently pressed key. MM_KEYBOARD_META +contains the current state of the keyboard's Shift, Control, and Alt keys +in the following bits: + + 0 = Shift + 1 = Control + 2 = Alt + +If you wish to wait for a keypress, write a 0 to MM_KEYBOARD_CHARACTER and +then poll until the value changes. + +Random Number Generator: + +To make it easy to get a random number for use in your code, a psuedo-random +number generator is provided. Simply read from MM_PRNG for a random value +between 0 and 255, inclusive. + +Clock: + +The value at MM_CLOCK increments every 100 milliseconds. It is intended for +simple timing operations and not actual "wall clock" time keeping. + +Code Start: + +By default, when the CPU starts, anything located at MM_CODE_START will be +executed. If you wish to relocate your code, either place a JMP at this +address or in "machine.js" change the default PC value in the call to +"cpu.attach()". diff --git a/vm/computer/clock.js b/vm/computer/clock.js new file mode 100644 index 0000000..c784bde --- /dev/null +++ b/vm/computer/clock.js @@ -0,0 +1,57 @@ +/* + * 6502 Based Virtual Computer + * Copyright (C) 2011 Scott C. Duensing + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// Our Clock object. +function clock() { + + // --- Private variables. + + // Memory location for PRNG value. + var MEMORY = null; + var MEMORY_START = 0; + var CLOCK_ADDRESS = 0; + + // Clock values. + var MILLISECONDS = 0; + var CLOCK = 0; + + + // --- Private methods. + + var clockTick = function() { + MEMORY.writeByte(CLOCK_ADDRESS, CLOCK++); + if (CLOCK > 255) CLOCK = 0; + setTimeout(function(){ + clockTick(); + }, MILLISECONDS); + }; + + + // --- Public methods. + + // Attach clock to DOM. + this.attach = function(newMemory, newStartPosition, newMilliseconds) { + MEMORY = newMemory; + MEMORY_START = newStartPosition; + CLOCK_ADDRESS = MEMORY_START; + MILLISECONDS = newMilliseconds; + clockTick(); + }; + +} diff --git a/vm/computer/cpu6502.js b/vm/computer/cpu6502.js new file mode 100644 index 0000000..0a92075 --- /dev/null +++ b/vm/computer/cpu6502.js @@ -0,0 +1,1533 @@ +/* + * 6502 Based Virtual Computer + * Copyright (C) 2011 Scott C. Duensing + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// 6502 CPU Core - based on http://www.6502asm.com +function cpu6502() { + + // --- Private variables. + + var self = this; + + // Memory Pointer. + var MEMORY = null; + + // Default Program Counter. + var DEFAULT_PC = 0x600; + + // Registers. + var REG_A = 0; + var REG_X = 0; + var REG_Y = 0; + var REG_P = 0; + var REG_PC = DEFAULT_PC; + var REG_SP = 0x100; + + // Internal CPU Status. + var CODE_RUNNING = false; + + // CPU Event Callback. + var CALLBACK = null; + + // Instruction Function "Pointers". + var INSTRUCTION_POINTERS = null; + + + // --- Public variables, enums. + + this.E_HALT = 0; + this.E_UNKNOWN_OPCODE = 1; + this.E_STACK_OVERFLOW = 2; + this.E_STACK_UNDERFLOW = 3; + + + // --- Private methods, general. + + var doCompare = function(reg, val) { + if (reg >= val) + REG_P |= 1; + else + REG_P &= 0xfe; + val = (reg - val); + if (val) + REG_P &= 0xfd; + else + REG_P |= 0x02; + if (val & 0x80) + REG_P |= 0x80; + else + REG_P &= 0x7f; + } + + // Might as well Jump... JUMP! + var jumpBranch = function(offset) { + if (offset > 0x7f) + REG_PC = (REG_PC - (0x100 - offset)); + else + REG_PC = (REG_PC + offset); + }; + + // Fetch the next byte pointed to by the program counter. + var popByte = function() { + return (MEMORY.readByte(REG_PC++) & 0xff); + }; + + // Fetch the next word pointed to by the program counter. + var popWord = function() { + return popByte() + (popByte() << 8); + }; + + // Run a block of instructions. This is used by 'run' to avoid blocking the browser event queue. + var runBlock = function() { + var instructions = 100; + while ((instructions-- > 0) && (CODE_RUNNING)) + self.execute(); + if (CODE_RUNNING) + setTimeout(runBlock, 0); + }; + + // Pop a value off the stack. + var stackPop = function() { + if (REG_SP < 0x100) { + var value = MEMORY.readByte(REG_SP + 0x100); + REG_SP++; + return value; + } else { + CODE_RUNNING = false; + CALLBACK(self.E_STACK_UNDERFLOW); + return 0; + } + }; + + // Push a value onto the stack. + var stackPush = function(value) { + if (REG_SP >= 0) { + REG_SP--; + MEMORY.writeByte((REG_SP & 0xff) + 0x100, value & 0xff); + } else { + CODE_RUNNING = false; + CALLBACK(self.E_STACK_OVERFLOW); + } + }; + + var testSBC = function(value) { + var vflag = 0; + var tmp = 0; + var w = 0; + if ((REG_A ^ value) & 0x80) + vflag = 1; + if (REG_P & 8) { + tmp = 0xf + (REG_A & 0xf) - (value & 0xf) + (REG_P & 1); + if (tmp < 0x10) { + w = 0; + tmp -= 6; + } else { + w = 0x10; + tmp -= 0x10; + } + w += 0xf0 + (REG_A & 0xf0) - (value & 0xf0); + if (w < 0x100) { + REG_P &= 0xfe; + if ((REG_P & 0xbf) && w < 0x80) + REG_P &= 0xbf; + w -= 0x60; + } else { + REG_P |= 1; + if ((REG_P & 0xbf) && w >= 0x180) + REG_P &= 0xbf; + } + w += tmp; + } else { + w = 0xff + REG_A - value + (REG_P & 1); + if (w<0x100) { + REG_P &= 0xfe; + if ((REG_P & 0xbf) && w < 0x80) + REG_P &= 0xbf; + } else { + REG_P |= 1; + if ((REG_P & 0xbf) && w >= 0x180) + REG_P &= 0xbf; + } + } + REG_A = w & 0xff; + if (REG_A) + REG_P &= 0xfd; + else + REG_P |= 0x02; + if (REG_A & 0x80) + REG_P |= 0x80; + else + REG_P &= 0x7f; + } + + var testADC = function(value) { + var tmp = 0; + if ((REG_A ^ value) & 0x80) + REG_P &= 0xbf; + else + REG_P |= 0x40; + if (REG_P & 8) { + tmp = (REG_A & 0xf) + (value & 0xf) + (REG_P & 1); + if (tmp >= 10) + tmp = 0x10 | ((tmp + 6) & 0xf); + tmp += (REG_A & 0xf0) + (value & 0xf0); + if (tmp >= 160) { + REG_P |= 1; + if ((REG_P & 0xbf) && tmp >= 0x180) + REG_P &= 0xbf; + tmp += 0x60; + } else { + REG_P &= 0xfe; + if ((REG_P & 0xbf) && tmp < 0x80) + REG_P &= 0xbf; + } + } else { + tmp = REG_A + value + (REG_P & 1); + if (tmp >= 0x100) { + REG_P |= 1; + if ((REG_P & 0xbf) && tmp >= 0x180) + REG_P &= 0xbf; + } else { + REG_P &= 0xfe; + if ((REG_P & 0xbf) && tmp < 0x80) + REG_P &= 0xbf; + } + } + REG_A = tmp & 0xff; + if (REG_A) + REG_P &= 0xfd; + else + REG_P |= 0x02; + if (REG_A & 0x80) + REG_P |= 0x80; + else + REG_P &= 0x7f; + } + + + // --- Private methods, CPU core. + + var i00 = function() { + CODE_RUNNING = false; + }; + + var i01 = function() { + var addr = popByte() + REG_X; + var value = MEMORY.readByte(addr) + (MEMORY.readByte(addr + 1) << 8); + REG_A |= value; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i05 = function() { + var zp = popByte(); + REG_A |= MEMORY.readByte(zp); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i06 = function() { + var zp = popByte(); + var value = MEMORY.readByte(zp); + REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); + value = value << 1; + MEMORY.writeByte(zp, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i08 = function() { + stackPush(REG_P); + }; + + var i09 = function() { + REG_A |= popByte(); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i0a = function() { + REG_P = (REG_P & 0xfe) | ((REG_A >> 7) & 1); + REG_A = REG_A << 1; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i0d = function() { + REG_A |= MEMORY.readByte(popWord()); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i0e = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); + value = value << 1; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 2; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i10 = function() { + var offset = popByte(); + if ((REG_P & 0x80) == 0) jumpBranch(offset); + }; + + var i11 = function() { + var zp = popByte(); + var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + REG_A |= MEMORY.readByte(value); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i15 = function() { + var addr = (popByte() + REG_X) & 0xff; + REG_A |= MEMORY.readByte(addr); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i16 = function() { + var addr = (popByte() + REG_X) & 0xff; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); + value = value << 1; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i18 = function() { + REG_P &= 0xfe; + }; + + var i19 = function() { + addr = popWord() + REG_Y; + REG_A |= MEMORY.readByte(addr); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i1d = function() { + var addr = popWord() + REG_X; + REG_A |= MEMORY.readByte(addr); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i1e = function() { + var addr = popWord() + REG_X; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); + value = value << 1; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i20 = function() { + var addr = popWord(); + var currAddr = REG_PC - 1; + stackPush(((currAddr >> 8) & 0xff)); + stackPush((currAddr & 0xff)); + REG_PC = addr; + }; + + var i21 = function() { + var addr = (popByte() + REG_X) & 0xff; + var value = MEMORY.readByte(addr) + (MEMORY.readByte(addr + 1) << 8); + REG_A &= value; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i24 = function() { + var zp = popByte(); + var value = MEMORY.readByte(zp); + if (value & REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + REG_P = (REG_P & 0x3f) | (value & 0xc0); + }; + + var i25 = function() { + var zp = popByte(); + REG_A &= MEMORY.readByte(zp); + if (REG_A) REG_P &= 0xfd; else REG_P |= 2; + if (REG_A & 0x80) REG_P &= 0x80; else REG_P &= 0x7f; + }; + + var i26 = function() { + var sf = (REG_P & 1); + var addr = popByte(); + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); + value = value << 1; + value |= sf; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i28 = function() { + REG_P = stackPop() | 0x20; + }; + + var i29 = function() { + REG_A &= popByte(); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i2a = function() { + var sf = (REG_P & 1); + REG_P = (REG_P & 0xfe) | ((REG_A >> 7) & 1); + REG_A = REG_A << 1; + REG_A |= sf; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i2c = function() { + var value = MEMORY.readByte(popWord()); + if (value & REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + REG_P = (REG_P & 0x3f) | (value & 0xc0); + }; + + var i2d = function() { + var value = MEMORY.readByte(popWord()); + REG_A &= value; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i2e = function() { + var sf = REG_P & 1; + var addr = popWord(); + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); + value = value << 1; + value |= sf; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i30 = function() { + var offset = popByte(); + if (REG_P & 0x80) jumpBranch(offset); + }; + + var i31 = function() { + var zp = popByte(); + var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + REG_A &= MEMORY.readByte(value); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i35 = function() { + var zp = popByte(); + var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_X; + REG_A &= MEMORY.readByte(value); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i36 = function() { + var sf = REG_P & 1; + var addr = (popByte() + REG_X) & 0xff; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); + value = value << 1; + value |= sf; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i38 = function() { + REG_P |= 1; + }; + + var i39 = function() { + var addr = popWord() + REG_Y; + var value = MEMORY.readByte(addr); + REG_A &= value; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i3d = function() { + var addr = popWord() + REG_X; + var value = MEMORY.readByte(addr); + REG_A &= value; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i3e = function() { + var sf = REG_P & 1; + var addr = popWord() + REG_X; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); + value = value << 1; + value |= sf; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i40 = function() { + }; + + var i41 = function() { + var zp = (popByte() + REG_X) & 0xff; + var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + REG_A ^= MEMORY.readByte(value); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i45 = function() { + var addr = (popByte() + REG_X) & 0xff; + var value = MEMORY.readByte(addr); + REG_A ^= value; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i46 = function() { + var addr = popByte() & 0xff; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | (value & 1); + value = value >> 1; + MEMORY.writeByte(addr, value); + if (value != 0) REG_P &= 0xfd; else REG_P |= 2; + if ((value & 0x80) == 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i48 = function() { + stackPush(REG_A); + }; + + var i49 = function() { + REG_A ^= popByte(); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i4a = function() { + REG_P = (REG_P & 0xfe) | (REG_A & 1); + REG_A = REG_A >> 1; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i4c = function() { + REG_PC = popWord(); + }; + + var i4d = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr); + REG_A ^= value; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i4e = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | (value & 1); + value = value >> 1; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i50 = function() { + var offset = popByte(); + if ((REG_P & 0x40) == 0) jumpBranch(offset); + }; + + var i51 = function() { + var zp = popByte(); + var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + REG_A ^= MEMORY.readByte(value); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i55 = function() { + var addr = (popByte() + REG_X) & 0xff; + REG_A ^= MEMORY.readByte(addr); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i56 = function() { + var addr = (popByte() + REG_X) & 0xff; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | (value & 1); + value = value >> 1; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i58 = function() { + }; + + var i59 = function() { + var addr = popWord() + REG_Y; + var value = MEMORY.readByte(addr); + REG_A ^= value; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i5d = function() { + var addr = popWord() + REG_X; + var value = MEMORY.readByte(addr); + REG_A ^= value; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i5e = function() { + var addr = popWord() + REG_X; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | (value & 1); + value = value >> 1; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i60 = function() { + REG_PC = (stackPop() + 1) | (stackPop() << 8); + }; + + var i61 = function() { + var zp = (popByte() + REG_X) & 0xff; + var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + value = MEMORY.readByte(addr); + testADC(value); + }; + + var i65 = function() { + var addr = popByte(); + var value = MEMORY.readByte(addr); + testADC(value); + }; + + var i66 = function() { + var sf = REG_P & 1; + var addr = popByte(); + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | (value & 1); + value = value >> 1; + if (sf) value |= 0x80; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i68 = function() { + REG_A = stackPop(); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i69 = function() { + var value = popByte(); + testADC(value); + }; + + var i6a = function() { + var sf = REG_P & 1; + REG_P = (REG_P & 0xfe) | (REG_A & 1); + REG_A = REG_A >> 1; + if (sf) REG_A |= 0x80; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i6c = function() { + }; + + var i6d = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr); + testADC(value); + }; + + var i6e = function() { + var sf = REG_P & 1; + var addr = popWord(); + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | (value & 1); + value = value >> 1; + if (sf) value |= 0x80; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i70 = function() { + var offset = popByte(); + if (REG_P & 0x40) jumpBranch(offset); + }; + + var i71 = function() { + var zp = popByte(); + var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + var value = MEMORY.readByte(addr + REG_Y); + testADC(value); + }; + + var i75 = function() { + var addr = (popByte() + REG_X) & 0xff; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | (value & 1); + testADC(value); + }; + + var i76 = function() { + var sf = (REG_P & 1); + var addr = (popByte() + REG_X) & 0xff; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | (value & 1); + value = value >> 1; + if (sf) value |= 0x80; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i78 = function() { + }; + + var i79 = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr + REG_Y); + testADC(value); + }; + + var i7d = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr + REG_X); + testADC(value); + }; + + var i7e = function() { + //var sf = REG_P & 1; + var addr = popWord() + REG_X; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | (value & 1); + value = value >> 1; + if (value) value |= 0x80; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i81 = function() { + var zp = (popByte() + REG_X) & 0xff; + var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + MEMORY.writeByte(addr, REG_A); + }; + + var i84 = function() { + MEMORY.writeByte(popByte(), REG_Y); + }; + + var i85 = function() { + MEMORY.writeByte(popByte(), REG_A); + }; + + var i86 = function() { + MEMORY.writeByte(popByte(), REG_X); + }; + + var i88 = function() { + REG_Y = (REG_Y - 1) & 0xff; + if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i8a = function() { + REG_A = REG_X & 0xff; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i8c = function() { + MEMORY.writeByte(popWord(), REG_Y); + }; + + var i8d = function() { + MEMORY.writeByte(popWord(), REG_A); + }; + + var i8e = function() { + MEMORY.writeByte(popWord(), REG_X); + }; + + var i90 = function() { + var offset = popByte(); + if ((REG_P & 1) == 0) jumpBranch(offset); + }; + + var i91 = function() { + var zp = popByte(); + var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + MEMORY.writeByte(addr, REG_A); + }; + + var i94 = function() { + MEMORY.writeByte(popByte() + REG_X, REG_Y); + }; + + var i95 = function() { + MEMORY.writeByte(popByte() + REG_X, REG_A); + }; + + var i96 = function() { + MEMORY.writeByte(popByte() + REG_Y, REG_X); + }; + + var i98 = function() { + REG_A = REG_Y & 0xff; + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var i99 = function() { + MEMORY.writeByte(popWord() + REG_Y, REG_A); + }; + + var i9a = function() { + REG_SP = REG_X & 0xff; + }; + + var i9d = function() { + var addr = popWord(); + MEMORY.writeByte(addr + REG_X, REG_A); + }; + + var ia0 = function() { + REG_Y = popByte(); + if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ia1 = function() { + var zp = (popByte() + REG_X) & 0xff; + var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + REG_A = MEMORY.readByte(addr); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ia2 = function() { + REG_X = popByte(); + if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ia4 = function() { + REG_Y = MEMORY.readByte(popByte()); + if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ia5 = function() { + REG_A = MEMORY.readByte(popByte()); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ia6 = function() { + REG_X = MEMORY.readByte(popByte()); + if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ia8 = function() { + REG_Y = REG_A & 0xff; + if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ia9 = function() { + REG_A = popByte(); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var iaa = function() { + REG_X = REG_A & 0xff; + if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var iac = function() { + REG_Y = MEMORY.readByte(popWord()); + if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var iad = function() { + REG_A = MEMORY.readByte(popWord()); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var iae = function() { + REG_X = MEMORY.readByte(popWord()); + if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ib0 = function() { + var offset = popByte(); + if (REG_P & 1) jumpBranch(offset); + }; + + var ib1 = function() { + var zp = popByte(); + var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + REG_A = MEMORY.readByte(addr); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ib4 = function() { + REG_Y = MEMORY.readByte(popByte() + REG_X); + if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ib5 = function() { + REG_A = MEMORY.readByte((popByte() + REG_X) & 0xff); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ib6 = function() { + REG_X = MEMORY.readByte(popByte() + REG_Y); + if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ib8 = function() { + REG_P &= 0xbf; + }; + + var ib9 = function() { + var addr = popWord() + REG_Y; + REG_A = MEMORY.readByte(addr); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var iba = function() { + REG_X = REG_SP & 0xff; + }; + + var ibc = function() { + var addr = popWord() + REG_X; + REG_Y = MEMORY.readByte(addr); + if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ibd = function() { + var addr = popWord() + REG_X; + REG_A = MEMORY.readByte(addr); + if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ibe = function() { + var addr = popWord() + REG_Y; + REG_X = MEMORY.readByte(addr); + if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ic0 = function() { + var value = popByte(); + if ((REG_Y + value) > 0xff) REG_P |= 1; else REG_P &= 0xfe; + //var ov = value; + value = (REG_Y-value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ic1 = function() { + var zp = popByte(); + var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + var value = MEMORY.readByte(addr); + doCompare(REG_A, value); + }; + + var ic4 = function() { + var value = MEMORY.readByte(popByte()); + doCompare(REG_Y, value); + }; + + var ic5 = function() { + var value = MEMORY.readByte(popByte()); + doCompare(REG_A, value); + }; + + var ic6 = function() { + var zp = popByte(); + var value = MEMORY.readByte(zp); + --value; + MEMORY.writeByte(zp, value & 0xff); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ic8 = function() { + REG_Y = (REG_Y + 1) & 0xff; + if (REG_Y) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_Y & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ic9 = function() { + var value = popByte(); + doCompare(REG_A, value); + }; + + var ica = function() { + REG_X = (REG_X - 1) & 0xff; + if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var icc = function() { + var value = MEMORY.readByte(popWord()); + doCompare(REG_Y, value); + }; + + var icd = function() { + var value = MEMORY.readByte(popWord()); + doCompare(REG_A, value); + }; + + /* + var ice = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr); + --value; + value = value & 0xff; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + */ + + var id0 = function() { + var offset = popByte(); + if ((REG_P & 2) == 0) jumpBranch(offset); + }; + + var id1 = function() { + var zp = popByte(); + var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + var value = MEMORY.readByte(addr); + doCompare(REG_A, value); + }; + + var id5 = function() { + var value = MEMORY.readByte(popByte() + REG_X); + doCompare(REG_A, value); + }; + + var id6 = function() { + var addr = popByte() + REG_X; + var value = MEMORY.readByte(addr); + --value; + value = value & 0xff; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var id8 = function() { + REG_P &= 0xf7; + }; + + var id9 = function() { + var addr = popWord() + REG_Y; + var value = MEMORY.readByte(addr); + doCompare(REG_A, value); + }; + + var idd = function() { + var addr = popWord() + REG_X; + var value = MEMORY.readByte(addr); + doCompare(REG_A, value); + }; + + var ide = function() { + var addr = popWord() + REG_X; + var value = MEMORY.readByte(addr); + --value; + value = value&0xff; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ie0 = function() { + var value = popByte(); + doCompare(REG_X, value); + }; + + var ie1 = function() { + var zp = (popByte()+REG_X)&0xff; + var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + var value = MEMORY.readByte(addr); + testSBC(value); + }; + + var ie4 = function() { + var value = MEMORY.readByte(popByte()); + doCompare(REG_X, value); + }; + + var ie5 = function() { + var addr = popByte(); + var value = MEMORY.readByte(addr); + testSBC(value); + }; + + var ie6 = function() { + var zp = popByte(); + var value = MEMORY.readByte(zp); + ++value; + value = value & 0xff; + MEMORY.writeByte(zp, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ie8 = function() { + REG_X = (REG_X + 1) & 0xff; + if (REG_X) REG_P &= 0xfd; else REG_P |= 0x02; + if (REG_X & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ie9 = function() { + var value = popByte(); + testSBC(value); + }; + + var iea = function() { + }; + + var iec = function() { + var value = MEMORY.readByte(popWord()); + doCompare(REG_X, value); + }; + + var ied = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr); + testSBC(value); + }; + + var iee = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr); + ++value; + value = value & 0xff; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var if0 = function() { + var offset = popByte(); + if (REG_P & 2) jumpBranch(offset); + }; + + var if1 = function() { + var zp = popByte(); + var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + var value = MEMORY.readByte(addr + REG_Y); + testSBC(value); + }; + + var if5 = function() { + var addr = (popByte() + REG_X) & 0xff; + var value = MEMORY.readByte(addr); + REG_P = (REG_P & 0xfe) | (value & 1); + testSBC(value); + }; + + var if6 = function() { + var addr = popByte() + REG_X; + var value = MEMORY.readByte(addr); + ++value; + value = value & 0xff; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var if8 = function() { + REG_P |= 8; + }; + + var if9 = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr + REG_Y); + testSBC(value); + }; + + var ifd = function() { + var addr = popWord(); + var value = MEMORY.readByte(addr + REG_X); + testSBC(value); + }; + + var ife = function() { + var addr = popWord() + REG_X; + var value = MEMORY.readByte(addr); + ++value; + value = value & 0xff; + MEMORY.writeByte(addr, value); + if (value) REG_P &= 0xfd; else REG_P |= 0x02; + if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + }; + + var ierr = function() { + CODE_RUNNING = false; + CALLBACK(self.E_UNKNOWN_OPCODE); + }; + + + // --- Public methods. + + // Attach the CPU to the VM. + this.attach = function(newMemory, newPC, newCallback) { + MEMORY = newMemory; + DEFAULT_PC = newPC; + CALLBACK = newCallback; + INSTRUCTION_POINTERS = [ + i00, //00 + i01, //01 + ierr, //02 + ierr, //03 + ierr, //04 + i05, //05 + i06, //06 + ierr, //07 + i08, //08 + i09, //09 + i0a, //0a + ierr, //0b + ierr, //0c + i0d, //0d + i0e, //0e + ierr, //0f + i10, //10 + i11, //11 + ierr, //12 + ierr, //13 + ierr, //14 + i15, //15 + i16, //16 + ierr, //17 + i18, //18 + i19, //19 + ierr, //1a + ierr, //1b + ierr, //1c + i1d, //1d + i1e, //1e + ierr, //1f + i20, //20 + i21, //21 + ierr, //22 + ierr, //23 + i24, //24 + i25, //25 + i26, //26 + ierr, //27 + i28, //28 + i29, //29 + i2a, //2a + ierr, //2b + i2c, //2c + i2d, //2d + i2e, //2e + ierr, //2f + i30, //30 + i31, //31 + ierr, //32 + ierr, //33 + ierr, //34 + i35, //35 + i36, //36 + ierr, //37 + i38, //38 + i39, //39 + ierr, //3a + ierr, //3b + ierr, //3c + i3d, //3d + i3e, //3e + ierr, //3f + i40, //40 + i41, //41 + ierr, //42 + ierr, //43 + ierr, //44 + i45, //45 + i46, //46 + ierr, //47 + i48, //48 + i49, //49 + i4a, //4a + ierr, //4b + i4c, //4c + i4d, //4d + i4e, //4e + ierr, //4f + i50, //50 + i51, //51 + ierr, //52 + ierr, //53 + ierr, //54 + i55, //55 + i56, //56 + ierr, //57 + i58, //58 + i59, //59 + ierr, //5a + ierr, //5b + ierr, //5c + i5d, //5d + i5e, //5e + ierr, //5f + i60, //60 + i61, //61 + ierr, //62 + ierr, //63 + ierr, //64 + i65, //65 + i66, //66 + ierr, //67 + i68, //68 + i69, //69 + i6a, //6a + ierr, //6b + i6c, //6c + i6d, //6d + i6e, //6e + ierr, //6f + i70, //70 + i71, //71 + ierr, //72 + ierr, //73 + ierr, //74 + i75, //75 + i76, //76 + ierr, //77 + i78, //78 + i79, //79 + ierr, //7a + ierr, //7b + ierr, //7c + i7d, //7d + i7e, //7e + ierr, //7f + ierr, //80 + i81, //81 + ierr, //82 + ierr, //83 + i84, //84 + i85, //85 + i86, //86 + ierr, //87 + i88, //88 + ierr, //89 + i8a, //8a + ierr, //8b + i8c, //8c + i8d, //8d + i8e, //8e + ierr, //8f + i90, //90 + i91, //91 + ierr, //92 + ierr, //93 + i94, //94 + i95, //95 + i96, //96 + ierr, //97 + i98, //98 + i99, //99 + i9a, //9a + ierr, //9b + ierr, //9c + i9d, //9d + ierr, //9e + ierr, //9f + ia0, //a0 + ia1, //a1 + ia2, //a2 + ierr, //a3 + ia4, //a4 + ia5, //a5 + ia6, //a6 + ierr, //a7 + ia8, //a8 + ia9, //a9 + iaa, //aa + ierr, //ab + iac, //ac + iad, //ad + iae, //ae + ierr, //af + ib0, //b0 + ib1, //b1 + ierr, //b2 + ierr, //b3 + ib4, //b4 + ib5, //b5 + ib6, //b6 + ierr, //b7 + ib8, //b8 + ib9, //b9 + iba, //ba + ierr, //bb + ibc, //bc + ibd, //bd + ibe, //be + ierr, //bf + ic0, //c0 + ic1, //c1 + ierr, //c2 + ierr, //c3 + ic4, //c4 + ic5, //c5 + ic6, //c6 + ierr, //c7 + ic8, //c8 + ic9, //c9 + ica, //ca + ierr, //cb + icc, //cc + icd, //cd + ierr, //ce + ierr, //cf + id0, //d0 + id1, //d1 + ierr, //d2 + ierr, //d3 + ierr, //d4 + id5, //d5 + id6, //d6 + ierr, //d7 + id8, //d8 + id9, //d9 + ierr, //da + ierr, //db + ierr, //dc + idd, //dd + ide, //de + ierr, //df + ie0, //e0 + ie1, //e1 + ierr, //e2 + ierr, //e3 + ie4, //e4 + ie5, //e5 + ie6, //e6 + ierr, //e7 + ie8, //e8 + ie9, //e9 + iea, //ea + ierr, //eb + iec, //ec + ied, //ed + iee, //ee + ierr, //ef + if0, //f0 + if1, //f1 + ierr, //f2 + ierr, //f3 + ierr, //f4 + if5, //f5 + if6, //f6 + ierr, //f7 + if8, //f8 + if9, //f9 + ierr, //fa + ierr, //fb + ierr, //fc + ifd, //fd + ife, //fe + ierr //ff + ]; + this.reset(); + }; + + // Execute a single instruction. + this.execute = function() { + if (!CODE_RUNNING) return; + var opcode = popByte(); + //console.log("PC = 0x" + (REG_PC - 1).toString(16) + " OP = 0x" + opcode.toString(16)); + INSTRUCTION_POINTERS[opcode](); + if ((REG_PC == 0) || (!CODE_RUNNING)) { + CODE_RUNNING = false; + CALLBACK(self.E_HALT); + } + }; + + // Get PC. + this.getPC = function() { + return REG_PC; + }; + + // Reset CPU. + this.reset = function() { + REG_A = 0; + REG_X = 0; + REG_Y = 0; + REG_P = 0; + REG_PC = DEFAULT_PC; + REG_SP = 0x100; + }; + + // Run until we die. + this.run = function() { + console.log("Starting CPU."); + CODE_RUNNING = true; + setTimeout(runBlock, 0); + }; +} diff --git a/vm/computer/keyboard.js b/vm/computer/keyboard.js new file mode 100644 index 0000000..da89444 --- /dev/null +++ b/vm/computer/keyboard.js @@ -0,0 +1,151 @@ +/* + * 6502 Based Virtual Computer + * Copyright (C) 2011 Scott C. Duensing + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// Our keyboard object. +function keyboard() { + + // OMFGWTFBBQ - http://unixpapa.com/js/key.html + + // --- Private variables. + + // Memory location for keyboard status. + var MEMORY = null; + var MEMORY_START = 0; + var CHARACTER_ADDRESS = 0; + var META_ADDRESS = 0; + + // Keymaps. Swiped from http://jonathan.tang.name/code/js_keycode + var KEY_MAP = {}; + var SHIFTED_SYMBOLS = { + 58: 59, // : -> ; + 43: 61, // = -> + + 60: 44, // < -> , + 95: 45, // _ -> - + 62: 46, // > -> . + 63: 47, // ? -> / + 96: 192, // ` -> ~ + 124: 92, // | -> \ + 39: 222, // ' -> 222 + 34: 222, // " -> 222 + 33: 49, // ! -> 1 + 64: 50, // @ -> 2 + 35: 51, // # -> 3 + 36: 52, // $ -> 4 + 37: 53, // % -> 5 + 94: 54, // ^ -> 6 + 38: 55, // & -> 7 + 42: 56, // * -> 8 + 40: 57, // ( -> 9 + 41: 58, // ) -> 0 + 123: 91, // { -> [ + 125: 93 // } -> ] + }; + var GECKO_IE_KEYMAP = { + 186: 59, // ;: in IE + 187: 61, // =+ in IE + 188: 44, // ,< + 109: 95, // -_ in Mozilla + 107: 61, // =+ in Mozilla + 189: 95, // -_ in IE + 190: 62, // .> + 191: 47, // /? + 192: 126, // `~ + 219: 91, // {[ + 220: 92, // \| + 221: 93 // }] + }; + var OPERA_KEYMAP = {}; + + // Browser detection taken from quirksmode.org + var IS_WINDOWS = navigator.platform.indexOf('Win') != -1; + var IS_OPERA = window.opera && window.opera.version() < '9.5'; + var IS_KONQUEROR = navigator.vendor && navigator.vendor.indexOf('KDE') != -1; + var IS_ICAB = navigator.vendor && navigator.vendor.indexOf('iCab') != -1; + + + // --- Public methods. + + // Attach keyboard to DOM. + this.attach = function(newMemory, newStartPosition) { + MEMORY = newMemory; + MEMORY_START = newStartPosition; + CHARACTER_ADDRESS = MEMORY_START; + META_ADDRESS = MEMORY_START + 1; + // Determine key map. + if (IS_OPERA && IS_WINDOWS) { + KEY_MAP = OPERA_KEYMAP; + } else if (IS_OPERA || IS_KONQUEROR || IS_ICAB) { + var unshift = [33, 64, 35, 36, 37, 94, 38, 42, 40, 41, + 58, 43, 60, 95, 62, 63, 124, 34]; + KEY_MAP = OPERA_KEYMAP; + for (var i = 0; i < unshift.length; ++i) { + KEY_MAP[unshift[i]] = SHIFTED_SYMBOLS[unshift[i]]; + } + } else { + // IE and Gecko are close enough that we can use the same map for both, + // and the rest of the world (eg. Opera 9.50) seems to be standardizing + // on them + KEY_MAP = GECKO_IE_KEYMAP; + } + if (IS_KONQUEROR) { + KEY_MAP[0] = 45; + KEY_MAP[127] = 46; + KEY_MAP[45] = 95; + } + // Hook browser for keydown events. + document.onkeydown = function(e){ + e = e || window.event; + // Process the key press. + var code = e.which || e.keyCode; + var k = { + code: KEY_MAP[code] || code, + shift: e.shiftKey, + alt: e.altKey, + ctrl: e.ctrlKey + }; + // Un-funk the input. + if (k.shift) { + // Fix shifted characters. + var oldC = "1234567890,/=[]\\';"; + var newC = "!@#$%^&*()= 0) k.code = newC.charCodeAt(x); + if (k.code == 222) k.code = 34; // " + } else { + // Fix unshifted characters. + var oldC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ~_>"; + var newC = "abcdefghijklmnopqrstuvwxyz`-."; + var x = oldC.indexOf(String.fromCharCode(k.code)); + if (x >= 0) k.code = newC.charCodeAt(x); + if (k.code == 222) k.code = 39; // ' + } + // Write meta first. Then we can process on the character being written. + MEMORY.writeByte(META_ADDRESS, (k.alt * 4) + (k.ctrl * 2) + k.shift); + // Ignore "naked" meta keys in the character address. + if ((k.code != 16) && (k.code != 17) && (k.code != 18) && (k.code != 145) && (k.code != 146)) { + MEMORY.writeByte(CHARACTER_ADDRESS, k.code); + } + }; + }; + + // Fetch how much RAM the current settings will require. + this.getMemoryNeeded = function() { + return 2; // Complicated, huh? + }; +} diff --git a/vm/computer/prng.js b/vm/computer/prng.js new file mode 100644 index 0000000..17bf9a4 --- /dev/null +++ b/vm/computer/prng.js @@ -0,0 +1,44 @@ +/* + * 6502 Based Virtual Computer + * Copyright (C) 2011 Scott C. Duensing + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// Our Psuedo-Random Number Generator object. +function prng() { + + // --- Private variables. + + // Memory location for PRNG value. + var MEMORY = null; + var MEMORY_START = 0; + var PRNG_ADDRESS = 0; + + + // --- Public methods. + + // Attach PRNG to DOM. + this.attach = function(newMemory, newStartPosition) { + MEMORY = newMemory; + MEMORY_START = newStartPosition; + PRNG_ADDRESS = MEMORY_START; + MEMORY.addReadCallback(PRNG_ADDRESS, PRNG_ADDRESS, null, function(address, value, data){ + // On read, change the random value. + MEMORY.writeByte(address, Math.floor(Math.random() * 256)); + }); + }; + +} diff --git a/vm/computer/ram.js b/vm/computer/ram.js new file mode 100644 index 0000000..f703c83 --- /dev/null +++ b/vm/computer/ram.js @@ -0,0 +1,118 @@ +/* + * 6502 Based Virtual Computer + * Copyright (C) 2011 Scott C. Duensing + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// Memory +function ram(newSize) { + + // --- Private Types. + + function T_EVENT() { + this.callback = null; + this.startAddress = 0; + this.endAddress = 0; + this.anyData = null; + }; + + + // --- Private variables. + + var MEMORY = new Array(newSize); + var WRITE_EVENTS = new Array(); + var READ_EVENTS = new Array(); + var EVENTS_ENABLED = true; + var EVENTS_ENABLED_COUNTER = 0; + + + // --- Public methods. + + // Add callback for memory read event. + this.addReadCallback = function(newStart, newEnd, newData, newCallback) { + var event = new T_EVENT(); + event.callback = newCallback; + event.startAddress = newStart; + event.endAddress = newEnd; + event.anyData = newData; + READ_EVENTS.push(event); + console.log("Read event " + READ_EVENTS.length + " added from 0x" + newStart.toString(16) + " to 0x" + newEnd.toString(16)); + }; + + // Add callback for memory write event. + this.addWriteCallback = function(newStart, newEnd, newData, newCallback) { + var event = new T_EVENT(); + event.callback = newCallback; + event.startAddress = newStart; + event.endAddress = newEnd; + event.anyData = newData; + WRITE_EVENTS.push(event); + console.log("Write event " + WRITE_EVENTS.length + " added from 0x" + newStart.toString(16) + " to 0x" + newEnd.toString(16)); + }; + + // Are we performing callbacks? + this.callbacksEnabled = function(trueFalse) { + EVENTS_ENABLED_COUNTER += (trueFalse ? -1 : 1); + EVENTS_ENABLED = (EVENTS_ENABLED_COUNTER == 0); + }; + + // See how much RAM we have. + this.getSize = function() { + return MEMORY.length; + }; + + // Read a byte and return it. + this.readByte = function(address) { + if ((address < MEMORY.length) && (address >= 0)) { + if ((READ_EVENTS.length > 0) && (EVENTS_ENABLED)) { + for (var x=0; x= event.startAddress) && (address <= event.endAddress)) + event.callback(address, MEMORY[address] & 0xff, event.anyData); + } + } + return MEMORY[address] & 0xff; + } + }; + + // Remove registered events. + this.removeEvents = function() { + READ_EVENTS = new Array(); + WRITE_EVENTS = new Array(); + }; + + // Set all the memory to zero. Does NOT fire events. + this.reset = function() { + for (var x=0; x= 0)) { + MEMORY[address] = value & 0xff; + if ((WRITE_EVENTS.length > 0) && (EVENTS_ENABLED)) { + for (var x=0; x= event.startAddress) && (address <= event.endAddress)) + event.callback(address, MEMORY[address] & 0xff, event.anyData); + } + } + } + }; + +} diff --git a/vm/computer/textDisplay.js b/vm/computer/textDisplay.js new file mode 100644 index 0000000..c3aed74 --- /dev/null +++ b/vm/computer/textDisplay.js @@ -0,0 +1,322 @@ +/* + * 6502 Based Virtual Computer + * Copyright (C) 2011 Scott C. Duensing + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// Our display adapter object. +function textDisplay() { + + // This display adapter can render text in 16 foreground colors + // on 16 background colors. A simple "BIOS" is available by + // writing command data into the byte directly following the + // display memory and then the command into the byte after that. + // You may also write directly to display RAM. + + + // --- Private variables. + + var self = this; + + // Memory Information. + var MEMORY = null; + var MEMORY_START = 0; + var DATA_BYTE = 0; + var DATA_LSB = 0; + var DATA_BYTE_ADDRESS = 0; + var COMMAND_BYTE_ADDRESS = 0; + + // Font size. + var FONT_SIZE = 12; + + // Default character cell size. + var CELL_WIDTH = 8; + var CELL_HEIGHT = 12; + + // Default display size. + var WIDTH = 80; + var HEIGHT = 25; + + // Is this a color display? + var IS_COLOR = true; + + // Cursor position. + var CURSOR_X = 0; + var CURSOR_Y = 0; + + // Current color attribute. Boring white on black. (LSN=FG, MSN=BG) + var COLOR_VALUE = 7; + + + // --- Private methods. + + // Clear the screen to the current color attributes. + var clearDisplay = function() { + var byte = MEMORY_START; + for (var y=0; y 0) + x--; + } else if (c == 9) { + // TAB + x = (x + 8) & ~7; + } else if (c == 10) { + // Line Feed + y++; + } else if (c == 13) { + // Carriage Return + x = 0; + } else { + // Other characters. + MEMORY.writeByte(byte++, c); + if (IS_COLOR) MEMORY.writeByte(byte++, COLOR_VALUE); + self.refresh(x, y); + x++; + } + // Line wrap? + if (x >= WIDTH - 1) { + x = 0; + y++; + } + // Scroll required? + var didScroll = false; + while (y >= HEIGHT) { + didScroll = true; + byte = MEMORY_START; + for (var i=0; i<(HEIGHT - 1) * WIDTH; i++) { + MEMORY.writeByte(byte, MEMORY.readByte(byte + WIDTH * color)); + byte++; + if (IS_COLOR) { + MEMORY.writeByte(byte, MEMORY.readByte(byte + WIDTH * color)); + byte++; + } + } + //byte = MEMORY_START + WIDTH * (HEIGHT - 1) * color; + for (var i=0; i"; + for (var w=0; w "; + } + html += ""; + } + html += ""; + document.getElementById(divId).innerHTML = html; + DATA_BYTE_ADDRESS = byte++; + COMMAND_BYTE_ADDRESS = byte++; + moveCursor(0, 0); + this.refreshAll(); + // Set up memory mapped hardware ports. + MEMORY.addWriteCallback(MEMORY_START, MEMORY_START + this.getMemoryNeeded() - 1, null, function(address, value, data){ + MEMORY.callbacksEnabled(false); + var offset = address - MEMORY_START; + // Is this a write to one of the command ports or direct to video memory? + if (address >= DATA_BYTE_ADDRESS) { + // Command byte? + if (address == COMMAND_BYTE_ADDRESS) { + if (value == 0) DATA_LSB = DATA_BYTE; + if (value == 1) clearDisplay(); + if (value == 2) moveCursor(DATA_BYTE, CURSOR_Y); + if (value == 3) moveCursor(CURSOR_X, DATA_BYTE); + if (value == 4) MEMORY.writeByte(DATA_BYTE_ADDRESS, CURSOR_X); + if (value == 5) MEMORY.writeByte(DATA_BYTE_ADDRESS, CURSOR_Y); + if (value == 6) COLOR_VALUE = DATA_BYTE; + if (value == 7) drawCharacter(DATA_BYTE); + if (value == 8) drawString((DATA_BYTE << 8) + DATA_LSB); + } else { + // Data byte. + DATA_BYTE = value; + } + } else { + if (IS_COLOR) { + // Is this the character byte or the color byte? + if (offset % 2 == 0) + offset = offset / 2; // Character. + else + offset = ((offset + 1) / 2) - 1; // Color. + } + var y = Math.floor(offset / monitor.getWidth()); + var x = offset - y * monitor.getWidth(); + monitor.refresh(x, y); + } + MEMORY.callbacksEnabled(true); + }); + }; + + // Refresh a single character cell. + this.refresh = function(x, y) { + // These are good old PC DOS colors. + // black, dark red, dark green, brown, dark blue, dark magenta, dark cyan, gray + // dim gray, red, green, yellow, blue, magenta, cyan, white + var colors = ["#000000", "#8B0000", "#006400", "#8B8B00", "#00008B", "#8B008B", "#008B8B", "#808080", + "#696969", "#FF0000", "#00FF00", "#FFFF00", "#0000FF", "#FF00FF", "#00FFFF", "#FFFFFF"]; + var byte = MEMORY_START + (y * WIDTH + x) * (IS_COLOR ? 2 : 1); + var foreground = colors[7]; + var background = colors[0]; + if (IS_COLOR) { + // LSN=FG, MSN=BG + var value = MEMORY.readByte(byte + 1); + var LSN = value & 0x0f; + var MSN = (value & 0xf0) >> 4; + foreground = colors[LSN]; + background = colors[MSN]; + } + var element = document.getElementById("cc" + x + "x" + y); + var style = element.style; + var html = ""; + html += String.fromCharCode(MEMORY.readByte(byte)); + html += ""; + element.innerHTML = html; + style.background = background; + }; + + // Refresh the entire display. + this.refreshAll = function() { + for (var h=0; h + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +--> + + + + + Virtual Computer Test + + + + + + + + + +

+ + + diff --git a/vm/machine.js b/vm/machine.js new file mode 100644 index 0000000..ff516af --- /dev/null +++ b/vm/machine.js @@ -0,0 +1,101 @@ +/* + * 6502 Based Virtual Computer + * Copyright (C) 2011 Scott C. Duensing + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// Try it out! + +// Declare components of our computer. +console.log("Creating components."); +var memory = new ram(64 * 1024); +var cpu = new cpu6502(); +var monitor = new textDisplay(); +var keys = new keyboard(); +var random = new prng(); +var timer = new clock(); +console.log("Creating components - success!"); + +// Memory Map (64k): +var MM_ZERO_PAGE = 0x0000; +var MM_STACK = 0x0100; +var MM_CODE_START = 0x0200; +var MM_HEAP_END = 0xdfff; +var MM_DISPLAY_ADAPTER = 0xe000; +var MM_DISPLAY_DATA = MM_DISPLAY_ADAPTER + monitor.getMemoryNeeded() - 2; +var MM_DISPLAY_COMMAND = MM_DISPLAY_DATA + 1; +var MM_KEYBOARD_CHARACTER = MM_DISPLAY_COMMAND + 1; +var MM_KEYBOARD_META = MM_KEYBOARD_CHARACTER + 1; +var MM_PRNG = MM_KEYBOARD_META + 1; +var MM_CLOCK = MM_PRNG + 1; +var MM_ROM_START = 0xf000; +var MM_ROM_END = 0xfff9; +var MM_NMI_VECTOR = 0xfffa; +var MM_RESET_VECTOR = 0xfffc; +var MM_IRQ_VECTOR = 0xfffe; + +console.log("MM_IRQ_VECTOR = 0x" + MM_IRQ_VECTOR.toString(16)); +console.log("MM_RESET_VECTOR = 0x" + MM_RESET_VECTOR.toString(16)); +console.log("MM_NMI_VECTOR = 0x" + MM_NMI_VECTOR.toString(16)); +console.log("MM_ROM_END = 0x" + MM_ROM_END.toString(16)); +console.log("MM_ROM_START = 0x" + MM_ROM_START.toString(16)); +console.log("MM_CLOCK = 0x" + MM_CLOCK.toString(16)); +console.log("MM_PRNG = 0x" + MM_PRNG.toString(16)); +console.log("MM_KEYBOARD_META = 0x" + MM_KEYBOARD_META.toString(16)); +console.log("MM_KEYBOARD_CHARACTER = 0x" + MM_KEYBOARD_CHARACTER.toString(16)); +console.log("MM_DISPLAY_COMMAND = 0x" + MM_DISPLAY_COMMAND.toString(16)); +console.log("MM_DISPLAY_DATA = 0x" + MM_DISPLAY_DATA.toString(16)); +console.log("MM_DISPLAY_ADAPTER = 0x" + MM_DISPLAY_ADAPTER.toString(16)); +console.log("MM_HEAP_END = 0x" + MM_HEAP_END.toString(16)); +console.log("MM_CODE_START = 0x" + MM_CODE_START.toString(16)); +console.log("MM_STACK = 0x" + MM_STACK.toString(16)); +console.log("MM_ZERO_PAGE = 0x" + MM_ZERO_PAGE.toString(16)); + +// Configure components & connect them together. +cpu.attach(memory, MM_CODE_START, function(event){ + // We don't care about events just yet. + var message = "Event " + event + " @ 0x" + cpu.getPC().toString(16) + " = " + memory.readByte(cpu.getPC()).toString(16); + console.log(message); + alert(message); +}); +monitor.attach(memory, MM_DISPLAY_ADAPTER, "display"); +keys.attach(memory, MM_KEYBOARD_CHARACTER); +random.attach(memory, MM_PRNG); +timer.attach(memory, MM_CLOCK, 0x100); + +// Power on! +memory.reset(); +cpu.reset(); + +// Make it do something! +var HELLO_WORLD = [0xA2, 0x00, 0xBD, 0x14, 0x02, 0xF0, 0x0B, 0x8D, 0xA0, 0xEF, 0xA9, 0x07, + 0x8D, 0xA1, 0xEF, 0xE8, 0xD0, 0xF0, 0x00, 0x00, 0x48, 0x65, 0x6C, + 0x6C, 0x6F, 0x2C, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00]; + +for (var x=0; x + Copyright (C) 20yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 20yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/xa-2.3.5/ChangeLog b/xa-2.3.5/ChangeLog new file mode 100644 index 0000000..a5eb775 --- /dev/null +++ b/xa-2.3.5/ChangeLog @@ -0,0 +1,297 @@ +xa-2.1.0 + + * Rewrite of command line option handling to better look like + usual (cc) options. + * Removed ^M from all files. + * Removed all external declarations to header files, + and made all static functions static. + | Now compiles almost without warning with 'gcc -W -Wall'. + + -- André Fachat 31 Oct, 1996 + +xa-2.1.0a + + * Introduced concept of code relocation. Now each label being set to + the program counter is a 'pointer', that gets an entry in a + relocation table. Simple arithmetic operations are allowed. The + relocation table is still just printed unsortedly. + + -- André Fachat 31 Oct, 1996 + +xa-2.1.0b + + * Produces some preliminary kind of relocatable file, including header + etc. Problems -- relocation table does as if file is assembled for + address 0. Need + a) a better way to set program counter. + b) pseudo opcodes for distinguishing segments. + c) a way to temporarily disable relocation. + d) a way to include extra headers and options into the file. + + -- André Fachat 31 Oct, 1996 + + * Assembler now produces a relocatable file format, as described in + the file ``fileformat.txt''. Temporarily disabling relocation is with + the ``*=value'' directive, while switching back to relocation mode + goes with ``*='' (without value). New pseudo opcodes ``.text'', + ``.data'', ``.bss'', ``.zero'' switch between the segments. + + -- André Fachat 02 Nov, 1996 + +xa-2.1.0e + + * There was a bug in the arithmetic routine that had set all pointer + to the text segment, if something was added. + * There also was a bug in the loader when actually using options. + * A new pseudo opcode was added -- ``.fopt''. + | Works like ``.byte'', but puts these bytes in a file option. + | The length in the file option is automagically set. ``.fopt'' + | may appear anywhere in the file, but it should be at the + | beginning | (might be mandatory in a later version). + + -- André Fachat 06 Nov, 1996 + +xa-2.1.0f + + * Added a command line switch ``-M'' to ignore colons in a comment + after a semicolon. + * Without it, a colon separates complete mnemonics, including + the semicolon comment. + | Well, actually this switch is a ``MASM'' compatibility switch, and + will surely be expanded someday, when I get more info on MASM. + * Now ``*'' and ``='' can be separated for the definition + of the program counter and ``.byte'' is also accepted. + This makes it more MASM compatible. ".end" is ignored. + Still missing is ``.include''. + + -- André Fachat 11 Nov, 1996 + +xa-2.1.0g + + * Started working on ``official'' o65 fileformat. + If there are no undefined labels, and no relocated code + is embedded in absolute code, the thing should work. + + -- André Fachat 21 Dec, 1996 + +xa-2.1.1 + + * ``.dsb'' now has an _optional_ parameter ``fillbyte''. + * Undefined references are now put into the relocation table + (i.e. handled correctly) if the ``-c'' option is given. + * The file format conforms to o65 version 1 file format. + * Embedding absolute in relocatable code and vice versa is buggy... + + -- André Fachat 21 Dec, 1996 + +xa-2.1.1a + + * Embedding absolute code in relocatable seems to work now. + + -- André Fachat 21 Dec, 1996 + +xa-2.1.1e + + * The option to embed relocatable code in absolute code has been + dropped. Therefore the command line options + ``-A'' (make it romable), ``-b?'' (set segment start addresses), + and ``-G'' (omit exported globals from file) have been added. + * Internally, the whole thing has been made dynamic; except for the + preprocessor (and the storage between pass1 and pass2), everything + uses dynamically allocated tables. m_alloc, which had been + introduced long time ago because of the buggy malloc + on the Atari ST is gone now! + + -- André Fachat 22 Dec, 1996 + +xa-2.1.1f + + * Added the ``-a'' and ``-A'' options to file65, so that it can now + print the start addresses for following files in the ROM when making + romable code. + * Added shell (bash) script ``mkrom.sh'' that assembles a given list + of files and builds a ROMable file. The first two bytes are single + linked list pointers, and then comes the file. + + -- André Fachat 02 Jan, 1997 + +xa-2.1.1g + + * Added the file ``reloc65'', to relocate o65 files without + reassembling them. + * Fixed quite some bugs in xa (segment numbering in the globals list + and switched low/high byte relocation entry type in relocation + table. Now conforms to documentation, i.e. fileformat.txt) + + -- André Fachat 03 Jan, 1997 + +xa-2.1.2 + + * Added ``ld65'', a simple linker for o65 files. + * Another bug in xa fixed now. + + -- André Fachat 04 Jan, 1997 + +xa-2.1.3 + + * Allows to use ``.data'' etc in absolute mode, too. No relocation + entries are generated then. Segment start can be set with ``-b?'' + command line options, though. Also the data segment is discarded + with this method! This allows to use the normal ``.data'' etc + syntax even when assembling a ROM (which is done in absolute mode.) + * Fixed a bug where ``.dsb'' in a data segment didn't fill with the + right value + + -- André Fachat 25 Mar, 1997 + +xa-2.1.3e + + * Added preprocessor continuation lines, and .block and .bend + pseudo-opcodes (They map to ``.('' and ``.)'' respectively.) + + -- André Fachat 27 Jul, 1997 + +xa-2.1.4 + + * Do not leave output file around after an error -- this is + better for ``make''. + * Also it seems to have settled for a while, so I can release + a new version. + + -- André Fachat 11 Sep, 1997 + +xa-2.1.4c + + * Fixed a nasty bug that prevented correct relocation table + entries when a ``label2=label1'' contruct was used and + ``label2'' was accessed. + * Added ``-I'' option. + + -- André Fachat 30 Dec, 1997 + +xa-2.1.4d + + * fixed align code. Now inserts NOP opcodes into text segment, and + sets file mode appropriately. + + -- André Fachat 26 Jan, 1998 + +xa-2.1.4e + + * Changed o65 fileformat and adopted it in xa. + + -- André Fachat 26 Jan, 1998 + +xa-2.1.4g + + * Fix handling of !zeropageaddress, that was broken (did not result + in absolute address, but gave error message.) + * Add cross reference list to labels if switched on on command line. + * Fix the filename when multiple files are given on the command line + (used to give the first filename for all errors in second pass.) + + -- André Fachat 25 Nov, 1998 + +xa-2.1.4h + + * In file65 added support for undefined labels and globals, + also for (some known) file options. + * Fix a preprocessor bug. + + -- André Fachat 12 Dec, 1998 + +xa-2.2.0-p1-1 + + * Update COPYING to the latest version (Y2K-fixed + new address to GNU) + * Lots of fixes to the Makefiles + * Cleaned up the structure of the TODO file + * Added manual-pages for file65, ld65, printcbm, reloc65, uncpk, and xa + * Commented out LIB-flags -lm, -lcurses and -ltermcap, + since they are all unused + * Added `--help' and `--version' to all binaries + * Removed `-h', `-?' and `-v' options where applicable + * Created a file containing the version-function; version.h + * Moved common macros to a separate file; xad.h + * Restructuring of printcbm to become more readable + * Added ifndef/define/endif traps to all header-files + * Fixed a few typos + * Renamed romadr to romaddr + * Renamed all functions matching *such* to *search* + * Fixed all warnings + * Cleaned up all header-files + * Reformatted xa.log + + -- David Weinehall 20 Aug, 2002 + +xa-2.3.0 + + * Version number jump for all the unofficial xa's out there + * Fixed addressing bugs for @, ! and completed 65816 merge + Thanks to David for the report + * Moderate legibility overhaul to xat.c (will continue on this) + * More compiler warnings corrected + Thanks to David for the report + * man files completed + * Documentation updated + * Last line bug corrected (where last line not assembled if no newline) + Thanks to silverdr for the report + * ld65 is now ldo65 to avoid conflicts with cc65 package + * Post-defined labels work better, or at least somewhat (no longer attempts + to optimize in pass 2 and generate bad code). Can be forced with ` + Thanks to silverdr for the report + * Makefile bugs multiplied + * @ now mostly obligatory for 24-bit addressing + + -- Cameron Kaiser 2 Apr, 2006 + +xa-2.3.2 + + * Introduced switch to convert values in quotes to different character + sets. Currently supported are ASCII (default) and PETSCII + * Fixed some quote bugs + + -- André Fachat 23 Dec, 2006 + + Thomas Giesel's reports and suggestions, thank you: + * -M works on colons in comments and nowhere else, as documented + * macro function arguments are properly recursively evaluated + * cpp output now grokked for more complex pre-parsing, rather than + reinvent the wheel + Other things: + * .xl, .xs, .al, .as weren't documented, and now they are (always worked) + for 65816 mode + * ! for forward-defined labels calculated wrong instruction length, fixed + * xap.c cleaned up some, xat.c cleaned up some more + Legibility work is going to be a long-term project. + * -x is now deprecated + * Documentation updated + + -- Cameron Kaiser 13 Jan, 2007 + +xa-2.3.3 + + * Compatibility update for Microsoft Visual Studio and mingw/MSYS (thanks + Fabian Nunez and Mikkel Holm Olsen). + + -- Cameron Kaiser 15 May, 2007 + +xa-2.3.4 + + * -p to define alternate synonym for # for preprocessor to avoid cpp/xa + preprocessor clashes and messes. + * Direct linking into output stream of binary files (.bin). + * Minor overhaul of error system to facilitate future expansion. + * Documentation updated. + + -- Cameron Kaiser 1 July, 2008 + +xa-2.3.5 + + Most of this was suggested by Martin Wendt. + * Fixed bug where .bin was affected by the current character set. + * Added PETSCREEN and HIGH character sets. + * Added .aasc. + * Some more legibility work. + * Documentation updated. + + -- Cameron Kaiser 7 February, 2009 diff --git a/xa-2.3.5/Makefile b/xa-2.3.5/Makefile new file mode 100644 index 0000000..78ca224 --- /dev/null +++ b/xa-2.3.5/Makefile @@ -0,0 +1,62 @@ +# Unix gcc or DOS go32 cross-compiling gcc +# +CC = gcc +LD = gcc +CFLAGS = -O2 -W -Wall -pedantic -ansi +LDFLAGS = -lc + +# for DOS? +# CC = gcc-go32 +# LD = gcc-go32 +# CFLAGS = -W -Wall -pedantic + +# Other cc +#CC = cc +#CFLAGS = +#LD = ld + +DESTDIR = /usr/local + +BINDIR = $(DESTDIR)/bin +MANDIR = $(DESTDIR)/share/man/man1 +DOCDIR = $(DESTDIR)/share/doc + +MKDIR = mkdir -p +INSTALL = install + +all: xa uncpk + +xa: + (cd src && LD=${LD} CC="${CC} ${CFLAGS}" ${MAKE}) + +load: + (cd loader && CC="${CC} ${CFLAGS}" ${MAKE}) + +uncpk: + (cd misc && CC="${CC} ${CFLAGS}" ${MAKE}) + +dos: clean + (cd src && LD=gcc-go32 CC=gcc-go32 CFLAGS="-W -Wall -pedantic" ${MAKE}) + (cd misc && CC=gcc-go32 CFLAGS="-W -Wall -pedantic" ${MAKE}) + rm -f xa file65 ldo65 uncpk printcbm reloc65 mkrom.sh src/*.o + +mingw: clean + (cd src && LD=${LD} CC=${CC} CFLAGS="${CFLAGS}" LDFLAGS="" ${MAKE}) + (cd misc && LD=${LD} CC=${CC} CFLAGS="${CFLAGS}" LDFLAGS="" ${MAKE}) + +clean: + (cd src && ${MAKE} clean) + (cd loader && ${MAKE} clean) + (cd misc && ${MAKE} mrproper) + rm -f xa *.exe *.o65 + +install: xa uncpk + $(MKDIR) $(BINDIR) + $(MKDIR) $(MANDIR) + $(INSTALL) xa reloc65 ldo65 file65 printcbm uncpk $(BINDIR) + $(INSTALL) man/file65.1 man/ldo65.1 man/printcbm.1 man/reloc65.1 man/uncpk.1 man/xa.1 $(MANDIR) + #$(MKDIR) $(DOCDIR)/xa65 + +dist: clean + #cd .. ; tar cvf xa-2.3.5A.tar xa-2.3.5 ; gzip xa-2.3.5A.tar + cd .. ; tar cvf xa-2.3.5.tar xa-2.3.5 ; gzip xa-2.3.5.tar diff --git a/xa-2.3.5/README.1st b/xa-2.3.5/README.1st new file mode 100644 index 0000000..0c7994a --- /dev/null +++ b/xa-2.3.5/README.1st @@ -0,0 +1,116 @@ +This is the readme for xa, a cross-assembler for the 6502 and 65816 CPUs (and +derivatives). xa is a small, fast, portable two-pass assembler that compiles +under most ANSI C compilers. It is distributed under the GNU Public License +(see COPYING). + +The current version is 2.3.4, which implements multiple improvements on +2.3.2, a bug fix to the 2.3.0 version. 2.3.0 itself features many +compatibility improvements and new man-based documentation. It also completed +the merge of the 65816 and 6502/R65C02 versions and thus the current xa can +generate code for all targets now. + +To install on a generic Unixy thing, you should be able to just type + + % make # to build the executable, and if it works ... + % make install # to install man pages and binaries into the system + +This will create xa along with its various support utilities. Try assembling +the cpk depacker in examples/ as a test. xa also comes with uncpk (a program +for generating cpk archives) and printcbm (a program for listing Commodore +BASIC test) and file65, ldo65 and reloc65 for displaying, linking and +relocating o65 files in Andre's relocatable format (see doc/fileformats.txt). +The loader/ directory also has goodies for managing relocatable binaries. + +Don't forget the man pages in man/. Install these into your MANPATH at your +leisure, or read them with nroff -man (and/or groff -man). + +xa is no longer broadly supported outside of Unix due to my inability to test +it, but has nothing that should impair it from compiling elsewhere. To wit, +DOS compilation is still supported with the GO32 package. You should just be +able to type + + C:\> make dos + +In addition, there are compatibility updates to allow it to compile under +Microsoft Visual Studio and mingw. It should compile under VS2005 as written; +look in the vstudio directory for solution and project files provided by +Fabian Nunez. For mingw, use + + make mingw + +Similarly, Amiga and Atari ST compilation should still also function with +their particular compatible packages. + +xa has a companion disassembler, the dxa package. dxa is not included in the +standard xa distribution, but can be downloaded from the xa home page at + + http://www.floodgap.com/retrotech/xa/ + +Please check by periodically for the latest version of both packages. + +xa was originally written and maintained by Andre Fachat. The current version +is maintained by Cameron Kaiser. + +Please send me your comments at ckaiser@floodgap.com -- Andre's original +readme follows and applies generally to the present version. + +------------------------------------------------------------------------------- + +XA is a 6502 cross compiler: + + - under GNU public license + + - can produce _relocatable_ binaries + + - The full fileformat description and 6502 file loader included. + + - also included relocation and info utilites, as well as linker + + - for any ANSI-C compliant computer (only utilities need 'stat' call + for file size). + + - fast by hashtables + + - Rockwell CMOS opcodes + + - running under DOS and any ANSI C system (Unix, Amiga, Atari ST) + +I developed this cross assembler for the 6502 CPU family quite some time +ago on my Atari ST. The assembler has successfully been ported to Amiga +and Unix computer (ported? just compiled... :-) +Lately I came across the problem to need relocatable 6502 binary files, so +I revised the assembler from version 2.0.7 to 2.1.0, adding a (admittedly +proprietary) 6502 relocatable binary format. But there are not many other +formats around and they didn't fit my needs. I have developed this format +myself and it is under the GNU public license. +With version 2.1.1 the 'official' version of the fileformat is supported. + +To compile it, just type "make" (if you have the GNU gcc. If not, edit the +Makefile for the compiler options). This produces "xa", the cross assembler; +"uncpk", a small packing utility (where the C64 counterpart is in the +examples subdirectory), "printcbm", that lists C64 BASIC files and +'file65' that prints some information about o65 files. The "loader" in +the loader subdirectory is a basic 6502 implementation of a relocating +binary loader. +"file65" prints file information on 'o65' relocatable files. "reloc65" +can relocate 'o65' files. + +If you want to use it under DOS, you have to have the GO32 DOS crosscompiling +tools to compile. Then just type "make dos" and you'll end up with the +appropriate DOS binaries. This has been tested only under i386 Linux, however. +Another archive with the DOS binaries included is provided. + +One problem on the Atari was it's broken "malloc". Therefore I used to +alloc everything in one chunk and divide the memory by hand. So everything +was kind of statically allocated. This is almost gone now. Only the +temporary storage between pass1 and pass2 and the preprocessor are still +allocated in one chunk (size definitions in xah.h). The rest is allocated +as needed. + +The docs are in the 'doc' subdir. There also is a description of the +6502 relocatable binary format. If you think some things could be +expressed in a better way, feel free and mail me to improve my english ;-) +[ The documentation is now maintained in man(1) format in man/ . -- CK ] + +Andre + diff --git a/xa-2.3.5/TODO b/xa-2.3.5/TODO new file mode 100644 index 0000000..0944889 --- /dev/null +++ b/xa-2.3.5/TODO @@ -0,0 +1,10 @@ +o nm65 that prints labels from o65 files + +o `-L' option for ldo65, such that globals can be suppressed, + but KERNEL can be kept + +o inc a -> ina, dec a -> dea (right now uses bare inc and dec) + +o VICE label file support + +o Smarter -X that can cope with non-block-aligned segment sizes diff --git a/xa-2.3.5/doc/README b/xa-2.3.5/doc/README new file mode 100644 index 0000000..8f9bf19 --- /dev/null +++ b/xa-2.3.5/doc/README @@ -0,0 +1,6 @@ +As of 2.3.0, the official documentation will be maintained in the ../man/ +directory. However, as the feature set is mostly mature, there is likely to +be little difference. The 2.2.x documents are here for your interest and +historical reference. + +Cameron Kaiser diff --git a/xa-2.3.5/doc/fileformat.txt b/xa-2.3.5/doc/fileformat.txt new file mode 100644 index 0000000..6e5d3ab --- /dev/null +++ b/xa-2.3.5/doc/fileformat.txt @@ -0,0 +1,582 @@ + + 6502 binary relocation format + +V1.2 as of 26jan1998 + + (c) André Fachat (a.fachat@physik.tu-chemnitz.de) + _________________________________________________________________ + + Changes from V1.1 + + The order for saving the undefined reference and the low byte of a + high byte relocation entry has changed. This makes the OS/A65 lib6502 + implementation easier. How many other people use this format + anyway...? + _________________________________________________________________ + + 0) Preface + + With some new 6502/C64/C128 operating systems comes the need for a new + binary format. In multitasking operating systems like Lunix, SMOS, or + OS/A65, a binary file cannot be loaded to a fixed location that might + already be used by another program. Therefore it must be possible to + relocate the program to an arbitrary address at least at load time. In + addition to that, more specific information might be stored in a + binary executable file, like interrupt vectors for example. + + This text gives a good solution to this problem for the 6502 CPU and + an assembler source format to use this format in a general manner. The + file format can even be used as an object file format, i.e. a format a + linker can use as an input file. It is also usable as a 65816 file + format. Instead of zeropage addressing modes, the 65816 has direct + addressing modes, that add the contents of the direct register to the + zeropage address in the opcode. + + 1) 6502/65816 specifics + + The 6502 has the special feature of a 'zeropage', i.e. a very limited + memory address range used for special addressing modes. So the format + should not only provide a means to relocate absolute addresses but + also zeropage addresses. The 65816 replaces zeropage addressing with + direct addressing modes. + + The stack space is also very limited. A binary format has to provide a + measure of how much stack space is needed for the application. + + Such limits should be defined as 2 byte values, even if the 6502 only + has a range of 8 address bits for zeropage and stack. But the 65816 + behaves differently, it has a 16 bit stack pointer for example. For + further expandability, a 32 bit format should be provided, although + the 16 bit format suffices for the 65816 already. + + Another problem is, that an address can be 'split', i.e. you can just + use the high byte or the low byte separately in an opcode. This gives + need to a special relocation table format, that can cope with + half-address references. The 65816 can even have three byte addresses, + i.e. address in a segment and segment number. + + 2) binary format + + 2.1) General + + The file differs from the known Commodore file formats, in that a lot + more information is stored in the file. First the data is structured + in separate segments to allow different handling of text (program + code), data (like tables) and bss (uninitialized data). + + Also tables are included to allow late binding, i.e. linking the file + with other files at load time, and relocation, i.e. executing the file + at different addresses in 6502 address space. + + 2.2) Segments + + As already used in other formats, the assembler uses three different + segment types, i.e. text (the actual program code), data (initialized + variables), and bss (uninitialized variables). To have these different + segments seems to be 'overdesigned', but they actually make memory + handling easier in more complex operating systems on systems with + virtual addresses (OS/A65, for example). + + The text segment is defined to be read-only memory. This doesn't allow + self-modifying code in this segment, but allows memory sharing in + virtual memory architectures. The data segment actually is like the + text segment, only it is allocated writable. This segment might not be + shared between different processes. The contents of these two segments + are loaded from the file. The bss segment is uninitialized data, i.e. + upon program start, it is not defined - and not loaded from the file. + This area is read-write and can be used during program execution. It + is also not shared between processes. In addition to these segments, + the 6502 format also includes a zeropage segment type, to allow + zeropage variables to be relocated. This zeropage segment is like a + bss segment, in that only the length, but not the data is saved. For + the 65816 the zeropage segment changes its meaning to a bank zero + segment. + + The different segments hold different type of data and can be located + anywhere in memory (except zero segment, which has to be in the + zeropage resp. bank zero). The program must therefore not assume + anything about the relative addresses between different segments. + + 2.3) Relocation + + In general, there are three ways to handle the relocation problem so + far: + +- Tables: have a relocation table for a text segment + if the relocation table is put in front of code + you have to save the table in a side-storage + if table is behind, you still cannot relocate 'on the fly'. + +- Deassembling: go through the code, deassemble it and change all absolute + addresses. Problem: needs to know or have hints about where some + data is in the code. + +- Relocation info in the code: here each address is preceeded with an + 'escape' code and is relocated when loading. But this disallows block + oriented transfer from storage media to memory. + + This binary format uses the first method, with the table after the + code/data. This way block oriented transfer for the text/data segment + can be used. And while reading the relocation tables bytewise, the + relocation can be done without the need to save the table somewhere. + + 2.4) External References & Exported Globals + + As this file format should not only be used as an executable format, + but also as object file format, it must provide a way to define + references - references exported from this object and labels + referenced in this object. The external references list (also called + 'undefined list') lists the addresses where labels not defined in this + object are referenced. The exported globals list lists the addresses + that are available for other objects. The labels are named by + null-terminated ASCII strings. + + Even an executable file can have non-empty globals and externals + lists, but only if the operating system allows this. In this case, so + called 'late binding' is used to link the object with some global + libraries at link time. + + 2.5) File extension + + The proposed standard extension for the described format is ".o65" + when used as an object file. + + 2.6) Format description + + The binary format is the following: + ( + header + + text segment + + data segment + + external references list + + relocation table for text segment + + relocation table for data segment + + exported globals list + ) + + The description of the parts follows: + + 2.6.1) Header + + The header contains the minimum needed data in a fixed struct. The + rest of the necessary information is put into the header options. + [Note: .word is a 16 bit value, low byte first, .byt is a simple byte. + .long is a 32 bit value, low byte first. .size is a 16 or 32 bit value + according to .word and .long, depending on the size bit in the mode + field ] + + This is the fixed struct: + ( + .byt $01,$00 ; non-C64 marker + + .asc "o65" ; o65 MAGIC! + .byt 0 ; version + + .word mode ; mode word + + .size tbase ; address to which text is assembled to + ; originally + .size tlen ; length of text segment + .size dbase ; originating address for data segment + .size dlen ; length of data segment + .size bbase ; originating address for bss segment + .size blen ; length of bss segment + .size zbase ; originating address for zero segment + .size zlen ; length of zero segment + .size stack ; minimum needed stack size, 0= not known. + ; the OS should add reasonable values for + ; interrupt handling before allocating + ; stack space + ) + + The mode word currently has these defined bits: + mode.15 : CPU 0= 6502 1= 65816 + mode.14 : reloc 0= bytewise... 1= page(256byte)wise relocation + allowed + mode.13 : size 0= size=16 bit, 1= size=32 bit + mode.12 : obj 0= executable 1= object file + + mode.0-1: align 0= byte align, + 1= word (i.e. 2 byte) align + 2= long (4 byte) align + 3= block (256 byte) align + + The CPU bit tells the loader for which CPU the file was made. This has + implications on the zero segment, for example. Also a system can check + if the program will run at all (on a 6502 that is). The reloc bit + defines if an object file can be relocated bytewise, or if it must be + page-aligned. A page has 256 bytes. The restriction to pagewise + relocation simplifies the relocation table and also allows simpler + compilers/assemblers. The size bit determines the size of the segment + base address and length entries. Currently the 16 bit size (size bit = + 0) works for 6502 and 65816 CPUs. + + The obj bit distinguishes between object files and executables. An + object file is used as assembler output that can be linked with other + object files to build an executable or an object library. The two + align bits give the address boundary the segments can be placed. Even + the 6502 needs this, as, for example, "jmp ($xxFF)" is broken. The + align bits are valid for all of the segments. [Note: if reloc=1, then + align should be 3. But if align=3, reloc need not be 1, because reloc + switches to a simpler version of the relocation table. The reloc bit + might be obsoleted in newer versions of this format. Though it should + be set, if necessary.] + + All unused bits in the mode field must be zero. + + Note that the header size is 26 if the size bit is zero and 44 if the + size bit is one. + + The fixed sized struct is immediately followed by a list of header + options. Each header option consists of a single byte total length, a + type byte and some data bytes if needed. A single length byte of $00 + ends the header option list. + + ( + { ; optional options, more than one allowed + .byt olen ; overall length (including length and type + ; byte + .byt otype ; option type + [ .byt option_bytes ] + } + .byt $00 ; end of options marker (i.e. option len=0) + ) + + The header options currently defined/proposed are: +- Filename: + type=0; len=strlen(filename_in_ascii)+3; content="filename_in_ascii",0 + The string contains the name of the object. + +- Operating System Header + type=1; len=? + the first data byte is the OS type: + 1 OSA/65 header supplement + 2 Lunix header supplement + [others to follow?] + the following data contains OS specific information. + A suggested data byte is the OS version as second byte. + +- Assemblerprogram: + type=2; len=strlen(ass)+3; content="ass",0 + The string contains the name of the assembler resp. linker that produced + this file/object. + For example (syntax see below) + .fopt 2, "xa 2.1.1g",0 + becomes + 0c 02 78 61 20 32 2e 31 2e 31 67 00 + in the file. + +- Author: + type=3; len=strlen(author)+3; content="author",0 + The string contains the author of the file. + +- Creation data: + type=4; len=strlen(date)+3; content="date_string",0 + The string contains the creation date in format like: + "Sat Dec 21 14:00:23 MET 1996", where we have the day, Month, date, + time, timezone and year. See output of `date`... + + 2.6.2) text and data segments + + The text and data segments are just the assembled code. The only + difference between text and data segments is the read/write mode of + the two segments. Therefore, to be compliant to this file format, + self-modifying code goes into the data segment. + + 2.6.3) Undefined references list + + The next list is an ASCII list of labels that are referenced in this + file but not defined. The lists is preceeded with the number of + undefined labels (16 or 32 bits, according to the mode.size bit). + +undef_list: number_of_undefined_labels.s + "undefined_label1",0 + "undefined_label2",0 + ... + + 2.6.4) Relocation tables + + The relocation tables are the same format for the two segments, text + and data. In general a relocation entry consists of the offset from + the previous relocation address to the next one, the type of the + relocation and additional info. Relocation not only defines the + relocation when moving object code to a different address, but also + filling in the undefined references. + + Each table starts at relocation address = segment base address -1. + I.e. if the segment base address is $1000 for example, the first entry + has an offset computed from base address-1 = $0fff. The offset to the + next relocation address is the first byte of each entry. If the offset + is larger than 254 (i.e. 255 or above), than a 255 is set as offset + byte, the offset is decremented by 254 (note the difference) and the + entry is started again. + +{ [255,...,255,] offset of next relocation (b), typebyte|segmentID [, low_byte] + }+ + + where typebyte has the bits 5, 6 and 7 and is one of +WORD $80 2 byte address +HIGH $40 high byte of an address +LOW $20 low byte of an address +SEGADR $c0 3 byte address (65816) +SEG $a0 segment byte of 3 byte address + + The segmentID stands for the segment the reference points to: +0 undefined +1 absolute value +2 text segment +3 data segment +4 bss segment +5 zero segment + + (Of course the absolute value will never appear in a relocation table, + but this value is necessary for the exported list) + + If the type is HIGH, the low byte of the value is stored behind the + relocation table entry, if bytewise relocation is allowed (header mode + field bit 14). If only pagewise relocation is allowed, then only HIGH + relocation entries can occur, and the low byte is implicitely set zero + (i.e. it is _not_ saved in the relocation table). + + If the type is SEG, then the two lower bytes of the three byte segment + address are stored behind the entry in the relocation table, lower + byte first. + + If the segment is "undefined", the typebyte is immediately followed by + the two (mode size=0) or four (mode size=1) byte value index in the + undefined references list. If it is a high byte relocation, the low + byte is saved behind the index value. The index value determines the + undefined reference, which must be looked up by the loader. + + The value taken from the relocation address in the segment, together + with the low byte from the relocation table (if HIGH entry) form the + address used if the segment would be used unrelocated. To relocate the + segment, the difference between the relocated segment base address and + the segment base address from the file is then added to the above + address. The result is again saved in the segment. + + A zero offset byte ends the relocation table. The first offset is + computed from the segment base address-1, to avoid a 0 value in the + first entry. + + Note that direct addressing modes do not generate entries in the + relocation table. instead it is assumed that the 65816 direct register + holds the correct value (i.e. zero segment base address) when running + this program. + + Example: + + Segment Base address in file (header.tbase) is $1000. The start + address of the text segment after relocation is real.tbase = $1234. + + Now the first (unrelocated) address at which a relocation should take + place is here: + +$1222 A9 23 lda #>vector + + This generates the offset: $1222-($1000-1) = $223. This is larger than + 254 ($fe), so the first byte is 255 ($ff). The offset is decremented + by $fe, and gives $125. This again is larger than $fe, so the next + byte is $ff again. After substracting $fe again, we have $27. But this + is the address of the opcode. To get the address of the address byte, + we have to add 1 to get $28, which becomes the third byte. The next + offset is then computed from $1223, because this is the last + relocation address. + + Now we reference the high byte of an address, lets say vector=$23d0 + (not relocated), in the text segment. Therefore the relocation type + becomes 'HIGH | text_segmentID = $42', which is the next byte. Because + we are referencing a high byte of an address, the low byte of the + unrelocated address is saved behind the typebyte in the relocation + entry. This byte is missing when referencing a low byte or address. + + The relocation table entry is now: +$ff, $ff, $28, $42, $d0. + + When actually doing the relocation, the relocation pointer is + initialized to real.tbase-1 = $1233. Then we compute the offset to + $224, which brings us to $1457, where the address byte of the above + opcode is after loading the file to $1234. We now have to compute the + new address, where vector is after relocation. So we take the + unrelocated low byte from the relocation table ($d0) and the high byte + from $1457 ($23). + +vector_file = ($23 +To this value we add +the difference between the address the program is assembled to and the +real load address: + +vector_relocated = vector_file + (real.tbase - header.tbase) + = $23d0 + ($1234 - $1000) + = $23d0 + $234 + = $2604 + +From this value the high byte is then written back to the address $1457. +Had we not saved the low byte in the relocation table, and only added +the high bytes, we would have missed the carry bit that increments +the high byte in this case! + +Had "vector" now been an undefined reference, and "vector" would be +the second label in the undefined references list, we would get the +following relocation table entry (assuming mode.size=0): + +$ff, $ff, $28, $40, $00, $02, $00 + +The value computed with the above formula for vector_file is now added +to the address the label "vector" now really has (This must of course +be looked up into an external table or list). +Had the opcode been "LDA #>vector+$567", then the low byte in the relocation +table would be $67, while the high byte in the opcode would be $05. +This value would result in vector_file and the real address of "vector" +would be added before wrting back the high byte to the opcode. + + + 2.6.5) exported globals list + + +The global list is a list of names, together with the target segment +and the offset in the segment for each name. It is preceeded with the +number of exported labels. This allows the loader to allocate a table +large enough, if needed. The number of labels and the offset value +are 16 bit or 32 bit values according to the size bit in the header mode +field. The segmentID is a byte value and the same as in the relocation +table entry (see section 2.6.3). + + number_of_exported_labels.s + "global_label_name_in_asc1",0, segmentID.b, value.s + ... + + + 3) assembler source format + + +The assembler source format is a suggestion only. It will be implemented +in xa65, a cross assembler for 6502 CPUs running on Unix/Atari ST/Amiga +as a reference platform. + +The assembler provides a way to embed absolute address code in relocatable +code. This is needed when code should be copied to a specific location +known at assemble time. +There also is a way to make a file 'romable'. You can give the start +address of the _file_ in ROM, and the assembler automatically sets +the text segment start address to where the code will be in the ROM. +Of course, the other segments must be taken care of with -b? command +line parameter, that set the segment start address. + + 3.1) embed absolute code in relocatable files + + +When the assembler is started in relocatable mode, everything is put into +a .o65 relocatable file. All address references generate relocation table +entries. If a "*= value" pseudo opcode is encountered, +then the assembler switches to absolute mode. The following opcodes don't +generate relocation table entries. If a "*=" without a value is read, +then the assembler switches back to relocatable mode. The relocation +program counter is increased with the length of the absolute part and +the absolute code is embedded between the relocatable parts. + + 3.2) embed relocatable code in absolute files + + +This is dropped - too complicated. Should better be done with some +objdump or linker programs or so. + + 3.2) Header options + + +Before any opcode (after starting in relocatable mode, or after a .reloc +opcode), a header option can be set by: + + .fopt byte1, byte2, ... + +The header option length is automatically set by the assembler. +An example for an file author entry: + + .fopt 3, "Andre Fachat",0 + +The 3 is the type byte for the author header option. The last zero ends +the name. The assembler can be configured to automatically include an +assembler header option into a file header. + + 3.3) allocation of data segment/zeropage segment address space + + +The assembler switches between the different segments by the means of +".text", ".data", ".bss" and ".zero" pseudo opcodes. After starting in +relocatable mode, the assembler is in the text segment. + +The text segment contains the program code. Data holds the initialized data, +while bss and zero segments contain uninitialized data for normal/zeropage +address space. +Everything that is between one of these segment opcodes and the next segment +opcode gets into the corresponding segment, i.e. labels, assembled code etc. +The text and data segments are saved in the file, while for the bss and +zero segments only the length is saved in the file. + +The assembler should issue a warning when a direct addressing mode +is used without a zero segment address and vice versa for 65816 CPUs. + + 3.4) referencing data/bss/zeropage addresses + + +One problem with the 6502 is, that it cannot load an address within one +step or assembler opcode. So an address is loaded with standard byte +opcodes, like "lda # +The assembler is now intelligent enough to evaluate such expressions +and check for: + +- no address label : ok, absolute +- one address label, only add to label : ok, relocate +- difference between two addresses : If addresses in same segment, compute + diff and set absolute, otherwise bail +- everything else : warning + +This way there is no change in syntax. Address labels are distinguished +by using the "label:" syntax, as opposed to "label = value". +Also, if the assembler is capable of doing so, an address label may be +defined by "label opcode", i.e. without a colon. + + 3.5) aligning code + + +The 6502 has the problem that some opcodes (e.g. "JMP ($xxFF)" are +broken, if the address given is at some (odd) address. But when loading +a relocatable file, one cannot know if an address will be odd or even. +Therefore there is a new opcode, + + .align 2 + +that aligns the next address at the given address boundary. Valid +values are 2, 4, and 256. + + 4) Clearance + + +This file is surely not the optimum and could be improved. Also the +header option "assigned numbers" should be added here. + +For this reason the author, André Fachat, will function as a +clearing point, where problems can be discussed and number can be assigned. + + +Dec. 22, 1996, + +André Fachat + +(fachat@physik.tu-chemnitz.de) + + +Appendix + + + + A) File examples + + +(to be done with reference assembler) diff --git a/xa-2.3.5/doc/xa-de.log b/xa-2.3.5/doc/xa-de.log new file mode 100644 index 0000000..71eee8e --- /dev/null +++ b/xa-2.3.5/doc/xa-de.log @@ -0,0 +1,89 @@ + +******** XASS65 1.0 ******** 15.11.89, Andre Fachat + +2-Pass-Assembler fr 65(C)02. von Rockwell. Der erste Pass berechnet +die Labels, im zweiten Pass wird assembliert. Es wird eine Block-Struktur +untersttzt, d.h. die Pseudo-Opcodes .( und .) verbergen dazwischenliegende +Labels vor Zugriffen aus anderen als darberliegenden Blocks. +Die Quell-, Object- und Fehlerdateien mssen in der Kommandozeile angegeben +werden. + +******** XASS 1.0 ******* 1.4.90, Andre Fachat + +Die Object- und Fehlerdateien werden, falls nicht anders angegeben aus +der ersten Quelldatei durch „ndern der Extension ermittelt. +Es gibt jetzt einen C-„hnlichen Preprozessor mit define, ifdef, include etc. +'Funktionen' k”nnen keine definiert werden. + +******** XA 2.00 ******** Andre Fachat + +Ab jetzt wird das, was im ersten Pass schon zur Ermittelung der Opcode- +L„nge assembliert wurde zwischengespeichert. Das verdoppelt die +Geschwindigkeit ann„hernd. Dabei wird alles, was bekannt ist, vertokend, +um sp„ter nicht wieder soviel suchen zu mssen. + +******** XA 2.02 ******** 23.9.90, Andre Fachat + +Labels k”nnen mit vorangestelltem '&' eine Blockstufe h”her definiert +werden, mit einem '-' werden sie global definiert (Block=0). +Bei eingeschalteter Option -c werden Fehler bei CMOS-Befehlen erzeugt. +Die Tabellen fr die Token-Erkennung sind vereinfacht und damit schneller +gemacht. + +******** XA 2.03 ******** 16.6.91, Andre Fachat + +Jetzt wird automatisch eine Liste mit Labels erzeugt und in einer .LAB-Datei +gespeichert. Im Preprozessor gibt es jetzt #if. + +******** XA 2.04 ******** 14.7.91, Andre Fachat + +Die Labeltabelle kann jetzt gr”žer als 32kByte werden. Da ein Eintrag in der +Label-Tabelle ohne den eigentlichen Namen 14 Byte betr„gt, sind jetzt mehr +als 2340 Labels m”glich. +Im Preprozessor sind jetzt auch 'Funktions'definitionen m”glich , also +z.B. #define mult(a,b) ((a)*(b)) + +******** XA 2.05 ******** 29.7.91, Andre Fachat + +Fr Labels, Defines und die Opcodes wird die Suche nach Hashcode durchgefhrt. +damit werden ca. 350kByte Quelltext fr ein 32kByte EPROM nicht mehr in +18 sondern in ca. 3 Minuten assembliert. + +******** XA 2.06 ******** 18.9.91,Andre Fachat + +Der Preprozessor hat jetzt #ifldef und #iflused, was eigentlich heižt +'if label defined' und 'if label used'. Damit lassen sich Bibliotheks- +„hnliche Dateien aufbauen. +Die Umstellung auf PureC bringt ihn dazu, 350kByte (s.o.) in 2 Minuten zu +assemblieren. Die Vergr”žerung der Datei-Puffer per setvbuf() auf 4kByte +bringt auch noch 7 Sekunden. Aužerdem wird am Ende eine Statistik ber +verbrauchte Resourcen gedruckt. + +******** XA 2.07 ******** 30.9.91, Andre Fachat + +Jetzt wird Zeit und Datum sowie die verbrauchten Sekunden in der Statistik +gezeigt. Die Environment-Variablen XAINPUT und XAOUTPUT werden untersttzt. +Falls die Quell- und Include-Dateien nicht gefunden werden, werden die in +XAINPUT aufgefhrten Pfade der Reihe nach durchgetestet. Falls XAOUTPUT +existiert, wird dieser Pfad als Pfad fr .ERR, .LAB und .OBJ-Dateien +benutzt. Nach einem Turbo-C Referenzhandbuch sind alle Systemaufrufe +(malloc, fopen etc.) jetzt ANSI-kompatibel. (Der C-Quellcode allerdings +ist die Darstellung von Chaos im besten K&R-Stil...) + +******** 16.2.92, Andre Fachat + +#print und #printdef werden unterdrckt, wenn sie in der entsprechenden +#if-Strukur sind (war bisher im Gegensatz zu echo und include nicht so) + +******** XA 2.07b ******** 5.1.94, Andre Fachat + +Auf englisch uebersetzt, die Abhaengigkeit des Header-files von +stdio.h beseitigt, ebenso die Abhaengigkeit von der integer size. +in xah.h gibt es jetzt einen Define DIRCHAR und DIRCSTRING, die +das Verzeichnistrennzeichen definieren (DIRCHAR als '/' bzw '\\' und +DIRCSTRING als "/" bzw. "\\") + +******** XA 2.0.7d ******** 96, Andre Fachat + +Mehr auf Englisch übersetzt. #undef kann ein define löschen. + diff --git a/xa-2.3.5/doc/xa-de.txt b/xa-2.3.5/doc/xa-de.txt new file mode 100644 index 0000000..008846c --- /dev/null +++ b/xa-2.3.5/doc/xa-de.txt @@ -0,0 +1,394 @@ +---------------------------------------------------------------------------- + + XA 2.1.0 + + 65(c)02 Cross-Assembler + + von Andre Fachat + +---------------------------------------------------------------------------- + + * Block-Struktur (Versteckte Label) + + * Sehr schnell durch hashing + + * C-ähnlicher Preprozessor + +---------------------------------------------------------------------------- + + 1. Was ist das überhaupt und Geschichte + + 2. Aufruf und Features + + 3. 6502 Assembler + + 4. Pseudo-Opcodes, Block-Struktur und Gültigkeitsbereich von Labels + + 5. Preprozessor + +---------------------------------------------------------------------------- + + +1. Was ist das überhaupt und Geschichte +---------------------------------------- + +Mit dem Cross-Assembler können auf einem Rechner Programme für einen +anderen Rechner(typ) erstellt werden. Die Programmdateien müssen dann +nur noch auf das Zielsystem übertragen werden. In diesem Fall handelt +es sich um einen 6502-Cross-Assembler. Der 6502 ist ein 8-Bit-Prozessor, +der ursprünglich von MOS Technologies entwickelt wurde. Er tut seine +Dienste u.a. im Apple II, Commodore PET, VC20 und C64 (in abgewandelter +Form) und vielen anderen. +Inzwischen gibt es ihn in vielen Varianten mit verschiedenen Zusätzen +und in verschiedenen Gehäusen. Gemeinsam ist allen der Befehlssatz, +der für den Assembler die Grundlage bildet. Die CMOS-Versionen bieten +allerdings Erweiterungen des Befehlssatzes. +Die Idee zu einem Cross-Assembler entstand, als ich mir einen 6502-Computer +selbst baute und der (ebenfalls selbstgeschriebene) Assembler auf dem C64 +zu langsam wurde. Nachdem auch noch ein Atari ST bei mir rumstand, war +die Idee schnell in die Tat umgesetzt. + + +2. Aufruf und Features +----------------------- + +Der Assembler besteht nur aus dem Programm "xa". +Der Assembler verarbeitet eine oder mehrere Quelldateien zu einer +Objektdatei, die direkt verwendet werden kann. Das Linken entfällt, da der +Aufwand zu groß und die Geschwindigkeit hoch genug ist, um die +'Libraries' im Quelltext einzubinden. Ca. 350kByte Quelltext werden in +1 Minute und 50 Sekunden zu einer 32kByte Objektdatei für ein EPROM +assembliert (naja, der 8MHz Atari war nicht schnell. Aber dafür ist der +Wert ziemlich gut. Mein 486DX4/100 braucht vielleicht 2 Sekunden)! +Als Ausgabedateien werden eine Objektdatei, eine Fehlerdatei und eine +Labeldatei geschrieben. + +Der Aufruf lautet: +XA Quell1 [Quell2 ...] [-oObject] [-eFehler] [-lLabel] [-C] [-v] [-x] + +Die Angabe der Objekt-, Fehler- und Labeldatei ist optional, ohne Angabe +wird die Endung der ersten Quelldatei auf 'obj', 'err' und 'lab' verändert, +wenn "-x" angegeben ist. Ansonsten wird "a.o65" als Ausgabedatei verwendet +und keine Fehler- und Labeldatei geschrieben. +Die Option -C erzeugt Fehlermeldungen bei CMOS-Befehlen. +Im Environment werden die Variablen XAOUTPUT und XAINPUT unterstützt. +Falls die Quell- und Include-Dateien nicht gefunden werden, werden die in +XAINPUT aufgeführten Pfade der Reihe nach durchgetestet. Falls XAOUTPUT +existiert, wird dieser Pfad als Pfad für .err, .lab und .obj-Dateien +benutzt. Die Komponenten des Pfades sind mit ',' getrennt. + +Die Labeldatei enthält hinterher eine Liste aller Labels mit Block-Nummer +und Wert in dezimal in der Form: 'Label, 1,-1234' in lesbarem ASCII. +Die Fehlerdatei enthält die Version des Assemblers, Datum und Uhrzeit des +Assemblerlaufs, die Liste der Fehler, die Ausdrücke, die mit #echo +und #print im Preprozessor erzeugt werden und eine Statistik über die +benutzten Resourcen (Speicherplatz etc.). +Die Objektdatei wird nur durch die Quelldatei bestimmt, es wird kein Code +vorgesetzt oder angehängt. +Die Quelldatei muß im ASCII-Format vorliegen. + + +3. 6502 Assembler +------------------ + +Da dies kein 6502-Assemblerkurs werden soll, nur eine ganz kurze +Beschreibung. Unterstützt wird der Code für alle Standard-6502 sowie +der Code für die Rockwell 65C02-CPU. Der Prozessor hat drei Register, +über die die meisten Operationen laufen. Transferoperationen benötigen +deshalb immer einen Load- und einen Store-Befehl. +Zusätzlich gibt es das Statusregister und den Stackpointer sowie, +natürlich, den Programmzähler. Der Stack liegt immer im Bereich $100 und +$1ff (Hexadezimal mit vorangestelltem '$'), weshalb der Stackpointer ein +8-Bit-Register ist. Eine besondere Behandlung durch kürzere Befehle +erfährt die Zeropage ($0-$ff), bei deren Adresse das Hi-Byte Null ist. +Das Statusregister besitzt folgende Flags: + +N = Negativ +O = Overflow +B = Break +D = Dezimal +I = Interrupt +Z = Zeroflag +C = Carry + +Befehle: + +LDA lade Akkumulator +LDX lade X-Register +LDY lade Y-Register + +STA speichere Akkumulator +STX speichere X-Register +STY speichere Y-Register +STZ speichere NULL (*) + +TAX Kopiere Akku nach X +TAY Kopiere Akku nach Y +TXA Kopiere X nach Akku +TYA Kopiere Y nach Akku +TSX Kopiere Stackpointer nach X +TXS Kopiere X nach Stackpointer + +ADC Addiere zu Akku mit Übertrag (Carry) (D) +SBC Subtrahiere von Akku mit Carry (D) +AND Logisches Und mit Akku +ORA Logisches Oder mit Akku +EOR Exklusiv-Oder mit Akku +BIT Bit-Test: Z=A&M, N=M7, O=M6 +ROL Rotiere Links Akku oder Speicher A=A*2+C, C=A7 +ROR Rotiere Rechts A=A/2+C*127, C=A0 +ASL Arithmetisches Linksschieben A=A*2 +LSR Logisches Rechtsschieben A=A/2 +INX Erhöhe X-Register um eins +INY Y +INC Erhöhe Akku oder Speicher um eins +DEX Erniedrige X-Register um eins +DEY Y +DEC Erniedrige Akku oder Speicher um eins + +CMP Vergleiche mit Akku (Substraktion ohne Akku zu ver„ndern) +CPX Vergleiche mit X-Register +CPY Vergleiche mit Y-Register + +BNE Verzweige falls nicht Null +BEQ Null +BMI Negativ +BPL Positiv +BVC Overflow Clear +BVS Overflow Set +BCS Carry Set +BCC Carry Clear +BRA Verzweige immer (*) + +JMP Springe an Adresse +JSR Springe in Unterroutine, Rcksprungadresse auf dem Stack +RTS Return from Subroutine + +CLC Carry-Flag löschen +SEC setzen +CLD Dezimal-Flag löschen +SED setzen +CLI Interrupt freigeben +SEI sperren +CLV Overflow-Flag löschen + +PHA Akku auf Stack legen +PHX XR (*) +PHY YR (*) +PHP Status +PLA Akku vom Stack holen +PLX XR (*) +PLY YR (*) +PLP Status + +BRK Löst Interrupt mit gesetztem Break-Flag aus +RTI Return from Interrupt + +NOP No Operation + +TRB Test und Reset Speicher mit Akku (*) +BBR Branch on Bit Reset (*) +BBS Branch on Bit Set (*) +RMB Reset Memory Bit (*) +SMB Set Memory Bit (*) + +Die mit (*) markierten Befehle sind CMOS-Befehle. Außerdem haben einige +der anderen Befehle zusätzliche Addressierungsarten. Die mit (D) markierten +Befehle arbeiten im Dezimal-Mode (Dezimal-Flag gesetzt) anders, nämlich +im BCD-Mode (eine Ziffer von 0-9 in 4 Bit). + +Addressierungsarten: + +-Immediate LDA #$12 +-Absolute STA $1234 +-Zeropage EOR $10 +-Bit,ZP,REL BBR #7,$10,label +-Akku ASL +-Implied TAX +-(Indirect,x) LDA ($10,X) +-(Indirect),y STA ($3e),Y +-Zeropage,x CMP $12,X +-Absolut,x LDY $4356,x +-(Absolut,x) jmp (jumptabelle,x) +-Absolut,y ORA $2345,y +-Relative BNE irgendwohin +-(Indirect) jmp (berVektor) +-Zeropage,y ldx $12,y +-Bit,Zeropage RMB #1,zeropage + +Bei Adressierungsarten, die in der Zeropage und Absolut existieren, wird, +soweit möglich die Zeropage-Adressierung angewendet. Ein vorangestelltes +'!' erzwingt absolute Adressierung, auch bei einem Wert kleiner 256. +Als Wert oder Adresse können arithmetische Ausdrücke mit Hierarchie und +Klammerung verwendet werden. Der Assembler versteht folgende Operanden: + + 123 -Dezimal + $234 -Hexadezimal + &123 -Oktal + %010110 -Binär + * -Programmzähler + "A" -ASCII-Code + labelx -Label + -(lab1+1) -Ausdruck + +Folgende Operatoren können benutzt werden: + + + -Addition 9 + - -Subtraktion 9 + * -Multiplikation 10 + / -Integer-Division 10 + << -Shift nach links 8 + >> -Shift nach rechts 8 + >=,=> -größer oder gleich 7 + <=,=< -kleiner oder gleich 7 + < -kleiner 7 + > -größer 7 + = -gleich 6 + <>,>< -ungleich 6 + && -Logisches UND 2 + || -Logisches ODER 1 + & -Bitweises UND 5 + | -Bitweises ODER 3 + ^ -Bitweises Exklusiv-Oder 4 + +Die Operatoren mit der höheren Priorität werden zuerst bearbeitet. +Ein gültiger Ausdruck ist dann z.B. + + LDA base+number*2,x + +Bei Addressierungsarten, die nicht mit einer Klammer beginnen, darf +auch im ersten Ausdruck keine Klammer am Anfang stehen: + + LDX (1+2)*2,y ; Falsch ! + LDX 2*(1+2),y ; Richtig ! + +Vor einem Ausdruck kann ein unärer Operator stehen: + + < bildet Lo-Byte des Wertes + > bildet Hi-Byte des Wertes + + LDA # xa 2.1.4 6502 Cross Assembler + +
+

XA 2.1.4

+

65(c)02 Cross Assembler

+

(c) Andre Fachat

+

email: fachat@galileo.rhein-neckar.de

+
+
    +
  1. what it is +
  2. parameters and features +
  3. 6502 Assembler +
  4. pseudo-opcodes, block structures and where labels are valid +
  5. pre-processor +
  6. utilities +
    +
  • literature +
+ +
+ +

What it is

+ +This Cross-Assembler makes programms for another computer that has a 6502-like +CPU. This CPU has been widely used in the famous Apple II, all the Commodore +8-Bit Computers (PET, VC20 and a derivate in the C64) and many others. +Some are still used in one-chip microcontrollers, e.g. the Rockwell modem +chipset. +All these chip share a common set of standard machine language commands, +some of them (e.g. the CMOS versions) have additional (software) features. +

+I had the idea for this assembler when I built my small 6502 System that +had place for 32kByte ROM to take the kernel and lots of other programms. +(After all, it became a multitasking micro-kernel with file-systems for +IBM and Commodore, I can even use the IBM drives as Floppy for my C64 with +this computer as controller. Piping and i/o-redirection included, of course) +Development on my old C64 began to suck with programms growing. So I decided +to do a Cross-Assembler on my new Atari ST. +

+First versions were very like the old Assembler on the C64, not really +using the resources (Reading all files two times completely etc). +With files growing the assembler also became more sophisticated. +Now hashcodes are used for mnemonics, preprocessor definition and label +search (Version >= 2.0.5). The files are only read once, putting the +preassembled code into memory (Version >= 2.0), taking it from there on pass 2. +Now it makes about 350kByte Source Code to about 30kByte ROM code in +less then 2 Minutes on an 8 MHz Atari ST with 2.5 MByte RAM and Harddisk. +(Well, the Atari is not fast. On my 486DX4/100 it takes about 2 seconds...) +But adding the whole relocation stuff slowed it down again. +

+

Parameters and features

+ +The assembler contains only a single programm called "xa" (for Atari: XA.TTP). +It takes one or more Source files into one object file, that can directly +be used. +But the assembler also has a mode to produce relocatable files, conforming +to the 'o65' fileformat (See fileformat.txt). +

+Call: +

+xa [options] Source1 [Source2 ...] 
+
+Object: this is the name, the output (object) file gets +Error: Here you will find the Error listing. +Label: this is the label list +

+'-C' 		gives error codes when using CMOS-opcodes. Default is not to 
+		complain.
+'-c'		do not produce o65 executable, but object files that can 
+		contain undefined references.
+'-v' 		go into verbose mode
+'-x' 		old filename behaviour (overrides -o, -e and -l)
+'-R' 		do not produce absolute code, but do relocation and all that.
+'-o filename'	set output filename
+'-e filename'	set errorlog filename
+'-l filename'	set labellist filename
+'-r'		add crossreference list to labellist output 
+		(i.e list of filename/line where label is used)
+'-M'		allow ':' to appear in comments after a semicolon (MASM mode)
+'-b? adr'	set segment start address for ? = t(ext), d(ata), b(ss) or
+		z(ero) segment.
+'-A adr'	If the _file_ starts at adr in a ROM, then the text segment
+		need not be relocated. That of course only works, if the
+		data/bss/zero segments are not occupied by other programs too!
+'-G'		omit writing the exported globals to the file.
+'-B'		Show lines with '.(' or '.)' pseudo opcodes
+'-Llabel'	defines 'label' as absolute, undefined reference
+'-DDEF=TEXT'	define a preprocessor replacement
+'-Ipath'        additional include path for include files. Is evaluated before
+                the XAINPUT environment variable. One path per '-I',
+                multiple '-Ipath' allowed.
+
+ +Omitting the errorfile or labelfile Parameter will cause xa to not +write these files. Using '-x' will cause xa to take the name of the +first source file and change the extension (on an Atari there is only +one, like in DOS) to 'obj', 'err' and 'lab' respectively - if the old +behaviour is selected with the '-x' option or the files are defined with +"-l" and "-e". If no output file is given, "a.o65" is used. +

+

Environment variables:

+You can use the variables XAOUTPUT and XAINPUT to adjust the directory +structure. If source or include files are not found, the Path in XAINPUT +is being searched for the files. The different paths are separated by a +comma (','). XAINPUT gives the directory where the *.obj, *.err and +*.lab files are put. +If they are not set, there will be no search, respectively the files +are saved to the current directory. +

+The label file is a readable ASCII-file and lists all the labels +together with their block-count (see below) and their address. +The error file lists the version of the assembler, date and time of the +assembler run, all the error messages and the stuff being printed +with #echo and #print and last but not least a statistics of used +resources. + +

6502 Assembler

+ +xa supports both the standard 6502 opcodes as well as the CMOS versions +(Rockwell 65c02). Not supported are the 6502 undocumented opcodes, they have +to be put in by hand (with ".byte" directives). +

+For an introduction to 6502 Assembler please see elsewhere. A (very) short +introduction is given in the german version of this text. +

+ +

Some Assembler specific details:

+ +When using addressing modes that could be zeropage or absolute, zeropage +will be taken if possible. This can be prevented by prefixing the address +with a '!'. Then absolute addressing is taken, regardless of the address. +

+Values or Addresses can be expressed by arithmetik expressions with +hierachy and bracket. The following operands are understood: +

+123       -decimal
+$234      -hexadecimal
+&123      -octal
+%010110   -binary
+*         -program counter
+"A"       -ASCII-code
+labelx    -label
+-(lab1+1) -expression
+
+The following operands can be used (third column is priority): +

++         -addition                     9
+-         -subtraction                  9
+*         -multiplication               10
+/         -integer-division             10
+<<        -shift left                   8
+>>        -shift right                  8
+>=,=>     -more or equal                7 
+<=,=<     -less or equal                7
+<         -less                         7
+>         -more                         7 
+=         -equal                        6
+<>,><     -not equal                    6
+&&        -logical AND                  2
+||        -Logical OR                   1
+&         -Bitwise AND                  5
+|         -Bitwise OR                   3
+^         -Bitwise XOR                  4
+
+Operators with higher priority are evaluated first. +Brackets can be used as usual. +

+Valid expressions are, e.g.: +

+LDA       base+number*2,x
+
+For Addressing modes that do not start with a bracket, you can even use +a bracket at the beginning of an expression. Otherwise try this: +

+LDX       (1+2)*2,y                ; Wrong!
+LDX       2*(1+2),y                ; Right!
+
+Before an expression you can use these unitary operators: +

    
+<      Gives the low byte of the expression
+>      Gives the high byte
+
+LDA  #<adresse
+
+Single Assembler statements are being separated by a ':' (You remember +the C64 :-) or a newline. Behind Each statement, separated by a ';' +you can write some comments. The next colon or a newline ends the +comment and starts a new statement. +In MASM compatibility mode ('-M' command line option), then a colon +in a comment is ignored, i.e. the comment lasts till the newline. +

+

Pseudo opcodes, Block structures and where Labels are valid

+ +In addition to the 6502 opcodes you have the following Pseudo opcodes: +

+.byt      value1,value2,value3, ...
+.word     value1,value2, ...
+.asc      "text1","text2", ...
+.dsb      length ,fillbte
+.fopt     value1, value2, ...
+.text	
+.data
+.bss
+.zero
+.align    value
+*=
+.(
+.)
+
+
+'.byt' and '.asc' are identical and save values to the memory (object file) +bytewise. '.word' does the same with words (2 Bytes). +'.dsb' fills a block with a given length with the value of fillbyte. +If fillbyte is not given, zero is taken. +

+'*=' changes the programm counter. The programm counter is not saved +to the file as no rewind is being done. Just the internal counter +is reloaded. +If a value is given when the assembler has been started in relocation mode +('-R command line option), the assembler goes into no-relocation mode, i.e +assembles everything without creating relocation table entries. +For '*=' without a value, the assembler switches back to relocation mode. +The absolute code is 'embedded' in the text segment, so the text segment +program counter is increased by the length of the absolute code. +

+'.(' opens a new 'block'. All labels in a block are local, i.e. +are only visible from inside the block - including sub-blocks. +An error is returned if a label is defined that is already defined +'above'. +With '.)' the block is closed. You can have a stack of up to 16 blocks +in each other (i.e. 16 times '.(' before the first '.)' will work, 17 not). +

+'.text', '.data', '.bss', '.zero' switch between the different segments. +The text segment is where the code goes in. The data segment is where +some initialized data goes in (it's actually like a second text segment). +The data segment might be allocated separated from the text segment. +The contents of the bss and the zero segment are not saved, just the +labels are evaluated. Here goes the uninitialized data stuff. +The zero segment allows allocation of zeropage space, bss is normal address +space. +These opcodes can be used in relative and absolute mode. +

+'.align' aligns the current segment to a byte boundary given by the +value. Allowed values are 2, 4, and 256. When using relative mode, the +align value is written to the file header, such that relocation keeps +the alignment. +

+'.fopt' works like ".byte", but saves the bytes as a fileoption (see +fileformat.txt). The length is computed automatically, so the first +byte in the ".fopt" list of values should be the type. +For example, the following line sets the filename for the object file. +

+.fopt 0, "filename", 0
+
+A label is defined by not being an opcode: +

+label1 LDA #0              ; assignes the programm counter
+label2 =1234               ; explicit definition
+label3 label4 label5       ; implicit programm counter
+label6 label7 = 3	   ; label6 becomes the program counter, while
+			   ; label7 is set to 3
+label8:   sta label2	   ; As ':' divides opcodes, this is also
+			   ; working
+
+You can use more than one label for definition, except for explicit +definition. +Labels are case sensitive. +If a label is proceeded by a '+', this label is defined global. +If a label is proceeded by a '&', this label is defined one level 'up' +in the block hierachy, and you can use more than one '&'. +

+Redefinition of a label is possible by proceeding it with a dash '-'. +

+-sysmem   +=4  ; here you can use ==, +=, -=, *=, /=, &=, |=
+-syszp    =123
+
+ +

Preprocessor

+ +The preprocessor is very close to the one of the language C. +So in addition to the ';'-comments you can also use C-like +comments in '/*' and '*/'. Comments can be nested. +

+#include  "filename"     includes a file on exactly this position.
+			 if the file is not found, it is searched using 
+			 XAINPUT. 
+
+#echo  comment           gives a comment to the error file.
+
+#print expression        prints an expression to the error file (after
+			 preprocessing and calculating)
+
+#printdef DEFINED        prints the definition of a preprocessor define to
+			 the error file.
+
+#define DEF  text	 defines 'DEF' by 'text'
+
+#ifdef DEF               The source code from here to the following #endif
+			 or #else is only assembled if 'DEF' is defined
+			 with #define.
+
+#else                    just else... (optionally)
+
+#endif                   ends an #if-construct. This is a must to end #IF* 
+
+#ifndef  DEF             .... if DEF is not defined
+
+#if expression           .... if expression is not zero
+
+#iflused label           .... if a label has already been used
+
+#ifldef label            .... if a label is already defined
+
+#iflused and #ifldef work an labels, not on preprocessor defs! With these +commands a kind of library is easily built: +

+#iflused  label
+#ifldef   label
+#echo     label already defined, not from library
+#else
+label     lda #0
+          ....
+#endif
+#endif
+
+You can have up to 15 #if* on stack before the first #endif +

+You can also use #define with functions, like in C. +

+#define mult(a,b)   ((a)*(b))
+
+The preprocessor also allows continuation lines. I.e. lines that end +with a '\' directly before the newline have the following line +concatenated to it. +

+

Utilities

+ +There now are a few utilities that come with the assembler: +

+file65	: prints some information about an o65 file. Can compute the 
+          "-A" parameter for xa, to built the following file in a ROM.
+reloc65 : relocates o65 files.
+mkrom.sh: example shell (bash) script to show how to use the file65 utility
+	  to build a ROM image with several in the ROM runnable programs.
+ld65	: a linker for o65 files. The given files are linked together and
+	  one o65 executable file is produced. All header options of all files
+	  are put in the new file. There must not be any undefined reference
+	  left, otherwise the output file is corrupt, because
+	  for now, ld65 cannot produce object files. But you get a warning.
+
+

+

Literature

+
    +
  • "Das Maschinensprachebuch zum Commodore 64"
    + Lothar Englisch, Data Becker GmbH +
  • "Controller Products Data Book"
    + Rockwell International, Semiconductor Products Division +
  • "Programmieren in C"
    + Kernighan, Ritchie, Hanser Verlag +
+ + diff --git a/xa-2.3.5/doc/xa.log b/xa-2.3.5/doc/xa.log new file mode 100644 index 0000000..8685448 --- /dev/null +++ b/xa-2.3.5/doc/xa.log @@ -0,0 +1,254 @@ +xa-2.1.0 + + * Rewrite of command line option handling to better look like + usual (cc) options. + * Removed ^M from all files. + * Removed all external declarations to header files, + and made all static functions static. + | Now compiles almost without warning with 'gcc -W -Wall'. + + -- André Fachat 31 Oct, 1996 + +xa-2.1.0a + + * Introduced concept of code relocation. Now each label being set to + the program counter is a 'pointer', that gets an entry in a + relocation table. Simple arithmetic operations are allowed. The + relocation table is still just printed unsortedly. + + -- André Fachat 31 Oct, 1996 + +xa-2.1.0b + + * Produces some preliminary kind of relocatable file, including header + etc. Problems -- relocation table does as if file is assembled for + address 0. Need + a) a better way to set program counter. + b) pseudo opcodes for distinguishing segments. + c) a way to temporarily disable relocation. + d) a way to include extra headers and options into the file. + + -- André Fachat 31 Oct, 1996 + + * Assembler now produces a relocatable file format, as described in + the file ``fileformat.txt''. Temporarily disabling relocation is with + the ``*=value'' directive, while switching back to relocation mode + goes with ``*='' (without value). New pseudo opcodes ``.text'', + ``.data'', ``.bss'', ``.zero'' switch between the segments. + + -- André Fachat 02 Nov, 1996 + +xa-2.1.0e + + * There was a bug in the arithmetic routine that had set all pointer + to the text segment, if something was added. + * There also was a bug in the loader when actually using options. + * A new pseudo opcode was added -- ``.fopt''. + | Works like ``.byte'', but puts these bytes in a file option. + | The length in the file option is automagically set. ``.fopt'' + | may appear anywhere in the file, but it should be at the + | beginning | (might be mandatory in a later version). + + -- André Fachat 06 Nov, 1996 + +xa-2.1.0f + + * Added a command line switch ``-M'' to ignore colons in a comment + after a semicolon. + * Without it, a colon separates complete mnemonics, including + the semicolon comment. + | Well, actually this switch is a ``MASM'' compatibility switch, and + will surely be expanded someday, when I get more info on MASM. + * Now ``*'' and ``='' can be separated for the definition + of the program counter and ``.byte'' is also accepted. + This makes it more MASM compatible. ".end" is ignored. + Still missing is ``.include''. + + -- André Fachat 11 Nov, 1996 + +xa-2.1.0g + + * Started working on ``official'' o65 fileformat. + If there are no undefined labels, and no relocated code + is embedded in absolute code, the thing should work. + + -- André Fachat 21 Dec, 1996 + +xa-2.1.1 + + * ``.dsb'' now has an _optional_ parameter ``fillbyte''. + * Undefined references are now put into the relocation table + (i.e. handled correctly) if the ``-c'' option is given. + * The file format conforms to o65 version 1 file format. + * Embedding absolute in relocatable code and vice versa is buggy... + + -- André Fachat 21 Dec, 1996 + +xa-2.1.1a + + * Embedding absolute code in relocatable seems to work now. + + -- André Fachat 21 Dec, 1996 + +xa-2.1.1e + + * The option to embed relocatable code in absolute code has been + dropped. Therefore the command line options + ``-A'' (make it romable), ``-b?'' (set segment start addresses), + and ``-G'' (omit exported globals from file) have been added. + * Internally, the whole thing has been made dynamic; except for the + preprocessor (and the storage between pass1 and pass2), everything + uses dynamically allocated tables. m_alloc, which had been + introduced long time ago because of the buggy malloc + on the Atari ST is gone now! + + -- André Fachat 22 Dec, 1996 + +xa-2.1.1f + + * Added the ``-a'' and ``-A'' options to file65, so that it can now + print the start addresses for following files in the ROM when making + romable code. + * Added shell (bash) script ``mkrom.sh'' that assembles a given list + of files and builds a ROMable file. The first two bytes are single + linked list pointers, and then comes the file. + + -- André Fachat 02 Jan, 1997 + +xa-2.1.1g + + * Added the file ``reloc65'', to relocate o65 files without + reassembling them. + * Fixed quite some bugs in xa (segment numbering in the globals list + and switched low/high byte relocation entry type in relocation + table. Now conforms to documentation, i.e. fileformat.txt) + + -- André Fachat 03 Jan, 1997 + +xa-2.1.2 + + * Added ``ld65'', a simple linker for o65 files. + * Another bug in xa fixed now. + + -- André Fachat 04 Jan, 1997 + +xa-2.1.3 + + * Allows to use ``.data'' etc in absolute mode, too. No relocation + entries are generated then. Segment start can be set with ``-b?'' + command line options, though. Also the data segment is discarded + with this method! This allows to use the normal ``.data'' etc + syntax even when assembling a ROM (which is done in absolute mode.) + * Fixed a bug where ``.dsb'' in a data segment didn't fill with the + right value + + -- André Fachat 25 Mar, 1997 + +xa-2.1.3e + + * Added preprocessor continuation lines, and .block and .bend + pseudo-opcodes (They map to ``.('' and ``.)'' respectively.) + + -- André Fachat 27 Jul, 1997 + +xa-2.1.4 + + * Do not leave output file around after an error -- this is + better for ``make''. + * Also it seems to have settled for a while, so I can release + a new version. + + -- André Fachat 11 Sep, 1997 + +xa-2.1.4c + + * Fixed a nasty bug that prevented correct relocation table + entries when a ``label2=label1'' contruct was used and + ``label2'' was accessed. + * Added ``-I'' option. + + -- André Fachat 30 Dec, 1997 + +xa-2.1.4d + + * fixed align code. Now inserts NOP opcodes into text segment, and + sets file mode appropriately. + + -- André Fachat 26 Jan, 1998 + +xa-2.1.4e + + * Changed o65 fileformat and adopted it in xa. + + -- André Fachat 26 Jan, 1998 + +xa-2.1.4g + + * Fix handling of !zeropageaddress, that was broken (did not result + in absolute address, but gave error message.) + * Add cross reference list to labels if switched on on command line. + * Fix the filename when multiple files are given on the command line + (used to give the first filename for all errors in second pass.) + + -- André Fachat 25 Nov, 1998 + +xa-2.1.4h + + * In file65 added support for undefined labels and globals, + also for (some known) file options. + * Fix a preprocessor bug. + + -- André Fachat 12 Dec, 1998 + +xa-2.2.0-p1-1 + + * Update COPYING to the latest version (Y2K-fixed + new address to GNU) + * Lots of fixes to the Makefiles + * Cleaned up the structure of the TODO file + * Added manual-pages for file65, ld65, printcbm, reloc65, uncpk, and xa + * Commented out LIB-flags -lm, -lcurses and -ltermcap, + since they are all unused + * Added `--help' and `--version' to all binaries + * Removed `-h', `-?' and `-v' options where applicable + * Created a file containing the version-function; version.h + * Moved common macros to a separate file; xad.h + * Restructuring of printcbm to become more readable + * Added ifndef/define/endif traps to all header-files + * Fixed a few typos + * Renamed romadr to romaddr + * Renamed all functions matching *such* to *search* + * Fixed all warnings + * Cleaned up all header-files + * Reformatted xa.log + + -- David Weinehall 20 Aug, 2002 + +xa-2.3.0 + + * Version number jump for all the unofficial xa's out there + * Fixed addressing bugs for @, ! and completed 65816 merge + Thanks to David for the report + * Moderate legibility overhaul to xat.c (will continue on this) + * More compiler warnings corrected + Thanks to David for the report + * man files completed + * Documentation updated + * Last line bug corrected (where last line not assembled if no newline) + Thanks to silverdr for the report + * ld65 is now ldo65 to avoid conflicts with cc65 package + * Post-defined labels work better, or at least somewhat (no longer attempts + to optimize in pass 2 and generate bad code). Can be forced with ` + Thanks to silverdr for the report + * Makefile bugs multiplied + * @ now mostly obligatory for 24-bit addressing + + -- Cameron Kaiser 2 Apr, 2006 + +xa-?? + + * Introduced switch to convert values in quotes to different character + sets. Currently supported are ASCII (default) and PETSCII + * Fixed some quote bugs + + -- André Fachat 23 Dec, 2006 + diff --git a/xa-2.3.5/doc/xa.txt b/xa-2.3.5/doc/xa.txt new file mode 100644 index 0000000..39cecaf --- /dev/null +++ b/xa-2.3.5/doc/xa.txt @@ -0,0 +1,358 @@ + + _________________________________________________________________ + + XA 2.1.4 + + 65(c)02 Cross Assembler + + (c) Andre Fachat + + email: fachat@galileo.rhein-neckar.de + _________________________________________________________________ + + 1. what it is + 2. parameters and features + 3. 6502 Assembler + 4. pseudo-opcodes, block structures and where labels are valid + 5. pre-processor + 6. utilities + + * literature + _________________________________________________________________ + + What it is + + This Cross-Assembler makes programms for another computer that has a + 6502-like CPU. This CPU has been widely used in the famous Apple II, + all the Commodore 8-Bit Computers (PET, VC20 and a derivate in the + C64) and many others. Some are still used in one-chip + microcontrollers, e.g. the Rockwell modem chipset. All these chip + share a common set of standard machine language commands, some of them + (e.g. the CMOS versions) have additional (software) features. + + I had the idea for this assembler when I built my small 6502 System + that had place for 32kByte ROM to take the kernel and lots of other + programms. (After all, it became a multitasking micro-kernel with + file-systems for IBM and Commodore, I can even use the IBM drives as + Floppy for my C64 with this computer as controller. Piping and + i/o-redirection included, of course) Development on my old C64 began + to suck with programms growing. So I decided to do a Cross-Assembler + on my new Atari ST. + + First versions were very like the old Assembler on the C64, not really + using the resources (Reading all files two times completely etc). With + files growing the assembler also became more sophisticated. Now + hashcodes are used for mnemonics, preprocessor definition and label + search (Version >= 2.0.5). The files are only read once, putting the + preassembled code into memory (Version >= 2.0), taking it from there + on pass 2. Now it makes about 350kByte Source Code to about 30kByte + ROM code in less then 2 Minutes on an 8 MHz Atari ST with 2.5 MByte + RAM and Harddisk. (Well, the Atari is not fast. On my 486DX4/100 it + takes about 2 seconds...) But adding the whole relocation stuff slowed + it down again. + + Parameters and features + + The assembler contains only a single programm called "xa" (for Atari: + XA.TTP). It takes one or more Source files into one object file, that + can directly be used. But the assembler also has a mode to produce + relocatable files, conforming to the 'o65' fileformat (See + fileformat.txt). + + Call: + +xa [options] Source1 [Source2 ...] + + Object: this is the name, the output (object) file gets Error: Here + you will find the Error listing. Label: this is the label list + +'-C' gives error codes when using CMOS-opcodes. Default is not to + complain. +'-c' do not produce o65 executable, but object files that can + contain undefined references. +'-v' go into verbose mode +'-x' old filename behaviour (overrides -o, -e and -l) +'-R' do not produce absolute code, but do relocation and all that. +'-o filename' set output filename +'-e filename' set errorlog filename +'-l filename' set labellist filename +'-r' add crossreference list to labellist output + (i.e list of filename/line where label is used) +'-M' allow ':' to appear in comments after a semicolon (MASM mode) +'-b? adr' set segment start address for ? = t(ext), d(ata), b(ss) or + z(ero) segment. +'-A adr' If the _file_ starts at adr in a ROM, then the text segment + need not be relocated. That of course only works, if the + data/bss/zero segments are not occupied by other programs too! +'-G' omit writing the exported globals to the file. +'-B' Show lines with '.(' or '.)' pseudo opcodes +'-Llabel' defines 'label' as absolute, undefined reference +'-DDEF=TEXT' define a preprocessor replacement +'-Ipath' additional include path for include files. Is evaluated before + the XAINPUT environment variable. One path per '-I', + multiple '-Ipath' allowed. + + Omitting the errorfile or labelfile Parameter will cause xa to not + write these files. Using '-x' will cause xa to take the name of the + first source file and change the extension (on an Atari there is only + one, like in DOS) to 'obj', 'err' and 'lab' respectively - if the old + behaviour is selected with the '-x' option or the files are defined + with "-l" and "-e". If no output file is given, "a.o65" is used. + + Environment variables: + + You can use the variables XAOUTPUT and XAINPUT to adjust the directory + structure. If source or include files are not found, the Path in + XAINPUT is being searched for the files. The different paths are + separated by a comma (','). XAINPUT gives the directory where the + *.obj, *.err and *.lab files are put. If they are not set, there will + be no search, respectively the files are saved to the current + directory. + + The label file is a readable ASCII-file and lists all the labels + together with their block-count (see below) and their address. The + error file lists the version of the assembler, date and time of the + assembler run, all the error messages and the stuff being printed with + #echo and #print and last but not least a statistics of used + resources. + + 6502 Assembler + + xa supports both the standard 6502 opcodes as well as the CMOS + versions (Rockwell 65c02). Not supported are the 6502 undocumented + opcodes, they have to be put in by hand (with ".byte" directives). + + For an introduction to 6502 Assembler please see elsewhere. A (very) + short introduction is given in the german version of this text. + + Some Assembler specific details: + + When using addressing modes that could be zeropage or absolute, + zeropage will be taken if possible. This can be prevented by prefixing + the address with a '!'. Then absolute addressing is taken, regardless + of the address. + + Values or Addresses can be expressed by arithmetik expressions with + hierachy and bracket. The following operands are understood: + +123 -decimal +$234 -hexadecimal +&123 -octal +%010110 -binary +* -program counter +"A" -ASCII-code +labelx -label +-(lab1+1) -expression + + The following operands can be used (third column is priority): + ++ -addition 9 +- -subtraction 9 +* -multiplication 10 +/ -integer-division 10 +<< -shift left 8 +>> -shift right 8 +>=,=> -more or equal 7 +<=,=< -less or equal 7 +< -less 7 +> -more 7 += -equal 6 +<>,>< -not equal 6 +&& -logical AND 2 +|| -Logical OR 1 +& -Bitwise AND 5 +| -Bitwise OR 3 +^ -Bitwise XOR 4 + + Operators with higher priority are evaluated first. Brackets can be + used as usual. + + Valid expressions are, e.g.: + +LDA base+number*2,x + + For Addressing modes that do not start with a bracket, you can even + use a bracket at the beginning of an expression. Otherwise try this: + +LDX (1+2)*2,y ; Wrong! +LDX 2*(1+2),y ; Right! + + Before an expression you can use these unitary operators: + + +< Gives the low byte of the expression +> Gives the high byte + +LDA #-jb4bn}vZ7bWjE|LGY{l zuvG0DecQmTA1X04)mz(|f*>sv3L7&|I(?+|<6z3@Dr4DWa~+b@=eaj&UB?6;;pNIPNNBqZ*^)Y3;4+3xXNBmv= z@Ma`@4E&LkRmPV{sI3d#dM==Vpu1O3}= z4{oXpwoovBbqk><66op=M+O*f2aBXD=nr)Vidf(e9WI6-2m=@jbOpoc5_;u;zb_yV zFdeC8N@A^^SepmzPOU_*gU?c={C8q?iQ=`VYgQf&M@r)Dra5?1{B_I98mT&!NNA{_)&syJt105n~6s zkh6ArA@0EXp)>7>UX6iNu!@_}JpAB*GeXL#R>;E+x>(Q?)FaG3?FmM1cgDPuMyib( zCN@fSdGYt@MhRTP&t6G?EFW;7M#m*eHmDb9DYBt)?r*xqGt<09)vE|wRqc@j;2mfS z`Ccd8SlPZULfK}IUYc$3C`hJqE-#J}i{VOZorYCw(=F#K7rp8q6P0l?*X|i3mbJ7- z^gn+ZPLQ|-7ED@-9U3ozc@q}RlofUe)rd2Y zeitvtykg|}*$q}(moT1Z5k7#q@{}8{I9XbEz=uwGq#VYayzul1$7e6r4~phkb=aX$^8Ex|@Q%%y6Pcm$C%~G~Ws#d1=NQR|D!4~pK zu_h^Dg&QUkRpA#im{binl`)9!#99F$e(%|`Uh7uZ8tuf*Kqdm7DJG;7L(Eu}p%oKJO@!ZU*gn3{sR z@Cg<)|5QQit|r)wdbaq&^s>l0yNovhXIbx#HMo_jdiQ9fJ3UhGPA@gM^|BxqaZpK= zf@V%NxR>hPM#EgA8`&bcs;fbI5_a89s`fYV4-2&G?%+2t<4DUb*b_01h}@Owj3~dy zTn2H)>o^GCoAfMvQOIc03?d>VO+1o23)m0gTXRs`t6Nyd$>%B%uFg#_Gqck!`1Mh8 z9ubmrMTB@M9_=I^*l|_Qpl~Cn8xwgpL|_YeUX%1v`BViev>N>$ws5g!`CLevs2|U1 zHO9`18|g*2dXF}vz+nqJU_6ZGQANqcjY;&ua6o^2`!%DCd*QES9)>D75&bViL}5bJ zUz#k;nNt;Tlt&ITl1`h&nS7`%w|OFG0mx#QCln{D%oWbyREoh78Ppc_cSO;bVV$q z7=t6d*r-0D1Z71iTu#sv>OI~Mp}@&!E>U*7b_#?Z*^YweYzrm-Je2L%YY_zl#WDO@ zMvN_s39_&v1{d<;*ortfmKWDa:jsr Txtout:.) +#define Aout(a) .(:lda #b:jsr Txtout:jmp c:b .byt a,0:c .) +#define Ibout(a) .(:ldx a:lda #0:jsr INTOUT:.) +#define Iout(a) .(:ldx a:lda a+1:jsr INTOUT:.) + +#define PFADLEN 40 +#define FN_WR 3 +#define FN_RD 4 +#define XCODE $f7 +#define Version 1 + + .( + .word $0801 + *=$0801 + + .word basicend,10 + .byt $9e,"2064",0 ;sys $0810 +basicend .word 0 + .byt 0,0,0 + + .( + jsr CLRCH + jsr iniscreen + jsr inipar +menu1 + Tout(m1atxt) + Tout(quellpfad) + Tout(m1btxt) + Ibout(quelldrv) + Tout(m1ctxt) + Tout(zielpfad) + Tout(m1dtxt) + Ibout(zieldrv) + Tout(m1etxt) + +next jsr GET + beq next + + ldx #0 +l1 cmp befkeys,x + beq exe + inx + cpx #Anzbefs + bcc l1 + bcs next + +exe jsr exec + jmp menu1 + +exec txa + asl + tax + lda madr+1,x + pha + lda madr,x + pha + rts + +madr .word pack-1,unpack-1,quelle-1,ziel-1,switch-1,dir-1,qdrv-1,zdrv-1 +befkeys .asc TC_F1,TC_F2,TC_F3,TC_F5,TC_F8,TC_F7,TC_F4,TC_F6 +Anzbefs =8 + +m1atxt .asc TC_LCH,TC_SCO,TC_FF,TC_LF,TC_LF + .asc "(F1) PACK PROGRAMMS",TC_CR,TC_LF + .asc "(F2) EXTRACT FROM ARCHIVE",TC_CR,TC_LF + .asc "(F3) SOURCEPATH/ARC:",0 +m1btxt .asc TC_CR,TC_LF + .asc "(F4) SOURCEDEVICE:",0 +m1ctxt .asc TC_CR,TC_LF + .asc "(F5) TARGETPATH/ARC:",0 +m1dtxt .asc TC_CR,TC_LF + .asc "(F6) TARGETDEVICE :",0 +m1etxt .asc TC_CR,TC_LF + .asc "(F7) SOURCEDIRECTORY",TC_CR,TC_LF + .asc "(F8) EXCHANGE TARGET AND SOURCE",TC_CR,TC_LF + .asc "YOUR CHOICE PLEASE",TC_CR,0 + .) + +unpack .( + jsr openarcrd + bcs cls + + lda #0 + sta rcnt + sta rcnt+1 + + jsr rbyte + cmp #Version + bne verr + +loop jsr unpackfile + bcc loop + + Tout(t1) + lda rcnt+1 + ldx rcnt + jsr INTOUT + lda #TC_CR + jsr BSOUT + jsr waitkey + +cls lda #FN_RD + jsr CLOSE + rts + +verr Tout(verrtxt) + jmp cls +verrtxt .asc "UNKNOWN ARCHIVE VERSION",0 + +t1 .asc "ARCHIVE HAD BYTES #",0 + .) + +unpackfile + .( + lda #0 + sta wcnt + sta wcnt+1 + lda #TC_CR + jsr BSOUT + ldy #0 + sty P1 +l1 jsr rbyte + bcs endx + ldy P1 + sta filetab,y + inc P1 + cmp #0 + beq endnam + jsr BSOUT + jmp l1 + +endnam Tout(ask) + jsr waitkey + cmp #"J" + bne nounpack + Tout(tok) + lda #<-1 + sta P1+1 + jsr fxopen + jsr Getzst + bcs xa + bcc lo + +endx jmp end +nounpack Tout(tno) +xa lda #0 + sta P1+1 + +lo jsr rbyte + bcs cls + cmp #XCODE + bne xb + jsr rbyte + sta wxanz + cmp #0 + clc + beq cls + jsr rbyte + sta wxbyt + bit P1+1 + bpl lo +ly lda wxbyt + jsr wbyte + dec wxanz + bne ly + jmp lo + +xb bit P1+1 + bpl lo + jsr wbyte + jmp lo + +cls php + jsr wbuf + lda #FN_WR + jsr CLOSE + + Tout(t1) + lda wcnt+1 + ldx wcnt + jsr INTOUT + lda #TC_CR + jsr BSOUT + + plp +end rts + +ask .asc TC_CR,"EXTRACT FILE (Y/N)?",0 + +t1 .asc "GIVES BYTES #",0 + .) + +incwcnt .( + inc wcnt + bne l1 + inc wcnt+1 +l1 rts + .) + +fxopen .( + ldy #0 + sty INT +l1 lda zielpfad,y + sta INBUF,y + beq l2 + iny + cmp #":" + beq l1a + cmp #"/" + bne l1b +l1a sty INT +l1b cpy #PFADLEN + bcc l1 + +l2 ldx INT + ldy #0 +l3 lda filetab,y + sta INBUF,x + inx + iny + cmp #0 + bne l3 + dex + txa + ldx #INBUF + jsr SETFNPAR + lda #FN_WR + ldx zieldrv + ldy #1 + jsr SETFPAR + + jsr OPEN + bcs err + jsr clrwrbuf + clc +err rts + .) + +openarcrd .( + ldy #0 +l0 lda quellpfad,y + beq l1 + iny + cpy #PFADLEN + bcc l0 +l1 cpy #0 + beq err + tya + ldx #quellpfad + jsr SETFNPAR + lda #FN_RD + ldx quelldrv + ldy #0 + jsr SETFPAR + jsr OPEN + bcs err + jsr Getqst + bcs err + jsr clrrdbuf + clc + rts +err sec + rts + .) + +pack .( + jsr getlist + lda anzfiles + beq end + jsr openarcwr + bcs cls + + lda #Version + jsr wbyte + + lda #0 + sta P1 +l1 jsr setfadr + sta P2 + stx P2+1 + jsr packfile + inc P1 + lda P1 + cmp anzfiles + bcc l1 + + jsr wbuf + +cls lda #FN_WR + jsr CLOSE + + jsr Getzst + +end jmp LINEIN:rts + .) + +packfile .( + Tout(lft) + + ldy #0 +l1 sty P1+1 + lda (P2),y + jsr BSOUT + jsr wbyte + ldy P1+1 + iny + cmp #0 + bne l1 + + jsr fopen + bcs le + + jsr clrwxbyt +l2 jsr rbyte + bcs l3 + jsr wxbyte + jmp l2 +l3 jsr savwxbyt + +le lda #FN_RD + jsr CLOSE + + lda #XCODE + jsr wbyte + lda #0 + jsr wbyte + + lda #TC_CR + jsr BSOUT + rts + +lft .asc TC_CR,"cOPYING ",0 + + .) + +fopen .( + ldy #0 + sty INT +l1 lda quellpfad,y + sta INBUF,y + beq l2 + iny + cmp #":" + beq l1a + cmp #"/" + bne l1b +l1a sty INT +l1b cpy #PFADLEN + bcc l1 + +l2 ldx INT + ldy #0 +l3 lda (P2),y + sta INBUF,x + inx + iny + cmp #0 + bne l3 + dex + txa + ldx #INBUF + jsr SETFNPAR + lda #FN_RD + ldx quelldrv + ldy #0 + jsr SETFPAR + + jsr OPEN + bcs err + jsr clrrdbuf + clc +err rts + .) + +incrcnt .( + inc rcnt + bne l1 + inc rcnt+1 +l1 rts + .) + +rbyte .( + ldy ro + cpy ri + beq leerbuf + lda rb,y + inc ro + clc + rts + +leerbuf lda rf + beq ldbuf + sec + rts + +ldbuf lda #0 + sta ri + sta ro + ldx #FN_RD + jsr CHKIN + lda #0 + sta STATUS + +lok jsr BASIN + + pha + lda STATUS + beq l0 + lda #"L" + jsr BSOUT + Ibout($90) + lda #TC_CR + jsr BSOUT +l0 pla + + jsr incrcnt + ldy ri + sta rb,y + iny + sty ri + iny + ;cpy ro + beq le + lda STATUS + beq lok + +le lda STATUS + sta rf + jsr CLRCH + jmp rbyte + .) + +clrrdbuf .( + lda #0 + sta ri + sta ro + sta rf + rts + .) + +wxbyte .( + ldx wxanz + beq add + inx + bne ad2 + pha + jsr savwxbyt + pla + jmp add + +ad2 cmp wxbyt + beq adx + pha + jsr savwxbyt + pla +add sta wxbyt +adx inc wxanz + + rts + .) + +clrwxbyt .( + lda #0 + sta wxanz + rts + .) + +savwxbyt .( + lda wxanz + beq nosav + cmp #4 + bcs savs + lda wxbyt + cmp #XCODE + beq savs + +l1 lda wxbyt + jsr wbyte + dec wxanz + bne l1 + rts + +savs lda #XCODE + jsr wbyte + lda wxanz + jsr wbyte + lda wxbyt + jsr wbyte + lda #0 + sta wxanz +nosav rts + .) + +openarcwr .( + ldy #0 +l0 lda zielpfad,y + beq l1 + iny + cpy #PFADLEN + bcc l0 +l1 cpy #0 + beq err + tya + ldx #zielpfad + jsr SETFNPAR + lda #FN_WR + ldx zieldrv + ldy #1 + jsr SETFPAR + jsr OPEN + bcs err + lda zieldrv + jsr Getzst + bcs err + jsr clrwrbuf + clc + rts +err sec + rts + .) + +clrwrbuf .( + lda #0 + sta wi + sta wo + rts + .) + +wbyte .( + ldy wi + sta wb,y + iny + sty wi + iny + cpy wo + bne nowr + pha + jsr wbuf + pla +nowr rts + .) + +wbuf .( + ldx #FN_WR + jsr CKOUT + ldy wo +l1 cpy wi + beq end + lda wb,y + jsr BSOUT + + lda STATUS + beq l0 + tya + pha + lda #"W" + jsr $e716 + lda $90 + ora #$40 + jsr $e716 + lda #TC_CR + jsr $e716 + pla + tay +l0 + jsr incwcnt + iny + jmp l1 +end lda #0 + sta wi + sta wo + jsr CLRCH + rts + .) + + .( +&Getqst lda quelldrv + jmp Getst +&Getzst lda zieldrv +&Getst + pha + jsr CLRCH + lda #TC_CR + jsr BSOUT + pla + jsr TALK + lda #15+$60 + jsr SECTALK + lda #0 + sta STATUS + jsr IECIN + pha + jsr BSOUT + +l1 jsr IECIN + cmp #0 + beq l2 + jsr BSOUT + lda STATUS + beq l1 +l2 jsr UNTALK + pla + cmp #"0" + bne err + clc + rts +err sec + rts + .) + +/* +showlist .( + lda #0 + sta P1 + +l1 lda P1 + cmp anzfiles + bcs le + + jsr setfadr + lda #TC_CR + jsr BSOUT + lda INT + ldy INT+1 + jsr Txtout + inc P1 + jmp l1 +le rts + .) +*/ + .( +l4x jmp l4 + +&getlist + lda #0 + sta anzfiles + + lda #TC_FF + jsr BSOUT + + jsr setdirnam + jsr SENDNAM + lda DEVADR + jsr TALK + lda SECADR + jsr SECTALK + + lda #0 + sta STATUS + ldy #3 +l0 sty P1 +l1 jsr IECIN + sta P1+1 + ldy STATUS + bne l4x + jsr IECIN + dec P1 + bne l1 + ldx P1+1 + jsr INTOUT + lda #" " + jsr BSOUT + +la jsr IECIN + cmp #0 + beq l4x + cmp #TC_REV + bne l3x + jmp l3 +l3x jsr BSOUT + cmp #34 + bne la + + lda anzfiles + jsr setfadr + sta P2 + stx P2+1 + + ldy #0 +lb sty P1 + jsr IECIN + jsr BSOUT + ldy P1 + cmp #34 + beq lc + sta (P2),y + iny + cpy #17 + bcc lb +lc lda #"," + sta (P2),y + iny + +ld sty P1 + jsr IECIN + jsr BSOUT + ldy P1 + cmp #" " + beq ld + sta P1+1 + sta (P2),y + iny + lda #0 + sta (P2),y +/* + lda #TC_CR + jsr BSOUT + lda P2+1 + ldx P2 + jsr INTOUT + lda #":" + jsr BSOUT + lda P2 + ldy P2+1 + jsr Txtout +*/ +lf tax + jsr IECIN + jsr BSOUT + cmp #" " + bne lf + cpx #"<" + beq lg + lda #" " + jsr BSOUT +lg lda #" " + jsr BSOUT + + lda P1+1 + jsr testkeys + +lh jsr IECIN + cmp #0 + bne lh + beq l2 + +l3 jsr IECIN + ldx STATUS + bne l4 + tax + beq l2 + jsr BSOUT + jmp l3 + +l2 lda #TC_CR + jsr BSOUT + jsr GET + beq l5 + jsr waitkey +l5 ldy #2 + beq l4 + jmp l0 +l4 jsr CLSFIL + jmp waitkey + .) + +testkeys .( + cmp #"P" + beq ok + cmp #"S" + beq ok + rts +ok Tout(t1) + jsr waitkey + cmp #"J" + beq ja + Tout(tno) + rts +ja Tout(tok) + inc anzfiles + rts + +t1 .asc TC_REV,"YES/NO ",TC_CRL,TC_CRL,TC_CRL + .asc TC_CRL,TC_CRL,TC_CRL,TC_CRL,0 +&tno .asc TC_REO,"NO ",0 +&tok .asc TC_REO,"YES ",0 + .) + +setfadr .( + ldx #0 + stx INT+1 + asl + rol INT+1 + asl + rol INT+1 + sta INT + ldx INT+1 + asl + rol INT+1 + asl + rol INT+1 + clc + adc INT + sta INT + txa + adc INT+1 + sta INT+1 + lda #filetab + adc INT+1 + sta INT+1 + tax + pla + rts + .) + +dir .( + lda #TC_FF + jsr BSOUT + + jsr setdirnam + jsr SENDNAM + lda DEVADR + jsr TALK + lda SECADR + jsr SECTALK + + lda #0 + sta STATUS + ldy #3 +l0 sty P1 +l1 jsr IECIN + sta P1+1 + ldy STATUS + bne l4 + jsr IECIN + dec P1 + bne l1 + ldx P1+1 + jsr INTOUT + lda #" " + jsr BSOUT +l3 jsr IECIN + ldx STATUS + bne l4 + tax + beq l2 + jsr BSOUT + jmp l3 +l2 lda #TC_CR + jsr BSOUT + jsr GET + beq l5 + jsr waitkey +l5 ldy #2 + bne l0 +l4 jsr CLSFIL + jmp waitkey + .) + +waitkey jsr GET + beq waitkey + rts + +setdirnam .( +p1 =INT + + lda #"$" + sta INBUF + ldx #1 + stx p1 + + ldy #0 +l1 lda quellpfad,y + beq nodp + cmp #":" + beq dp + iny + cpy #PFADLEN + bcc l1 +nodp lda #":" + sta INBUF,x + inx +dp ldy #0 +dp1 lda quellpfad,y + sta INBUF,x + beq end + cmp #":" + beq l2a + cmp #"/" + bne l2 +l2a stx p1 +l2 inx + iny + cpy #PFADLEN + bcc dp1 +end ldx p1 + inx + lda #"*" + sta INBUF,x + inx + lda #"." + sta INBUF,x + inx + lda #"*" + sta INBUF,x + inx + txa + ldx #INBUF + jsr SETFNPAR + lda #1 + ldx quelldrv + ldy #0 + jmp SETFPAR + .) + +qdrv .( + inc quelldrv + lda quelldrv + cmp #12 + bcc ok + lda #8 + sta quelldrv +ok rts + .) + +zdrv .( + inc zieldrv + lda zieldrv + cmp #12 + bcc ok + lda #8 + sta zieldrv +ok rts + .) + +quelle .( + Tout(quelltxt) + jsr LINEIN + ldy #0 +q1 lda INBUF,y + sta quellpfad,y + beq end + iny + cpy #PFADLEN-1 + bcc q1 + lda #0 + sta quellpfad,y +end rts + +quelltxt .asc TC_CR,"PLEASE INPUT NEW SOURCEPATH/ARCHIVE:",TC_CR,0 + .) + +ziel .( + Tout(quelltxt) + jsr LINEIN + ldy #0 +q1 lda INBUF,y + sta zielpfad,y + beq end + iny + cpy #PFADLEN-1 + bcc q1 + lda #0 + sta zielpfad,y +end rts + +quelltxt .asc TC_CR,"PLEASE INPUT NEW TARGETPATH/ARCHIVE:",TC_CR,0 + .) + +switch .( + lda quelldrv + ldx zieldrv + stx quelldrv + sta zieldrv + ldx #0 +l1 lda quellpfad,x + pha + lda zielpfad,x + sta quellpfad,x + pla + sta zielpfad,x + inx + cpx #PFADLEN + bcc l1 + rts + .) + +inipar .( + lda #0 + sta quellpfad + sta zielpfad + lda DEVADR + cmp #8 + bcc noval + cmp #12 + bcc ok +noval lda #8 +ok sta quelldrv + sta zieldrv + rts + .) + +Txtout .( +p =$22 + + sta p + sty p+1 +l1 ldy #0 + lda (p),y + beq le + jsr BSOUT + inc p + bne l1 + inc p+1 + bne l1 +le rts + .) + +iniscreen .( + lda #COL_SCHWARZ + sta VIC+VIC_EXTCOL + sta VIC+VIC_BCKCOL0 + lda #TC_HELLGRUEN + jsr BSOUT + rts + .) + +sysmem =* + +quelldrv =sysmem +zieldrv =sysmem+1 +-sysmem +=2 + +quellpfad =sysmem +zielpfad =sysmem+PFADLEN +-sysmem +=2*PFADLEN + +anzfiles =sysmem +-sysmem +=1 + +wi =sysmem +wo =sysmem+1 +-sysmem +=2 +wb =sysmem +-sysmem +=256 + +wxbyt =sysmem +wxanz =sysmem+1 +-sysmem +=2 + +ri =sysmem +ro =sysmem+1 +rf =sysmem+2 +-sysmem +=3 +rb =sysmem +-sysmem +=256 + +wcnt =sysmem +-sysmem +=2 +rcnt =sysmem +-sysmem +=2 +ecnt =sysmem +-sysmem +=2 + +filetab =sysmem +ende .) + diff --git a/xa-2.3.5/examples/pack_ger.a65 b/xa-2.3.5/examples/pack_ger.a65 new file mode 100644 index 0000000..c65851d --- /dev/null +++ b/xa-2.3.5/examples/pack_ger.a65 @@ -0,0 +1,1070 @@ + +#include "c64def.def" + +#define Tout(a) .(:lda #a:jsr Txtout:.) +#define Aout(a) .(:lda #b:jsr Txtout:jmp c:b .byt a,0:c .) +#define Ibout(a) .(:ldx a:lda #0:jsr INTOUT:.) +#define Iout(a) .(:ldx a:lda a+1:jsr INTOUT:.) + +#define PFADLEN 40 +#define FN_WR 3 +#define FN_RD 4 +#define XCODE $f7 +#define Version 1 + + .( + .word $0801 + *=$0801 + + .word basicend,10 + .byt $9e,"2064",0 ;sys $0810 +basicend .word 0 + .byt 0,0,0 + + .( + jsr CLRCH + jsr iniscreen + jsr inipar +menu1 + Tout(m1atxt) + Tout(quellpfad) + Tout(m1btxt) + Ibout(quelldrv) + Tout(m1ctxt) + Tout(zielpfad) + Tout(m1dtxt) + Ibout(zieldrv) + Tout(m1etxt) + +next jsr GET + beq next + + ldx #0 +l1 cmp befkeys,x + beq exe + inx + cpx #Anzbefs + bcc l1 + bcs next + +exe jsr exec + jmp menu1 + +exec txa + asl + tax + lda madr+1,x + pha + lda madr,x + pha + rts + +madr .word pack-1,unpack-1,quelle-1,ziel-1,switch-1,dir-1,qdrv-1,zdrv-1 +befkeys .asc TC_F1,TC_F2,TC_F3,TC_F5,TC_F8,TC_F7,TC_F4,TC_F6 +Anzbefs =8 + +m1atxt .asc TC_LCH,TC_SCO,TC_FF,TC_LF,TC_LF + .asc "(F1) PROGRAMME ZUSAMMENPACKEN",TC_CR,TC_LF + .asc "(F2) ARCHIV AUSPACKEN",TC_CR,TC_LF + .asc "(F3) QUELLPFAD:",0 +m1btxt .asc TC_CR,TC_LF + .asc "(F4) QUELLDEVICE:",0 +m1ctxt .asc TC_CR,TC_LF + .asc "(F5) ZIELPFAD :",0 +m1dtxt .asc TC_CR,TC_LF + .asc "(F6) ZIELDEVICE :",0 +m1etxt .asc TC_CR,TC_LF + .asc "(F7) QUELLDIRECTORY",TC_CR,TC_LF + .asc "(F8) QUELLE UND ZIEL TAUSCHEN",TC_CR,TC_LF + .asc "IHRE EINGABE BITTE",TC_CR,0 + .) + +unpack .( + jsr openarcrd + bcs cls + + lda #0 + sta rcnt + sta rcnt+1 + + jsr rbyte + cmp #Version + bne verr + +loop jsr unpackfile + bcc loop + + Tout(t1) + lda rcnt+1 + ldx rcnt + jsr INTOUT + lda #TC_CR + jsr BSOUT + jsr waitkey + +cls lda #FN_RD + jsr CLOSE + rts + +verr Tout(verrtxt) + jmp cls +verrtxt .asc "UNGUELTIGE ARCHIV-VERSION",0 + +t1 .asc "ARCHIV HATTE BYTES #",0 + .) + +unpackfile + .( + lda #0 + sta wcnt + sta wcnt+1 + lda #TC_CR + jsr BSOUT + ldy #0 + sty P1 +l1 jsr rbyte + bcs endx + ldy P1 + sta filetab,y + inc P1 + cmp #0 + beq endnam + jsr BSOUT + jmp l1 + +endnam Tout(ask) + jsr waitkey + cmp #"J" + bne nounpack + Tout(tok) + lda #<-1 + sta P1+1 + jsr fxopen + jsr Getzst + bcs xa + bcc lo + +endx jmp end +nounpack Tout(tno) +xa lda #0 + sta P1+1 + +lo jsr rbyte + bcs cls + cmp #XCODE + bne xb + jsr rbyte + sta wxanz + cmp #0 + clc + beq cls + jsr rbyte + sta wxbyt + bit P1+1 + bpl lo +ly lda wxbyt + jsr wbyte + dec wxanz + bne ly + jmp lo + +xb bit P1+1 + bpl lo + jsr wbyte + jmp lo + +cls php + jsr wbuf + lda #FN_WR + jsr CLOSE + + Tout(t1) + lda wcnt+1 + ldx wcnt + jsr INTOUT + lda #TC_CR + jsr BSOUT + + plp +end rts + +ask .asc TC_CR,"DATEI AUSPACKEN (J/N)?",0 + +t1 .asc "ERGIBT BYTES #",0 + .) + +incwcnt .( + inc wcnt + bne l1 + inc wcnt+1 +l1 rts + .) + +fxopen .( + ldy #0 + sty INT +l1 lda zielpfad,y + sta INBUF,y + beq l2 + iny + cmp #":" + beq l1a + cmp #"/" + bne l1b +l1a sty INT +l1b cpy #PFADLEN + bcc l1 + +l2 ldx INT + ldy #0 +l3 lda filetab,y + sta INBUF,x + inx + iny + cmp #0 + bne l3 + dex + txa + ldx #INBUF + jsr SETFNPAR + lda #FN_WR + ldx zieldrv + ldy #1 + jsr SETFPAR + + jsr OPEN + bcs err + jsr clrwrbuf + clc +err rts + .) + +openarcrd .( + ldy #0 +l0 lda quellpfad,y + beq l1 + iny + cpy #PFADLEN + bcc l0 +l1 cpy #0 + beq err + tya + ldx #quellpfad + jsr SETFNPAR + lda #FN_RD + ldx quelldrv + ldy #0 + jsr SETFPAR + jsr OPEN + bcs err + jsr Getqst + bcs err + jsr clrrdbuf + clc + rts +err sec + rts + .) + +pack .( + jsr getlist + lda anzfiles + beq end + jsr openarcwr + bcs cls + + lda #Version + jsr wbyte + + lda #0 + sta P1 +l1 jsr setfadr + sta P2 + stx P2+1 + jsr packfile + inc P1 + lda P1 + cmp anzfiles + bcc l1 + + jsr wbuf + +cls lda #FN_WR + jsr CLOSE + + jsr Getzst + +end jmp LINEIN:rts + .) + +packfile .( + Tout(lft) + + ldy #0 +l1 sty P1+1 + lda (P2),y + jsr BSOUT + jsr wbyte + ldy P1+1 + iny + cmp #0 + bne l1 + + jsr fopen + bcs le + + jsr clrwxbyt +l2 jsr rbyte + bcs l3 + jsr wxbyte + jmp l2 +l3 jsr savwxbyt + +le lda #FN_RD + jsr CLOSE + + lda #XCODE + jsr wbyte + lda #0 + jsr wbyte + + lda #TC_CR + jsr BSOUT + rts + +lft .asc TC_CR,"cOPYING ",0 + + .) + +fopen .( + ldy #0 + sty INT +l1 lda quellpfad,y + sta INBUF,y + beq l2 + iny + cmp #":" + beq l1a + cmp #"/" + bne l1b +l1a sty INT +l1b cpy #PFADLEN + bcc l1 + +l2 ldx INT + ldy #0 +l3 lda (P2),y + sta INBUF,x + inx + iny + cmp #0 + bne l3 + dex + txa + ldx #INBUF + jsr SETFNPAR + lda #FN_RD + ldx quelldrv + ldy #0 + jsr SETFPAR + + jsr OPEN + bcs err + jsr clrrdbuf + clc +err rts + .) + +incrcnt .( + inc rcnt + bne l1 + inc rcnt+1 +l1 rts + .) + +rbyte .( + ldy ro + cpy ri + beq leerbuf + lda rb,y + inc ro + clc + rts + +leerbuf lda rf + beq ldbuf + sec + rts + +ldbuf lda #0 + sta ri + sta ro + ldx #FN_RD + jsr CHKIN + lda #0 + sta STATUS + +lok jsr BASIN + + pha + lda STATUS + beq l0 + lda #"L" + jsr BSOUT + Ibout($90) + lda #TC_CR + jsr BSOUT +l0 pla + + jsr incrcnt + ldy ri + sta rb,y + iny + sty ri + iny + ;cpy ro + beq le + lda STATUS + beq lok + +le lda STATUS + sta rf + jsr CLRCH + jmp rbyte + .) + +clrrdbuf .( + lda #0 + sta ri + sta ro + sta rf + rts + .) + +wxbyte .( + ldx wxanz + beq add + inx + bne ad2 + pha + jsr savwxbyt + pla + jmp add + +ad2 cmp wxbyt + beq adx + pha + jsr savwxbyt + pla +add sta wxbyt +adx inc wxanz + + rts + .) + +clrwxbyt .( + lda #0 + sta wxanz + rts + .) + +savwxbyt .( + lda wxanz + beq nosav + cmp #4 + bcs savs + lda wxbyt + cmp #XCODE + beq savs + +l1 lda wxbyt + jsr wbyte + dec wxanz + bne l1 + rts + +savs lda #XCODE + jsr wbyte + lda wxanz + jsr wbyte + lda wxbyt + jsr wbyte + lda #0 + sta wxanz +nosav rts + .) + +openarcwr .( + ldy #0 +l0 lda zielpfad,y + beq l1 + iny + cpy #PFADLEN + bcc l0 +l1 cpy #0 + beq err + tya + ldx #zielpfad + jsr SETFNPAR + lda #FN_WR + ldx zieldrv + ldy #1 + jsr SETFPAR + jsr OPEN + bcs err + lda zieldrv + jsr Getzst + bcs err + jsr clrwrbuf + clc + rts +err sec + rts + .) + +clrwrbuf .( + lda #0 + sta wi + sta wo + rts + .) + +wbyte .( + ldy wi + sta wb,y + iny + sty wi + iny + cpy wo + bne nowr + pha + jsr wbuf + pla +nowr rts + .) + +wbuf .( + ldx #FN_WR + jsr CKOUT + ldy wo +l1 cpy wi + beq end + lda wb,y + jsr BSOUT + + lda STATUS + beq l0 + tya + pha + lda #"W" + jsr $e716 + lda $90 + ora #$40 + jsr $e716 + lda #TC_CR + jsr $e716 + pla + tay +l0 + jsr incwcnt + iny + jmp l1 +end lda #0 + sta wi + sta wo + jsr CLRCH + rts + .) + + .( +&Getqst lda quelldrv + jmp Getst +&Getzst lda zieldrv +&Getst + pha + jsr CLRCH + lda #TC_CR + jsr BSOUT + pla + jsr TALK + lda #15+$60 + jsr SECTALK + lda #0 + sta STATUS + jsr IECIN + pha + jsr BSOUT + +l1 jsr IECIN + cmp #0 + beq l2 + jsr BSOUT + lda STATUS + beq l1 +l2 jsr UNTALK + pla + cmp #"0" + bne err + clc + rts +err sec + rts + .) + +/* +showlist .( + lda #0 + sta P1 + +l1 lda P1 + cmp anzfiles + bcs le + + jsr setfadr + lda #TC_CR + jsr BSOUT + lda INT + ldy INT+1 + jsr Txtout + inc P1 + jmp l1 +le rts + .) +*/ + .( +l4x jmp l4 + +&getlist + lda #0 + sta anzfiles + + lda #TC_FF + jsr BSOUT + + jsr setdirnam + jsr SENDNAM + lda DEVADR + jsr TALK + lda SECADR + jsr SECTALK + + lda #0 + sta STATUS + ldy #3 +l0 sty P1 +l1 jsr IECIN + sta P1+1 + ldy STATUS + bne l4x + jsr IECIN + dec P1 + bne l1 + ldx P1+1 + jsr INTOUT + lda #" " + jsr BSOUT + +la jsr IECIN + cmp #0 + beq l4x + cmp #TC_REV + bne l3x + jmp l3 +l3x jsr BSOUT + cmp #34 + bne la + + lda anzfiles + jsr setfadr + sta P2 + stx P2+1 + + ldy #0 +lb sty P1 + jsr IECIN + jsr BSOUT + ldy P1 + cmp #34 + beq lc + sta (P2),y + iny + cpy #17 + bcc lb +lc lda #"," + sta (P2),y + iny + +ld sty P1 + jsr IECIN + jsr BSOUT + ldy P1 + cmp #" " + beq ld + sta P1+1 + sta (P2),y + iny + lda #0 + sta (P2),y +/* + lda #TC_CR + jsr BSOUT + lda P2+1 + ldx P2 + jsr INTOUT + lda #":" + jsr BSOUT + lda P2 + ldy P2+1 + jsr Txtout +*/ +lf tax + jsr IECIN + jsr BSOUT + cmp #" " + bne lf + cpx #"<" + beq lg + lda #" " + jsr BSOUT +lg lda #" " + jsr BSOUT + + lda P1+1 + jsr testkeys + +lh jsr IECIN + cmp #0 + bne lh + beq l2 + +l3 jsr IECIN + ldx STATUS + bne l4 + tax + beq l2 + jsr BSOUT + jmp l3 + +l2 lda #TC_CR + jsr BSOUT + jsr GET + beq l5 + jsr waitkey +l5 ldy #2 + beq l4 + jmp l0 +l4 jsr CLSFIL + jmp waitkey + .) + +testkeys .( + cmp #"P" + beq ok + cmp #"S" + beq ok + rts +ok Tout(t1) + jsr waitkey + cmp #"J" + beq ja + Tout(tno) + rts +ja Tout(tok) + inc anzfiles + rts + +t1 .asc TC_REV,"JA/NEIN",TC_CRL,TC_CRL,TC_CRL + .asc TC_CRL,TC_CRL,TC_CRL,TC_CRL,0 +&tno .asc TC_REO,"NEIN ",0 +&tok .asc TC_REO,"JA ",0 + .) + +setfadr .( + ldx #0 + stx INT+1 + asl + rol INT+1 + asl + rol INT+1 + sta INT + ldx INT+1 + asl + rol INT+1 + asl + rol INT+1 + clc + adc INT + sta INT + txa + adc INT+1 + sta INT+1 + lda #filetab + adc INT+1 + sta INT+1 + tax + pla + rts + .) + +dir .( + lda #TC_FF + jsr BSOUT + + jsr setdirnam + jsr SENDNAM + lda DEVADR + jsr TALK + lda SECADR + jsr SECTALK + + lda #0 + sta STATUS + ldy #3 +l0 sty P1 +l1 jsr IECIN + sta P1+1 + ldy STATUS + bne l4 + jsr IECIN + dec P1 + bne l1 + ldx P1+1 + jsr INTOUT + lda #" " + jsr BSOUT +l3 jsr IECIN + ldx STATUS + bne l4 + tax + beq l2 + jsr BSOUT + jmp l3 +l2 lda #TC_CR + jsr BSOUT + jsr GET + beq l5 + jsr waitkey +l5 ldy #2 + bne l0 +l4 jsr CLSFIL + jmp waitkey + .) + +waitkey jsr GET + beq waitkey + rts + +setdirnam .( +p1 =INT + + lda #"$" + sta INBUF + ldx #1 + stx p1 + + ldy #0 +l1 lda quellpfad,y + beq nodp + cmp #":" + beq dp + iny + cpy #PFADLEN + bcc l1 +nodp lda #":" + sta INBUF,x + inx +dp ldy #0 +dp1 lda quellpfad,y + sta INBUF,x + beq end + cmp #":" + beq l2a + cmp #"/" + bne l2 +l2a stx p1 +l2 inx + iny + cpy #PFADLEN + bcc dp1 +end ldx p1 + inx + lda #"*" + sta INBUF,x + inx + lda #"." + sta INBUF,x + inx + lda #"*" + sta INBUF,x + inx + txa + ldx #INBUF + jsr SETFNPAR + lda #1 + ldx quelldrv + ldy #0 + jmp SETFPAR + .) + +qdrv .( + inc quelldrv + lda quelldrv + cmp #12 + bcc ok + lda #8 + sta quelldrv +ok rts + .) + +zdrv .( + inc zieldrv + lda zieldrv + cmp #12 + bcc ok + lda #8 + sta zieldrv +ok rts + .) + +quelle .( + Tout(quelltxt) + jsr LINEIN + ldy #0 +q1 lda INBUF,y + sta quellpfad,y + beq end + iny + cpy #PFADLEN-1 + bcc q1 + lda #0 + sta quellpfad,y +end rts + +quelltxt .asc TC_CR,"BITTE NEUEN QUELLPFAD EINGEBEN:",TC_CR,0 + .) + +ziel .( + Tout(quelltxt) + jsr LINEIN + ldy #0 +q1 lda INBUF,y + sta zielpfad,y + beq end + iny + cpy #PFADLEN-1 + bcc q1 + lda #0 + sta zielpfad,y +end rts + +quelltxt .asc TC_CR,"BITTE NEUEN ZIELPFAD EINGEBEN:",TC_CR,0 + .) + +switch .( + lda quelldrv + ldx zieldrv + stx quelldrv + sta zieldrv + ldx #0 +l1 lda quellpfad,x + pha + lda zielpfad,x + sta quellpfad,x + pla + sta zielpfad,x + inx + cpx #PFADLEN + bcc l1 + rts + .) + +inipar .( + lda #0 + sta quellpfad + sta zielpfad + lda DEVADR + cmp #8 + bcc noval + cmp #12 + bcc ok +noval lda #8 +ok sta quelldrv + sta zieldrv + rts + .) + +Txtout .( +p =$22 + + sta p + sty p+1 +l1 ldy #0 + lda (p),y + beq le + jsr BSOUT + inc p + bne l1 + inc p+1 + bne l1 +le rts + .) + +iniscreen .( + lda #COL_SCHWARZ + sta VIC+VIC_EXTCOL + sta VIC+VIC_BCKCOL0 + lda #TC_HELLGRUEN + jsr BSOUT + rts + .) + +sysmem =* + +quelldrv =sysmem +zieldrv =sysmem+1 +-sysmem +=2 + +quellpfad =sysmem +zielpfad =sysmem+PFADLEN +-sysmem +=2*PFADLEN + +anzfiles =sysmem +-sysmem +=1 + +wi =sysmem +wo =sysmem+1 +-sysmem +=2 +wb =sysmem +-sysmem +=256 + +wxbyt =sysmem +wxanz =sysmem+1 +-sysmem +=2 + +ri =sysmem +ro =sysmem+1 +rf =sysmem+2 +-sysmem +=3 +rb =sysmem +-sysmem +=256 + +wcnt =sysmem +-sysmem +=2 +rcnt =sysmem +-sysmem +=2 +ecnt =sysmem +-sysmem +=2 + +filetab =sysmem +ende .) + diff --git a/xa-2.3.5/examples/peng.l b/xa-2.3.5/examples/peng.l new file mode 100644 index 0000000..2307df7 --- /dev/null +++ b/xa-2.3.5/examples/peng.l @@ -0,0 +1,180 @@ +basicend, 0x080b, 1, 0x0000 +iniscreen, 0x10c4, 1, 0x0000 +inipar, 0x1093, 1, 0x0000 +menu1, 0x0819, 2, 0x0000 +m1atxt, 0x0897, 2, 0x0000 +Txtout, 0x10ae, 1, 0x0000 +quellpfad, 0x10d4, 1, 0x0000 +m1btxt, 0x08e1, 2, 0x0000 +quelldrv, 0x10d2, 1, 0x0000 +m1ctxt, 0x08f6, 2, 0x0000 +zielpfad, 0x10fc, 1, 0x0000 +m1dtxt, 0x090d, 2, 0x0000 +zieldrv, 0x10d3, 1, 0x0000 +m1etxt, 0x0923, 2, 0x0000 +next, 0x085a, 2, 0x0000 +l1, 0x0861, 2, 0x0000 +befkeys, 0x088f, 2, 0x0000 +exe, 0x086d, 2, 0x0000 +Anzbefs, 0x0008, 2, 0x0000 +exec, 0x0873, 2, 0x0000 +madr, 0x087f, 2, 0x0000 +pack, 0x0b32, 1, 0x0000 +unpack, 0x0970, 1, 0x0000 +quelle, 0x0fe5, 1, 0x0000 +ziel, 0x102b, 1, 0x0000 +switch, 0x1071, 1, 0x0000 +dir, 0x0f01, 1, 0x0000 +qdrv, 0x0fc5, 1, 0x0000 +zdrv, 0x0fd5, 1, 0x0000 +openarcrd, 0x0aff, 1, 0x0000 +cls, 0x09a1, 12, 0x0000 +rcnt, 0x132e, 1, 0x0000 +rbyte, 0x0c09, 1, 0x0000 +verr, 0x09a7, 12, 0x0000 +loop, 0x0984, 12, 0x0000 +unpackfile, 0x09dd, 1, 0x0000 +t1, 0x09c9, 12, 0x0000 +waitkey, 0x0f5f, 1, 0x0000 +verrtxt, 0x09b1, 12, 0x0000 +wcnt, 0x132c, 1, 0x0000 +l1, 0x09ee, 15, 0x0000 +endx, 0x0a27, 15, 0x0000 +filetab, 0x1332, 1, 0x0000 +endnam, 0x0a04, 15, 0x0000 +ask, 0x0a8b, 15, 0x0000 +nounpack, 0x0a2a, 15, 0x0000 +tok, 0x0ec9, 1, 0x0000 +fxopen, 0x0ab7, 1, 0x0000 +Getzst, 0x0d6a, 1, 0x0000 +xa, 0x0a31, 15, 0x0000 +lo, 0x0a35, 15, 0x0000 +end, 0x0a8a, 15, 0x0000 +tno, 0x0ec0, 1, 0x0000 +cls, 0x0a6b, 15, 0x0000 +xb, 0x0a61, 15, 0x0000 +wxanz, 0x1228, 1, 0x0000 +wxbyt, 0x1227, 1, 0x0000 +ly, 0x0a53, 15, 0x0000 +wbyte, 0x0d0f, 1, 0x0000 +wbuf, 0x0d25, 1, 0x0000 +t1, 0x0aa0, 15, 0x0000 +incwcnt, 0x0aae, 1, 0x0000 +l1, 0x0ab6, 20, 0x0000 +l1, 0x0abb, 21, 0x0000 +l2, 0x0ad2, 21, 0x0000 +l1a, 0x0acc, 21, 0x0000 +l1b, 0x0ace, 21, 0x0000 +l3, 0x0ad6, 21, 0x0000 +err, 0x0afe, 21, 0x0000 +clrwrbuf, 0x0d06, 1, 0x0000 +l0, 0x0b01, 22, 0x0000 +l1, 0x0b0b, 22, 0x0000 +err, 0x0b30, 22, 0x0000 +Getqst, 0x0d64, 1, 0x0000 +clrrdbuf, 0x0c6a, 1, 0x0000 +getlist, 0x0da7, 1, 0x0000 +anzfiles, 0x1124, 1, 0x0000 +end, 0x0b66, 23, 0x0000 +openarcwr, 0x0cd0, 1, 0x0000 +cls, 0x0b5e, 23, 0x0000 +l1, 0x0b48, 23, 0x0000 +setfadr, 0x0ed2, 1, 0x0000 +packfile, 0x0b6a, 1, 0x0000 +lft, 0x0baf, 24, 0x0000 +l1, 0x0b73, 24, 0x0000 +fopen, 0x0bb9, 1, 0x0000 +le, 0x0b9a, 24, 0x0000 +clrwxbyt, 0x0c97, 1, 0x0000 +l2, 0x0b8c, 24, 0x0000 +l3, 0x0b97, 24, 0x0000 +wxbyte, 0x0c76, 1, 0x0000 +savwxbyt, 0x0c9d, 1, 0x0000 +l1, 0x0bbd, 26, 0x0000 +l2, 0x0bd4, 26, 0x0000 +l1a, 0x0bce, 26, 0x0000 +l1b, 0x0bd0, 26, 0x0000 +l3, 0x0bd8, 26, 0x0000 +err, 0x0bff, 26, 0x0000 +incrcnt, 0x0c00, 1, 0x0000 +l1, 0x0c08, 27, 0x0000 +ro, 0x122a, 1, 0x0000 +ri, 0x1229, 1, 0x0000 +leerbuf, 0x0c19, 28, 0x0000 +rb, 0x122c, 1, 0x0000 +rf, 0x122b, 1, 0x0000 +ldbuf, 0x0c20, 28, 0x0000 +lok, 0x0c31, 28, 0x0000 +l0, 0x0c4a, 28, 0x0000 +le, 0x0c5f, 28, 0x0000 +add, 0x0c90, 31, 0x0000 +ad2, 0x0c86, 31, 0x0000 +adx, 0x0c93, 31, 0x0000 +nosav, 0x0ccf, 33, 0x0000 +savs, 0x0cb9, 33, 0x0000 +l1, 0x0cad, 33, 0x0000 +l0, 0x0cd2, 34, 0x0000 +l1, 0x0cdc, 34, 0x0000 +err, 0x0d04, 34, 0x0000 +wi, 0x1125, 1, 0x0000 +wo, 0x1126, 1, 0x0000 +wb, 0x1127, 1, 0x0000 +nowr, 0x0d24, 36, 0x0000 +l1, 0x0d2d, 37, 0x0000 +end, 0x0d58, 37, 0x0000 +l0, 0x0d51, 37, 0x0000 +Getst, 0x0d6d, 1, 0x0000 +l1, 0x0d8a, 38, 0x0000 +l2, 0x0d98, 38, 0x0000 +err, 0x0da2, 38, 0x0000 +l4x, 0x0da4, 39, 0x0000 +l4, 0x0e80, 39, 0x0000 +setdirnam, 0x0f65, 1, 0x0000 +l0, 0x0dc7, 39, 0x0000 +l1, 0x0dc9, 39, 0x0000 +la, 0x0de3, 39, 0x0000 +l3x, 0x0df1, 39, 0x0000 +l3, 0x0e5c, 39, 0x0000 +lb, 0x0e04, 39, 0x0000 +lc, 0x0e19, 39, 0x0000 +ld, 0x0e1e, 39, 0x0000 +lf, 0x0e35, 39, 0x0000 +lg, 0x0e49, 39, 0x0000 +testkeys, 0x0e86, 1, 0x0000 +lh, 0x0e53, 39, 0x0000 +l2, 0x0e6c, 39, 0x0000 +l5, 0x0e79, 39, 0x0000 +ok, 0x0e8f, 40, 0x0000 +t1, 0x0eb0, 40, 0x0000 +ja, 0x0ea5, 40, 0x0000 +l0, 0x0f1c, 45, 0x0000 +l1, 0x0f1e, 45, 0x0000 +l4, 0x0f59, 45, 0x0000 +l3, 0x0f38, 45, 0x0000 +l2, 0x0f48, 45, 0x0000 +l5, 0x0f55, 45, 0x0000 +p1, 0x0014, 46, 0x0000 +l1, 0x0f70, 46, 0x0000 +nodp, 0x0f7e, 46, 0x0000 +dp, 0x0f84, 46, 0x0000 +dp1, 0x0f86, 46, 0x0000 +end, 0x0f9e, 46, 0x0000 +l2a, 0x0f96, 46, 0x0000 +l2, 0x0f98, 46, 0x0000 +ok, 0x0fd4, 47, 0x0000 +ok, 0x0fe4, 48, 0x0000 +quelltxt, 0x1004, 49, 0x0000 +q1, 0x0ff1, 49, 0x0000 +end, 0x1003, 49, 0x0000 +quelltxt, 0x104a, 51, 0x0000 +q1, 0x1037, 51, 0x0000 +end, 0x1049, 51, 0x0000 +l1, 0x107f, 53, 0x0000 +noval, 0x10a5, 54, 0x0000 +ok, 0x10a7, 54, 0x0000 +p, 0x0022, 55, 0x0000 +l1, 0x10b2, 55, 0x0000 +le, 0x10c3, 55, 0x0000 +sysmem, 0x1332, 1, 0x0000 +ecnt, 0x1330, 1, 0x0000 +ende, 0x10d2, 1, 0x0000 diff --git a/xa-2.3.5/loader/Makefile b/xa-2.3.5/loader/Makefile new file mode 100644 index 0000000..3492c58 --- /dev/null +++ b/xa-2.3.5/loader/Makefile @@ -0,0 +1,18 @@ + +all: loader example test2 rom65 + +loader: loader.a65 file.def + ../xa loader.a65 -o loader + +clean: + rm -f loader test2 example a.o65 rom65 + +example: test.a + ../xa -R test.a -o example + +test2: test2.a + ../xa test2.a -o test2 + +rom65: test.a + ../mkrom.sh -O "-G" -S "-bd 1234" -R rom65 test.a test.a + diff --git a/xa-2.3.5/loader/README b/xa-2.3.5/loader/README new file mode 100644 index 0000000..4087007 --- /dev/null +++ b/xa-2.3.5/loader/README @@ -0,0 +1,8 @@ + +In this directory you find two test files, i.e. test.a and test2.a for +xa. test.a is assembled into the file "example", which is loaded by the +relocator "loader", when started on a C64. Don't try to execute this +file, it's just for testing. + + + diff --git a/xa-2.3.5/loader/ex2 b/xa-2.3.5/loader/ex2 new file mode 100644 index 0000000000000000000000000000000000000000..8170f618c51cf393198aff5fa48730a1c5ef102c GIT binary patch literal 126 zcmZQ%$Tu@(U|{&iFUe5Nz{0?&Ai}`OAOaNOVob}-NzF^lO=VaqaDibhCqn?kMur@A z0Um)@YZ(=Q{FNN+0{{6L7?_;6xto}pSX!7{85o*fkYr$CVPOz)5MdBw5CMvCF{Wkaq~;~&rZTK-xWKShk0F3zBSVf} sgK@*FwIU8{#TZs{=ryP{Ffi~oF*UKYFt@TQ88I+6F*z|RsR8u^079b^WdHyG literal 0 HcmV?d00001 diff --git a/xa-2.3.5/loader/file.def b/xa-2.3.5/loader/file.def new file mode 100644 index 0000000..9e5047e --- /dev/null +++ b/xa-2.3.5/loader/file.def @@ -0,0 +1,36 @@ + +/* These definitions are without the two leading version/marker bytes, + * length is without options + */ +#define HDR_MAGIC 0 +#define HDR_VERSION 3 +#define HDR_MODE 4 +#define HDR_TBASE 6 +#define HDR_TLEN 8 +#define HDR_DBASE 10 +#define HDR_DLEN 12 +#define HDR_BBASE 14 +#define HDR_BLEN 16 +#define HDR_ZBASE 18 +#define HDR_ZLEN 20 +#define HDR_STACKLEN 22 +#define HDR_LEN 24 + +#define A_ADR $80 +#define A_HIGH $40 /* or'd with the low byte */ +#define A_LOW $20 +#define A_MASK $e0 /* reloc type mask */ +#define A_FMASK $0f /* segment type mask */ + +#define SEG_UNDEF 0 +#define SEG_ABS 1 +#define SEG_TEXT 2 +#define SEG_DATA 3 +#define SEG_BSS 4 +#define SEG_ZERO 5 + +#define FM_OBJ %00010000 +#define FM_SIZE %00100000 +#define FM_RELOC %01000000 +#define FM_CPU %10000000 + diff --git a/xa-2.3.5/loader/loader.a65 b/xa-2.3.5/loader/loader.a65 new file mode 100644 index 0000000..ca2f8c3 --- /dev/null +++ b/xa-2.3.5/loader/loader.a65 @@ -0,0 +1,909 @@ + +/************************************************************************** + * + * Loader for 6502 relocatable binary format + * + * The loader supports 16 bit o65 version 1 files without undefined + * references. Also it doesn't like pagewise relocation and 65816 + * code, because there are different/additional relocation entries. + * + * The support routines, that have to be changed are at the end of the + * file. The stuff in this file is in absolute format (well, you have to + * bootstrap from something :-) + * The support routines for the file handling are for the operating system + * OS/A65, as of version 1.3.10b. The first part of the file (wrapped in + * "#ifdef C64") shows how to use it with a C64, for example. + * + * The subroutine 'loader' is called with a file descriptor, that has a + * meaning for the support routines, in the X register. + * The file must already be open. Also binit must have been called before. + * The loader doesn't close the file. + * + * Support routines are: + * binit a = hi byte start address memory to handle, + * x = hi byte length of memory to handle + * balloc a/y = length of block -> x = memory descriptor + * bfree x = memory block descriptor to free + * getbadr x = memory descriptor -> a/y address of memory block + * + * zalloc a = length of needed zeropage block. returns a=address + * zfree a = address of block to free + * + * fgetc x = file descriptor, returns read byte (c=0) or error (c=1) + * The error is passed through; fgetc blocks if no data + * available + * fgetb x = filedescriptor, a/y = address of block descriptor, + * i.e. a word start address and a word length of block. + * returns (c=0) or error in accu (c=1). + * + **************************************************************************/ + +/************************************************************************** + * This part is a binding for a C64 + * to show how it works + */ + +#define C64 + +#ifdef C64 + +sysmem =$033c +syszp =$57 + + + .word $0801 + *=$801 + .word nextl + .word 10 + .byt $9e, "2080",0 +nextl .word 0 + .dsb 2080-*, 0 + +c64load .( + lda #>PRGEND+255 + ldx #>$A000-PRGEND + jsr binit ; init memory handling + + lda #7 + ldx #fname + jsr $ffbd ; setfnpar + + lda #2 + ldx #11 + ldy #0 + jsr $ffba ; setfpar + + jsr $ffc0 ; open + bcs end + + ldx #2 + jsr $ffc6 ; chkin + bcs end + + jsr loader ; don't care about x, chkin did it + + php + pha + jsr $ffcc ; clrch + lda #2 + jsr $ffc3 ; close + pla + plp +end rts + +fname ;.asc "example",0 + .byt $45, $58, $41, $4d, $50, $4c, $45, 0 + .) + +fgetc .( + jsr $ffcf + php + pha + bcc carry + lda #"C" + jsr $ffd2 + pla + pha +carry + jsr hexout + lda #$20 + jsr $ffd2 + pla + plp + rts + .) + +hexout .( + pha + lsr + lsr + lsr + lsr + jsr nibout + pla +nibout and #$0f + clc + adc #$30 + cmp #$3a + bcc ok + adc #$41-$3a-1 +ok jmp $ffd2 + .) + +zalloc .( + cmp #$80 ; save from $90 upward = OS, below is only basic + bcs end + lda #$10 +end rts + .) + +zfree .( + clc + rts + .) + +#endif + +/************************************************************************** + * Here is the real loader code + */ + +#include "file.def" + +#define E_NOMEM <-40 +#define E_FVERSION <-41 + +loader .( +p1 =syszp +p2 =syszp+2 +-syszp +=4 + +tmp =sysmem +file =sysmem+1 +-sysmem +=2 +header =sysmem +-sysmem +=HDR_LEN +textb =sysmem ; memory block ID +texta =sysmem+1 ; address of block +textl =sysmem+3 ; address of block +textd =sysmem+5 ; difference to assemble address +-sysmem +=7 +datab =sysmem +dataa =sysmem+1 +datal =sysmem+3 +datad =sysmem+5 +-sysmem +=7 +bssb =sysmem +bssa =sysmem+1 +bssd =sysmem+3 +-sysmem +=5 +zeroa =sysmem +zerod =sysmem+1 +-sysmem +=3 + + stx file + jsr fgetc + bcs end + sta tmp + jsr fgetc + bcs end + tay + lda tmp + cpy #0 + bne rt + cmp #1 + beq load +rt lda #E_FVERSION ; ok, but not this version +end sec + rts + +load .( + lda #
header + sta p1+1 + lda #HDR_LEN + sta p1+3 + + ldx file + lda #p1 + jsr fgetb + bcs end + + ; header loaded, check magic and version + lda header+HDR_MAGIC + cmp #$6f + bne end + lda header+HDR_MAGIC+1 + cmp #"6" + bne end + lda header+HDR_MAGIC+2 + cmp #"5" + bne end + lda header+HDR_VERSION + cmp #0 + bne end + lda header+HDR_MODE+1 + and #%11110000 + bne end + ; now allocate buffers + lda header+HDR_TLEN + ldy header+HDR_TLEN+1 + sta textl + sty textl+1 + jsr balloc + stx textb + bcs no_text2 + jsr getbadr + sta texta + sty texta+1 + sec + sbc header+HDR_TBASE + sta textd + tya + sbc header+HDR_TBASE+1 + sta textd+1 + + lda header+HDR_DLEN + ldy header+HDR_DLEN+1 + sta datal + sty datal+1 + jsr balloc + stx datab + bcs no_data2 +no_text2 bcs no_text1 + jsr getbadr + sta dataa + sty dataa+1 + sec + sbc header+HDR_DBASE + sta datad + tya + sbc header+HDR_DBASE+1 + sta datad+1 + + lda header+HDR_BLEN + ldy header+HDR_BLEN+1 + jsr balloc + stx bssb + bcs no_bss +no_text1 bcs no_text +no_data2 bcs no_data + jsr getbadr + sta bssa + sty bssa+1 + sec + sbc header+HDR_BBASE + sta bssd + tya + sbc header+HDR_BBASE+1 + sta bssd+1 + + lda header+HDR_ZLEN + jsr zalloc + bcs no_zero + sta zeroa + sec + sbc header+HDR_ZBASE + sta zerod + lda #0 + sta zerod+1 + + jmp do_load + +&no_file lda zeroa + jsr zfree +no_zero ldx bssb + jsr bfree +no_bss ldx datab + jsr bfree +no_data ldx textb + jsr bfree +no_text rts + +do_load ; load options (i.e. ignore them now) + jsr fgetc + bcs no_file + cmp #0 + beq load_text + tay + dey +optl jsr fgetc + bcs no_file + dey + bne optl + beq do_load + +load_text ; load text segment + ldx file + lda #texta + jsr fgetb + bcs no_file + + ldx file + lda #dataa + jsr fgetb + bcs no_file + ; check number of undefined references + ldx file + jsr fgetc +&no_file2 bcs no_file + cmp #0 + bne no_file ; we have some -> not handled + ldx file + jsr fgetc + bcs no_file + cmp #0 + bne no_file + ; ok, text segments loaded, now relocate + lda texta + sec + sbc #1 + sta p1 + lda texta+1 + sbc #0 + sta p1+1 + jsr trel + + lda dataa + sec + sbc #1 + sta p1 + lda dataa+1 + sbc #0 + sta p1+1 + jsr trel + + lda texta ; return start of text segment + ldy texta+1 + clc + rts + .) + +trel .( + ldx file + jsr fgetc +no_file1 bcs no_file2 + cmp #0 + beq reloc_rts + cmp #255 + bne t1 + lda #254 + clc + adc p1 + sta p1 + bcc trel + inc p1+1 + jmp trel +t1 clc + adc p1 + sta p1 + bcc t1a + inc p1+1 +t1a ; p1 is the relocation address + ldx file + jsr fgetc + bcs no_file1 + tay + and #A_MASK + sta tmp + tya + and #A_FMASK + jsr getreldiff + ldy tmp + cpy #A_ADR + bne t2 + + ldy #0 + clc + adc (p1),y + sta (p1),y + iny + txa + adc (p1),y + sta (p1),y + jmp trel +t2 + cpy #A_LOW + bne t3 + ldy #0 + clc + adc (p1),y + sta (p1),y + jmp trel +t3 + cpy #A_HIGH + bne trel + sta p2 + stx p2+1 + ldx file + jsr fgetc + clc + adc p2 ; just get the carry bit + ldy #0 + lda p2+1 ; relocate high byte + adc (p1),y + sta (p1),y + jmp trel + +reloc_rts + clc + rts + .) + +getreldiff .( ; comparing with SEG_UNDEF would give a way + ; to get label value here for undefined refs + cmp #SEG_TEXT + bne notext + lda textd + ldx textd+1 + rts +notext cmp #SEG_DATA + bne nodata + lda datad + ldx datad+1 + rts +nodata cmp #SEG_BSS + bne nobss + lda bssd + ldx bssd+1 + rts +nobss cmp #SEG_ZERO + bne nozero + lda zerod + ldx zerod+1 +nozero rts + .) + + .) + +/************************************************************************** + * Here come the support routines + * + * first is a simple and basic implementation of fgetb, just using fgetc + */ + +fgetb .( +p =syszp +-syszp +=2 +file =sysmem +l =sysmem+1 +-sysmem +=3 + + stx file ; x=file, a=zp-adr of address, y=zp-adr of len + sta p + sty p+1 + ldy #3 + lda (p),y + sta l+1 + dey + lda (p),y + sta l + dey + lda (p),y + pha + dey + lda (p),y + sta p + pla + sta p+1 + +loop ldx file + jsr fgetc ; this is a simple implementation + bcs end + ldy #0 + sta (p),y + inc p + bne l0 + inc p+1 +l0 + lda l + bne l1 + dec l+1 +l1 dec l + + lda l + ora l+1 + bne loop + clc +end + rts + .) + +/************************************************************************** + * support for memory allocation + * + * These routines are taken from a preliminary SLIP implementation, as of + * OS/A65 version 1.3.10b + */ + +#define MAXSLOT 8 + +/**********************************************************************/ +/* New memory management for IP buffers */ +/* exports */ +/* binit */ +/* balloc, bfree, btrunc, bsplit, brealloc */ +/* getbadr, getblen */ + +#define MINBUF 8 +#define MINMASK %11111000 + + .( + +slotladr =sysmem +slothadr =sysmem+MAXSLOT +slotllen =sysmem+MAXSLOT*2 +slothlen =sysmem+MAXSLOT*3 +-sysmem +=MAXSLOT*4 + +flist =sysmem +slot =sysmem+2 +-sysmem +=3 + +p =syszp +p2 =syszp+2 +p3 =syszp+4 +p4 =syszp+6 +-syszp +=8 + +/* init memory management */ +&binit .( + sta p+1 ; hi byte startadress buffer + stx p2+1 ; hi byte length of buffer + + lda #0 + tay +l0 sta slotladr,y + sta slothadr,y + iny + cpy #MAXSLOT + bcc l0 + + sta p + tay + sta (p),y + iny + sta (p),y + iny + sta (p),y + iny + lda p2+1 + sta (p),y + + lda p + sta flist + lda p+1 + sta flist+1 + + clc + rts + .) + +/* a/y = size of buffer to be allocated -> x buffer-ID */ +&balloc .( + /* walk along freelist, and take first matching buffer + length is made a multiple of 8 (for freelist connectors */ + + pha + jsr getbslot + pla + bcc gotslot + lda #E_NOMEM + rts +gotslot + stx slot + + adc #MINBUF-1 + and #MINMASK + sta slotllen,x + tya + adc #0 + sta slothlen,x + + lda #0 + sta p2 + sta p2+1 + lda flist + sta p + lda flist+1 + sta p+1 +l0 + ldy #2 + lda (p),y + sec + sbc slotllen,x + sta p3 + iny + lda (p),y + sbc slothlen,x + sta p3+1 + bcs found + + lda p + sta p2 + lda p+1 + sta p2+1 + ldy #1 + lda (p2),y + sta p+1 + dey + lda (p2),y + sta p + ora p+1 + bne l0 + +oops lda #E_NOMEM + sec + rts + +found + /* ok, we found a free buffer: p points to the buffer, p2 to the + previous one. p3 is the length of the free buffer minus the + needed size. If the buffer is longer than needed, create a + new free buffer, then link new buffer to freelist */ + + lda p /* save buffer address */ + sta slotladr,x + lda p+1 + sta slothadr,x + + lda p3 /* check length */ + ora p3+1 + beq nocreate + + lda p /* get address of new free buffer */ + clc + adc slotllen,x + sta p4 + lda p+1 + adc slothlen,x + sta p4+1 + + ldy #0 /* copy next pointer */ + lda (p),y + sta (p4),y + iny + lda (p),y + sta (p4),y + + iny /* set new length */ + lda slotllen,x + sta (p),y + lda p3 + sta (p4),y + iny + lda slothlen,x + sta (p),y + lda p3+1 + sta (p4),y + + lda p4 + sta p + lda p4+1 + sta p+1 + +nocreate + lda p2 + ora p2+1 + beq freestart + + ldy #0 + lda p + sta (p2),y + iny + lda p+1 + sta (p2),y + clc + bcc geta +freestart + lda p + sta flist + lda p+1 + sta flist+1 + clc +geta + lda slotladr,x + ldy slothadr,x + rts + .) + +/* free buffer (ID=xr) */ +&bfree .( + lda slothadr,x + sta p3+1 + lda slotladr,x + sta p3 + ora p3+1 + beq end2 + + ldy #2 + lda slotllen,x + sta (p3),y + iny + lda slothlen,x + sta (p3),y + + lda #0 + sta slothadr,x + sta slotladr,x + + lda flist + ora flist+1 + bne ok /* no free buffer so far? */ + + lda p3 + sta flist + lda p3+1 + sta flist+1 + ldy #0 + tya + sta (p3),y + iny + sta (p3),y +end2 clc + rts +ok + lda #0 + sta p2 + sta p2+1 + lda flist + sta p + lda flist+1 + sta p+1 + + /* we have to find the place where to put the buffer in the + ordered free list. Then we have to check if we can merge + free buffers */ +loop + lda p3+1 + cmp p+1 + bcc found + bne next + lda p3 + cmp p + bcc found +next + lda p + sta p2 + lda p+1 + sta p2+1 + ldy #0 + lda (p2),y + sta p + iny + lda (p2),y + sta p+1 + ora p + bne loop + beq found +end + clc + rts + +found /* p2 is the buffer before the one to be freed, p the one behind. + p3 is the buffer to be inserted */ + + lda p2 + ora p2+1 + bne fok + ; insert before the first free buffer so far + ldy #0 + lda flist + sta (p3),y + iny + lda flist+1 + sta (p3),y + lda p3 + sta flist + ldy p3+1 + sty flist+1 + jsr bmerge + clc + rts +fok ; insert to list + ldy #1 + lda p+1 ;lda (p2),y + sta (p3),y + dey + lda p ;lda (p2),y + sta (p3),y + lda p3 + sta (p2),y + iny + lda p3+1 + sta (p2),y + + lda p3 + ldy p3+1 + jsr bmerge + lda p2 + ldy p2+1 + jsr bmerge + clc + rts + .) + +/* get adress of buffer */ +&getbadr .( + lda slotladr,x + ldy slothadr,x + clc + rts + .) + +/* get length of buffer */ +&getblen .( + lda slotllen,x + ldy slothlen,x + clc + rts + .) + +/* get free buffer-ID slot */ +getbslot .( + ldx #0 +l0 + clc + lda slotladr,x + ora slothadr,x + beq found + inx + cpx #MAXSLOT + bcc l0 +found + rts + .) + +/* check if two buffers (i.e. a/y plus following) can be merged */ +bmerge .( + sta p + sty p+1 + ldy #2 + clc + lda (p),y + adc p + sta p3 + iny + lda (p),y + adc p+1 + sta p3+1 + ldy #0 + lda (p),y + cmp p3 + bne nomerge + iny + lda (p),y + cmp p3+1 + bne nomerge +merge + ldy #2 + clc + lda (p3),y + adc (p),y + sta (p),y + iny + lda (p3),y + adc (p),y + sta (p),y + ldy #0 + lda (p3),y + sta (p),y + iny + lda (p3),y + sta (p),y +nomerge + clc + rts + .) + + .) + +PRGEND + diff --git a/xa-2.3.5/loader/test.a b/xa-2.3.5/loader/test.a new file mode 100644 index 0000000..874a2cd --- /dev/null +++ b/xa-2.3.5/loader/test.a @@ -0,0 +1,36 @@ + + .fopt 1, "filename" + .( + .text +&absv = 4 + lda #>test+4 + bne test +*=$8000 ;* + lda test-8 + .) + .text + nop + +; .fopt 1, "filename" + + lda bsslab + lda zerolab + lda #absv*2 + .bss +bsslab + .dsb 20,1 + .zero +zerolab + .dsb 20 + diff --git a/xa-2.3.5/loader/test2.a b/xa-2.3.5/loader/test2.a new file mode 100644 index 0000000..7921363 --- /dev/null +++ b/xa-2.3.5/loader/test2.a @@ -0,0 +1,28 @@ + + *=$8000 + .( +; .text + lda #>test+4 + bne test +;*=* + lda test-1 + .) +; .text + nop + lda bsslab + lda zerolab +; .bss +bsslab + .dsb 20,1 +; .zero +zerolab + .dsb 20 + diff --git a/xa-2.3.5/loader/test3.a b/xa-2.3.5/loader/test3.a new file mode 100644 index 0000000..d58a411 --- /dev/null +++ b/xa-2.3.5/loader/test3.a @@ -0,0 +1,3 @@ + + lda #label +lab2 diff --git a/xa-2.3.5/man/README b/xa-2.3.5/man/README new file mode 100644 index 0000000..4d54849 --- /dev/null +++ b/xa-2.3.5/man/README @@ -0,0 +1,3 @@ +Also look at ../doc/ for previous documentation files and the Change log. + +Cameron Kaiser diff --git a/xa-2.3.5/man/file65.1 b/xa-2.3.5/man/file65.1 new file mode 100644 index 0000000..7e292a2 --- /dev/null +++ b/xa-2.3.5/man/file65.1 @@ -0,0 +1,58 @@ +.TH FILE65 "1" "11 April 2006" + +.SH NAME +file65 \- print information for o65 object files + +.SH SYNOPSIS +.B file65 +[\fIOPTION\fR]... \fIFILE\fR... + +.SH DESCRIPTION +.B file65 +prints file information for files in the o65 object format. + +.SH OPTIONS +.TP +.B \-V +Print undefined and global labels. +.TP +.B \-P +Print the segment end addresses (suitable for the +.BR xa (1) +command line parameter +.BR \-b ). +.TP +.B \-a offset +Print +.BR xa (1) +"ROMmable" parameter for another file behind this one in +the same ROM, located at the specified offset. +.TP +.B \-A offset +Does the same thing as +.B \-a +but only prints the starting address of the next file in the ROM. +.TP +.B \-\-help +Show summary of options. +.TP +.B \-\-version +Show version of program. + +.SH "SEE ALSO" +.BR ldo65 (1), +.BR printcbm (1), +.BR reloc65 (1), +.BR uncpk (1), +.BR xa (1), +.BR dxa (1) + +.SH AUTHOR +This manual page was written by David Weinehall +and Cameron Kaiser . +Original xa package (C)1989-1997 Andre Fachat. Additional changes +(C)1989-2006 Andre Fachat, Jolse Maginnis, David Weinehall and +Cameron Kaiser. The current maintainer is Cameron Kaiser. + +.SH WEBSITE +http://www.floodgap.com/retrotech/xa/ diff --git a/xa-2.3.5/man/ldo65.1 b/xa-2.3.5/man/ldo65.1 new file mode 100644 index 0000000..87e0b36 --- /dev/null +++ b/xa-2.3.5/man/ldo65.1 @@ -0,0 +1,60 @@ +.TH LDO65 "1" "11 April 2006" + +.SH NAME +ldo65 \- linker for o65 object files + +.SH SYNOPSIS +.B ldo65 +[\fIOPTION\fR]... \fIFILE\fR... + +.SH DESCRIPTION +.B ldo65 +is a linker for files in the `o65' object format, formerly +.B ld65 +but renamed to avoid conflicts with the +.B cc65 +package (a separate product). + +.SH OPTIONS +.TP +.B \-b? addr +Relocate segment +.B ? +to +.BR addr \&. +.B ? +must be either t, d, b or z to indicate the text, data, bss or zero +segment respectively. See the +.BR xa (1) +man page for an explanation. +.TP +.B \-o filename +Set output filename. The default is +.BR a.o65 \&. +.TP +.B \-G +Suppress writing of globals. +.TP +.B \-\-help +Show summary of options. +.TP +.B \-\-version +Show version of program. + +.SH "SEE ALSO" +.BR file65 (1), +.BR printcbm (1), +.BR reloc65 (1), +.BR uncpk (1), +.BR dxa (1), +.BR xa (1) + +.SH AUTHOR +This manual page was written by David Weinehall +and Cameron Kaiser . +Original xa package (C)1989-1997 Andre Fachat. Additional changes +(C)1989-2006 Andre Fachat, Jolse Maginnis, David Weinehall and +Cameron Kaiser. The current maintainer is Cameron Kaiser. + +.SH WEBSITE +http://www.floodgap.com/retrotech/xa/ diff --git a/xa-2.3.5/man/printcbm.1 b/xa-2.3.5/man/printcbm.1 new file mode 100644 index 0000000..db252e9 --- /dev/null +++ b/xa-2.3.5/man/printcbm.1 @@ -0,0 +1,37 @@ +.TH PRINTCBM "1" "11 April 2006" + +.SH NAME +printcbm \- list a Commodore BASIC file + +.SH SYNOPSIS +.B printcbm +\fIFILE\fR + +.SH DESCRIPTION +.B printcbm +lists all lines of the specified Commodore BASIC program. + +.SH OPTIONS +.TP +.B \-\-help +Show summary of options. +.TP +.B \-\-version +Show version of program. + +.SH "SEE ALSO" +.BR file65 (1), +.BR ldo65 (1), +.BR reloc65 (1), +.BR uncpk (1), +.BR dxa (1), +.BR xa (1) + +.SH AUTHOR +This manual page was written by David Weinehall . +Original xa package (C)1989-1997 Andre Fachat. Additional changes +(C)1989-2006 Andre Fachat, Jolse Maginnis, David Weinehall and +Cameron Kaiser. The current maintainer is Cameron Kaiser. + +.SH WEBSITE +http://www.floodgap.com/retrotech/xa/ diff --git a/xa-2.3.5/man/reloc65.1 b/xa-2.3.5/man/reloc65.1 new file mode 100644 index 0000000..ba891a9 --- /dev/null +++ b/xa-2.3.5/man/reloc65.1 @@ -0,0 +1,68 @@ +.TH RELOC65 "1" "11 April 2006" + +.SH NAME +reloc65 \- relocator for o65 object files + +.SH SYNOPSIS +.B reloc65 +[\fIOPTION\fR]... \fIFILE\fR... + +.SH DESCRIPTION +.B reloc65 +is a relocator for files in the +.B o65 +object format. + +.SH OPTIONS +.TP +.B \-o filename +Set output filename. The default is +.BR a.o65 \&. +.TP +.B \-b? addr +Relocate segment +.B ? +to +.BR addr \&. +.B ? +should be t, d, b or z to represent the text, data, bss or zero +segment respectively. See the +.BR xa (1) +man page for an explanation. +.TP +.B \-x? +Extract segment +.B ? +from the file instead of writing back the whole +file. Valid arguments are t and d for the text or data segment +respectively. Not valid for bss or zero. +.TP +.B \-X +Extract text and data segment together +from the file instead of writing back the whole +file. Relocating data segment to the end of the text segment +(ignoring the \-xd option) before extracting. +.TP +.B \-\-help +Show summary of options. +.TP +.B \-\-version +Show version of program. + +.SH "SEE ALSO" +.BR file65 (1), +.BR ldo65 (1), +.BR printcbm (1), +.BR uncpk (1), +.BR dxa (1), +.BR xa (1) + +.SH AUTHOR +This manual page was written by David Weinehall +and Cameron Kaiser . +Original xa package (C)1989-1997 Andre Fachat. Additional changes +(C)1989-2006 Andre Fachat, Jolse Maginnis, David Weinehall and +Cameron Kaiser. The current maintainer is Cameron Kaiser. + +.SH WEBSITE +http://www.floodgap.com/retrotech/xa/ diff --git a/xa-2.3.5/man/uncpk.1 b/xa-2.3.5/man/uncpk.1 new file mode 100644 index 0000000..20b905e --- /dev/null +++ b/xa-2.3.5/man/uncpk.1 @@ -0,0 +1,77 @@ +.TH UNCPK "1" "11 April 2006" + +.SH NAME +uncpk \- manage c64 cpk archives + +.SH SYNOPSIS +.B uncpk +[\fIOPTION\fR]... \fIFILE\fR... + +.SH DESCRIPTION +.B uncpk +is an archive tool for Commodore 64 .cpk-format archives. + +.SH OPTIONS +.TP +.B c +Create an archive. +.TP +.B x +Extract from an archive. +.TP +.B l +List contents of archive. +.TP +.B a +Add a file to the archive. +.TP +.B v +Verbose output. +.TP +.B \-\-help +Show summary of options. +.TP +.B \-\-version +Show version of program. + +.SH EXAMPLES +.TP +.B uncpk c foo.cpk bar +Create archive +.B foo.cpk +with the single file +.B bar +inside it. +.TP +.B uncpk a foo.cpk bar +Add file +.B bar +to archive +.B foo.cpk +(which must already exist). +.TP +.B uncpk x foo.cpk +Extract all files from archive +.BR foo.cpk \&. +.TP +.B uncpk l foo.cpk +List contents of archive +.BR foo.cpk \&. + +.SH "SEE ALSO" +.BR file65 (1), +.BR ldo65 (1), +.BR printcbm (1), +.BR reloc65 (1), +.BR dxa (1), +.BR xa (1) + +.SH AUTHOR +This manual page was written by David Weinehall +and Cameron Kaiser . +Original xa package (C)1989-1997 Andre Fachat. Additional changes +(C)1989-2006 Andre Fachat, Jolse Maginnis, David Weinehall and +Cameron Kaiser. The current maintainer is Cameron Kaiser. + +.SH WEBSITE +http://www.floodgap.com/retrotech/xa/ diff --git a/xa-2.3.5/man/xa.1 b/xa-2.3.5/man/xa.1 new file mode 100644 index 0000000..8dc5e88 --- /dev/null +++ b/xa-2.3.5/man/xa.1 @@ -0,0 +1,837 @@ +.TH XA "1" "7 February 2009" + +.SH NAME +xa \- 6502/R65C02/65816 cross-assembler + +.SH SYNOPSIS +.B xa +[\fIOPTION\fR]... \fIFILE\fR + +.SH DESCRIPTION +.B xa +is a multi-pass cross-assembler for the 8-bit processors in the 6502 series +(such as +the 6502, 65C02, 6504, 6507, +6510, 7501, 8500, 8501 and 8502), the Rockwell R65C02, and +the 16-bit 65816 processor. For a description of syntax, see +.B ASSEMBLER SYNTAX +further in this manual page. + +.SH OPTIONS +.TP +.B \-v +Verbose output. +.TP +.B \-x +Use old filename behaviour (overrides +.BR \-o , +.B \-e +and +.BR \-l ). +This option is now deprecated. +.TP +.B \-C +No CMOS opcodes (default is to allow R65C02 opcodes) +.TP +.B \-W +No 65816 opcodes (default). +.TP +.B \-w +Allow 65816 opcodes. +.TP +.B \-B +Show lines with block open/close (see +.BR PSEUDO-OPS ). +.TP +.B \-c +Produce o65 object files instead of executable files +(no linking performed); files may contain undefined references. +.TP +.B \-o filename +Set output filename. The default is +.BR a.o65 ; +use the special filename +.BR \- +to output to standard output. +.TP +.B \-e filename +Set errorlog filename, default is none. +.TP +.B \-l filename +Set labellist filename, default is none. This is the symbol table and can +be used by disassemblers such as +.BR dxa (1) +to reconstruct source. +.TP +.B \-r +Add cross-reference list to labellist (requires +.BR \-l ). +.TP +.B \-M +Allow colons to appear in comments; for MASM compatibility. This does +not affect colon interpretation elsewhere. +.TP +.B \-R +Start assembler in relocating mode. +.TP +.B \-Llabel +Defines +.B label +as an absolute (but undefined) label even when linking. +.TP +.B \-b? addr +Set segment base for segment +.B ? +to address +.BR addr \&. +.B ? +should be t, d, b or z for text, data, bss or zero segments, respectively. +.TP +.B \-A addr +Make text segment start at an address such that when the file +starts at address +.BR addr , +relocation is not necessary. Overrides +.BR \-bt ; +other segments still have to be taken care of with +.BR \-b \&. + +.TP +.B \-G +Suppress list of exported globals. +.TP +.B \-DDEF=TEXT +Define a preprocessor macro on the command line (see +.BR PREPROCESSOR ). +.TP +.B \-I dir +Add directory +.B dir +to the include path (before +.BR XAINPUT ; +see +.BR ENVIRONMENT ). +.TP +.B \-O charset +Define the output charset for character strings. Currently supported are ASCII +(default), PETSCII (Commodore ASCII), +PETSCREEN (Commodore screen codes) and HIGH (set high bit on all +characters). +.TP +.B \-p? +Set the alternative preprocessor character to +.BR ? . +This is useful when you wish to use +.BR cpp (1) +and the built-in preprocessor at the same time (see +.BR PREPROCESSOR ). +Characters may need to be quoted for your shell (example: +.B \-p'~' +). +.TP +.B \-\-help +Show summary of options. +.TP +.B \-\-version +Show version of program. + +.SH ASSEMBLER SYNTAX + +An introduction to 6502 assembly language programming and mnemonics is +beyond the scope of this manual page. We invite you to investigate any +number of the excellent books on the subject; one useful title is "Machine +Language For Beginners" by Richard Mansfield (COMPUTE!), covering the Atari, +Commodore and Apple 8-bit systems, and is widely available on the used market. +.LP +.B xa +supports both the standard NMOS 6502 opcodes as well as the Rockwell +CMOS opcodes used in the 65C02 (R65C02). With the +.B \-w +option, +.B xa +will also accept opcodes for the 65816. NMOS 6502 +undocumented opcodes are intentionally not supported, and should be entered +manually using the +.B \.byte +pseudo-op (see +.BR PSEUDO-OPS ). +Due to conflicts between the R65C02 and 65816 instruction sets and +undocumented instructions on the NMOS 6502, their use is discouraged. +.LP +In general, +.B xa +accepts the more-or-less standard 6502 assembler format as popularised by +MASM and TurboAssembler. Values and addresses +can be expressed either as literals, or as expressions; to wit, +.TP 10 +.B 123 +decimal value +.TP +.B $234 +hexadecimal value +.TP +.B &123 +octal +.TP +.B %010110 +binary +.TP +.B * +current value of the program counter +.LP +The ASCII value of any quoted character is +inserted directly into the program text (example: +.B """A""" +inserts the byte "A" into the output stream); see also the +.B PSEUDO-OPS +section. This is affected by the currently selected character set, if any. +.LP +.B Labels +define locations within the program text, just as in other multi-pass +assemblers. A label is defined by anything that is not an opcode; for +example, a line such as +.IP +.B label1 lda #0 +.LP +defines +.B label1 +to be the current location of the program counter (thus the +address of the +.B LDA +opcode). A label can be explicitly defined by assigning it the value of +an expression, such as +.IP +.B label2 = $d000 +.LP +which defines +.B label2 +to be the address $d000, namely, the start of the VIC-II register block on +Commodore 64 computers. The program counter +.B * +is considered to be a special kind of label, and can be assigned to with +statements such as +.IP +.B * = $c000 +.LP +which sets the program counter to decimal location 49152. With the exception +of the program counter, labels cannot be assigned multiple times. To explicitly +declare redefinition of a label, place a - (dash) before it, e.g., +.IP +.B \-label2 = $d020 +.LP +which sets +.B label2 +to the Commodore 64 border colour register. The scope of a label is affected +by the block it resides within (see +.B PSEUDO-OPS +for block instructions). A label may also be hard-specified with the +.B \-L +command line option. +.LP +For those instructions where the accumulator is the implied argument (such as +.B asl +and +.BR lsr ; +.B inc +and +.B dec +on R65C02; etc.), the idiom of explicitly specifying the accumulator with +.B a +is unnecessary as the proper form will be selected if there is no explicit +argument. In fact, for consistency with label handing, if there is a label +named +.BR a , +this will actually generate code referencing that label as a memory +location and not the accumulator. Otherwise, the assembler will complain. +.LP +Labels and opcodes may take +.B expressions +as their arguments to allow computed values, and may themselves reference +other labels and/or the program counter. An expression such as +.B lab1+1 +(which operates on the current value of label +.B lab1 +and increments it by one) may use the following operands, given from highest +to lowest priority: +.TP 8 +.B * +multiplication (priority 10) +.TP +.B / +integer division (priority 10) +.TP +.B + +addition (priority 9) +.TP +.B \- +subtraction (9) +.TP +.B << +shift left (8) +.TP +.B >> +shift right (8) +.TP +.B >= => +greater than or equal to (7) +.TP +.B < +greater than (7) +.TP +.B <= =< +less than or equal to (7) +.TP +.B < +less than (7) +.TP +.B = +equal to (6) +.TP +.B <> >< +does not equal (6) +.TP +.B & +bitwise AND (5) +.TP +.B ^ +bitwise XOR (4) +.TP +.B | +bitwise OR (3) +.TP +.B && +logical AND (2) +.TP +.B || +logical OR (1) +.LP +Parentheses are valid. When redefining a label, combining arithmetic or +bitwise operators with the = (equals) operator such as +.B += +and so on are valid, e.g., +.IP +.B \-redeflabel += (label12/4) +.LP +Normally, +.B xa +attempts to ascertain the value of the operand and (when referring to +a memory location) use zero page, +16-bit or (for 65816) 24-bit addressing where appropriate and where +supported by the particular opcode. This generates smaller and faster +code, and is almost always preferable. +.LP +Nevertheless, you can use these prefix operators to force a particular +rendering of the operand. Those that generate an eight bit result can also be +used in 8-bit addressing modes, such as immediate and zero page. +.TP +.B < +low byte of expression, e.g., +.B lda # +high byte of expression +.TP +.B ! +in situations where the expression could be understood as either an absolute +or zero page value, do not attempt to optimize to a zero page argument +for those opcodes that support it (i.e., keep as 16 bit word) +.TP +.B @ +render as 24-bit quantity for 65816 (must specify +.B \-w +command-line option). +.B This is required to specify any +.B 24-bit quantity! +.TP +.B ` +force further optimization, even if the length of the instruction cannot +be reliably determined (see +.BR NOTES'N'BUGS ) +.LP +Expressions can occur as arguments to opcodes or within the preprocessor +(see +.B PREPROCESSOR +for syntax). For example, +.IP +.B lda label2+1 +.LP +takes the value at +.B label2+1 +(using our previous label's value, this would be $d021), and will be assembled +as +.B $ad $21 $d0 +to disk. Similarly, +.IP +.B lda # test.xa +.br +.B xa test.xa +.LP +No special arguments need to be passed to +.BR xa ; +the presence of +.BR cpp (1) +output is detected automatically. +.LP +Note that passing your file through +.BR cpp (1) +may interfere with +.BR xa 's +own preprocessor directives. In this case, to mask directives from +.BR cpp (1), +use the +.B \-p +option to specify an alternative character instead of +.BR # , +such as the tilde (e.g., +.B \-p'~' +). With this option and argument specified, then instead of +.BR #include , +for example, you can also use +.BR ~include , +in addition to +.B #include +(which will also still be accepted by the +.B xa +preprocessor, assuming any survive +.BR cpp (1)). +Any character can be used, although frankly pathologic choices may lead +to amusing and frustrating glitches during parsing. +You can also use this option to defer preprocessor directives that +.BR cpp (1) +may interpret too early until the file actually gets to +.B xa +itself for processing. +.LP +The following preprocessor directives are supported. + +.TP +.B #include """filename""" +Inserts the contents of file +.B filename +at this position. If the file is not found, it is searched using paths +specified by the +.B \-I +command line option or the environment variable +.B XAINPUT +(q.v.). When inserted, the file will also be parsed for preprocessor +directives. +.TP +.B #echo comment +Inserts comment +.B comment +into the errorlog file, specified with the +.B \-e +command line option. +.TP +.B #print expression +Computes the value of expression +.B expression +and prints it into the errorlog file. +.TP +.B #define DEFINE text +Equates macro +.B DEFINE +with text +.B text +such that wherever +.B DEFINE +appears in the assembly source, +.B text +is substituted in its place (just like +.BR cpp (1) +would do). In addition, +.B #define +can specify macro functions like +.BR cpp (1) +such that a directive like +.B #define mult(a,b) ((a)*(b)) +would generate the expected result wherever an expression of the form +.B mult(a,b) +appears in the source. This can also be specified on the command line with +the +.B \-D +option. The arguments of a macro function may be recursively evaluated, +unlike other +.BR #define s; +the preprocessor will attempt to re-evaluate any argument refencing +another preprocessor definition up to ten times before complaining. +.LP +The following directives are conditionals. If the conditional is not +satisfied, then the source code between the directive and its terminating +.B #endif +are expunged and not assembled. Up to fifteen levels of nesting are supported. +.TP +.B #endif +Closes a conditional block. +.TP +.B #else +Implements alternate path for a conditional block. +.TP +.B #ifdef DEFINE +True only if macro +.B DEFINE +is defined. +.TP +.B #ifndef DEFINE +The opposite; true only if macro +.B DEFINE +has not been previously defined. +.TP +.B #if expression +True if expression +.B expression +evaluates to non-zero. +.B expression +may reference other macros. +.TP +.B #iflused label +True if label +.B label +has been used (but not necessarily instantiated with a value). +.I This works on labels, not macros! +.TP +.B #ifldef label +True if label +.B label +is defined +.I and +assigned with a value. +.I This works on labels, not macros! +.LP +Unclosed conditional blocks at the end of included files generate warnings; +unclosed conditional blocks at the end of assembly generate an error. +.LP +.B #iflused +and +.B #ifldef +are useful for building up a library based on labels. For example, +you might use something like this in your library's code: +.IP +.B #iflused label +.br +.B #ifldef label +.br +.B #echo label already defined, library function label cannot be inserted +.br +.B #else +.br +.B label /* your code */ +.br +.B #endif +.br +.B #endif + +.SH ENVIRONMENT + +.B xa +utilises the following environment variables, if they exist: + +.TP +.B XAINPUT +Include file path; components should be separated by `,'. +.TP +.B XAOUTPUT +Output file path. + +.SH NOTES'N'BUGS +The R65C02 instructions +.B ina +(often rendered +.B inc +.BR a ) +and +.B dea +.RB ( dec +.BR a ) +must be rendered as bare +.B inc +and +.B dec +instructions respectively. +.LP +Forward-defined labels -- that is, labels that are defined after the current +instruction is processed -- cannot be optimized into zero +page instructions even if the label does end up being defined as a zero page +location, because the assembler does not know the value of the label in +advance during the first pass when the length of an +instruction is computed. On the second pass, a warning will be issued when an +instruction that could have been optimized can't be because of this limitation. +(Obviously, this does not apply to branching or jumping instructions because +they're not optimizable anyhow, and those instructions that can +.I only +take an 8-bit parameter will always be casted to an 8-bit quantity.) +If the label cannot otherwise be defined ahead of the instruction, the backtick +prefix +.B ` +may be used to force further optimization no matter where the label is defined +as long as the instruction supports it. +Indiscriminately forcing the issue can be fraught with peril, however, and +is not recommended; to discourage this, the assembler will complain about its +use in addressing mode situations where no ambiguity exists, such as indirect +indexed, branching and so on. +.LP +Also, as a further consequence of the way optimization is managed, we repeat +that +.B all +24-bit quantities and labels that reference a 24-bit quantity in 65816 mode, +anteriorly declared or otherwise, +.B MUST +be prepended with the +.B @ +prefix. Otherwise, the assembler will attempt to optimize to 16 bits, which +may be undesirable. + +.SH "SEE ALSO" +.BR file65 (1), +.BR ldo65 (1), +.BR printcbm (1), +.BR reloc65 (1), +.BR uncpk (1), +.BR dxa (1) + +.SH AUTHOR +This manual page was written by David Weinehall , +Andre Fachat +and Cameron Kaiser . +Original xa package (C)1989-1997 Andre Fachat. Additional changes +(C)1989-2009 Andre Fachat, Jolse Maginnis, David Weinehall, +Cameron Kaiser. The official maintainer is Cameron Kaiser. + +.SH WEBSITE +http://www.floodgap.com/retrotech/xa/ diff --git a/xa-2.3.5/misc/Makefile b/xa-2.3.5/misc/Makefile new file mode 100644 index 0000000..b93b7fd --- /dev/null +++ b/xa-2.3.5/misc/Makefile @@ -0,0 +1,44 @@ + +XCBMLIB = .. + +CFLAGS = -Wall -O2 -ansi -W + +LIBS = #-lncurses -ltermcap -lm + + +all: ../mkrom.sh ../uncpk ../printcbm ../file65 ../reloc65 ../ldo65 + +../uncpk: uncpk.c + ${CC} ${CFLAGS} uncpk.c -o $(XCBMLIB)/uncpk + +../printcbm: printcbm.c + ${CC} ${CFLAGS} printcbm.c -o $(XCBMLIB)/printcbm + +../file65: file65.c + ${CC} ${CFLAGS} file65.c -o $(XCBMLIB)/file65 + +../ldo65: ldo65.c + ${CC} ${CFLAGS} ldo65.c -o $(XCBMLIB)/ldo65 + +../reloc65: reloc65.c + ${CC} ${CFLAGS} reloc65.c -o $(XCBMLIB)/reloc65 + +../mkrom.sh: mkrom.sh + cp mkrom.sh ../mkrom.sh + +lt1: lt1.a + ../xa -R -c -o lt1 lt1.a + +lt2: lt2.a + ../xa -R -c -o lt2 lt2.a + +lt: lt1 lt2 + ../ldo65 -o lt lt1 lt2 + +clean: + rm -f *.o + +mrproper: clean + rm -f ../uncpk ../printcbm ../file65 ../mkrom.sh ../reloc65 ../ldo65 + rm -f lt1 lt2 lt + diff --git a/xa-2.3.5/misc/file65.c b/xa-2.3.5/misc/file65.c new file mode 100644 index 0000000..e96add1 --- /dev/null +++ b/xa-2.3.5/misc/file65.c @@ -0,0 +1,312 @@ +/* file65 -- A part of xa65 - 65xx/65816 cross-assembler and utility suite + * Print information about o65 files + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#ifndef _MSC_VER +#include +#endif +#include +#include + +#include "version.h" + +#define BUF (9*4+8) + +#define programname "file65" +#define progversion "v0.2.1" +#define author "Written by André Fachat" +#define copyright "Copyright (C) 1997-2002 André Fachat." + +int read_options(FILE *fp); +int print_labels(FILE *fp, int offset); + +unsigned char hdr[BUF]; +unsigned char cmp[] = { 1, 0, 'o', '6', '5' }; + +int xapar = 0; +int rompar = 0; +int romoff = 0; +int labels = 0; + +void usage(FILE *fp) +{ + fprintf(fp, + "Usage: %s [options] [file]\n" + "Print file information about o65 files\n" + "\n", + programname); + fprintf(fp, + " -P print the segment end addresses according to `xa' command line\n" + " parameters `-b?'\n" + " -a offset print `xa' ``romable'' parameter for another file behind this one\n" + " in the same ROM. Add offset to start address.\n" + " -A offset same as `-a', but only print the start address of the next\n" + " file in the ROM\n" + " -V print undefined and global labels\n" + " --version output version information and exit\n" + " --help display this help and exit\n"); +} + +int main(int argc, char *argv[]) { + int i = 1, n, mode, hlen; + FILE *fp; + char *aligntxt[4]= {"[align 1]","[align 2]","[align 4]","[align 256]"}; + if(argc<=1) { + usage(stderr); + exit(1); + } + + if (strstr(argv[1], "--help")) { + usage(stdout); + exit(0); + } + + if (strstr(argv[1], "--version")) { + version(programname, progversion, author, copyright); + exit(0); + } + + while(i=8) && (!memcmp(hdr, cmp, 5))) { + mode=hdr[7]*256+hdr[6]; + if(!xapar && !rompar) { + printf("%s: o65 version %d %s file\n", argv[i], hdr[5], + hdr[7]&0x10 ? "object" : "executable"); + printf(" mode: %04x =",mode ); + printf("%s%s%s%s%s\n", + (mode & 0x1000)?"[object]":"[executable]", + (mode & 0x2000)?"[32bit]":"[16bit]", + (mode & 0x4000)?"[page relocation]":"[byte relocation]", + (mode & 0x8000)?"[CPU 65816]":"[CPU 6502]", + aligntxt[mode & 3]); + } + if(mode & 0x2000) { + fprintf(stderr,"file65: %s: 32 bit size not supported\n", argv[i]); + } else { + n=fread(hdr+8, 1, 18, fp); + if(n<18) { + fprintf(stderr,"file65: %s: truncated file\n", argv[i]); + } else { + if(!xapar && !rompar) { + printf(" text segment @ $%04x - $%04x [$%04x bytes]\n", hdr[9]*256+hdr[8], hdr[9]*256+hdr[8]+hdr[11]*256+hdr[10], hdr[11]*256+hdr[10]); + printf(" data segment @ $%04x - $%04x [$%04x bytes]\n", hdr[13]*256+hdr[12], hdr[13]*256+hdr[12]+hdr[15]*256+hdr[14], hdr[15]*256+hdr[14]); + printf(" bss segment @ $%04x - $%04x [$%04x bytes]\n", hdr[17]*256+hdr[16], hdr[17]*256+hdr[16]+hdr[19]*256+hdr[18], hdr[19]*256+hdr[18]); + printf(" zero segment @ $%04x - $%04x [$%04x bytes]\n", hdr[21]*256+hdr[20], hdr[21]*256+hdr[20]+hdr[23]*256+hdr[22], hdr[23]*256+hdr[22]); + printf(" stack size $%04x bytes %s\n", hdr[25]*256+hdr[24], + (hdr[25]*256+hdr[24])==0?"(i.e. unknown)":""); + if(labels) { + read_options(fp); + print_labels(fp, hdr[11]*256+hdr[10] + hdr[15]*256+hdr[14]); + } + } else { + struct stat fbuf; + hlen = 8+18+read_options(fp); + stat(argv[i],&fbuf); + if(xapar) { + if(!rompar) printf("-bt %d ", + (hdr[9]*256+hdr[8]) + (hdr[11]*256+hdr[10]) + ); + printf("-bd %d -bb %d -bz %d ", + (hdr[13]*256+hdr[12]) + (hdr[15]*256+hdr[14]), + (hdr[17]*256+hdr[16]) + (hdr[19]*256+hdr[18]), + (hdr[21]*256+hdr[20]) + (hdr[23]*256+hdr[22]) + ); + } + if(rompar==1) { + printf("-A %lu ", (unsigned long)((hdr[9]*256+hdr[8]) + -hlen +romoff +fbuf.st_size)); + } else + if(rompar==2) { + printf("%lu ", (unsigned long)((hdr[9]*256+hdr[8]) + -hlen +romoff +fbuf.st_size)); + } + printf("\n"); + } + } + } + } else { + fprintf(stderr,"file65: %s: not an o65 file!\n", argv[i]); + if(hdr[0]==1 && hdr[1]==8 && hdr[3]==8) { + printf("%s: C64 BASIC executable (start address $0801)?\n", argv[i]); + } else + if(hdr[0]==1 && hdr[1]==4 && hdr[3]==4) { + printf("%s: CBM PET BASIC executable (start address $0401)?\n", argv[i]); + } + } + } else { + fprintf(stderr,"file65: %s: %s\n", argv[i], strerror(errno)); + } + } + i++; + } + return 0; +} + +static struct { int opt; int strfl; char *string; } otab[] = { + { 0, 1, "Filename" }, + { 1, 0, "O/S Type" }, + { 2, 1, "Assembler" }, + { 3, 1, "Author" }, + { 4, 1, "Creation Date" }, + { -1, -1, NULL } +}; + +static char* stab[] = { + "undefined" , + "absolute" , + "text" , + "data" , + "bss" , + "zero" , + "-" , + "-" +}; + +void print_option(unsigned char *buf, int len) { + int i, strfl=0; + for(i=0;otab[i].opt>=0; i++) if(*buf==otab[i].opt) break; + if(otab[i].opt>=0) { + printf("fopt: %-17s: ", otab[i].string); + strfl = otab[i].strfl; + } else { + printf("fopt: Unknown Type $%02x : ", (*buf & 0xff)); + } + if(strfl) { + buf[len]=0; + printf("%s\n", buf+1); + } else { + for (i=1; i +#include +#include +#include +#ifndef _MSC_VER +#include +#endif +#include + +#include "version.h" + +#define BUF (9*2+8) /* 16 bit header */ + +#define programname "ldo65" +#define progversion "v0.1.1" +#define author "Written by André Fachat" +#define copyright "Copyright (C) 1997-2002 André Fachat. Formerly ld65." + +typedef struct { + char *name; + int len; +} undefs; + +/* file information */ +typedef struct { + char *fname; /* file name */ + size_t fsize; /* length of file */ + unsigned char *buf; /* file content */ + + int tbase; /* header: text base */ + int tlen; /* text length */ + int dbase; /* data base */ + int dlen; /* data length */ + int bbase; /* bss base */ + int blen; /* bss length */ + int zbase; /* zero base */ + int zlen; /* zero length */ + + int tdiff; /* text segment relocation diff */ + int ddiff; /* data segment relocation diff */ + int bdiff; /* bss segment relocation diff */ + int zdiff; /* zero segment relocation diff */ + + int tpos; /* position of text segment in file */ + int dpos; /* position of data segment in file */ + int upos; /* position of undef'd list in file */ + int trpos; /* position of text reloc tab in file */ + int drpos; /* position of data reloc tab in file */ + int gpos; /* position of globals list in file */ + + int lasttreloc; + int lastdreloc; + + int nundef; /* number of undefined labels */ + undefs *ud; /* undefined labels list NULL if none */ +} file65; + +/* globally defined lables are stored in this struct */ +typedef struct { + char *name; + int len; /* length of labelname */ + int fl; /* 0=ok, 1=multiply defined */ + int val; /* address value */ + int seg; /* segment */ + file65 *file; /* in which file is it? */ +} glob; + +file65 *load_file(char *fname); + +int read_options(unsigned char *f); +int read_undef(unsigned char *f, file65 *fp); +int len_reloc_seg(unsigned char *buf, int ri); +int reloc_seg(unsigned char *buf, int adr, int ri, int *lreloc, file65 *fp); +unsigned char *reloc_globals(unsigned char *, file65 *fp); +int read_globals(file65 *file); +int write_options(FILE *fp, file65 *file); +int write_reloc(file65 *fp[], int nfp, FILE *f); +int write_globals(FILE *fp); + +file65 file; +unsigned char cmp[] = { 1, 0, 'o', '6', '5' }; +unsigned char hdr[26] = { 1, 0, 'o', '6', '5', 0 }; + +void usage(FILE *fp) +{ + fprintf(fp, + "Usage: %s [OPTION]... [FILE]...\n" + "Linker for o65 object files\n" + "\n" + " -b? addr relocates segment `?' (i.e. `t' for text segment,\n" + " `d' for data, `b' for bss, and `z' for zeropage) to the new\n" + " address `addr'\n" + " -o file uses `file' as output file. Default is `a.o65'\n" + " -G suppress writing of globals\n" + " --version output version information and exit\n" + " --help display this help and exit\n", + programname); +} + +int main(int argc, char *argv[]) { + int noglob=0; + int i = 1; + int tbase = 0x0400, dbase = 0x1000, bbase = 0x4000, zbase = 0x0002; + int ttlen, tdlen, tblen, tzlen; + char *outfile = "a.o65"; + int j, jm; + file65 *file, **fp = NULL; + FILE *fd; + + if (argc <= 1) { + usage(stderr); + exit(1); + } + + if (strstr(argv[1], "--help")) { + usage(stdout); + exit(0); + } + + if (strstr(argv[1], "--version")) { + version(programname, progversion, author, copyright); + exit(0); + } + + /* read options */ + while(i=jm) fp=realloc(fp, (jm=(jm?jm*2:10))*sizeof(file65*)); + if(!fp) { fprintf(stderr,"Oops, no more memory\n"); exit(1); } + fp[j++] = f; + } + i++; + } + + /* now [tdbz]base holds new segment base address */ + /* set total length to zero */ + ttlen = tdlen = tblen = tzlen = 0; + + /* find new addresses for the files and read globals */ + for(i=0;itdiff = ((tbase + ttlen) - file->tbase); + file->ddiff = ((dbase + tdlen) - file->dbase); + file->bdiff = ((bbase + tblen) - file->bbase); + file->zdiff = ((zbase + tzlen) - file->zbase); +/*printf("tbase=%04x, file->tbase=%04x, ttlen=%04x -> tdiff=%04x\n", + tbase, file->tbase, ttlen, file->tdiff);*/ + + /* update globals (for result file) */ + ttlen += file->tlen; + tdlen += file->dlen; + tblen += file->blen; + tzlen += file->zlen; + + read_globals(file); + } + + for(i=0;ibuf, + file->tpos, + file->trpos, + &(file->lasttreloc), + file); + reloc_seg(file->buf, + file->dpos, + file->drpos, + &(file->lastdreloc), + file); + reloc_globals(file->buf+file->gpos, file); + + file->tbase += file->tdiff; + file->dbase += file->ddiff; + file->bbase += file->bdiff; + file->zbase += file->zdiff; + + file->lasttreloc += file->tbase - file->tpos; + file->lastdreloc += file->dbase - file->dpos; + + } + + hdr[ 6] = 0; hdr[ 7] = 0; + hdr[ 8] = tbase & 255; hdr[ 9] = (tbase>>8) & 255; + hdr[10] = ttlen & 255; hdr[11] = (ttlen >>8)& 255; + hdr[12] = dbase & 255; hdr[13] = (dbase>>8) & 255; + hdr[14] = tdlen & 255; hdr[15] = (tdlen >>8)& 255; + hdr[16] = bbase & 255; hdr[17] = (bbase>>8) & 255; + hdr[18] = tblen & 255; hdr[19] = (tblen >>8)& 255; + hdr[20] = zbase & 255; hdr[21] = (zbase>>8) & 255; + hdr[22] = tzlen & 255; hdr[23] = (tzlen >>8)& 255; + hdr[24] = 0; hdr[25] = 0; + + fd = fopen(outfile, "wb"); + if(!fd) { + fprintf(stderr,"Couldn't open output file %s (%s)\n", + outfile, strerror(errno)); + exit(2); + } + fwrite(hdr, 1, 26, fd); + /* this writes _all_ options from _all_files! */ + for(i=0;ibuf + fp[i]->tpos, 1, fp[i]->tlen, fd); + } + /* write data segment */ + for(i=0;ibuf + fp[i]->dpos, 1, fp[i]->dlen, fd); + } + write_reloc(fp, j, fd); + if(!noglob) { + write_globals(fd); + } else { + fputc(0,fd); + fputc(0,fd); + } + + fclose(fd); + return 0; +} + +/***************************************************************************/ + +int write_options(FILE *fp, file65 *file) { + return fwrite(file->buf+BUF, 1, file->tpos-BUF-1, fp); +} + +int read_options(unsigned char *buf) { + int c, l=0; + + c=buf[0]; + while(c && c!=EOF) { + c&=255; + l+=c; + c=buf[l]; + } + return ++l; +} + +int read_undef(unsigned char *buf, file65 *file) { + int i, n, l = 2, ll; + + n = buf[0] + 256*buf[1]; + + file->nundef = n; + + if (n == 0) { + file->ud = NULL; + } else { + file->ud = malloc(n*sizeof(undefs)); + if(!file->ud) { + fprintf(stderr,"Oops, no more memory\n"); + exit(1); + } + i=0; + while(iud[i].name = (char*) buf+l; + ll=l; + while(buf[l++]); + file->ud[i].len = l-ll-1; +/*printf("read undef '%s'(%p), len=%d, ll=%d, l=%d, buf[l]=%d\n", + file->ud[i].name, file->ud[i].name, file->ud[i].len,ll,l,buf[l]);*/ + i++; + } + } + return l; +} + +/* compute and return the length of the relocation table */ +int len_reloc_seg(unsigned char *buf, int ri) { + int type, seg; + + while(buf[ri]) { + if((buf[ri] & 255) == 255) { + ri++; + } else { + ri++; + type = buf[ri] & 0xe0; + seg = buf[ri] & 0x07; +/*printf("reloc entry @ rtab=%p (offset=%d), adr=%04x, type=%02x, seg=%d\n",buf+ri-1, *(buf+ri-1), adr, type, seg);*/ + ri++; + switch(type) { + case 0x80: + break; + case 0x40: + ri++; + break; + case 0x20: + break; + } + if(seg==0) ri+=2; + } + } + return ++ri; +} + +#define reldiff(s) (((s)==2)?fp->tdiff:(((s)==3)?fp->ddiff:(((s)==4)?fp->bdiff:(((s)==5)?fp->zdiff:0)))) + +unsigned char *reloc_globals(unsigned char *buf, file65 *fp) { + int n, old, new, seg; + + n = buf[0] + 256*buf[1]; + buf +=2; + + while(n) { +/*printf("relocating %s, ", buf);*/ + while(*(buf++)); + seg = *buf; + old = buf[1] + 256*buf[2]; + new = old + reldiff(seg); +/*printf("old=%04x, seg=%d, rel=%04x, new=%04x\n", old, seg, reldiff(seg), new);*/ + buf[1] = new & 255; + buf[2] = (new>>8) & 255; + buf +=3; + n--; + } + return buf; +} + +/***************************************************************************/ + +file65 *load_file(char *fname) { + file65 *file; + struct stat fs; + FILE *fp; + int mode, hlen; + size_t n; + + file=malloc(sizeof(file65)); + if(!file) { + fprintf(stderr,"Oops, not enough memory!\n"); + exit(1); + } + +/*printf("load_file(%s)\n",fname);*/ + + file->fname=fname; + stat(fname, &fs); + file->fsize=fs.st_size; + file->buf=malloc(file->fsize); + if(!file->buf) { + fprintf(stderr,"Oops, no more memory!\n"); + exit(1); + } + + fp = fopen(fname,"rb"); + if(fp) { + n = fread(file->buf, 1, file->fsize, fp); + fclose(fp); + if((n>=file->fsize) && (!memcmp(file->buf, cmp, 5))) { + mode=file->buf[7]*256+file->buf[6]; + if(mode & 0x2000) { + fprintf(stderr,"file65: %s: 32 bit size not supported\n", fname); + free(file->buf); free(file); file=NULL; + } else + if(mode & 0x4000) { + fprintf(stderr,"file65: %s: pagewise relocation not supported\n", + fname); + free(file->buf); free(file); file=NULL; + } else { + hlen = BUF+read_options(file->buf+BUF); + + file->tbase = file->buf[ 9]*256+file->buf[ 8]; + file->tlen = file->buf[11]*256+file->buf[10]; + file->dbase = file->buf[13]*256+file->buf[12]; + file->dlen = file->buf[15]*256+file->buf[14]; + file->bbase = file->buf[17]*256+file->buf[16]; + file->blen = file->buf[19]*256+file->buf[18]; + file->zbase = file->buf[21]*256+file->buf[20]; + file->zlen = file->buf[23]*256+file->buf[21]; + + file->tpos = hlen; + file->dpos = hlen + file->tlen; + file->upos = file->dpos + file->dlen; + file->trpos= file->upos + read_undef(file->buf+file->upos, file); + file->drpos= len_reloc_seg(file->buf, file->trpos); + file->gpos = len_reloc_seg(file->buf, file->drpos); + } + } else + fprintf(stderr,"file65: %s: %s\n", fname, strerror(errno)); + } else + fprintf(stderr,"file65: %s: %s\n", fname, strerror(errno)); + + return file; +} + +/***************************************************************************/ + +glob *gp = NULL; +int gm=0; +int g=0; + +int write_reloc(file65 *fp[], int nfp, FILE *f) { + int tpc, pc, i; + unsigned char *p; + int low = 0, seg, typ, lab; + + /* no undefined labels ? TODO */ + fputc(0,f); + fputc(0,f); + + tpc = fp[0]->tbase-1; + + for(i=0;itbase-1; + p = fp[i]->buf + fp[i]->trpos; + + while(*p) { + while((*p)==255) { pc+=254; p++; } + pc+=*(p++); + seg=(*p)&7; + typ=(*p)&0xe0; + if(typ==0x40) low=*(++p); + p++; + if(seg==0) { + lab=p[0]+256*p[1]; + seg=gp[lab].seg; + p+=2; + } + if(seg>1) { + while(pc-tpc>254) { + fputc(255,f); + tpc+=254; + } + fputc(pc-tpc, f); + tpc=pc; + fputc(typ | seg, f); + if(typ==0x40) { + fputc(low,f); + } + } + } + } + fputc(0,f); + + tpc = fp[0]->dbase-1; + + for(i=0;idbase-1; + p = fp[i]->buf + fp[i]->drpos; + + while(*p) { + while((*p)==255) { pc+=254; p++; } + pc+=*(p++); + seg=(*p)&7; + typ=(*p)&0xe0; + if(typ==0x40) low=*(++p); + p++; + if(seg==0) { + lab=p[0]+256*p[1]; + seg=gp[lab].seg; + p+=2; + } + if(seg>1) { + while(pc-tpc>254) { + fputc(255,f); + tpc+=254; + } + fputc(pc-tpc, f); + tpc=pc; + fputc(typ | seg, f); + if(typ==0x40) { + fputc(low,f); + } + } + } + } + fputc(0,f); + + return 0; +} + +int write_globals(FILE *fp) { + int i; + + fputc(g&255, fp); + fputc((g>>8)&255, fp); + + for(i=0;i>8)&255); + } + return 0; +} + +int read_globals(file65 *fp) { + int i, l, n, old, new, seg, ll; + char *name; + unsigned char *buf = fp->buf + fp->gpos; + + n = buf[0] + 256*buf[1]; + buf +=2; + + while(n) { +/*printf("reading %s, ", buf);*/ + name = (char*) buf; + l=0; + while(buf[l++]); + buf+=l; + ll=l-1; + seg = *buf; + old = buf[1] + 256*buf[2]; + new = old + reldiff(seg); +/*printf("old=%04x, seg=%d, rel=%04x, new=%04x\n", old, seg, reldiff(seg), new);*/ + + /* multiply defined? */ + for(i=0;ifname, gp[i].file->fname); + gp[i].fl = 1; + break; + } + } + /* not already defined */ + if(i>=g) { + if(g>=gm) { + gp = realloc(gp, (gm=(gm?2*gm:40))*sizeof(glob)); + if(!gp) { + fprintf(stderr,"Oops, no more memory\n"); + exit(1); + } + } + if(g>=0x10000) { + fprintf(stderr,"Outch, maximum number of labels (65536) exceeded!\n"); + exit(3); + } + gp[g].name = name; + gp[g].len = ll; + gp[g].seg = seg; + gp[g].val = new; + gp[g].fl = 0; + gp[g].file = fp; +/*printf("set label '%s' (l=%d, seg=%d, val=%04x)\n", gp[g].name, + gp[g].len, gp[g].seg, gp[g].val);*/ + g++; + } + + buf +=3; + n--; + } + return 0; +} + +int find_global(unsigned char *bp, file65 *fp, int *seg) { + int i,l; + char *n; + int nl = bp[0]+256*bp[1]; + + l=fp->ud[nl].len; + n=fp->ud[nl].name; +/*printf("find_global(%s (len=%d))\n",n,l);*/ + + for(i=0;i>8) & 255; +/*printf("return gp[%d]=%s (len=%d), val=%04x\n",i,gp[i].name,gp[i].len,gp[i].val);*/ + return gp[i].val; + } + } + fprintf(stderr,"Warning: undefined label '%s' in file %s\n", + n, fp->fname); + return 0; +} + +int reloc_seg(unsigned char *buf, int pos, int ri, int *lreloc, file65 *fp) { + int type, seg, old, new; + + /* + pos = position of segment in *buf + ri = position of relocation table in *buf + */ + pos--; +/*printf("reloc_seg: adr=%04x, tdiff=%04x, ddiff=%04x, bdiff=%04x, zdiff=%04x\n", pos, fp->tdiff, fp->ddiff, fp->bdiff, fp->zdiff); */ + while(buf[ri]) { + if((buf[ri] & 255) == 255) { + pos += 254; + ri++; + } else { + pos += buf[ri] & 255; + ri++; + type = buf[ri] & 0xe0; + seg = buf[ri] & 0x07; +/*printf("reloc entry @ ri=%04x, pos=%04x, type=%02x, seg=%d\n",ri, pos, type, seg);*/ + ri++; + switch(type) { + case 0x80: + old = buf[pos] + 256*buf[pos+1]; + if(seg) { + new = old + reldiff(seg); + } else { + new = old + find_global(buf+ri, fp, &seg); + ri += 2; /* account for label number */ + } +/*printf("old=%04x, new=%04x\n",old,new);*/ + buf[pos] = new & 255; + buf[pos+1] = (new>>8)&255; + break; + case 0x40: + old = buf[pos]*256 + buf[ri]; + if(seg) { + new = old + reldiff(seg); + } else { + new = old + find_global(buf+ri+1, fp, &seg); + ri += 2; /* account for label number */ + } + buf[pos] = (new>>8)&255; + buf[ri] = new & 255; + ri++; + break; + case 0x20: + old = buf[pos]; + if(seg) { + new = old + reldiff(seg); + } else { + new = old + find_global(buf+ri, fp, &seg); + ri += 2; /* account for label number */ + } + buf[pos] = new & 255; + break; + } + } + } + *lreloc = pos; + return ++ri; +} diff --git a/xa-2.3.5/misc/mkrom.sh b/xa-2.3.5/misc/mkrom.sh new file mode 100755 index 0000000..3f0297b --- /dev/null +++ b/xa-2.3.5/misc/mkrom.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# +# xa65 - 6502 cross assembler and utility suite +# mkrom.sh - assemble several 'romable' files into one binary +# Copyright (C) 1997 André Fachat (a.fachat@physik.tu-chemnitz.de) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +XA="../xa" +FILE=../file65 + +start=32768 +ende=65536 +romfile=rom65 + +next=$[ start + 2 ]; +pars="-A $next" + +umask 077 + +tmp1=/tmp/mkrom65.sh.$$.a +tmp2=/tmp/mkrom65.sh.$$.b +tmp3=/tmp/mkrom65.sh.$$.c + +echo -e "#include \nvoid main(int argc, char *argv[]) { printf(\"%c%c\",atoi(argv[1])&255,(atoi(argv[1])/256)&255);}" \ + > $tmp3; +cc $tmp3 -o $tmp2 + +while [ $# -ne 0 ]; do + case $1 in + -A) # get start address + start=$[ $2 ]; + shift + ;; + -E) # get end address + ende=$[ $2 ]; + shift + ;; + -R) # get ROM filename + romfile=$2; + shift + ;; + -O) # xa options + XA="$XA $2"; + shift + ;; + -S) # segment addresses - in xa option format + pars="$pars $2"; + shift + ;; + *) + break; + ;; + esac; + shift +done + +#get file list +list="$*" + + +echo -n > $romfile + +for i in $list; do + #echo "next=$next, start=$start, pars=$pars" + #echo "cmd= ${XA} -R $pars -o $tmp1 $i" + $XA -R $pars -o $tmp1 $i + pars=`$FILE -a 2 -P $tmp1`; + next=`$FILE -A 0 $tmp1`; + + $tmp2 $next >> $romfile + cat $tmp1 >> $romfile; + +done; + +$tmp2 65535 >> $romfile + +rm -f $tmp1 $tmp2 $tmp3 + diff --git a/xa-2.3.5/misc/printcbm.c b/xa-2.3.5/misc/printcbm.c new file mode 100644 index 0000000..1ab7c23 --- /dev/null +++ b/xa-2.3.5/misc/printcbm.c @@ -0,0 +1,109 @@ +/* printcbm -- A part of xa65 - 65xx/65816 cross-assembler and utility suite + * list CBM BASIC programs + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include "version.h" + +#define programname "printcbm" +#define progversion "v1.0.0" +#define author "Written by André Fachat" +#define copyright "Copyright (C) 1997-2002 André Fachat." + +char *cmd[] = { + "end", "for", "next", "data", "input#", "input", "dim", "read", + "let", "goto", "run", "if", "restore", "gosub", "return", + "rem", "stop", "on", "wait", "load", "save", "verify", "def", + "poke", "print#", "print", "cont", "list", "clr", "cmd", "sys", + "open", "close", "get", "new", "tab(", "to", "fn", "spc(", + "then", "not", "step", "+", "-", "*", "/", "^", "and", "or", + ">", "=", "<", "sgn", "int", "abs", "usr", "fre", "pos", "sqr", + "rnd", "log", "exp", "cos", "sin", "tan", "atn", "peek", "len", + "str$", "val", "asc", "chr$", "left$", "right$", "mid$", "go" +}; + +void usage(FILE *fp) +{ + fprintf(fp, + "Usage: %s [OPTION]... [FILE]...\n" + "List CBM BASIC programs\n" + "\n" + " --version output version information and exit\n" + " --help display this help and exit\n", + programname); +} + +int main(int argc, char *argv[]) +{ + FILE *fp; + int a, b, c; + + if (argc < 2) { + usage(stderr); + exit(1); + } + + if (strstr(argv[1], "--help")) { + usage(stdout); + exit(0); + } + + if (strstr(argv[1], "--version")) { + version(programname, progversion, author, copyright); + exit(0); + } + + fp = fopen(argv[1], "rb"); + + if (fp) { + b = fgetc(fp); + b = fgetc(fp); + + while (b != EOF) { + a = fgetc(fp); + a = a + 256 * fgetc(fp); + + if (a) { + a = fgetc(fp); + a = a + 256 * fgetc(fp); + printf("%d ", a); + + while ((c = fgetc(fp))) { + if (c == EOF) + break; + if (c >= 0x80 && c < 0xcc) + printf("%s", cmd[c - 0x80]); + else + printf("%c", c); + } + printf("\n"); + } else { + break; + } + } + fclose(fp); + } else { + printf("File %s not found!\n", argv[1]); + } + + return 0; +} diff --git a/xa-2.3.5/misc/reloc65.c b/xa-2.3.5/misc/reloc65.c new file mode 100644 index 0000000..9b528f7 --- /dev/null +++ b/xa-2.3.5/misc/reloc65.c @@ -0,0 +1,384 @@ +/* reloc65 -- A part of xa65 - 65xx/65816 cross-assembler and utility suite + * o65 file relocator + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "version.h" + +#define BUF (9*2+8) /* 16 bit header */ + +#define programname "reloc65" +#define progversion "v0.2.1" +#define author "Written by André Fachat" +#define copyright "Copyright (C) 1997-2002 André Fachat." + +typedef struct { + char *fname; + size_t fsize; + unsigned char *buf; + int tbase, tlen, dbase, dlen, bbase, blen, zbase, zlen; + int tdiff, ddiff, bdiff, zdiff; + unsigned char *segt; + unsigned char *segd; + unsigned char *utab; + unsigned char *rttab; + unsigned char *rdtab; + unsigned char *extab; +} file65; + + +int read_options(unsigned char *f); +int read_undef(unsigned char *f); +unsigned char *reloc_seg(unsigned char *f, int len, unsigned char *rtab, file65 *fp, int undefwarn); +unsigned char *reloc_globals(unsigned char *, file65 *fp); + +file65 file; +unsigned char cmp[] = { 1, 0, 'o', '6', '5' }; + +void usage(FILE *fp) +{ + fprintf(fp, + "Usage: %s [OPTION]... [FILE]...\n" + "Relocator for o65 object files\n" + "\n" + " -b? addr relocates segment '?' (i.e. 't' for text segment,\n" + " 'd' for data, 'b' for bss and 'z' for zeropage) to the new\n" + " address `addr'\n" + " -o file uses `file' as output file. Default is `a.o65'\n" + " -x? extracts text `?' = `t' or data `?' = `d' segment from file\n", + programname); + fprintf(fp, + " instead of writing back the whole file\n" + " -X extracts the file such that text and data\n" + " segments are chained, i.e. possibly relocating\n" + " the data segment to the end of the text segment\n" + " --version output version information and exit\n" + " --help display this help and exit\n"); +} + +int main(int argc, char *argv[]) { + int i = 1, mode, hlen; + size_t n; + FILE *fp; + int tflag = 0, dflag = 0, bflag = 0, zflag = 0; + int tbase = 0, dbase = 0, bbase = 0, zbase = 0; + char *outfile = "a.o65"; + int extract = 0; + + if (argc <= 1) { + usage(stderr); + exit(1); + } + + if (strstr(argv[1], "--help")) { + usage(stdout); + exit(0); + } + + if (strstr(argv[1], "--version")) { + version(programname, progversion, author, copyright); + exit(0); + } + + while(i %s\n",argv[i],outfile); + fp = fopen(argv[i],"rb"); + if(fp) { + n = fread(file.buf, 1, file.fsize, fp); + fclose(fp); + if((n>=file.fsize) && (!memcmp(file.buf, cmp, 5))) { + mode=file.buf[7]*256+file.buf[6]; + if(mode & 0x2000) { + fprintf(stderr,"reloc65: %s: 32 bit size not supported\n", argv[i]); + } else + if(mode & 0x4000) { + fprintf(stderr,"reloc65: %s: pagewise relocation not supported\n", argv[i]); + } else { + hlen = BUF+read_options(file.buf+BUF); + + file.tbase = file.buf[ 9]*256+file.buf[ 8]; + file.tlen = file.buf[11]*256+file.buf[10]; + file.tdiff = tflag? tbase - file.tbase : 0; + file.dbase = file.buf[13]*256+file.buf[12]; + file.dlen = file.buf[15]*256+file.buf[14]; + if (extract == 3) { + if (dflag) { + fprintf(stderr,"reloc65: %s: Warning: data segment address ignored for -X option\n", argv[i]); + } + dbase = file.tbase + file.tdiff + file.tlen; + file.ddiff = dbase - file.dbase; + } else { + file.ddiff = dflag? dbase - file.dbase : 0; + } + file.bbase = file.buf[17]*256+file.buf[16]; + file.blen = file.buf[19]*256+file.buf[18]; + file.bdiff = bflag? bbase - file.bbase : 0; + file.zbase = file.buf[21]*256+file.buf[20]; + file.zlen = file.buf[23]*256+file.buf[21]; + file.zdiff = zflag? zbase - file.zbase : 0; + + file.segt = file.buf + hlen; + file.segd = file.segt + file.tlen; + file.utab = file.segd + file.dlen; + + file.rttab = file.utab + read_undef(file.utab); + file.rdtab = reloc_seg(file.segt, file.tlen, file.rttab, + &file, extract); + file.extab = reloc_seg(file.segd, file.dlen, file.rdtab, + &file, extract); + + reloc_globals(file.extab, &file); + + if(tflag) { + file.buf[ 9]= (tbase>>8)&255; + file.buf[ 8]= tbase & 255; + } + if(dflag) { + file.buf[13]= (dbase>>8)&255; + file.buf[12]= dbase & 255; + } + if(bflag) { + file.buf[17]= (bbase>>8)&255; + file.buf[16]= bbase & 255; + } + if(zflag) { + file.buf[21]= (zbase>>8)&255; + file.buf[20]= zbase & 255; + } + + fp = fopen(outfile, "wb"); + if(fp) { + switch(extract) { + case 0: /* whole file */ + fwrite(file.buf, 1, file.fsize, fp); + break; + case 1: /* text segment */ + fwrite(file.segt, 1, file.tlen, fp); + break; + case 2: /* data segment */ + fwrite(file.segd, 1, file.dlen, fp); + break; + case 3: /* text+data */ + fwrite(file.segt, 1, file.tlen, fp); + fwrite(file.segd, 1, file.dlen, fp); + break; + } + fclose(fp); + } else { + fprintf(stderr,"reloc65: write '%s': %s\n", + outfile, strerror(errno)); + } + } + } else { + fprintf(stderr,"reloc65: %s: not an o65 file!\n", argv[i]); + if(file.buf[0]==1 && file.buf[1]==8 && file.buf[3]==8) { + printf("%s: C64 BASIC executable (start address $0801)?\n", argv[i]); + } else + if(file.buf[0]==1 && file.buf[1]==4 && file.buf[3]==4) { + printf("%s: CBM PET BASIC executable (start address $0401)?\n", argv[i]); + } + } + } else { + fprintf(stderr,"reloc65: read '%s': %s\n", + argv[i], strerror(errno)); + } + } + i++; + } + exit(0); +} + + +int read_options(unsigned char *buf) { + int c, l=0; + + c=buf[0]; + while(c && c!=EOF) { + c&=255; + l+=c; + c=buf[l]; + } + return ++l; +} + +int read_undef(unsigned char *buf) { + int n, l = 2; + + n = buf[0] + 256*buf[1]; + while(n){ + n--; + while(buf[l] != 0) { + l++; + } + l++; + } + return l; +} + +#define reldiff(s) (((s)==2)?fp->tdiff:(((s)==3)?fp->ddiff:(((s)==4)?fp->bdiff:(((s)==5)?fp->zdiff:0)))) + +unsigned char *reloc_seg(unsigned char *buf, int len, unsigned char *rtab, + file65 *fp, int undefwarn) { + int adr = -1; + int type, seg, old, new; +/*printf("tdiff=%04x, ddiff=%04x, bdiff=%04x, zdiff=%04x\n", + fp->tdiff, fp->ddiff, fp->bdiff, fp->zdiff);*/ + while(*rtab) { + if((*rtab & 255) == 255) { + adr += 254; + rtab++; + } else { + adr += *rtab & 255; + rtab++; + type = *rtab & 0xe0; + seg = *rtab & 0x07; +/*printf("reloc entry @ rtab=%p (offset=%d), adr=%04x, type=%02x, seg=%d\n",rtab-1, *(rtab-1), adr, type, seg);*/ + rtab++; + switch(type) { + case 0x80: /* WORD - two byte address */ + old = buf[adr] + 256*buf[adr+1]; + new = old + reldiff(seg); + buf[adr] = new & 255; + buf[adr+1] = (new>>8)&255; + break; + case 0x40: /* HIGH - high byte of an address */ + old = buf[adr]*256 + *rtab; + new = old + reldiff(seg); + buf[adr] = (new>>8)&255; + *rtab = new & 255; + rtab++; + break; + case 0x20: /* LOW - low byt of an address */ + old = buf[adr]; + new = old + reldiff(seg); + buf[adr] = new & 255; + break; + } + if(seg==0) { + /* undefined segment entry */ + if (undefwarn) { + fprintf(stderr,"reloc65: %s: Warning: undefined relocation table entry not handled!\n", fp->fname); + } + rtab+=2; + } + } + } + if(adr > len) { + fprintf(stderr,"reloc65: %s: Warning: relocation table entries past segment end!\n", + fp->fname); + fprintf(stderr, "reloc65: adr=%x len=%x\n", adr, len); + } + return ++rtab; +} + +unsigned char *reloc_globals(unsigned char *buf, file65 *fp) { + int n, old, new, seg; + + n = buf[0] + 256*buf[1]; + buf +=2; + + while(n) { +/*printf("relocating %s, ", buf);*/ + while(*(buf++)); + seg = *buf; + old = buf[1] + 256*buf[2]; + new = old + reldiff(seg); +/*printf("old=%04x, seg=%d, rel=%04x, new=%04x\n", old, seg, reldiff(seg), new);*/ + buf[1] = new & 255; + buf[2] = (new>>8) & 255; + buf +=3; + n--; + } + return buf; +} + diff --git a/xa-2.3.5/misc/uncpk.c b/xa-2.3.5/misc/uncpk.c new file mode 100644 index 0000000..353d1d4 --- /dev/null +++ b/xa-2.3.5/misc/uncpk.c @@ -0,0 +1,207 @@ +/* reloc65 -- A part of xa65 - 65xx/65816 cross-assembler and utility suite + * Pack/Unpack cpk archive files + * + * Copyright (C) 1989-2002 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include "version.h" + +#define max(a, b) (a > b) ? a : b +#define min(a, b) (a < b) ? a : b + +#define programname "uncpk" +#define progversion "v0.2.1" +#define author "Written by André Fachat" +#define copyright "Copyright (C) 1997-2002 André Fachat." + +FILE *fp; +char name[100]; +char *s; + +void usage(FILE *fp) +{ + fprintf(fp, + "Usage: %s [OPTION]... [FILE]...\n" + "Manage c64 cpk archives\n" + "\n" + " c create an archive\n" + " a add a file to an archive\n" + " x extract archive\n" + " l list contents of archive\n" + " v verbose output\n" + " --version output version information and exit\n" + " --help display this help and exit\n", + programname); +} + +int list=0,verbose=0,add=0,create=0; + +int main(int argc, char *argv[]) +{ + int i,c,c2,fileok, nc; + size_t n,n2; + FILE *fp,*fpo=NULL; + + if (argc <= 1) { + usage(stderr); + exit(1); + } + + if (strstr(argv[1], "--help")) { + usage(stdout); + exit(0); + } + + if (strstr(argv[1], "--version")) { + version(programname, progversion, author, copyright); + exit(0); + } + + if(strchr(argv[1],(int)'l')) { + list=1; + } + if(strchr(argv[1],(int)'v')) { + verbose=1; + } + if(strchr(argv[1],(int)'a')) { + add=1; + } + if(strchr(argv[1],(int)'c')) { + create=1; + } + + if(add||create) { + if (argc <= 3) { + usage(stderr); + exit(1); + } + if(add) { + fpo=fopen(argv[2],"ab"); + } else + if(create) { + fpo=fopen(argv[2],"wb"); + } + if(fpo) { + if(!add) fputc(1,fpo); /* Version Byte */ + for(i=3;i=4 || c==0xf7) { + n2=min(255,n); + fprintf(fpo,"\xf7%c%c",(char)n2,(char)c); + n-=n2; + } else { + fputc(c,fpo); + n--; + } + } + c=c2; + } + fclose(fp); + fputc(0xf7,fpo); fputc(0,fpo); + } else { + fprintf(stderr,"Couldn't open file '%s' for reading!",argv[i]); + } + } + fclose(fpo); + } else { + fprintf(stderr,"Couldn't open file '%s' for writing!",argv[1]); + } + } else { + if (argc != 3) { + usage(stderr); + exit(1); + } + fp=fopen(argv[2],"rb"); + if(fp){ + if(fgetc(fp)==1){ + do{ + /* read name */ + i=0; + while((c=fgetc(fp))){ + if(c==EOF) break; + name[i++]=c; + } + name[i++]='\0'; + if(!c){ /* end of archive ? */ + while((s=strchr(name,'/'))) *s=':'; + + if(verbose+list) printf("%s\n",name); + + if(!list) { + fpo=fopen(name,"wb"); + if(!fpo) { + fprintf(stderr,"Couldn't open output file %s !\n",name); + } + } + fileok=0; + while((c=fgetc(fp))!=EOF){ + /* test if 'compressed' */ + if(c==0xf7){ + nc=fgetc(fp); + if(!nc) { + fileok=1; + break; + } + c=fgetc(fp); + if(fpo) { /* extract */ + if(nc!=EOF && c!=EOF) { + nc &= 255; + while(nc--) { + fputc(c,fpo); + } + } + } + } else { + if(fpo) { + fputc(c,fpo); + } + } + } + if(fpo) { + fclose(fpo); + fpo=NULL; + } + if(!fileok) { + fprintf(stderr,"Unexpected end of file!\n"); + } + } + } while(c!=EOF); + } else + fprintf(stderr,"Wrong Version!\n"); + fclose(fp); + } else { + fprintf(stderr,"File %s not found!\n",argv[1]); + } + } + return(0); +} diff --git a/xa-2.3.5/misc/version.h b/xa-2.3.5/misc/version.h new file mode 100644 index 0000000..cdfbd09 --- /dev/null +++ b/xa-2.3.5/misc/version.h @@ -0,0 +1,37 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_VERSION_H__ +#define __XA65_VERSION_H__ + +void version(const char *programname, const char *progversion, + const char *authors, const char *copyright) +{ + fprintf(stdout, + "%s (xa65) %s\n" + "%s\n" + "\n" + "%s\n" + "This is free software; see the source for " + "copying conditions. There is NO\n" + "warranty; not even for MERCHANTABILIY or " + "FITNESS FOR A PARTICULAR PURPOSE.\n", + programname, progversion, authors, copyright); +} + +#endif /* __XA65_VERSION_H__ */ diff --git a/xa-2.3.5/src/Makefile b/xa-2.3.5/src/Makefile new file mode 100644 index 0000000..dc15ea6 --- /dev/null +++ b/xa-2.3.5/src/Makefile @@ -0,0 +1,18 @@ +OBJ = xa.o xaa.o xal.o xap.o xat.o xar.o xar2.o xao.o xau.o xam.o xacharset.o + +#CFLAGS=-W -Wall -pedantic -ansi #-g +#CFLAGS=-W -Wall -ansi -O2 +#LD = ${CC} +#LDFLAGS = "-lc" + +all: xa + +xa: ${OBJ} + ${LD} -o ../xa ${OBJ} ${LDFLAGS} + +clean: + rm -f *.o *.o65 + +mrproper: clean + rm -f ../xa + diff --git a/xa-2.3.5/src/version.h b/xa-2.3.5/src/version.h new file mode 100644 index 0000000..cdfbd09 --- /dev/null +++ b/xa-2.3.5/src/version.h @@ -0,0 +1,37 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_VERSION_H__ +#define __XA65_VERSION_H__ + +void version(const char *programname, const char *progversion, + const char *authors, const char *copyright) +{ + fprintf(stdout, + "%s (xa65) %s\n" + "%s\n" + "\n" + "%s\n" + "This is free software; see the source for " + "copying conditions. There is NO\n" + "warranty; not even for MERCHANTABILIY or " + "FITNESS FOR A PARTICULAR PURPOSE.\n", + programname, progversion, authors, copyright); +} + +#endif /* __XA65_VERSION_H__ */ diff --git a/xa-2.3.5/src/xa.c b/xa-2.3.5/src/xa.c new file mode 100644 index 0000000..1bebcf8 --- /dev/null +++ b/xa-2.3.5/src/xa.c @@ -0,0 +1,1131 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * maintained by Cameron Kaiser (ckaiser@floodgap.com) + * + * Main program + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#endif + +/* macros */ +#include "xad.h" + +/* structs and defs */ +#include "xah.h" +#include "xah2.h" + +/* exported functions are defined here */ +#include "xa.h" +#include "xal.h" +#include "xam.h" +#include "xao.h" +#include "xap.h" +#include "xar.h" +#include "xat.h" +#include "xacharset.h" + +#include "version.h" + +/* ANZERR: total number of errors */ +/* ANZWARN: total number of warnings */ + +#define ANZERR 64 +#define ANZWARN 13 + +#define programname "xa" +#define progversion "v2.3.5" +#define authors "Written by Andre Fachat, Jolse Maginnis, David Weinehall and Cameron Kaiser" +#define copyright "Copyright (C) 1989-2009 Andre Fachat, Jolse Maginnis, David Weinehall\nand Cameron Kaiser." + +/* exported globals */ +int ncmos, cmosfl, w65816, n65816; +int masm = 0; +int nolink = 0; +int romable = 0; +int romaddr = 0; +int noglob = 0; +int showblk = 0; +int crossref = 0; +char altppchar; + +/* local variables */ +static char out[MAXLINE]; +static time_t tim1, tim2; +static FILE *fpout, *fperr, *fplab; +static int ner = 0; + +static int align = 1; + +static void printstat(void); +static void usage(int, FILE *); +static int setfext(char *, char *); +static int x_init(void); +static int pass1(void); +static int pass2(void); +static int puttmp(int); +static int puttmps(signed char *, int); +static void chrput(int); +static int xagetline(char *); +static void lineout(void); +static long ga_p1(void); +static long gm_p1(void); + +/* text */ +int memode,xmode; +int segment; +int tlen=0, tbase=0x1000; +int dlen=0, dbase=0x0400; +int blen=0, bbase=0x4000; +int zlen=0, zbase=4; +int fmode=0; +int relmode=0; + +int pc[SEG_MAX]; /* segments */ + +int main(int argc,char *argv[]) +{ + int er=1,i; + signed char *s=NULL; + char *tmpp; + + int mifiles = 5; + int nifiles = 0; + int verbose = 0; + int oldfile = 0; + int no_link = 0; + + char **ifiles; + char *ofile; + char *efile; + char *lfile; + char *ifile; + + char old_e[MAXLINE]; + char old_l[MAXLINE]; + char old_o[MAXLINE]; + + tim1=time(NULL); + + ncmos=0; + n65816=0; + cmosfl=1; + w65816=0; /* default: 6502 only */ + + altppchar = '#' ; /* i.e., NO alternate char */ + + if((tmpp = strrchr(argv[0],'/'))) { + tmpp++; + } else { + tmpp = argv[0]; + } + if( (!strcmp(tmpp,"xa65816")) + || (!strcmp(tmpp,"XA65816")) + || (!strcmp(tmpp,"xa816")) + || (!strcmp(tmpp,"XA816")) + ) { + w65816 = 1; /* allow 65816 per default */ + } + + /* default output charset for strings in quotes */ + set_charset("ASCII"); + + ifiles = malloc(mifiles*sizeof(char*)); + + afile = alloc_file(); + + if (argc <= 1) { + usage(w65816, stderr); + exit(1); + } + + if (strstr(argv[1], "--help")) { + usage(w65816, stdout); + exit(0); + } + + if (strstr(argv[1], "--version")) { + version(programname, progversion, authors, copyright); + exit(0); + } + + ofile="a.o65"; + efile=NULL; + lfile=NULL; + + if(pp_init()) { + logout("fatal: pp: no memory!"); + return 1; + } + if(b_init()) { + logout("fatal: b: no memory!"); + return 1; + } + if(l_init()) { + logout("fatal: l: no memory!"); + return 1; + } + + i=1; + while(i filename */ + ifiles[nifiles++] = argv[i]; + if(nifiles>=mifiles) { + mifiles += 5; + ifiles=realloc(ifiles, mifiles*sizeof(char*)); + if(!ifiles) { + fprintf(stderr, "Oops: couldn't alloc enough mem for filelist table..!\n"); + exit(1); + } + } + } + i++; + } + if(!nifiles) { + fprintf(stderr, "No input files given!\n"); + exit(0); + } + + if(oldfile) { + strcpy(old_e, ifiles[0]); + strcpy(old_o, ifiles[0]); + strcpy(old_l, ifiles[0]); + + if(setfext(old_e,".err")==0) efile = old_e; + if(setfext(old_o,".obj")==0) ofile = old_o; + if(setfext(old_l,".lab")==0) lfile = old_l; + } + + fplab= lfile ? xfopen(lfile,"w") : NULL; + fperr= efile ? xfopen(efile,"w") : NULL; + if(!strcmp(ofile,"-")) { + ofile=NULL; + fpout = stdout; + } else { + fpout= xfopen(ofile,"wb"); + } + if(!fpout) { + fprintf(stderr, "Couldn't open output file!\n"); + exit(1); + } + + if(verbose) fprintf(stderr, "%s\n",copyright); + + if(1 /*!m_init()*/) + { + if(1 /*!b_init()*/) + { + if(1 /*!l_init()*/) + { + /*if(!pp_init())*/ + { + if(!x_init()) + { + if(fperr) fprintf(fperr,"%s\n",copyright); + if(verbose) logout(ctime(&tim1)); + + /* Pass 1 */ + + pc[SEG_ABS]= 0; /* abs addressing */ + seg_start(fmode, tbase, dbase, bbase, zbase, 0, relmode); + + if(relmode) { + r_mode(RMODE_RELOC); + segment = SEG_TEXT; + } else { + r_mode(RMODE_ABS); + } + + nolink = no_link; + + for (i=0; ifname)); + + if(!er) { + er=pass1(); + pp_close(); + } else { + sprintf(out, "Couldn't open source file '%s'!\n", ifile); + logout(out); + } + } + + if((er=b_depth())) { + sprintf(out,"Still %d blocks open at end of file!\n",er); + logout(out); + } + + if(tbase & (align-1)) { + sprintf(out,"Warning: text segment ($%04x) start address doesn't align to %d!\n", tbase, align); + logout(out); + } + if(dbase & (align-1)) { + sprintf(out,"Warning: data segment ($%04x) start address doesn't align to %d!\n", dbase, align); + logout(out); + } + if(bbase & (align-1)) { + sprintf(out,"Warning: bss segment ($%04x) start address doesn't align to %d!\n", bbase, align); + logout(out); + } + if(zbase & (align-1)) { + sprintf(out,"Warning: zero segment ($%04x) start address doesn't align to %d!\n", zbase, align); + logout(out); + } + if (n65816>0) + fmode |= 0x8000; + switch(align) { + case 1: break; + case 2: fmode |= 1; break; + case 4: fmode |= 2; break; + case 256: fmode |=3; break; + } + + if((!er) && relmode) + h_write(fpout, fmode, tlen, dlen, blen, zlen, 0); + + + if(!er) + { + if(verbose) logout("xAss65: Pass 2:\n"); + + seg_pass2(); + + if(!relmode) { + r_mode(RMODE_ABS); + } else { + r_mode(RMODE_RELOC); + segment = SEG_TEXT; + } + er=pass2(); + } + + if(fplab) printllist(fplab); + tim2=time(NULL); + if(verbose) printstat(); + + if((!er) && relmode) seg_end(fpout); /* write reloc/label info */ + + if(fperr) fclose(fperr); + if(fplab) fclose(fplab); + if(fpout) fclose(fpout); + + } else { + logout("fatal: x: no memory!\n"); + } + pp_end(); +/* } else { + logout("fatal: pp: no memory!");*/ + } + } else { + logout("fatal: l: no memory!\n"); + } + } else { + logout("fatal: b: no memory!\n"); + } + /*m_exit();*/ + } else { + logout("Not enough memory available!\n"); + } + + if(ner || er) + { + fprintf(stderr, "Break after %d error%c\n",ner,ner?'s':0); + /*unlink();*/ + if(ofile) { + unlink(ofile); + } + } + + free(ifiles); + + return( (er || ner) ? 1 : 0 ); +} + +static void printstat(void) +{ + logout("Statistics:\n"); + sprintf(out," %8d of %8d label used\n",ga_lab(),gm_lab()); logout(out); + sprintf(out," %8ld of %8ld byte label-memory used\n",ga_labm(),gm_labm()); logout(out); + sprintf(out," %8d of %8d PP-defs used\n",ga_pp(),gm_pp()); logout(out); + sprintf(out," %8ld of %8ld byte PP-memory used\n",ga_ppm(),gm_ppm()); logout(out); + sprintf(out," %8ld of %8ld byte buffer memory used\n",ga_p1(),gm_p1()); logout(out); + sprintf(out," %8d blocks used\n",ga_blk()); logout(out); + sprintf(out," %8ld seconds used\n",(long)difftime(tim2,tim1)); logout(out); +} + +int h_length(void) { + return 26+o_length(); +} + +#if 0 +/* write header for relocatable output format */ +int h_write(FILE *fp, int tbase, int tlen, int dbase, int dlen, + int bbase, int blen, int zbase, int zlen) { + + fputc(1, fp); /* version byte */ + fputc(0, fp); /* hi address 0 -> no C64 */ + fputc("o", fp); + fputc("6", fp); + fputc("5", fp); + fputc(0, fp); /* format version */ + fputw(mode, fp); /* file mode */ + fputw(tbase,fp); /* text base */ + fputw(tlen,fp); /* text length */ + fputw(dbase,fp); /* data base */ + fputw(dlen,fp); /* data length */ + fputw(bbase,fp); /* bss base */ + fputw(blen,fp); /* bss length */ + fputw(zbase,fp); /* zerop base */ + fputw(zlen,fp); /* zerop length */ + + o_write(fp); + + return 0; +} +#endif + +static int setfext(char *s, char *ext) +{ + int j,i=(int)strlen(s); + + if(i>MAXLINE-5) + return(-1); + + for(j=i-1;j>=0;j--) + { + if(s[j]==DIRCHAR) + { + strcpy(s+i,ext); + break; + } + if(s[j]=='.') + { + strcpy(s+j,ext); + break; + } + } + if(!j) + strcpy(s+i,ext); + + return(0); +} + +/* +static char *tmp; +static unsigned long tmpz; +static unsigned long tmpe; +*/ + +static long ga_p1(void) +{ + return(afile->mn.tmpz); +} +static long gm_p1(void) +{ + return(TMPMEM); +} + +static int pass2(void) +{ + int c,er,l,ll,i,al; + Datei datei; + signed char *dataseg=NULL; + signed char *datap=NULL; + + memode=0; + xmode=0; + if((dataseg=malloc(dlen))) { + if(!dataseg) { + fprintf(stderr, "Couldn't alloc dataseg memory...\n"); + exit(1); + } + datap=dataseg; + } + filep=&datei; + afile->mn.tmpe=0L; + + while(ner<20 && afile->mn.tmpemn.tmpz) + { + l=afile->mn.tmp[afile->mn.tmpe++]; + ll=l; + + if(!l) + { + if(afile->mn.tmp[afile->mn.tmpe]==T_LINE) + { + datei.fline=(afile->mn.tmp[afile->mn.tmpe+1]&255)+(afile->mn.tmp[afile->mn.tmpe+2]<<8); + afile->mn.tmpe+=3; + } else + if(afile->mn.tmp[afile->mn.tmpe]==T_FILE) + { + datei.fline=(afile->mn.tmp[afile->mn.tmpe+1]&255)+(afile->mn.tmp[afile->mn.tmpe+2]<<8); + + memcpy(&datei.fname, afile->mn.tmp+afile->mn.tmpe+3, sizeof(datei.fname)); + afile->mn.tmpe+=3+sizeof(datei.fname); +/* + datei.fname = malloc(strlen((char*) afile->mn.tmp+afile->mn.tmpe+3)+1); + if(!datei.fname) { + fprintf(stderr,"Oops, no more memory\n"); + exit(1); + } + strcpy(datei.fname,(char*) afile->mn.tmp+afile->mn.tmpe+3); + afile->mn.tmpe+=3+strlen(datei.fname); +*/ + } + } else + { +/* do not attempt address mode optimization on pass 2 */ + er=t_p2(afile->mn.tmp+afile->mn.tmpe,&ll,1,&al); + + if(er==E_NOLINE) + { + } else + if(er==E_OK) + { + if(segmentmn.tmp[afile->mn.tmpe+i]); + } else if (segment==SEG_DATA && datap) { + memcpy(datap,afile->mn.tmp+afile->mn.tmpe,ll); + datap+=ll; + } + } else + if(er==E_DSB) + { + c=afile->mn.tmp[afile->mn.tmpe]; + if(segmentmn.tmp[afile->mn.tmpe]);*/ + for(i=0;imn.tmpe; +/* + fprintf(stderr, "ok, ready to insert\n"); + for (i=0; imn.tmp[afile->mn.tmpe+i]); + } +*/ + + offset = afile->mn.tmp[i] + + (afile->mn.tmp[i+1] << 8) + + (afile->mn.tmp[i+2] << 16); + fstart = afile->mn.tmp[i+3] + 1 + + (afile->mn.tmp[i+4] << 8); + /* usually redundant but here for single-char names + that get interpreted as chars */ + flen = afile->mn.tmp[i+5]; + if (flen > 1) fstart++; + /* now fstart points either to string past quote and + length mark, OR, single char byte */ +/* +fprintf(stderr, "offset = %i length = %i fstart = %i flen = %i charo = %c\n", + offset, ll, fstart, flen, afile->mn.tmp[afile->mn.tmpe+fstart]); +*/ + /* there is a race condition here where altering the + file between validation in t_p2 (xat.c) and + here will cause problems. I'm not going to + worry about this right now. */ + + for(j=0; jmn.tmp[i+fstart+j]; + } + binfnam[flen] = '\0'; +/* + fprintf(stderr, "fnam = %s\n", binfnam); +*/ + /* primitive insurance */ + if (!(foo = fopen(binfnam, "r"))) { + errout(E_FNF); + ner++; + } else { + fseek(foo, offset, SEEK_SET); + for(j=0; jmn.tmpe+=abs(l); + } + if(relmode) { + if((ll=fwrite(dataseg, 1, dlen, fpout))mn.tmpz); +*/ + } + + if(er!=E_EOF) { + fprintf(stderr, "foul through\n"); + errout(er); + } + + + +/* { int i; printf("Pass 1 \n"); + for(i=0;imn.tmpz;i++) + fprintf(stderr, " %02x",255 & afile->mn.tmp[i]); + getchar();} +*/ + return(ner); +} + +static void usage(int default816, FILE *fp) +{ + fprintf(fp, + "Usage: %s [options] file\n" + "Cross-assembler for 65xx/R65C02/65816\n" + "\n", + programname); + fprintf(fp, + " -v verbose output\n" + " -x old filename behaviour (overrides `-o', `-e', `-l')\n" + " This is deprecated and may disappear in future versions!\n" + " -C no CMOS-opcodes\n" + " -W no 65816-opcodes%s\n" + " -w allow 65816-opcodes%s\n", + default816 ? "" : " (default)", + default816 ? " (default)" : ""); + fprintf(fp, + " -B show lines with block open/close\n" + " -c produce `o65' object instead of executable files (i.e. don't link)\n" + " -o filename sets output filename, default is `a.o65'\n" + " A filename of `-' sets stdout as output file\n"); + fprintf(fp, + " -e filename sets errorlog filename, default is none\n" + " -l filename sets labellist filename, default is none\n" + " -r adds crossreference list to labellist (if `-l' given)\n" + " -M allow ``:'' to appear in comments for MASM compatibility\n" + " -R start assembler in relocating mode\n"); + fprintf(fp, + " -Llabel defines `label' as absolute, undefined label even when linking\n" + " -b? addr set segment base address to integer value addr\n" + " `?' stands for t(ext), d(ata), b(ss) and z(ero) segment\n" + " (address can be given more than once, last one is used)\n"); + fprintf(fp, + " -A addr make text segment start at an address that when the _file_\n" + " starts at addr, relocation is not necessary. Overrides -bt\n" + " Other segments must be specified with `-b?'\n" + " -G suppress list of exported globals\n"); + fprintf(fp, + " -DDEF=TEXT defines a preprocessor replacement\n" + " -Ocharset set output charset (PETSCII or ASCII), case-sensitive\n" + " -Idir add directory `dir' to include path (before XAINPUT)\n" + " --version output version information and exit\n" + " --help display this help and exit\n"); +} + +/* +static char *ertxt[] = { "Syntax","Label definiert", + "Label nicht definiert","Labeltabelle voll", + "Label erwartet","Speicher voll","Illegaler Opcode", + "Falsche Adressierungsart","Branch ausserhalb des Bereichs", + "Ueberlauf","Division durch Null","Pseudo-Opcode erwartet", + "Block-Stack-Ueberlauf","Datei nicht gefunden", + "End of File","Block-Struktur nicht abgeschlossen", + "NoBlk","NoKey","NoLine","OKDef","DSB","NewLine", + "NewFile","CMOS-Befehl","pp:Falsche Anzahl Parameter" }; +*/ +static char *ertxt[] = { + "Syntax", + "Label already defined", + "Label not defined", + "Label table full", + "Label expected", + "Out of memory", + "Illegal opcode", + "Wrong addressing mode", + "Branch out of range", + "Overflow", + "Division by zero", + "Pseudo-opcode expected", + "Block stack overflow", + "File not found", + "End of file", + "Unmatched block close", + "NoBlk", + "NoKey", + "NoLine", + "OKDef", + "DSB", + "NewLine", + "NewFile", + "CMOS-Befehl", + "pp:Wrong parameter count", + "Illegal pointer arithmetic", + "Illegal segment", + "File header option too long", + "File option not at file start (when ROM-able)", + "Illegal align value", + "65816 mode used/required", + "Exceeded recursion limit for label evaluation", + "Unresolved preprocessor directive at end of file", + "Data underflow", + "Illegal quantity", + ".bin", +/* placeholders for future fatal errors */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", +/* warnings */ + "Cutting word relocation in byte value", + "Byte relocation in word value", + "Illegal pointer arithmetic", + "Address access to low or high byte pointer", + "High byte access to low byte pointer", + "Low byte access to high byte pointer", + "Can't optimize forward-defined label; using absolute addressing", + "Open preprocessor directive at end of file (intentional?)", + "Included binary data exceeds 64KB", + "Included binary data exceeds 16MB", +/* more placeholders */ + "", + "", + "", + + }; + +static int gl; +static int gf; + +static int x_init(void) +{ + return 0; +#if 0 + int er=0; + /*er=m_alloc(TMPMEM,&tmp);*/ + afile->mn.tmp=malloc(TMPMEM); + if(!afile->mn.tmp) er=E_NOMEM; + afile->mn.tmpz=0L; + return(er); +#endif +} + +static int puttmp(int c) +{ + int er=E_NOMEM; +/*printf("puttmp: afile=%p, tmp=%p, tmpz=%d\n",afile, afile?afile->mn.tmp:0, afile?afile->mn.tmpz:0);*/ + if(afile->mn.tmpzmn.tmp[afile->mn.tmpz++]=c; + er=E_OK; + } + return(er); +} + +static int puttmps(signed char *s, int l) +{ + int i=0,er=E_NOMEM; + + if(afile->mn.tmpz+lmn.tmp[afile->mn.tmpz++]=s[i++]; + + er=E_OK; + } + return(er); +} + +static char l[MAXLINE]; + +static int xagetline(char *s) +{ + static int ec; + + static int i,c; + int hkfl,j,comcom; + + j=hkfl=comcom=0; + ec=E_OK; + + if(!gl) + { + do + { + ec=pgetline(l); + i=0; + while(l[i]==' ') + i++; + while(l[i]!='\0' && isdigit(l[i])) + i++; + gf=1; + + if(ec==E_NEWLINE) + { + puttmp(0); + puttmp(T_LINE); + puttmp((filep->fline)&255); + puttmp(((filep->fline)>>8)&255); + ec=E_OK; + + } + else + if(ec==E_NEWFILE) + { + puttmp(0); + puttmp(T_FILE); + puttmp((filep->fline)&255); + puttmp(((filep->fline)>>8)&255); + puttmps((signed char*)&(filep->fname), sizeof(filep->fname)); +/* + puttmps((signed char*)filep->fname, + 1+(int)strlen(filep->fname)); +*/ + ec=E_OK; + } + } while(!ec && l[i]=='\0'); + } + + gl=0; + if(!ec || ec==E_EOF) + { + do { + c=s[j]=l[i++]; + + if (c=='\"') + hkfl^=1; + if (c==';' && !hkfl) + comcom = 1; + if (c=='\0') + break; /* hkfl = comcom = 0 */ + if (c==':' && !hkfl && (!comcom || !masm)) { + gl=1; + break; + } + j++; + } while (c!='\0' && jalign)?a:align; +} + +static void lineout(void) +{ + if(gf) + { + logout(filep->flinep); + logout("\n"); + gf=0; + } +} + +void errout(int er) +{ + if (er<-ANZERR || er>-1) { + if(er>=-(ANZERR+ANZWARN) && er < -ANZERR) { + sprintf(out,"%s:line %d: %04x: Warning - %s\n", + filep->fname, filep->fline, pc[segment], ertxt[(-er)-1]); + } else { + /* sprintf(out,"%s:Zeile %d: %04x:Unbekannter Fehler Nr.: %d\n",*/ + sprintf(out,"%s:line %d: %04x: Unknown error # %d\n", + filep->fname,filep->fline,pc[segment],er); + ner++; + } + } else { + if (er==E_NODEF) + sprintf(out,"%s:line %d: %04x:Label '%s' not defined\n", + filep->fname,filep->fline,pc[segment],lz); + else + sprintf(out,"%s:line %d: %04x:%s error\n", + filep->fname,filep->fline,pc[segment],ertxt[(-er)-1]); + + ner++; + } + logout(out); +} + +static void chrput(int c) +{ + /* printf(" %02x",c&255);*/ + + putc( c&0x00ff,fpout); +} + +void logout(char *s) +{ + fprintf(stderr, "%s",s); + if(fperr) + fprintf(fperr,"%s",s); +} + diff --git a/xa-2.3.5/src/xa.h b/xa-2.3.5/src/xa.h new file mode 100644 index 0000000..67ee3fe --- /dev/null +++ b/xa-2.3.5/src/xa.h @@ -0,0 +1,49 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_XA_H__ +#define __XA65_XA_H__ + +#include "xah.h" /* For SEG_MAX */ + +extern int ncmos, cmosfl, w65816, n65816; +extern int masm, nolink; +extern int noglob; +extern int showblk; +extern int relmode; +extern int crossref; +extern char altppchar; + +extern int tlen, tbase; +extern int blen, bbase; +extern int dlen, dbase; +extern int zlen, zbase; +extern int romable, romaddr; + +extern int memode,xmode; +extern int segment; +extern int pc[SEG_MAX]; + +int h_length(void); + +void set_align(int align_value); + +void errout(int er); +void logout(char *s); + +#endif /*__XA65_XA_H__ */ diff --git a/xa-2.3.5/src/xaa.c b/xa-2.3.5/src/xaa.c new file mode 100644 index 0000000..73dd861 --- /dev/null +++ b/xa-2.3.5/src/xaa.c @@ -0,0 +1,274 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * Preprocessing arithmetic module + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include "xah.h" + +#include "xad.h" +#include "xar.h" +#include "xa.h" +#include "xal.h" +#include "xaa.h" +#include "xat.h" + +static int pr[]= { P_START,P_ADD,P_ADD,P_MULT,P_MULT,P_SHIFT,P_SHIFT,P_CMP, + P_CMP,P_EQU,P_CMP,P_CMP,P_EQU,P_AND,P_XOR,P_OR, + P_LAND,P_LOR }; + +static int pp,pcc; +static int fundef; + +static int ag_term(signed char*,int,int*,int*,int*); +static int get_op(signed char*,int*); +static int do_op(int*,int,int); + +/* s = string, v = variable */ +int a_term(signed char *s, int *v, int *l, int xpc, int *pfl, int *label, int f) +{ + int er=E_OK; + int afl = 0, bfl; + + *pfl = 0; + fundef = f; + + pp=0; + pcc=xpc; + + if(s[0]=='<') + { + pp++; + er=ag_term(s,P_START,v,&afl, label); + bfl = afl & (A_MASK>>8); + if( bfl && (bfl != (A_ADR>>8)) && (bfl != (A_LOW>>8)) ) { +/*fprintf(stderr,"low byte relocation for a high byte - won't work!\n");*/ + errout(W_LOWACC); + } + if(afl) *pfl=A_LOW | ((afl<<8) & A_FMASK); + *v = *v & 255; + } else + if(s[pp]=='>') + { + pp++; + er=ag_term(s,P_START,v,&afl, label); + bfl = afl & (A_MASK>>8); + if( bfl && (bfl != (A_ADR>>8)) && (bfl != (A_HIGH>>8)) ) { +/*fprintf(stderr,"high byte relocation for a low byte - won't work!\n");*/ + errout(W_HIGHACC); + } + if(afl) *pfl=A_HIGH | ((afl<<8) & A_FMASK) | (*v & 255); + *v=(*v>>8)&255; + } + else { + er=ag_term(s,P_START,v,&afl, label); + bfl = afl & (A_MASK>>8); + if(bfl && (bfl != (A_ADR>>8)) ) { +/*fprintf(stderr,"address relocation for a low or high byte - won't work!\n");*/ + errout(W_ADDRACC); + } + if(afl) *pfl = A_ADR | ((afl<<8) & A_FMASK); + } + + *l=pp; +/* printf("a_term: afl->%04x *pfl=%04x, (pc=%04x)\n",afl,*pfl, xpc); */ + return(er); +} + +static int ag_term(signed char *s, int p, int *v, int *nafl, int *label) +{ + int er=E_OK,o,w,mf=1,afl; + + afl = 0; + +/*printf("ag_term(%02x %02x %02x %02x %02x %02x\n",s[0],s[1],s[2],s[3],s[4],s[5]);*/ + while(s[pp]=='-') + { + pp++; + mf=-mf; + } + + if(s[pp]=='(') + { + pp++; + if(!(er=ag_term(s,P_START,v,&afl,label))) + { + if(s[pp]!=')') + er=E_SYNTAX; + else + pp++; + } + } else + if(s[pp]==T_LABEL) + { + er=l_get(cval(s+pp+1),v, &afl); +/* printf("label: er=%d, seg=%d, afl=%d, nolink=%d, fundef=%d\n", + er, segment, afl, nolink, fundef); */ + if(er==E_NODEF && segment != SEG_ABS && fundef ) { + if( nolink || (afl==SEG_UNDEF)) { + er = E_OK; + *v = 0; + afl = SEG_UNDEF; + *label = cval(s+pp+1); + } + } + pp+=3; + } + else + if(s[pp]==T_VALUE) + { + *v=lval(s+pp+1); + pp+=4; +/* printf("value: v=%04x\n",*v); */ + } + else + if(s[pp]==T_POINTER) + { + afl = s[pp+1]; + *v=cval(s+pp+2); + pp+=4; +/* printf("pointer: v=%04x, afl=%04x\n",*v,afl); */ + } + else + if(s[pp]=='*') + { + *v=pcc; + pp++; + afl = segment; + } + else { + er=E_SYNTAX; + } + + *v *= mf; + + while(!er && s[pp]!=')' && s[pp]!=']' && s[pp]!=',' && s[pp]!=T_END) + { + er=get_op(s,&o); + + if(!er && pr[o]>p) + { + pp+=1; + if(!(er=ag_term(s,pr[o],&w, nafl, label))) + { + if(afl || *nafl) { /* check pointer arithmetic */ + if((afl == *nafl) && (afl!=SEG_UNDEF) && o==2) { + afl = 0; /* substract two pointers */ + } else + if(((afl && !*nafl) || (*nafl && !afl)) && o==1) { + afl=(afl | *nafl); /* add constant to pointer */ + } else + if((afl && !*nafl) && o==2) { + afl=(afl | *nafl); /* substract constant from pointer */ + } else { + if(segment!=SEG_ABS) { + if(!dsb_len) { + er=E_ILLPOINTER; + } + } + afl=0; + } + } + if(!er) er=do_op(v,w,o); + } + } else { + break; + } + } + *nafl = afl; + return(er); +} + +static int get_op(signed char *s, int *o) +{ + int er; + + *o=s[pp]; + + if(*o<1 || *o>17) + er=E_SYNTAX; + else + er=E_OK; + + return(er); +} + +static int do_op(int *w,int w2,int o) +{ + int er=E_OK; + switch (o) { + case 1: + *w +=w2; + break; + case 2: + *w -=w2; + break; + case 3: + *w *=w2; + break; + case 4: + if (w!=0) + *w /=w2; + else + er =E_DIV; + break; + case 5: + *w >>=w2; + break; + case 6: + *w <<=w2; + break; + case 7: + *w = *ww2; + break; + case 9: + *w = *w==w2; + break; + case 10: + *w = *w<=w2; + break; + case 11: + *w = *w>=w2; + break; + case 12: + *w = *w!=w2; + break; + case 13: + *w &=w2; + break; + case 14: + *w ^=w2; + break; + case 15: + *w |=w2; + break; + case 16: + *w =*w&&w2; + break; + case 17: + *w =*w||w2; + break; + } + return(er); +} + diff --git a/xa-2.3.5/src/xaa.h b/xa-2.3.5/src/xaa.h new file mode 100644 index 0000000..bbae273 --- /dev/null +++ b/xa-2.3.5/src/xaa.h @@ -0,0 +1,26 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_XAA_H__ +#define __XA65_XAA_H__ + +/* f = 0 -> label must exist; f = 1 -> SEG_UNDEF entry */ +int a_term(signed char *s, int *v, int *l, int xpc, int *afl, + int *label, int f); + +#endif /* __XA65_XAA_H__ */ diff --git a/xa-2.3.5/src/xacharset.c b/xa-2.3.5/src/xacharset.c new file mode 100644 index 0000000..4f74b48 --- /dev/null +++ b/xa-2.3.5/src/xacharset.c @@ -0,0 +1,125 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 Andre Fachat (a.fachat@physik.tu-chemnitz.de) + * Maintained by Cameron Kaiser + * + * Charset conversion module + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include + +#include "xacharset.h" + +static signed char (*convert_func)(signed char); + +static signed char convert_char_ascii(signed char c) { + return c; +} + +/* + * PETSCII conversion roughly follows the PETSCII to UNICODE + * mapping at http://www.df.lth.se/~triad/krad/recode/petscii_c64en_lc.txt + * linked from wikipedia http://en.wikipedia.org/wiki/PETSCII + */ +static signed char convert_char_petscii(signed char c) { + if (c >= 0x41 && c < 0x5b) { + return c + 0x20; + } + if (c >= 0x61 && c < 0x7b) { + return c - 0x20; + } + if (c == 0x7f) { + return 0x14; + } + return c; +} + +/* + * Built upon Steve Judd's suggested PETSCII -> screen code algorithm + * This could probably be written a lot better, but it works. + * http://www.floodgap.com/retrobits/ckb/display.cgi?572 + */ +static signed char convert_char_petscreen(signed char c) { + int i; + + i = (int)convert_char_petscii(c); +#ifdef SIGH +fprintf(stderr, "input: %i output: %i\n", c, i); +#endif + if (i< 0) + i += 0x80; + i ^= 0xe0; +#ifdef SIGH +fprintf(stderr, "(1)input: %i output: %i\n", c, i); +#endif + i += 0x20; + i &= 0xff; +#ifdef SIGH +fprintf(stderr, "(2)input: %i output: %i\n", c, i); +#endif + if (i < 0x80) + return (signed char)i; + i += 0x40; + i &= 0xff; +#ifdef SIGH +fprintf(stderr, "(3)input: %i output: %i\n", c, i); +#endif + if (i < 0x80) + return (signed char)i; + i ^= 0xa0; +#ifdef SIGH +fprintf(stderr, "(4)input: %i output: %i\n", c, i); +#endif + return (signed char)i; +} + +static signed char convert_char_high(signed char c) { + return (c | 0x80); +} + +typedef struct { + char *name; + signed char (*func)(signed char); +} charset; + +static charset charsets[] = { + { "ASCII", convert_char_ascii }, + { "PETSCII", convert_char_petscii }, + { "PETSCREEN", convert_char_petscreen }, + { "HIGH", convert_char_high }, + { NULL, NULL } +}; + +int set_charset(char *charset_name) { + int i = 0; + while (charsets[i].name != NULL) { + if (strcmp(charsets[i].name, charset_name) == 0) { + convert_func = charsets[i].func; + return 0; + } + i++; + } + return -1; +} + +signed char convert_char(signed char c) { + return convert_func(c); +} + + diff --git a/xa-2.3.5/src/xacharset.h b/xa-2.3.5/src/xacharset.h new file mode 100644 index 0000000..f42ae6e --- /dev/null +++ b/xa-2.3.5/src/xacharset.h @@ -0,0 +1,31 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-2006 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_XA_CHARSET_H__ +#define __XA65_XA_CHARSET_H__ + +/* set the target character set the chars - values in quotes - should + be converted to + returns 0 on success and -1 when the name is not found */ +int set_charset(char *charset_name); + +/* convert a char */ +signed char convert_char(signed char c); + +#endif /*__XA65_XA_H__ */ + diff --git a/xa-2.3.5/src/xad.h b/xa-2.3.5/src/xad.h new file mode 100644 index 0000000..79528fc --- /dev/null +++ b/xa-2.3.5/src/xad.h @@ -0,0 +1,41 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_XAD_H__ +#define __XA65_XAD_H__ + +#ifndef abs +#define abs(a) (a >= 0) ? a : -a +#endif + +#define hashcode(n, l) (n[0] & 0x0f) | (((l - 1) ? (n[1] & 0x0f) : 0) << 4) +#define fputw(a, fp) do { \ + fputc(a & 255, fp); \ + fputc((a >> 8) & 255, fp); \ + } while (0) + +#define cval(s) 256 * ((s)[1] & 255) + ((s)[0]&255) +#define lval(s) 65536 * ((s)[2] & 255) + 256 * ((s)[1] & 255) + ((s)[0] & 255) +#define wval(i, v) do { \ + t[i++] = T_VALUE; \ + t[i++] = v & 255; \ + t[i++] = (v >> 8) & 255; \ + t[i++] = (v >> 16) & 255; \ + } while (0) + +#endif /* __XA65_XAD_H__ */ diff --git a/xa-2.3.5/src/xah.h b/xa-2.3.5/src/xah.h new file mode 100644 index 0000000..8b98bc8 --- /dev/null +++ b/xa-2.3.5/src/xah.h @@ -0,0 +1,227 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * Maintained by Cameron Kaiser + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_XAH_H__ +#define __XA65_XAH_H__ + +#define ANZLAB 5000 /* mal 14 -> Byte */ +#define LABMEM 40000L +#define MAXLAB 32 +#define MAXBLK 16 +#define MAXFILE 7 +#define MAXLINE 2048 +#define MAXPP 40000L +#define ANZDEF 2340 /* mal 14 -> Byte, ANZDEF * 14 < 32768 */ +#define TMPMEM 200000L /* Zwischenspeicher von Pass1 nach Pass 2 */ + +typedef struct LabOcc { + struct LabOcc *next; + int line; + char *fname; +} LabOcc; + +typedef struct { + int blk; + int val; + int len; + int fl; /* 0 = label value not valid/known, + * 1 = label value known + */ + int afl; /* 0 = no address (no relocation), 1 = address label */ + int nextindex; + char *n; + struct LabOcc *occlist; +} Labtab; + +typedef struct { + char *search; + int s_len; + char *replace; + int p_anz; + int nextindex; +} List; + + +#define MEMLEN (4 + TMPMEM + MAXPP + LABMEM + \ + (long)(sizeof (Labtab) * ANZLAB) + \ + (long)(sizeof (List) * ANZDEF)) + +#define DIRCHAR '/' +#define DIRCSTRING "/" +/* for Atari: +#define DIRCHAR '\\' +#define DIRCSTRING "\\" +*/ + +#define BUFSIZE 4096 /* File-Puffegroesse (wg Festplatte) */ + +#define E_OK 0 /* Fehlernummern */ +#define E_SYNTAX -1 /* Syntax Fehler */ +#define E_LABDEF -2 /* Label definiert */ +#define E_NODEF -3 /* Label nicht definiert */ +#define E_LABFULL -4 /* Labeltabelle voll */ +#define E_LABEXP -5 /* Label erwartet */ +#define E_NOMEM -6 /* kein Speicher mehr */ +#define E_ILLCODE -7 /* Illegaler Opcode */ +#define E_ADRESS -8 /* Illegale Adressierung */ +#define E_RANGE -9 /* Branch out of range */ +#define E_OVERFLOW -10 /* Ueberlauf */ +#define E_DIV -11 /* Division durch Null */ +#define E_PSOEXP -12 /* Pseudo-Opcode erwartet */ +#define E_BLKOVR -13 /* Block-Stack Uebergelaufen */ +#define E_FNF -14 /* File not found (pp) */ +#define E_EOF -15 /* End of File */ +#define E_BLOCK -16 /* Block inkonsistent */ +#define E_NOBLK -17 +#define E_NOKEY -18 +#define E_NOLINE -19 +#define E_OKDEF -20 /* okdef */ +#define E_DSB -21 +#define E_NEWLINE -22 +#define E_NEWFILE -23 +#define E_CMOS -24 +#define E_ANZPAR -25 +#define E_ILLPOINTER -26 /* illegal pointer arithmetic! */ +#define E_ILLSEGMENT -27 /* illegal pointer arithmetic! */ +#define E_OPTLEN -28 /* file header option too long */ +#define E_ROMOPT -29 /* header option not directly after + * file start in romable mode + */ +#define E_ILLALIGN -30 /* illegal align value */ + +#define E_65816 -31 + +#define E_ORECMAC -32 /* exceeded recursion limit for label eval */ +#define E_OPENPP -33 /* open preprocessor directive */ +#define E_OUTOFDATA -34 /* out of data */ +#define E_ILLQUANT -35 /* generic illegal quantity error */ +#define E_BIN -36 /* okdef */ +/* errors thru 63 are placeholders */ + +#define W_ADRRELOC -64 /* word relocation in byte value */ +#define W_BYTRELOC -65 /* byte relocation in word value */ +#define E_WPOINTER -66 /* illegal pointer arithmetic! */ +#define W_ADDRACC -67 /* addr access to low or high byte pointer */ +#define W_HIGHACC -68 /* high byte access to low byte pointer */ +#define W_LOWACC -69 /* low byte access to high byte pointer */ +#define W_FORLAB -70 /* no zp-optimization for a forward label */ +#define W_OPENPP -71 /* warning about open preprocessor directive */ +#define W_OVER64K -72 /* included binary over 64K in 6502 mode */ +#define W_OVER16M -73 /* included binary over 16M in 65816 mode */ +/* warnings 74-76 are placeholders */ + +#define T_VALUE -1 +#define T_LABEL -2 +#define T_OP -3 +#define T_END -4 +#define T_LINE -5 +#define T_FILE -6 +#define T_POINTER -7 + +#define P_START 0 /* Prioritaeten fuer Arithmetik */ +#define P_LOR 1 /* Von zwei Operationen wird immer */ +#define P_LAND 2 /* die mit der hoeheren Prioritaet */ +#define P_OR 3 /* zuerst ausgefuehrt */ +#define P_XOR 4 +#define P_AND 5 +#define P_EQU 6 +#define P_CMP 7 +#define P_SHIFT 8 +#define P_ADD 9 +#define P_MULT 10 +#define P_INV 11 + +#define A_ADR 0x8000 /* all are or'd with (afl = segment type)<<8 */ +#define A_HIGH 0x4000 /* or'd with the low byte */ +#define A_LOW 0x2000 +#define A_MASK 0xe000 /* reloc type mask */ +#define A_FMASK 0x0f00 /* segment type mask */ + +#define A_LONG 0xc000 + +#define FM_OBJ 0x1000 +#define FM_SIZE 0x2000 +#define FM_RELOC 0x4000 +#define FM_CPU 0x8000 + +#define SEG_ABS 0 +#define SEG_UNDEF 1 +#define SEG_TEXT 2 +#define SEG_DATA 3 +#define SEG_BSS 4 +#define SEG_ZERO 5 +#define SEG_MAX 6 + +typedef struct Fopt { + signed char *text; /* text after pass1 */ + int len; +} Fopt; + +typedef struct relocateInfo { + int next; + int adr; + int afl; + int lab; +} relocateInfo; + +typedef struct File { + int fmode; + int slen; + int relmode; + int old_abspc; + int base[SEG_MAX]; + int len[SEG_MAX]; + struct { + signed char *tmp; + unsigned long tmpz; + unsigned long tmpe; + } mn; + struct { + int *ulist; + int un; + int um; + } ud; + struct { + relocateInfo *rlist; + int mlist; + int nlist; + int first; + } rt; + struct { + relocateInfo *rlist; + int mlist; + int nlist; + int first; + } rd; + struct { + Fopt *olist; + int mlist; + int nlist; + } fo; + struct { + int hashindex[256]; + Labtab *lt; + int lti; + int ltm; + } la; +} File; + +extern File *afile; + +#endif /* __XA65_XAH_H__ */ diff --git a/xa-2.3.5/src/xah2.h b/xa-2.3.5/src/xah2.h new file mode 100644 index 0000000..2b64cc3 --- /dev/null +++ b/xa-2.3.5/src/xah2.h @@ -0,0 +1,30 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_XAH2_H__ +#define __XA65_XAH2_H__ + +typedef struct { + char *fname; /* fname[MAXLINE]; */ + int fline; + int bdepth; + FILE *filep; + char *flinep; +} Datei; + +#endif /* __XA65_XAH2_H__ */ diff --git a/xa-2.3.5/src/xal.c b/xa-2.3.5/src/xal.c new file mode 100644 index 0000000..5598c75 --- /dev/null +++ b/xa-2.3.5/src/xal.c @@ -0,0 +1,595 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * Label management module (also see xau.c) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +/* structs and defs */ + +#include "xad.h" +#include "xah.h" +#include "xar.h" +#include "xah2.h" +#include "xap.h" +#include "xa.h" + +/* externals */ + +#include "xam.h" +#include "xal.h" + +/* exported globals */ + +char *lz; + +/* local prototypes */ + +static int b_fget(int*,int); +static int b_ltest(int,int); +static int b_get(int*); +static int b_test(int); +static int ll_def(char *s, int *n, int b); + +/* local variables */ + +/* +static int hashindex[256]; +static Labtab *lt = NULL; +static int lti = 0; +static int ltm = 0; +*/ + +/* +static char *ln; +static unsigned long lni; +static long sl; +*/ + +static Labtab *ltp; + +int l_init(void) +{ + return 0; +#if 0 + int er; + + for(er=0;er<256;er++) + hashindex[er]=0; + + /*sl=(long)sizeof(Labtab);*/ + +/* if(!(er=m_alloc((long)(sizeof(Labtab)*ANZLAB),(char**)<))) + er=m_alloc((long)LABMEM,&ln);*/ + + er=m_alloc((long)(sizeof(Labtab)*ANZLAB),(char**)<); + + lti=0; +/* lni=0L;*/ + + return(er); +#endif +} + +int ga_lab(void) +{ + return(afile->la.lti); +} + +int gm_lab(void) +{ + return(ANZLAB); +} + +long gm_labm(void) +{ + return((long)LABMEM); +} + +long ga_labm(void) +{ + return(0 /*lni*/); +} + +void printllist(fp) +FILE *fp; +{ + int i; + LabOcc *p; + char *fname = NULL; + + for(i=0;ila.lti;i++) + { + ltp=afile->la.lt+i; + fprintf(fp,"%s, 0x%04x, %d, 0x%04x\n",ltp->n,ltp->val,ltp->blk, + ltp->afl); + p = ltp->occlist; + if(p) { + while(p) { + if(fname != p->fname) { + if(p!=ltp->occlist) fprintf(fp,"\n"); + fprintf(fp," %s",p->fname); + fname=p->fname; + } + fprintf(fp," %d", p->line); + p=p->next; + } + fprintf(fp,"\n"); + } + fname=NULL; + } +} + +int lg_set(char *s ) { + int n, er; + + er = ll_search(s,&n); + + if(er==E_OK) { + fprintf(stderr,"Warning: global label doubly defined!\n"); + } else { + if(!(er=ll_def(s,&n,0))) { + ltp=afile->la.lt+n; + ltp->fl=2; + ltp->afl=SEG_UNDEF; + } + } + return er; +} + + +int l_def(char *s, int *l, int *x, int *f) +{ + int n,er,b,i=0; + + *f=0; + b=0; + n=0; + + if(s[0]=='-') + { + *f+=1; + i++; + } else + if(s[0]=='+') + { + i++; + n++; + b=0; + } + while(s[i]=='&') + { + n=0; + i++; + b++; + } + if(!n) + b_fget(&b,b); + + + if(!isalpha(s[i]) && s[i]!='_') + er=E_SYNTAX; + else + { + er=ll_search(s+i,&n); + + if(er==E_OK) + { + ltp=afile->la.lt+n; + + if(*f) + { + *l=ltp->len+i; + } else + if(ltp->fl==0) + { + *l=ltp->len+i; + if(b_ltest(ltp->blk,b)) + er=E_LABDEF; + else + ltp->blk=b; + + } else + er=E_LABDEF; + } else + if(er==E_NODEF) + { + if(!(er=ll_def(s+i,&n,b))) /* ll_def(...,*f) */ + { + ltp=afile->la.lt+n; + *l=ltp->len+i; + ltp->fl=0; + } + } + + *x=n; + } + return(er); +} + +int l_search(char *s, int *l, int *x, int *v, int *afl) +{ + int n,er,b; + + *afl=0; + + er=ll_search(s,&n); +/*printf("l_search: lab=%s(l=%d), afl=%d, er=%d, n=%d\n",s,*l, *afl,er,n);*/ + if(er==E_OK) + { + ltp=afile->la.lt+n; + *l=ltp->len; + if(ltp->fl == 1) + { + l_get(n,v,afl);/* *v=lt[n].val;*/ + *x=n; + } else + { + er=E_NODEF; + lz=ltp->n; + *x=n; + } + } + else + { + b_get(&b); + er=ll_def(s,x,b); /* ll_def(...,*v); */ + + ltp=afile->la.lt+(*x); + + *l=ltp->len; + + if(!er) + { + er=E_NODEF; + lz=ltp->n; + } + } + return(er); +} + +int l_vget(int n, int *v, char **s) +{ + ltp=afile->la.lt+n; + (*v)=ltp->val; + *s=ltp->n; + return 0; +} + +void l_addocc(int n, int *v, int *afl) { + LabOcc *p, *pp; + + (void)v; /* quench warning */ + (void)afl; /* quench warning */ + ltp = afile->la.lt+n; + pp=NULL; + p = ltp->occlist; + while(p) { + if (p->line == filep->fline && p->fname == filep->fname) return; + pp = p; + p = p->next; + } + p = malloc(sizeof(LabOcc)); + if(!p) { + fprintf(stderr,"Oops, out of memory!\n"); + exit(1); + } + p->next = NULL; + p->line = filep->fline; + p->fname = filep->fname; + if(pp) { + pp->next = p; + } else { + ltp->occlist = p; + } +} + +int l_get(int n, int *v, int *afl) +{ + if(crossref) l_addocc(n,v,afl); + + ltp=afile->la.lt+n; + (*v)=ltp->val; + lz=ltp->n; + *afl = ltp->afl; +/*printf("l_get('%s'(%d), v=$%04x, afl=%d, fl=%d\n",ltp->n, n, *v, *afl, ltp->fl);*/ + return( (ltp->fl==1) ? E_OK : E_NODEF); +} + +void l_set(int n, int v, int afl) +{ + ltp=afile->la.lt+n; + ltp->val = v; + ltp->fl = 1; + ltp->afl = afl; +/*printf("l_set('%s'(%d), v=$%04x, afl=%d\n",ltp->n, n, v, afl);*/ +} + +static void ll_exblk(int a, int b) +{ + int i; + for (i=0;ila.lti;i++) + { + ltp=afile->la.lt+i; + if((!ltp->fl) && (ltp->blk==a)) + ltp->blk=b; + } +} + +static int ll_def(char *s, int *n, int b) /* definiert naechstes Label nr->n */ +{ + int j=0,er=E_NOMEM,hash; + char *s2; + +/*printf("ll_def: s=%s\n",s); */ + + if(!afile->la.lt) { + afile->la.lti = 0; + afile->la.ltm = 1000; + afile->la.lt = malloc(afile->la.ltm * sizeof(Labtab)); + } + if(afile->la.lti>=afile->la.ltm) { + afile->la.ltm *= 1.5; + afile->la.lt = realloc(afile->la.lt, afile->la.ltm * sizeof(Labtab)); + } + if(!afile->la.lt) { + fprintf(stderr, "Oops: no memory!\n"); + exit(1); + } +#if 0 + if((ltila.lt+afile->la.lti; +/* + s2=ltp->n=ln+lni; + + while((jlen=j; + ltp->n = s2; + ltp->blk=b; + ltp->fl=0; + ltp->afl=0; + ltp->occlist=NULL; + hash=hashcode(s,j); + ltp->nextindex=afile->la.hashindex[hash]; + afile->la.hashindex[hash]=afile->la.lti; + *n=afile->la.lti; + afile->la.lti++; +/* lni+=j+1;*/ +/* } + } +*/ +/*printf("ll_def return: %d\n",er);*/ + return(er); +} + + +int ll_search(char *s, int *n) /* search Label in Tabelle ,nr->n */ +{ + int i,j=0,k,er=E_NODEF,hash; + + while(s[j] && (isalnum(s[j])||(s[j]=='_'))) j++; + + hash=hashcode(s,j); + i=afile->la.hashindex[hash]; + +/*printf("search?\n");*/ + if(i>=afile->la.ltm) return E_NODEF; + + do + { + ltp=afile->la.lt+i; + + if(j==ltp->len) + { + for (k=0;(kn[k]==s[k]);k++); + + if((j==k)&&(!b_test(ltp->blk))) + { + er=E_OK; + break; + } + } + + if(!i) + break; + + i=ltp->nextindex; + + }while(1); + + *n=i; +#if 0 + if(er!=E_OK && er!=E_NODEF) + { + fprintf(stderr, "Fehler in ll_search:er=%d\n",er); + getchar(); + } +#endif + return(er); +} + +int ll_pdef(char *t) +{ + int n; + + if(ll_search(t,&n)==E_OK) + { + ltp=afile->la.lt+n; + if(ltp->fl) + return(E_OK); + } + return(E_NODEF); +} + +int l_write(FILE *fp) +{ + int i, afl, n=0; + + if(noglob) { + fputc(0, fp); + fputc(0, fp); + return 0; + } + for (i=0;ila.lti;i++) { + ltp=afile->la.lt+i; + if((!ltp->blk) && (ltp->fl==1)) { + n++; + } + } + fputc(n&255, fp); + fputc((n>>8)&255, fp); + for (i=0;ila.lti;i++) + { + ltp=afile->la.lt+i; + if((!ltp->blk) && (ltp->fl==1)) { + fprintf(fp, "%s",ltp->n); + fputc(0,fp); + afl = ltp->afl; + /* hack to switch undef and abs flag from internal to file format */ +/*printf("label %s, afl=%04x, A_FMASK>>8=%04x\n", ltp->n, afl, A_FMASK>>8);*/ + if( (afl & (A_FMASK>>8)) < SEG_TEXT) afl^=1; + fputc(afl,fp); + fputc(ltp->val&255, fp); + fputc((ltp->val>>8)&255, fp); + } + } + /*fputc(0,fp);*/ + return 0; +} + +static int bt[MAXBLK]; +static int blk; +static int bi; + +int b_init(void) +{ + blk =0; + bi =0; + bt[bi]=blk; + + return(E_OK); +} + +int b_depth(void) +{ + return bi; +} + +int ga_blk(void) +{ + return(blk); +} + +int b_open(void) +{ + int er=E_BLKOVR; + + if(bi=0) + *n=bt[bi-i]; + else + *n=0; + return(E_OK); +} + +static int b_test(int n) +{ + int i=bi; + + while( i>=0 && n!=bt[i] ) + i--; + + return( i+1 ? E_OK : E_NOBLK ); +} + +static int b_ltest(int a, int b) /* testet ob bt^-1(b) in intervall [0,bt^-1(a)] */ +{ + int i=0,er=E_OK; + + if(a!=b) + { + er=E_OK; + + while(i<=bi && b!=bt[i]) + { + if(bt[i]==a) + { + er=E_NOBLK; + break; + } + i++; + } + } + return(er); +} + diff --git a/xa-2.3.5/src/xal.h b/xa-2.3.5/src/xal.h new file mode 100644 index 0000000..98a1d75 --- /dev/null +++ b/xa-2.3.5/src/xal.h @@ -0,0 +1,53 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_XAL_H__ +#define __XA65_XAL_H__ + +#include /* for FILE */ + +extern char *lz; + +int l_init(void); +int ga_lab(void); +int gm_lab(void); +long gm_labm(void); +long ga_labm(void); + +int lg_set(char *); + +int b_init(void); +int b_depth(void); + +void printllist(FILE *fp); +int ga_blk(void); + +int l_def(char *s, int* l, int *x, int *f); +int l_search(char *s, int *l, int *x, int *v, int *afl); +void l_set(int n, int v, int afl); +int l_get(int n, int *v, int *afl); +int l_vget(int n, int *v, char **s); +int ll_search(char *s, int *n); +int ll_pdef(char *t); + +int b_open(void); +int b_close(void); + +int l_write(FILE *fp); + +#endif /* __XA65_XAL_H__ */ diff --git a/xa-2.3.5/src/xam.c b/xa-2.3.5/src/xam.c new file mode 100644 index 0000000..5e4b8ef --- /dev/null +++ b/xa-2.3.5/src/xam.c @@ -0,0 +1,188 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * Memory manager/malloc() stub module + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include "xah.h" /* structs */ + +static int ninc = 0; +static char **nip = NULL; + +void reg_include(char *path) { + char **nip2; + if(path && *path) { + nip2 = realloc(nip,sizeof(char*)*(ninc+1)); + if(nip2) { + nip = nip2; + nip[ninc++] = path; + } else { + fprintf(stderr,"Warning: couldn' alloc mem (reg_include)\n"); + } + } +} + +FILE *xfopen(const char *fn,const char *mode) +{ + FILE *file; + char c,*cp,n[MAXLINE],path[MAXLINE]; + char xname[MAXLINE], n2[MAXLINE]; + int i,l=(int)strlen(fn); + + if(l>=MAXLINE) { + fprintf(stderr,"filename '%s' too long!\n",fn); + return NULL; + } + + for(i=0;i +#include +#include + +#include "xah.h" +#include "xar.h" +#include "xa.h" +#include "xat.h" +#include "xao.h" + +/* +static Fopt *olist =NULL; +static int mlist =0; +static int nlist =0; +*/ + +/* sets file option after pass 1 */ +void set_fopt(int l, signed char *buf, int reallen) { +/*printf("set_fopt(%s, l=%d\n",buf,l);*/ + while(afile->fo.mlist<=afile->fo.nlist) { + afile->fo.mlist +=5; + afile->fo.olist = realloc(afile->fo.olist, afile->fo.mlist*sizeof(Fopt)); + if(!afile->fo.olist) { + fprintf(stderr, "Fatal: Couldn't alloc memory (%lu bytes) for fopt list!\n", + (unsigned long)( + afile->fo.mlist*sizeof(Fopt))); + exit(1); + } + } + afile->fo.olist[afile->fo.nlist].text=malloc(l); + if(!afile->fo.olist[afile->fo.nlist].text) { + fprintf(stderr, "Fatal: Couldn't alloc memory (%d bytes) for fopt!\n",l); + exit(1); + } + memcpy(afile->fo.olist[afile->fo.nlist].text, buf, l); + afile->fo.olist[afile->fo.nlist++].len = reallen; +} + +/* writes file options to a file */ +void o_write(FILE *fp) { + int i,j,l,afl; + signed char *t; + + for(i=0;ifo.nlist;i++) { + l=afile->fo.olist[i].len; + t=afile->fo.olist[i].text; +/* do not optimize */ + t_p2(t, &l, 1, &afl); + + if(l>254) { + errout(E_OPTLEN); + } else { + fputc((l+1)&0xff,fp); + } + for(j=0;jfo.nlist;i++) { + free(afile->fo.olist[i].text); + } + free(afile->fo.olist); + afile->fo.olist = NULL; + afile->fo.nlist = 0; + afile->fo.mlist = 0; +} + +size_t o_length(void) { + int i; + size_t n = 0; + for(i=0;ifo.nlist;i++) { +/*printf("found option: %s, len=%d, n=%d\n", afile->fo.olist[i].text, afile->fo.olist[i].len,n);*/ + n += afile->fo.olist[i].len +1; + } + return ++n; +} + diff --git a/xa-2.3.5/src/xao.h b/xa-2.3.5/src/xao.h new file mode 100644 index 0000000..2f2df40 --- /dev/null +++ b/xa-2.3.5/src/xao.h @@ -0,0 +1,31 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_XAO_H__ +#define __XA65_XAO_H__ + +/* sets file option after pass 1 */ +void set_fopt(int l, signed char *buf, int reallen); + +/* writes file options to a file */ +void o_write(FILE *fp); + +/* return overall length of header options */ +size_t o_length(void); + +#endif /* __XA65_XAO_H__ */ diff --git a/xa-2.3.5/src/xap.c b/xa-2.3.5/src/xap.c new file mode 100644 index 0000000..da8e3c3 --- /dev/null +++ b/xa-2.3.5/src/xap.c @@ -0,0 +1,1020 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * Maintained by Cameron Kaiser + * + * File handling and preprocessor (also see xaa.c) module + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#endif + +#include "xad.h" +#include "xah.h" +#include "xah2.h" + +#include "xar.h" +#include "xa.h" +#include "xam.h" +#include "xal.h" +#include "xat.h" +#include "xap.h" + +/* define this for recursive evaluation output */ +/* #define DEBUG_RECMAC */ + +char s[MAXLINE]; +Datei *filep; + +static int tcompare(char*,char**,int); +static int pp_replace(char*,char*,int,int); +static int searchdef(char*); +static int fgetline(char*,int len, int *rlen, FILE*); + +static int icl_open(char*),pp_ifdef(char*),pp_ifndef(char*); +static int pp_else(char*),pp_endif(char*); +static int pp_echo(char*),pp_if(char*),pp_print(char*),pp_prdef(char*); +static int pp_ifldef(char*),pp_iflused(char*); +static int pp_undef(char*); + +#define ANZBEF 13 +#define VALBEF 6 + +static int ungeteof = 0; + +static char *cmd[]={ "echo","include","define","undef","printdef","print", + "ifdef","ifndef","else","endif", + "ifldef","iflused","if" }; + +static int (*func[])(char*) = { pp_echo,icl_open,pp_define,pp_undef, + pp_prdef,pp_print, pp_ifdef,pp_ifndef, + pp_else,pp_endif, + pp_ifldef,pp_iflused,pp_if }; + +static char *mem; +static unsigned long memfre; +static int nlf; +static int nff; +static int hashindex[256]; + +static List *liste; +static unsigned int rlist; +static int fsp; +static int loopfl; +static Datei flist[MAXFILE+1]; +static char in_line[MAXLINE]; + +int pp_comand(char *t) +{ + int i,l,er=1; + + i=tcompare(t,cmd,ANZBEF); + + if(i>=0) + { + if(loopfl && (i>1; + return(0); +} + + +/* stub for handling CPP directives */ +int pp_cpp(char *t) { + + if(sscanf(t, " %d \"%s\"", &filep->fline, filep->fname) == 2) { + /* massage it into our parameters and drop last quote */ + char *u = ""; + + filep->fline--; + if((u = (char *)strrchr(filep->fname, '"'))) + *u = '\0'; + return (0); + } else { + return(E_SYNTAX); + } +} + +/* pp_undef is a great hack to get it working fast... */ +int pp_undef(char *t) { + int i; + if((i=searchdef(t))) { + i+=rlist; + liste[i].s_len=0; + } + return 0; +} + +int pp_prdef(char *t) +{ + char *x; + int i,j; + + if((i=searchdef(t))) + { + i+=rlist; + x=liste[i].search; + sprintf(s,"\n%s",x); + if(liste[i].p_anz) + { + sprintf(s+strlen(s),"("); + for(j=0;j=ANZDEF || memfre=ANZDEF || memfre10) + errout(E_ORECMAC); + } + (void)strcpy(liste[rlist+i].replace, nfto); +#ifdef DEBUG_RECMAC + printf("FINAL: -%s=%s\n",liste[rlist+i].search,liste[rlist+i].replace); +#endif + } + if(!er) + er=pp_replace(fto,fti,rlist,rlist+i); +/* if(flag) printf("sl=%d,",sl);*/ + sl=(int)((long)y+1L-(long)t); +/* if(flag) printf("sl=%d\n",sl);*/ + rs=fto; +/* printf("->%s\n",fto);*/ + } + } + if(er) + return(er); + } + + d=(int)strlen(rs)-sl; + + if(strlen(to)+d>=MAXLINE) + return(E_NOMEM); + +/* + if(d<0) + { + y=t+sl+d; + x=t+sl; + while(*y++=*x++); + } + if(d>0) + { + for(ll=strlen(t);ll>=sl;ll--) + t[ll+d]=t[ll]; + } +*/ + if(d) + (void)strcpy(t+sl+d,ti+sl); + + i=0; + while((c=rs[i])) + t[i++]=c; + l=sl+d;/*=0;*/ + break; + } + } + if(!n) + break; + + n=liste[n].nextindex; + + } while(1); + } else + { + for(n=b-1;n>=a;n--) + { + sl=liste[n].s_len; + + if(sl && (sl==l)) + { + i=0; + x=liste[n].search; + while(t[i]==*x++ && t[i]) + i++; + + if(i==sl) + { + rs=liste[n].replace; + if(liste[n].p_anz) + { + (void)strcpy(fti,liste[n].replace); + if(rlist+liste[n].p_anz>=ANZDEF || memfre=MAXLINE) + return(E_NOMEM); +/* + if(d<0) + { + y=t+sl+d; + x=t+sl; + while(*y++=*x++); + } + if(d>0) + { + for(ll=strlen(t);ll>=sl;ll--) + t[ll+d]=t[ll]; + } +*/ + if(d) + (void)strcpy(t+sl+d,ti+sl); + + i=0; + while((c=rs[i])) + t[i++]=c; + l+=d;/*0;*/ + break; + } + } + } + } + ti+=ld; + t+=l; + } + } + return(E_OK); +} + +int pp_init(void) +{ + int er; + + for(er=0;er<256;er++) + hashindex[er]=0; + + fsp=0; + + er=0; + mem=malloc(MAXPP); + if(!mem) er=E_NOMEM; + + memfre=MAXPP; + rlist=0; + nlf=1; + nff=1; + if(!er) { + liste=malloc((long)ANZDEF*sizeof(List)); + if(!liste) er=E_NOMEM; + } + return(er); +} + +int pp_open(char *name) +{ + FILE *fp; + + fp=xfopen(name,"r"); + + /* we have to alloc it dynamically to make the name survive another + pp_open - it's used in the cross-reference list */ + flist[0].fname = malloc(strlen(name)+1); + if(!flist[0].fname) { + fprintf(stderr,"Oops, no more memory!\n"); + exit(1); + } + (void)strcpy(flist[0].fname,name); + flist[0].fline=0; + flist[0].bdepth=b_depth(); + flist[0].filep=fp; + flist[0].flinep=NULL; + + return(((long)fp)==0l); +} + +void pp_close(void) +{ + if(flist[fsp].bdepth != b_depth()) { + fprintf(stderr, "Blocks not consistent in file %s: start depth=%d, end depth=%d\n", + flist[fsp].fname, flist[fsp].bdepth, b_depth()); + } + fclose(flist[fsp].filep); +} + +void pp_end(void) { } + +Datei *pp_getidat(void) { + return &flist[fsp]; +} + +int icl_close(int *c) +{ + *c='\n'; + if(!fsp) + return(E_EOF); + + if(flist[fsp].bdepth != b_depth()) { + fprintf(stderr, "Blocks not consistent in file %s: start depth=%d, end depth=%d\n", + flist[fsp].fname, flist[fsp].bdepth, b_depth()); + } + + fclose(flist[fsp--].filep); + nff=1; + + return(E_OK); +} + +int icl_open(char *tt) +{ + FILE *fp2; + int j,i=0; + + pp_replace(s,tt,-1,rlist); + + if(fsp>=MAXFILE) + return(-1); + + if(s[i]=='<' || s[i]=='"') + i++; + + for(j=i;s[j];j++) + if(s[j]=='>' || s[j]=='"') + s[j]='\0'; + + fp2=xfopen(s+i,"r"); + + if(!fp2) + return(E_FNF); + + setvbuf(fp2,NULL,_IOFBF,BUFSIZE); + + fsp++; + + /* we have to alloc it dynamically to make the name survive another + pp_open - it's used in the cross-reference list */ + flist[fsp].fname = malloc(strlen(s+i)+1); + if(!flist[fsp].fname) { + fprintf(stderr,"Oops, no more memory!\n"); + exit(1); + } + strcpy(flist[fsp].fname,s+i); + flist[fsp].fline=0; + flist[fsp].bdepth=b_depth(); + flist[fsp].flinep=NULL; + flist[fsp].filep=fp2; + nff=1; + + return(0); +} + +int pgetline(char *t) +{ + int c,er=E_OK; + int rlen, tlen; + char *p = 0; + + loopfl =0; /* set if additional fetch needed */ + + filep =flist+fsp; + + do { + c=fgetline(in_line, MAXLINE, &rlen, flist[fsp].filep); + /* continuation lines */ + tlen = rlen; + while(c=='\n' && tlen && in_line[tlen-1]=='\\') { + c=fgetline(in_line + tlen-1, MAXLINE-tlen, &rlen, flist[fsp].filep); + tlen += rlen-1; + } + if(in_line[0]=='#' || in_line[0] == altppchar) + { + if (in_line[1]==' ') { /* cpp comment -- pp_comand doesn't + handle this right */ + er=pp_cpp(in_line+1); + } else { + if((er=pp_comand(in_line+1))) + { + if(er!=1) + { + logout(in_line); + logout("\n"); + } + } + } + } else + er=1; + + if(c==EOF) { + if (loopfl && fsp) { + char bletch[MAXLINE]; + sprintf(bletch, + "at end of included file %s:\n", flist[fsp].fname); + logout(bletch); + errout(W_OPENPP); + } + er=icl_close(&c); + } + + } while(!er || (loopfl && er!=E_EOF)); + + if (loopfl) { + errout(E_OPENPP); + } + + /* handle the double-slash comment (like in C++) */ + p = strchr(in_line, '/'); + if (p != NULL) { + if (p[1] == '/') { + *p = 0; /* terminate string */ + } + } + + if(!er || loopfl) { + in_line[0]='\0'; + } + + er= (er==1) ? E_OK : er ; + + if(!er) + er=pp_replace(t,in_line,-1,rlist); + + if(!er && nff) + er=E_NEWFILE; + if(!er && nlf) + er=E_NEWLINE; + nlf=nff=0; + + filep=flist+fsp; + filep->flinep=in_line; + + return(er); +} + + +/*************************************************************************/ + +/* this is the most disgusting code I have ever written, but Andre drove me +to it because I can't think of any other F$%Y#*U(%&Y##^#KING way to fix the +last line bug ... a very irritated Cameron */ + +/* however, it also solved the problem of open #ifdefs not bugging out */ + +/* #define DEBUG_EGETC */ +int egetc(FILE *fp) { + int c; + + c = getc(fp); + if (c == EOF) { + if (ungeteof) { +#ifdef DEBUG_EGETC + fprintf(stderr, "eof claimed\n"); +#endif + return c; + } else { +#ifdef DEBUG_EGETC + fprintf(stderr, "got eof!!\n"); +#endif + ungeteof = 1; + return '\n'; + } + } + ungeteof = 0; + return c; +} + +/* smart getc that can skip C comment blocks */ +int rgetc(FILE *fp) +{ + static int c,d,fl; + + fl=0; + + do + { + while((c=egetc(fp))==13); /* remove ^M for unices */ + + if(fl && (c=='*')) + { + if((d=egetc(fp))!='/') + ungetc(d,fp); + else + { + fl--; + while((c=egetc(fp))==13); + } + } + if(c=='\n') + { + flist[fsp].fline++; + nlf=1; + } else + if(c=='/') + { + if((d=egetc(fp))!='*') + ungetc(d,fp); + else + fl++; + } + + } while(fl && (c!=EOF)); + + return(c-'\t'?c:' '); +} + +int fgetline(char *t, int len, int *rlen, FILE *fp) +{ + static int c,i; + + i=0; + + do { + c=rgetc(fp); + + if(c==EOF || c=='\n') + { + t[i]='\0'; + break; + } + t[i]=c; + i= (i +#include + +#include "xad.h" +#include "xah.h" +#include "xar.h" +#include "xa.h" +#include "xal.h" +#include "xao.h" +#include "xau.h" + +File *afile = NULL; + +int rmode = RMODE_RELOC; + +int r_set(int pc, int afl, int l) { +/*printf("set relocation @$%04x, l=%d, afl=%04x, segment=%d\n",pc, l, afl,segment);*/ + if(segment==SEG_TEXT) return rt_set(pc,afl,l,0); + if(segment==SEG_DATA) return rd_set(pc,afl,l,0); + return 0; +} + +int u_set(int pc, int afl, int label, int l) { +/*printf("set relocation @$%04x, l=%d, afl=%04x, segment=%d, label=%d\n", + pc, l, afl,segment, label);*/ + if((afl & A_FMASK) == (SEG_UNDEF<<8)) + label = u_label(label); /* set label as undefined */ + if(segment==SEG_TEXT) return rt_set(pc,afl,l,label); + if(segment==SEG_DATA) return rd_set(pc,afl,l,label); + return 0; +} + +void r_mode(int m) { + static int old_segment = SEG_TEXT; +/*printf("setting mode to %s\n",(m==RMODE_RELOC)?"reloc":"abs");*/ + if(rmode!=m) { + if(m==RMODE_RELOC) { + segment = old_segment; + } else { /* absolute mode */ + old_segment = segment; + segment = SEG_ABS; + } + } + rmode = m; +} + +int rt_set(int pc, int afl, int l, int lab) { + int p,pp; + + if(!rmode) return 0; + + /*printf("set relocation @$%04x, l=%d, afl=%04x\n",pc, l, afl);*/ + + if(l==2 && ((afl & A_MASK)!=A_ADR)) { + errout(W_BYTRELOC); + /*printf("Warning: byte relocation in word value at PC=$%04x!\n",pc);*/ + } + if(l==1 && ((afl&A_MASK)==A_ADR)) { + if((afl & A_FMASK) != (SEG_ZERO<<8)) { +/*printf("afl=%04x\n",afl);*/ + errout(W_ADRRELOC); + } + /*printf("Warning: cutting address relocation in byte value at PC=$%04x!\n",pc);*/ + afl = (afl & (~A_MASK)) | A_LOW; + } + + if(afile->rt.nlist>=afile->rt.mlist) { + afile->rt.mlist+=500; + afile->rt.rlist=realloc(afile->rt.rlist, afile->rt.mlist*sizeof(relocateInfo)); + } + if(!afile->rt.rlist) { + fprintf(stderr, "Oops: no memory for relocation table!\n"); + exit(1); + } + + afile->rt.rlist[afile->rt.nlist].adr = pc; + afile->rt.rlist[afile->rt.nlist].afl = afl; + afile->rt.rlist[afile->rt.nlist].lab = lab; + afile->rt.rlist[afile->rt.nlist].next= -1; + + /* sorting this into the list is not optimized, to be honest... */ + if(afile->rt.first<0) { + afile->rt.first = afile->rt.nlist; + } else { + p=afile->rt.first; pp=-1; + while(afile->rt.rlist[p].adrrt.rlist[p].next>=0) { + pp=p; + p=afile->rt.rlist[p].next; + } +/* +printf("endloop: p=%d(%04x), pp=%d(%04x), nlist=%d(%04x)\n", + p,p<0?0:afile->rt.rlist[p].adr,pp,pp<0?0:afile->rt.rlist[pp].adr,afile->rt.nlist,afile->rt.nlist<0?0:afile->rt.rlist[afile->rt.nlist].adr); +*/ + if(afile->rt.rlist[p].next<0 && afile->rt.rlist[p].adrrt.rlist[p].next=afile->rt.nlist; + } else + if(pp==-1) { + afile->rt.rlist[afile->rt.nlist].next = afile->rt.first; + afile->rt.first = afile->rt.nlist; + } else { + afile->rt.rlist[afile->rt.nlist].next = p; + afile->rt.rlist[pp].next = afile->rt.nlist; + } + } + afile->rt.nlist++; + + return 0; +} + +int rt_write(FILE *fp, int pc) { + int p=afile->rt.first; + int pc2, afl; + + while(p>=0) { + pc2=afile->rt.rlist[p].adr; + afl=afile->rt.rlist[p].afl; + /* hack to switch undef and abs flag from internal to file format */ + if( ((afl & A_FMASK)>>8) < SEG_TEXT) afl^=0x100; +/*printf("rt_write: pc=%04x, pc2=%04x, afl=%x\n",pc,pc2,afl);*/ + if((pc2-pc) < 0) { + fprintf(stderr, "Oops, negative offset!\n"); + } else { + while((pc2-pc)>254) { + fputc(255,fp); + pc+=254; + } + fputc(pc2-pc, fp); + pc=pc2; + fputc((afl>>8)&255, fp); + if((afile->rt.rlist[p].afl&A_FMASK)==(SEG_UNDEF<<8)) { + fputc(afile->rt.rlist[p].lab & 255, fp); + fputc((afile->rt.rlist[p].lab>>8) & 255, fp); + } + if((afl&A_MASK)==A_HIGH) fputc(afl&255,fp); + } + p=afile->rt.rlist[p].next; + } + fputc(0, fp); + + free(afile->rt.rlist); + afile->rt.rlist = NULL; + afile->rt.mlist = afile->rt.nlist = 0; + afile->rt.first = -1; + + return 0; +} + + +void seg_start(int fmode, int t_base, int d_base, int b_base, int z_base, + int slen, int relmode) { + afile->fmode = fmode; + afile->slen = slen; + afile->relmode = relmode; + + pc[SEG_TEXT] = afile->base[SEG_TEXT] = t_base; + pc[SEG_DATA] = afile->base[SEG_DATA] = d_base; + pc[SEG_BSS] = afile->base[SEG_BSS] = b_base; + pc[SEG_ZERO] = afile->base[SEG_ZERO] = z_base; + + afile->old_abspc = pc[SEG_ABS]; + pc[SEG_ABS] = pc[SEG_TEXT]; +} + + +File *alloc_file(void) { + File *afile; + int i; + + afile = malloc(sizeof(File)); + if(!afile) { + fprintf(stderr,"Oops: not enough memory!\n"); + exit(1); + } + + afile->mn.tmp = malloc(TMPMEM); + if(!afile->mn.tmp) { + fprintf(stderr,"Oops: not enough memory!\n"); + exit(1); + } + afile->mn.tmpz = 0; + afile->mn.tmpe = 0; + + afile->ud.ulist = NULL; afile->ud.un = afile->ud.um = 0; + afile->rt.rlist = NULL; afile->rt.first = -1; + afile->rt.mlist = afile->rt.nlist = 0; + afile->rd.rlist = NULL; afile->rd.first = -1; + afile->rd.mlist = afile->rd.nlist = 0; + afile->fo.olist = NULL; + afile->fo.mlist = afile->fo.nlist = 0; + + for(i=0;i<256;i++) afile->la.hashindex[i]=0; + afile->la.lt = NULL; + afile->la.lti = 0; + afile->la.ltm = 0; + + afile->len[SEG_TEXT] = afile->len[SEG_DATA] = + afile->len[SEG_BSS] = afile->len[SEG_ZERO] = 0; + + return afile; +} + +void seg_pass2(void) { + + pc[SEG_TEXT] = afile->base[SEG_TEXT]; + pc[SEG_DATA] = afile->base[SEG_DATA]; + pc[SEG_BSS] = afile->base[SEG_BSS]; + pc[SEG_ZERO] = afile->base[SEG_ZERO]; + + afile->old_abspc = pc[SEG_ABS]; + pc[SEG_ABS] = pc[SEG_TEXT]; +} + +void seg_end(FILE *fpout) { +#if 0 + afile->len[SEG_TEXT] = pc[SEG_TEXT] - afile->base[SEG_TEXT]; + afile->len[SEG_DATA] = pc[SEG_DATA] - afile->base[SEG_DATA]; + afile->len[SEG_BSS ] = pc[SEG_BSS ] - afile->base[SEG_BSS ]; + afile->len[SEG_ZERO] = pc[SEG_ZERO] - afile->base[SEG_ZERO]; +#endif + /* TODO: file length to embed */ +/* pc[SEG_ABS] = afile->old_abspc + seg_flen();*/ + +/*printf("seg_end: len[text]=%d, len[data]=%d, len[bss]=%d, len[zero]=%d\n", + afile->len[SEG_TEXT], afile->len[SEG_DATA], afile->len[SEG_BSS], afile->len[SEG_ZERO]);*/ + segment = SEG_ABS; + + u_write(fpout); + rt_write(fpout, afile->base[SEG_TEXT]-1); + rd_write(fpout, afile->base[SEG_DATA]-1); + l_write(fpout); +} + +/* write header for relocatable output format */ +int h_write(FILE *fp, int mode, int tlen, int dlen, int blen, int zlen, + int stack) { + + afile->len[SEG_TEXT] = tlen; + afile->len[SEG_DATA] = dlen; + afile->len[SEG_BSS ] = blen; + afile->len[SEG_ZERO] = zlen; + + fputc(1, fp); /* version byte */ + fputc(0, fp); /* hi address 0 -> no C64 */ + fputc('o', fp); + fputc('6', fp); + fputc('5', fp); + fputc(0, fp); /* format version */ + fputw(mode, fp); /* file mode */ + fputw(afile->base[SEG_TEXT],fp); /* text base */ + fputw(tlen,fp); /* text length */ + fputw(afile->base[SEG_DATA],fp); /* data base */ + fputw(dlen,fp); /* data length */ + fputw(afile->base[SEG_BSS],fp); /* bss base */ + fputw(blen,fp); /* bss length */ + fputw(afile->base[SEG_ZERO],fp); /* zerop base */ + fputw(zlen,fp); /* zerop length */ + fputw(stack,fp); /* needed stack size */ + + o_write(fp); + + return 0; +} + diff --git a/xa-2.3.5/src/xar.h b/xa-2.3.5/src/xar.h new file mode 100644 index 0000000..01aa887 --- /dev/null +++ b/xa-2.3.5/src/xar.h @@ -0,0 +1,48 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_XAR_H__ +#define __XA65_XAR_H__ + +#define RMODE_ABS 0 +#define RMODE_RELOC 1 + +extern File *alloc_file(void); + +/* jumps to r[td]_set, depending on segment */ +int r_set(int pc, int reloc, int len); +int u_set(int pc, int reloc, int label, int len); + +int rt_set(int pc, int reloc, int len, int label); +int rd_set(int pc, int reloc, int len, int label); +int rt_write(FILE *fp, int pc); +int rd_write(FILE *fp, int pc); + +void r_mode(int mode); + +/* int rmode; */ + +int h_write(FILE *fp, int mode, int tlen, int dlen, int blen, int zlen, + int stacklen); + +void seg_start(int fmode, int tbase, int dbase, int bbase, int zbase, + int stacklen, int relmode); +void seg_end(FILE *); +void seg_pass2(void); + +#endif /* __XA65_XAR_H__ */ diff --git a/xa-2.3.5/src/xar2.c b/xa-2.3.5/src/xar2.c new file mode 100644 index 0000000..fcf87b1 --- /dev/null +++ b/xa-2.3.5/src/xar2.c @@ -0,0 +1,132 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include "xah.h" +#include "xa.h" +#include "xar.h" + +/* +static relocateInfo *rlist = NULL; +static int mlist = 0, nlist = 0; +static int first = -1; +*/ + +/* int rmode; */ + +int rd_set(int pc, int afl, int l, int lab) { + int p,pp; + +/* if(!rmode) return 0; */ + + /*printf("set relocation @$%04x, l=%d, afl=%04x\n",pc, l, afl);*/ + + if(l==2 && ((afl & A_MASK)!=A_ADR)) { + errout(W_BYTRELOC); + /*printf("Warning: byte relocation in word value at PC=$%04x!\n",pc);*/ + } + if(l==1 && ((afl&A_MASK)==A_ADR)) { + if((afl & A_FMASK) != (SEG_ZERO<<8)) errout(W_ADRRELOC); + /*printf("Warning: cutting address relocation in byte value at PC=$%04x!\n",pc);*/ + afl = (afl & (~A_MASK)) | A_LOW; + } + + if(afile->rd.nlist>=afile->rd.mlist) { + afile->rd.mlist+=500; + afile->rd.rlist=realloc(afile->rd.rlist, afile->rd.mlist*sizeof(relocateInfo)); + } + if(!afile->rd.rlist) { + fprintf(stderr, "Oops: no memory for relocation table!\n"); + exit(1); + } + + afile->rd.rlist[afile->rd.nlist].adr = pc; + afile->rd.rlist[afile->rd.nlist].afl = afl; + afile->rd.rlist[afile->rd.nlist].lab = lab; + afile->rd.rlist[afile->rd.nlist].next= -1; + + /* sorting this into the list is not optimized, to be honest... */ + if(afile->rd.first<0) { + afile->rd.first = afile->rd.nlist; + } else { + p=afile->rd.first; pp=-1; + while(afile->rd.rlist[p].adrrd.rlist[p].next>=0) { + pp=p; + p=afile->rd.rlist[p].next; + } +/* +printf("endloop: p=%d(%04x), pp=%d(%04x), nlist=%d(%04x)\n", + p,p<0?0:afile->rd.rlist[p].adr,pp,pp<0?0:afile->rd.rlist[pp].adr,afile->rd.nlist,afile->rd.nlist<0?0:afile->rd.rlist[afile->rd.nlist].adr); +*/ + if(afile->rd.rlist[p].next<0 && afile->rd.rlist[p].adrrd.rlist[p].next=afile->rd.nlist; + } else + if(pp==-1) { + afile->rd.rlist[afile->rd.nlist].next = afile->rd.first; + afile->rd.first = afile->rd.nlist; + } else { + afile->rd.rlist[afile->rd.nlist].next = p; + afile->rd.rlist[pp].next = afile->rd.nlist; + } + } + afile->rd.nlist++; + + return 0; +} + +int rd_write(FILE *fp, int pc) { + int p=afile->rd.first; + int pc2, afl; + + while(p>=0) { + pc2=afile->rd.rlist[p].adr; + afl=afile->rd.rlist[p].afl; +/*printf("rd_write: pc=%04x, pc2=%04x, afl=%x\n",pc,pc2,afl);*/ + /* hack to switch undef and abs flag from internal to file format */ + if( ((afl & A_FMASK)>>8) < SEG_TEXT) afl^=0x100; + if((pc2-pc) < 0) { + fprintf(stderr, "Oops, negative offset!\n"); + } else { + while((pc2-pc)>254) { + fputc(255,fp); + pc+=254; + } + fputc(pc2-pc, fp); + pc=pc2; + fputc((afl>>8)&255, fp); + if((afile->rd.rlist[p].afl&A_FMASK)==(SEG_UNDEF<<8)) { + fputc(afile->rd.rlist[p].lab & 255, fp); + fputc((afile->rd.rlist[p].lab>>8) & 255, fp); + } + if((afl&A_MASK)==A_HIGH) fputc(afl&255,fp); + } + p=afile->rd.rlist[p].next; + } + fputc(0, fp); + + free(afile->rd.rlist); + afile->rd.rlist = NULL; + afile->rd.mlist = afile->rd.nlist = 0; + afile->rd.first = -1; + + return 0; +} + diff --git a/xa-2.3.5/src/xat.c b/xa-2.3.5/src/xat.c new file mode 100644 index 0000000..a08539b --- /dev/null +++ b/xa-2.3.5/src/xat.c @@ -0,0 +1,2097 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * maintained by Cameron Kaiser + * + * Core tokenizing module/pass 1 and pass 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* enable this to turn on (copious) optimization output */ +/* #define DEBUG_AM */ + +#include +#include + +#include "xad.h" +#include "xah.h" +#include "xah2.h" + +#include "xar.h" +#include "xa.h" +#include "xaa.h" +#include "xal.h" +#include "xat.h" +#include "xao.h" +#include "xap.h" +#include "xacharset.h" + +int dsb_len = 0; + +static int t_conv(signed char*,signed char*,int*,int,int*,int*,int*,int,int*); +static int t_keyword(signed char*,int*,int*); +static int tg_asc(signed char*,signed char*,int*,int*,int*,int*,int); +static void tg_dez(signed char*,int*,int*); +static void tg_hex(signed char*,int*,int*); +static void tg_oct(signed char*,int*,int*); +static void tg_bin(signed char*,int*,int*); + +/* assembly mnemonics and pseudo-op tokens */ +/* ina and dea don't work yet */ +static char *kt[] ={ +/* 1 2 3 4 5 6 7 8 9 10 */ + "adc","and","asl","bbr","bbs","bcc","bcs","beq","bit","bmi", + "bne","bpl","bra","brk","bvc","bvs","brl","clc","cld","cli", +/* + "clv","cmp","cpx","cpy","cop","dea","dec","dex","dey","eor", +*/ + "clv","cmp","cpx","cpy","cop",/*"dea",*/"dec","dex","dey","eor", + +/* + "ina","inc","inx","iny","jmp","jsr","lda","ldx","ldy","lsr", +*/ + /*"ina",*/"inc","inx","iny","jmp","jsr","lda","ldx","ldy","lsr", + "mvp","mvn","nop","ora","pha","php","phx","phy","pla","plp", + "plx","ply","phb","phd","phk","plb","pld","pea","pei","per", + + "rmb","rol","ror","rti","rts","rep","rtl","sbc","sec","sed", + "sei","smb","sta","stx","sty","stz","sep","stp","tax","tay", + "trb","tsb","tsx","txa","txs","tya","txy","tyx","tcd","tdc", + + "tcs","tsc","wai","wdb","xba","xce", + + ".byt",".word",".asc",".dsb", ".(", ".)", "*=", ".text",".data",".bss", + ".zero",".fopt", ".byte", ".end", ".list", ".xlist", ".dupb", ".blkb", ".db", ".dw", + ".align",".block", ".bend",".al",".as",".xl",".xs", ".bin", ".aasc" + +}; + +static int lp[]= { 0,1,1,1,1,2,2,1,1,1,2,2,2,1,1,1,2,2 }; + +/* index into token array for pseudo-ops */ +/* last valid mnemonic */ +#define Lastbef 93 +/* last valid token+1 */ +#define Anzkey 123 + +#define Kbyt Lastbef+1 +#define Kword Lastbef+2 +#define Kasc Lastbef+3 +#define Kdsb Lastbef+4 +#define Kopen Lastbef+5 /* .( */ +#define Kclose Lastbef+6 /* .) */ +#define Kpcdef Lastbef+7 /* *=value */ +#define Ktext Lastbef+8 +#define Kdata Lastbef+9 +#define Kbss Lastbef+10 +#define Kzero Lastbef+11 +#define Kfopt Lastbef+12 +#define Kbyte Lastbef+13 /* gets remapped to Kbyt */ +#define Kend Lastbef+14 /* ignored (MASM compat.) */ +#define Klist Lastbef+15 /* ignored (MASM compat.) */ +#define Kxlist Lastbef+16 /* ignored (MASM compat.) */ +#define Kdupb Lastbef+17 /* gets remapped to Kdsb */ +#define Kblkb Lastbef+18 /* gets remapped to Kdsb */ +#define Kdb Lastbef+19 /* gets remapped to Kbyt */ +#define Kdw Lastbef+20 /* gets remapped to Kword */ +#define Kalign Lastbef+21 +#define Kblock Lastbef+22 /* gets remapped to .( */ +#define Kbend Lastbef+23 /* gets remapped to .) */ + +#define Kalong Lastbef+24 +#define Kashort Lastbef+25 +#define Kxlong Lastbef+26 +#define Kxshort Lastbef+27 + +#define Kbin Lastbef+28 +#define Kaasc Lastbef+29 + +#define Kreloc Anzkey /* *= (relocation mode) */ +#define Ksegment Anzkey+1 + +/* array used for hashing tokens (26 entries, a-z) */ + +static int ktp[]={ 0,3,17,25,28,29,29,29,29,32,34,34,38,40,41,42,58, + 58,65,76,90,90,90,92,94,94,94,Anzkey }; + +#define Admodes 24 + +/* + * opcodes for each addressing mode + * high byte: supported architecture (no bits = original NMOS 6502) + * bit 1: R65C02 + * bit 2: 65816 + * bit 3: 65816 and allows 16-bit quantity (immediate only) + * low byte: opcode itself + * + * each opcode is indexed in this order: *=65816, ^=R65C02 + * 00 = implied + * 01 = zero page + * 02 = zero page,x + * 03 = direct page,y* + * 04 = direct page (indirect)* + * 05 = (indirect,x) + * 06 = (indirect),y + * 07 = immediate (8-bit) + * 08 = absolute + * 09 = absolute,x + * 10 = absolute,y + * 11 = relative + * 12 = (indirect-16) i.e., jmp (some_vector) + * 13 = (absolute,x)* + * 14 = zero page+relative test'n'branch ^ + * 15 = zero page clear'n'set'bit ^ + * 16 = relative long* + * 17 = absolute long* + * 18 = absolute long,x* + * 19 = stack relative* + * 20 = stack relative (indirect),y* + * 21 = direct page (indirect long)* + * 22 = direct page (indirect long),y* + * 23 = (indirect long) + */ + +static int ct[Lastbef+1][Admodes] ={ +/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 imm */ +{ -1, 0x65,0x75,-1,0x172,0x61,0x71,0x469,0x6d,0x7d,0x79,-1, -1, -1, -1, -1, -1,0x26f,0x27f,0x263,0x273,0x267,0x277,-1 }, /*adc*/ +{ -1, 0x25,0x35,-1,0x132,0x21,0x31,0x429,0x2d,0x3d,0x39,-1, -1, -1, -1, -1, -1,0x22f,0x23f,0x223,0x233,0x227,0x237,-1 }, /*and*/ +{ 0x0a,0x06,0x16,-1, -1, -1, -1, -1, 0x0e,0x1e,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*asl*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x10f,-1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bbr*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x18f,-1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bbs*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x90,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bcc*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0xb0,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bcs*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0xf0,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*beq*/ +{ -1, 0x24,0x134,-1, -1, -1, -1, 0x589,0x2c,0x13c,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bit*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x30,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bmi*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0xd0,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bne*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x10,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bpl*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x180,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bra*/ +{ 0x00,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*brk*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x50,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bvc*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x70,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*bvs*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,0x282, -1, -1, -1, -1, -1, -1, -1 }, /*brl*/ +{ 0x18,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*clc*/ +{ 0xd8,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*cld*/ +{ 0x58,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*cli*/ +{ 0xb8,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*clv*/ +{ -1, 0xc5,0xd5,-1, 0x1d2,0xc1,0xd1,0x4c9,0xcd,0xdd,0xd9,-1, -1, -1, -1, -1,-1,0x2cf,0x2df,0x2c3,0x2d3,0x2c7,0x2d7,-1 }, /*cmp*/ +{ -1, 0xe4,-1, -1, -1, -1, -1, 0x8e0,0xec,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*cpx*/ +{ -1, 0xc4,-1, -1, -1, -1, -1, 0x8c0,0xcc,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*cpy*/ +{ -1, -1, -1, -1, -1, -1, -1, 0x202,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*cop*/ +/* +{ 0x13a,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },*/ /*dea*/ +{ 0x13a,0xc6,0xd6,-1, -1, -1, -1, -1, 0xce,0xde,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*dec*/ +{ 0xca,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*dex*/ +{ 0x88,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*dey*/ +{ -1, 0x45,0x55,-1, 0x152,0x41,0x51,0x449,0x4d,0x5d,0x59,-1, -1, -1, -1, -1,-1,0x24f,0x25f,0x243,0x253,0x247,0x257,-1 }, /*eor*/ +/* +{ 0x11a,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },*/ /*ina*/ +{ 0x11a,0xe6,0xf6,-1, -1, -1, -1, -1, 0xee,0xfe,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*inc*/ +{ 0xe8,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*inx*/ +{ 0xc8,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*iny*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, 0x4c,-1, -1, -1, 0x6c,0x17c,-1, -1, -1,0x25c, -1, -1, -1, -1, -1,0x2dc}, /*jmp*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, 0x20,-1, -1, -1, -1, 0x2fc,-1, -1, -1,0x222, -1, -1, -1, -1, -1, -1 }, /*jsr*/ +{ -1, 0xa5,0xb5,-1, 0x1b2,0xa1,0xb1,0x4a9,0xad,0xbd,0xb9,-1, -1, -1, -1, -1,-1,0x2af,0x2bf,0x2a3,0x2b3,0x2a7,0x2b7,-1 }, /*lda*/ +{ -1, 0xa6,-1, 0xb6,-1, -1, -1, 0x8a2,0xae,-1, 0xbe,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*ldx*/ +{ -1, 0xa4,0xb4,-1, -1, -1, -1, 0x8a0,0xac,0xbc,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*ldy*/ +{ 0x4a,0x46,0x56,-1, -1, -1, -1, -1, 0x4e,0x5e,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*lsr*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, 0x244,-1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*mvp*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, 0x254,-1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*mvn*/ +{ 0xea,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*nop*/ +{ -1, 0x05,0x15,-1, 0x112,0x01,0x11,0x409,0x0d,0x1d,0x19,-1, -1, -1, -1, -1,-1,0x20f,0x21f,0x203,0x213,0x207,0x217,-1 }, /*ora*/ +{ 0x48,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*pha*/ +{ 0x08,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*php*/ +{ 0x1da,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*phx*/ +{ 0x15a,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*phy*/ +{ 0x68,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*pla*/ +{ 0x28,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*plp*/ +{ 0x1fa,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*plx*/ +{ 0x17a,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*ply*/ +{ 0x28b,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*phb*/ +{ 0x20b,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*phd*/ +{ 0x24b,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*phk*/ +{ 0x2ab,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*plb*/ +{ 0x22b,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*pld*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, 0x2f4,-1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*pea*/ +{ -1, -1, -1, -1,0x2d4, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*pei*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, 0x262,-1, -1, -1, -1, -1, -1, -1 }, /*per*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,0x107, -1, -1, -1, -1, -1, -1, -1, -1 }, /*rmb*/ +{ 0x2a,0x26,0x36,-1, -1, -1, -1, -1, 0x2e,0x3e,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*rol*/ +{ 0x6a,0x66,0x76,-1, -1, -1, -1, -1, 0x6e,0x7e,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*ror*/ +{ 0x40,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*rti*/ +{ 0x60,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*rts*/ +{ -1, -1, -1, -1, -1, -1, -1, 0x2c2,-1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*rep*/ +{ 0x26b,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*rtl*/ +{ -1, 0xe5,0xf5,-1, 0x1f2,0xe1,0xf1,0x4e9,0xed,0xfd,0xf9,-1, -1, -1, -1, -1,-1,0x2ef,0x2ff,0x2e3,0x2f3,0x2e7,0x2f7,-1 }, /*sbc*/ +{ 0x38,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*sec*/ +{ 0xf8,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*sed*/ +{ 0x78,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*sei*/ +{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,0x187, -1, -1, -1, -1, -1, -1, -1, -1 }, /*smb*/ +{ -1, 0x85,0x95,-1, 0x192,0x81,0x91,-1, 0x8d,0x9d,0x99,-1, -1, -1, -1, -1,-1,0x28f,0x29f,0x283,0x293,0x287,0x297,-1 }, /*sta*/ +{ -1, 0x86,-1, 0x96,-1, -1, -1, -1, 0x8e,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*stx*/ +{ -1, 0x84,0x94,-1, -1, -1, -1, -1, 0x8c,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*sty*/ +{ -1, 0x164,0x174,-1, -1, -1, -1, -1, 0x19c,0x19e,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*stz*/ +{ -1, -1, -1, -1, -1, -1, -1, 0x2e2,-1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*sep*/ +{ 0x2db,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*stp*/ +{ 0xaa,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*tax*/ +{ 0xa8,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*tay*/ +{ -1, 0x114,-1, -1, -1, -1, -1, -1, 0x11c,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*trb*/ +{ -1, 0x104,-1, -1, -1, -1, -1, -1, 0x10c,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*tsb*/ +{ 0xba,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*tsx*/ +{ 0x8a,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*txa*/ +{ 0x9a,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*txs*/ +{ 0x98,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*tya*/ +{ 0x29b,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*txy*/ +{ 0x2bb,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*tyx*/ +{ 0x25b,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*tcd*/ +{ 0x27b,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*tdc*/ +{ 0x21b,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*tcs*/ +{ 0x23b,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*tsc*/ +{ 0x2cb,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*wai*/ +{ 0x242,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*wdb*/ +{ 0x2eb,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /*xba*/ +{ 0x2fb,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } /*xce*/ + + +} ; + +#define Syntax 14 +#define Maxbyt 4 + +/* grouped syntaxes. each row = operand type, column = bytes allowed */ +static int at[Syntax][Maxbyt] ={ +{ 0, -1, -1 ,-1 }, /* implied: no operand */ +{ -1, 7, -1 ,-1 }, /* immediate: single byte operand only */ +{ -1, 15, -1 ,-1 }, /* relative: single byte operand only */ +{ -1, -1, 14 ,-1 }, /* test'n'branch: two bytes only */ +{ -1, 1, 8 ,17 }, /* addressing: 1 byte for zp, + 2 for absolute, + 3 for absolute long */ +{ -1, 2, 9 ,18 }, /* ,x: same */ +{ -1, 3, 10 ,-1 }, /* ,y: 1 byte for dp,y, + 2 for absolute,y */ +{ -1, 4, 12 ,-1 }, /* (indirect): 1 byte for (dp), + 2 for (absolute) */ +{ -1, 5, 13 ,-1 }, /* (i,x): 1 byte for (zp,x), + 2 for (a,x) */ +{ -1, 6, -1 ,-1 }, /* (i),y: 1 byte only */ +{ -1, 21, 23 ,-1 }, /* (indirect long): 1 byte for (dp), + 2 for (a) */ +{ -1, 22, -1 ,-1 }, /* (indirect long),y: 1 byte only */ +{ -1, 19, -1 ,-1 }, /* stack relative: 1 byte only */ +{ -1, 20, -1 ,-1 } /* SR (in),y: 1 byte only */ +}; + +#define AnzAlt 5 + +/* disambiguation table. for example, arbitrary instruction xxx $0000 could + be either interpreted as an absolute operand, or possibly relative. + note: does not look at comma or after, if present. */ +static int xt[AnzAlt][2] ={ /* Alternativ Adr-Modes */ +{ 8, 11 }, /* abs -> rel */ +{ 2, 3 }, /* z,x -> z,y */ +{ 5, 6 }, /* ,x) -> ),y */ +{ 9, 10 }, /* a,x -> a,y */ + +{ 8, 16 } /* abs -> relong */ +}; + +/* cross check: instruction should be this many bytes long in total */ +/* indexed by addressing mode */ +static int le[] ={ 1,2,2,2,2,2,2,2,3,3,3,2,3,3,3,2, + /* new modes */ 3,4,4,2,2,2,2,3 }; + +/* indicates absolute->zp optimizable addressing modes (abs->zp) */ +/* indexed by addressing mode */ +static int opt[] ={ -1,-1,-1,-1,-1,-1,-1,-1,1,2,3,-1,4,5,-1,-1, + /*new*/ -1,8,9,-1,-1,-1,-1,-1 }; /* abs -> zp */ + +/* pass 1 */ +int t_p1(signed char *s, signed char *t, int *ll, int *al) +{ + static int er,l,n,v,nk,na1,na2,bl,am,sy,i,label,byte; /*,j,v2 ;*/ + int afl = 0; + +/* notes and typical conventions ... er = error code + am = addressing mode in use +*/ + + bl=0; + *al = 0; + +/* printf("\n"); */ + + /* convert the next token from string s */ +#ifdef DEBUG_AM +fprintf(stderr, "- p1 %d starting -\n", pc[segment]); +#endif + er=t_conv(s,t,&l,pc[segment],&nk,&na1,&na2,0,&byte); + /* leaving our token sequence in t */ + + *ll=l; +/* + printf("t_conv (er=%d):",er); + for(i=0;i1) && (t[0]base[SEG_TEXT] = pc[SEG_TEXT] = romaddr + h_length(); + romable=1; + } + + if(!er) + { + +/* + * + * pseudo-op dispatch (except .byt, .asc) + * + */ + n=t[0]; + /* TODO: make that a big switch statement... */ + /* maybe later. Cameron */ + + if(n==Kend || n==Klist || n==Kxlist) { + *ll = 0; /* ignore */ + } else + + if(n==Kfopt) { + if(romable==1) er=E_ROMOPT; + t[0] = Kbyt; + set_fopt(l,t,nk+1-na1+na2); + *ll = 0; + } else + if(n==Kpcdef) + { + int tmp; + if(!(er=a_term(t+1,&tmp /*&pc[SEG_ABS]*/,&l,pc[segment],&afl,&label,0))) + { + i=1; + wval(i,tmp /*pc[SEG_ABS]*/); + t[i++]=T_END; + *ll=6; + er=E_OKDEF; +/*printf("set pc=%04x, oldsegment=%d, pc[segm]=%04x, ", + pc[SEG_ABS], segment, pc[segment]); +printf(" wrote %02x %02x %02x %02x %02x %02x\n", + t[0],t[1],t[2],t[3],t[4],t[5]);*/ + if(segment==SEG_TEXT) { + pc[SEG_ABS] = tmp; + r_mode(RMODE_ABS); + } else { + if(!relmode) { + pc[segment] = tmp; + } else { + er = E_ILLSEGMENT; + } + } +/*printf("newsegment=%d, pc[ABS]=%04x\n", segment, pc[SEG_ABS]);*/ + } else { /* TODO: different error code */ + if((segment==SEG_ABS) && (er==E_SYNTAX && l==0)) { +/*printf("reloc: oldseg=%d, pc[oldseg]=%04x, pc[abs]=%04x, pc[text]=%04x\n", + segment, pc[segment], pc[SEG_ABS], pc[SEG_TEXT]);*/ + t[0]=Kreloc; + i=1; + wval(i,pc[SEG_TEXT]); + t[i++]=T_END; + *ll=6; + er=E_OKDEF; + r_mode(RMODE_RELOC); +/*printf(" : newseg=%d, pc[newseg]=%04x, pc[abs]=%04x, pc[text]=%04x\n", + segment, pc[segment], pc[SEG_ABS], pc[SEG_TEXT]);*/ + } + } + } else + if(n==Kopen) + { + if(showblk) fprintf(stderr, "%s line %d: .(\n", pp_getidat()->fname, pp_getidat()->fline); + b_open(); + er=E_NOLINE; + } else + if(n==Kclose) + { + if(showblk) fprintf(stderr, "%s line %d: .)\n", pp_getidat()->fname, pp_getidat()->fline); + er=b_close(); + if(!er) er=E_NOLINE; + } else + if(n==Kalong) + { + if (!w65816) { + er=E_65816; + } else { + memode=1; + t[0]=Kalong; + *ll=1; + er=E_OKDEF; + } + } else + if(n==Kashort) + { + memode=0; + t[0]=Kashort; + *ll=1; + er=E_OKDEF; + } else + if(n==Kxlong) + { + if (!w65816) { + er=E_65816; + } else { + xmode=1; + t[0]=Kxlong; + *ll=1; + er=E_OKDEF; + } + } else + if(n==Kxshort) + { + xmode=0; + t[0]=Kxshort; + *ll=1; + er=E_OKDEF; + } else + if(n==Kdsb) + { + dsb_len = 1; + if(!(er=a_term(t+1,&bl,&l,pc[segment],&afl,&label,0))) { + er=E_OKDEF; + } + dsb_len = 0; + } else + if(n==Ktext) { +/* if(segment!=SEG_ABS) { */ + segment = relmode ? SEG_TEXT : SEG_ABS; + t[0]=Ksegment; + t[1]=segment; + *ll=2; + er=E_OKDEF; +/* } else { + er=E_ILLSEGMENT; + } */ + } else + if(n==Kdata) { +/* if(segment!=SEG_ABS) { */ + segment = SEG_DATA; + t[0]=Ksegment; + t[1]=SEG_DATA; + *ll=2; + er=E_OKDEF; +/* } else { + er=E_ILLSEGMENT; + } */ + } else + if(n==Kbss) { +/* if(segment!=SEG_ABS) { */ + segment = SEG_BSS; + t[0]=Ksegment; + t[1]=SEG_BSS; + *ll=2; + er=E_OKDEF; +/* } else { + er=E_ILLSEGMENT; + } */ + } else + if(n==Kzero) { +/* if(segment!=SEG_ABS) { */ + segment = SEG_ZERO; + t[0]=Ksegment; + t[1]=SEG_ZERO; + *ll=2; + er=E_OKDEF; +/* } else { + er=E_ILLSEGMENT; + } */ + } else + if (n==Kbin) { + int j; + int l; + + /* this first pass just calculates a prospective length + for pass 2. */ + char binfnam[255]; + int offset; + int length; + int fstart; + + i = 1; + j = 0; + + /* get offset */ + if(!(er=a_term(t+i,&offset,&l,pc[segment],&afl,&label,1))) { + i += l; + } + if (offset < 0) + er = E_ILLQUANT; + if(t[i] == ',') { /* skip comma */ + i++; + } else { + er = E_SYNTAX; + } + + /* get length */ + if (!er && + !(er=a_term(t+i,&length,&l,pc[segment],&afl,&label,1))) + { + i += l; + } + if (length < 0) + er = E_ILLQUANT; + if(t[i] == ',') { /* skip comma */ + i++; + } else { + er = E_SYNTAX; + } + + /* get filename. + the tokenizer can either see it as a multichar string ... */ + if (!er) { + int k; + + fstart = i; + if(t[i]=='\"') { + i++; + k=t[i]+i+1; + i++; + while(i 255) + er = E_NOMEM; /* buffer overflow */ + } + binfnam[j] = '\0'; + /* or as a 'char' if it's a single character ("word" would + have been caught by the above) */ + } else + if(!(er=a_term(t+i,&v,&l,pc[segment],&afl,&label,1))) { + binfnam[0] = v; + binfnam[1] = '\0'; + i += l; + } + } + + /* three arguments only please */ + if (!er && t[i] != T_END) { + er = E_SYNTAX; + } + + if (!er) { + FILE *foo; + +#ifdef DEBUG_AM + fprintf(stderr, +"binclude1 offset = %i len = %i filename = %s endchar = %i\n", + offset, length, binfnam, i); +#endif + if (!(foo = fopen(binfnam, "r"))) { + er = E_FNF; + } else { + fseek(foo, 0, SEEK_END); + if ((length+offset) > ftell(foo)) { + er = E_OUTOFDATA; + } else { + length = (length) ? length : + (ftell(foo)-offset); + } + fclose(foo); + } + if (!er) { + if (length > 65535 && !w65816) { + errout(W_OVER64K); + } else if (length > 16777215) { + errout(W_OVER16M); + } + /* pass parameters back to xa.c */ + *ll=i+1; +/* + bl=length+2; +*/ + bl=length; + er = E_OKDEF; /* defer to pass 2 */ + } + } + } else + if(n==Kalign) { + int tmp; + if(segment!=SEG_ABS) { + if(!(er=a_term(t+1,&tmp,&l,pc[segment],&afl,&label,0))) { + if(tmp == 1 || tmp == 2 || tmp == 4 || tmp == 256) { + set_align(tmp); + if(pc[segment] & (tmp-1)) { /* not aligned */ + int tmp2; + t[0]=Kdsb; + i=1; + bl=tmp=(tmp - (pc[segment] & (tmp-1))) & (tmp-1); + wval(i,tmp); + t[i++]=','; + tmp2= 0xea; + wval(i,tmp2); /* nop opcode */ + t[i++]=T_END; + *ll=9; + er=E_OKDEF; + } else { + *ll=0; /* ignore if aligned right */ + } + } else { + er=E_ILLALIGN; + } + } + } else { + er=E_ILLSEGMENT; + } + } else +/* optimization okay on pass 1: use 0 for fl */ + { +#ifdef DEBUG_AM +fprintf(stderr, "E_OK ... t_p2 xat.c\n"); +#endif + er=t_p2(t,ll,(0 | byte), al); + } + + } else + if(er==E_NODEF) + { + +/* + * no label was found from t_conv! + * try to figure out most likely length + * + */ + +#ifdef DEBUG_AM +fprintf(stderr, "E_NODEF pass1 xat.c\n"); +#endif + er = E_OK; /* stuff error */ + n=t[0]; /* look at first token */ + + /* mnemonic dispatch -- abbreviated form in t_p2, but changed here + to not do anything other than 24-bit optimization since we + don't know the value of the label */ + + /* choose addressing mode; add commas found */ + + if(n>=0 && n<=Lastbef) + { + if(t[1]==T_END) + { + sy=0; /* implied */ + } else + if(t[1]=='#') + { + sy=1+nk; /* immediate */ + } else + if(t[1]=='(') + { + sy=7+nk; /* computed */ + } else + sy=4+nk; /* absolute or zero page */ + + /* length counter set to maximum length + 1 */ + bl=Maxbyt+1; + + /* find best fit for length of this operand */ + while(--bl) + { + + /* look at syntax table (at) using syntax (sy) as index. + is there an addressing mode for an operand + of this length? am = addressing mode */ + + if((am=at[sy][bl-1])>=0) + { + if(am>Admodes-1) /* no, it's -1, syntax error */ + { + er=E_SYNTAX; + break; + } + if(ct[n][am]>=0) /* yes, valid token *and* mode, + so we're done */ + break; + + /* no valid mode for this token, see if it's something + ambiguous; if so, try to interpret in that + context. */ + for(v=0;v=0) + break; + if(v=0 && am>16) /* <<< NOTE! */ + if(ct[n][opt[am]]>=0) + am=opt[am]; + } + /* if ` is declared, force further optimization */ + if (t[l-1]=='`') { + if (opt[am]<0 || ct[n][opt[am]]<0) + errout(E_ADRESS); + am=opt[am]; + } + /* if ! is declared, force to 16-bit quantity */ + if (t[l-1]=='!' && am>16 && opt[am]>=0 && bl) { + am=opt[am]; + } + + /* couldn't match anything for this opcode */ + if(!bl) + er=E_SYNTAX; + else { + /* ok, get length of instruction */ + bl=le[am]; + /* and add one for 65816 special instruction modes */ + if( ((ct[n][am]&0x400) && memode) || + ((ct[n][am]&0x800) && xmode)) { + bl++; + } + } + + + if (er == E_NODEF) + er = E_OK; + + /* .byt, .asc, .word, .dsb, .fopt pseudo-op dispatch */ + + } else + if(n==Kbyt || n==Kasc || n==Kaasc) + { +#ifdef DEBUG_AM +fprintf(stderr, "byt pass 1 %i\n", nk+1-na1+na2); +#endif + bl=nk+1-na1+na2; + } else + if(n==Kword) + { + bl=2*nk+2; + } else + if(n==Kdsb) + { + er=a_term(t+1,&bl,&l,pc[segment],&afl,&label,0); + } else + if(n==Kfopt) + { + set_fopt(l-1,t+1, nk+1-na1+na2); + *ll = 0; + } else + if(n==T_OP) + { + er=E_OKDEF; + } else + er=E_NODEF; + + if(!er) + er=E_OKDEF; +#ifdef DEBUG_AM +fprintf(stderr, "guessing instruction length is %d\n", bl); +#endif + } + if(er==E_NOLINE) + { + er=E_OK; + *ll=0; + } + + *al += bl; + pc[segment]+=bl; + if(segment==SEG_TEXT) pc[SEG_ABS]+=bl; + if(segment==SEG_ABS) pc[SEG_TEXT]+=bl; + + return(er); +} + +/*t_pass 2*/ +int t_p2(signed char *t, int *ll, int fl, int *al) +{ + static int afl,nafl, i,j,k,er,v,n,l,bl,sy,am,c,vv[3],v2,label; + static int rlt[3]; /* relocation table */ + static int lab[3]; /* undef. label table */ + +#if(0) + (void)fl; /* quench warning */ +#endif +/* fl was not used in 2.2.0 so I'm overloading it for zp-optimization + control */ + + er=E_OK; + bl=0; + if(*ll<0) /* <0 bei E_OK, >0 bei E_OKDEF */ + { + *ll=-*ll; + bl=*ll; + er=E_OK; + } else + { + n=t[0]; + if(n==T_OP) + { + n=cval(t+1); + er=a_term(t+4,&v,&l,pc[segment],&nafl,&label,0); + + if(!er) + { + if(t[3]=='=') + { + v2=v; + } else { + if( (!(er=l_get(n,&v2, &afl))) + && ((afl & A_FMASK)!=(SEG_UNDEF<<8)) ) + { + if(t[3]=='+') + { + if(afl && nafl) { errout(E_WPOINTER); nafl=0; } + nafl = afl; + v2+=v; + } else + if(t[3]=='-') + { + if( (((nafl & A_FMASK)>>8) != afl) + || ((nafl & A_MASK)==A_HIGH) ) { + errout(E_WPOINTER); + nafl=0; + } else { + nafl = afl; + } + v2-=v; + } else + if(t[3]=='*') + { + if(afl || nafl) { errout(E_WPOINTER); nafl=0; } + v2*=v; + } else + if(t[3]=='/') + { + if(afl || nafl) { errout(E_WPOINTER); nafl=0; } + if(v) + v2/=v; + else + er=E_DIV; + } else + if(t[3]=='|') + { + if(afl || nafl) { errout(E_WPOINTER); nafl=0; } + v2=v|v2; + } else + if(t[3]=='&') + { + if(afl || nafl) { errout(E_WPOINTER); nafl=0; } + v2=v2&v; + } + } + } + l_set(n,v2,nafl>>8); + + *ll=0; + if(!er) + er=E_NOLINE; + } + } else + if(n==Kword) + { + i=1; + j=0; + while(!er && t[i]!=T_END) + { + if(!(er=a_term(t+i,&v,&l,pc[segment],&afl,&label,1))) + { +/*if(afl) printf("relocation 1 %04x at pc=$%04x, value now =$%04x\n", + afl,pc[segment],v); */ + if(afl) u_set(pc[segment]+j, afl, label, 2); + t[j++]=v&255; + t[j++]=(v>>8)&255; + + i+=l; + if(t[i]!=T_END && t[i]!=',') + er=E_SYNTAX; + else + if(t[i]==',') + i++; + + } + } + *ll=j; + bl=j; + } else if (n == Kbin) { + int j; + int l; + + /* figure out our parameters again. repeat most of + the error checking since we might not be over + the total number of bogosities */ + char binfnam[255]; + int offset; + int length; + int fstart; + int flen; + + i = 1; + j = 0; + flen = 0; + + /* get offset */ + if(!(er=a_term(t+i,&offset,&l,pc[segment],&afl,&label,1))) { + i += l; + } + if (offset < 0) + er = E_ILLQUANT; + if(t[i] == ',') { /* skip comma */ + i++; + } else { + er = E_SYNTAX; + } + + /* get length */ + if (!er && + !(er=a_term(t+i,&length,&l,pc[segment],&afl,&label,1))) + { + i += l; + } + if (length < 0) + er = E_ILLQUANT; + if(t[i] == ',') { /* skip comma */ + i++; + } else { + er = E_SYNTAX; + } + + /* get filename. + the tokenizer can either see it as a multichar string ... */ + if (!er) { + int k; + + fstart = i; + if(t[i]=='\"') { + i++; + k=t[i]+i+1; + i++; + while(i 255) + er = E_NOMEM; /* buffer overflow */ + } + binfnam[j] = '\0'; + flen = j; + /* or as a 'char' if it's a single character ("word" would + have been caught by the above) */ + } else + if(!(er=a_term(t+i,&v,&l,pc[segment],&afl,&label,1))) { + binfnam[0] = v; + binfnam[1] = '\0'; + i += l; + flen = 1; + } + } + + /* three arguments only please */ + if (!er && t[i] != T_END) { + er = E_SYNTAX; + } + + if (!er) { + FILE *foo; + +#ifdef DEBUG_AM + fprintf(stderr, +"binclude2 offset = %i len = %i filename = %s endchar = %i\n", + offset, length, binfnam, i); +#endif + if (!(foo = fopen(binfnam, "r"))) { + er = E_FNF; + } else { + fseek(foo, 0, SEEK_END); + if ((length+offset) > ftell(foo)) { + er = E_OUTOFDATA; + } else { + length = (length) ? length : + (ftell(foo)-offset); + } + fclose(foo); + } + if (!er) { + if (length > 65535 && !w65816) { + errout(W_OVER64K); + } else if (length > 16777215) { + errout(W_OVER16M); + } + /* pass parameters back to xa.c */ + *ll=length; +/* + bl=length+2; +*/ + bl=length; + t[0] = offset & 255; + t[1] = (offset >> 8) & 255; + t[2] = (offset >> 16) & 255; + /* God help us if the index is > 65535 */ + t[3] = fstart & 255; + t[4] = (fstart >> 8) & 255; + t[5] = flen; /* to massage 'char' types */ + er = E_BIN; + } + } + } else if(n==Kasc || n==Kbyt || n==Kaasc) { + i=1; + j=0; + while(!er && t[i]!=T_END) + { + if(t[i]=='\"') + { + i++; + k=t[i]+i+1; + i++; + while(i>8) + er=E_OVERFLOW; + t[0]=v&255; + if(!er) + { + *ll=j; + bl=j; +#ifdef DEBUG_AM +fprintf(stderr, "Kdsb E_DSB %i\n", j); +#endif + er=E_DSB; + } + } + if(!er) + bl=j; + } + dsb_len = 0; + } else + if(n<=Lastbef) + { + if((c=t[1])=='#') + { + i=2; + sy=1; + if(!(er=a_term(t+i,vv,&l,pc[segment],&afl,&label,1))) + { +/* if(1) printf("a_term returns afl=%04x\n",afl); */ + + rlt[0] = afl; + lab[0] = label; + i+=l; + if(t[i]!=T_END) + { + if(t[i]!=',') + er=E_SYNTAX; + else + { + i++; + sy++; + if(!(er=a_term(t+i,vv+1,&l,pc[segment],&afl,&label,1))) + { + rlt[1] = afl; + lab[1] = label; + i+=l; + if(t[i]!=T_END) + { + if(t[i]!=',') + er=E_SYNTAX; + else + { + i++; + sy++; + if(!(er=a_term(t+i,vv+2,&l,pc[segment],&afl,&label,1))) + { + rlt[2] = afl; + lab[2] = label; + i+=l; + if(t[i]!=T_END) + er=E_SYNTAX; + } + } + } + } + } + } + } + } else + if(c==T_END) + { + sy=0; + } else + if(c=='(') + { + sy=7; + if(!(er=a_term(t+2,vv,&l,pc[segment],&afl,&label,1))) + { + rlt[0] = afl; + lab[0] = label; + + if(t[2+l]!=T_END) + { + if(t[2+l]==',') + { + if (tolower(t[3+l])=='x') + sy=8; + else + sy=13; + + } else + if(t[2+l]==')') + { + if(t[3+l]==',') + { + if(tolower(t[4+l])=='y') + sy=9; + else + er=E_SYNTAX; + } else + if(t[3+l]!=T_END) + er=E_SYNTAX; + } + } else + er=E_SYNTAX; + } + } else + if(c=='[') + { + sy=10; + if(!(er=a_term(t+2,vv,&l,pc[segment],&afl,&label,1))) + { + rlt[0] = afl; + lab[0] = label; + + if(t[2+l]!=T_END) + { + if(t[2+l]==']') + { + if(t[3+l]==',') + { + if(tolower(t[4+l])=='y') + sy=11; + else + er=E_SYNTAX; + } else + if(t[3+l]!=T_END) + er=E_SYNTAX; + } + } else + er=E_SYNTAX; + } + } else + { + sy=4; + if(!(er=a_term(t+1,vv,&l,pc[segment],&afl,&label,1))) + { + rlt[0] = afl; + lab[0] = label; + if(t[1+l]!=T_END) + { + if(t[1+l]==',') + { + if(tolower(t[2+l])=='y') + sy=6; + else + if(tolower(t[2+l])=='s') + sy=12; + else + sy=5; + } else + er=E_SYNTAX; + } + } + } + + bl=Maxbyt+1; + + while(--bl) + { + if((am=at[sy][bl-1])>=0) + { + if(am>Admodes) + { + er=E_SYNTAX; + break; + } + if(ct[n][am]>=0) + break; + + for(v=0;v=0) + break; + if(v16 && + !er && !(vv[0]&0xff0000) && opt[am]>=0) + if(ct[n][opt[am]]>=0) + am=opt[am]; +#ifdef DEBUG_AM +fprintf(stderr, +"aftaa1: pc= %d, am = %d and vv[0] = %d, optimize = %d, bitmask = %d\n", + pc[segment], am, vv[0], fl, (vv[0]&0xffff00)); +#endif + if(t[*ll-1]!='!') { + if(bl && !er && !(vv[0]&0xffff00) && opt[am]>=0) { + if(ct[n][opt[am]]>=0) { + if (!fl || t[*ll-1]=='`') { + am=opt[am]; + } else { + errout(W_FORLAB); + } + } + } + } +#ifdef DEBUG_AM +fprintf(stderr, +"aftaa2: pc=%d, am=%d and vv[0]=%d, optimize=%d, bitmask=%d, op=%d\n", + pc[segment], am, vv[0], fl, (vv[0]&0xffff00), ct[n][opt[am]]); +#endif + } + + if(!bl) + er=E_SYNTAX; + else + { + bl=le[am]; + if( ((ct[n][am]&0x400) && memode) || ((ct[n][am]&0x800) && xmode)) { + bl++; + } + *ll=bl; + + } + +#ifdef DEBUG_AM +fprintf(stderr, "byte length is now %d\n", bl); +#endif + + if(!er) + { + t[0]=ct[n][am]&0x00ff; + if(ct[n][am]&0x0300) + { + if(ct[n][am]&0x100) { + ncmos++; + if(!cmosfl) + er=E_CMOS; + } else { + n65816++; + if(!w65816) + er=E_65816; + } + } + if(am!=0) + { + if((am<8 && !( ((ct[n][am]&0x400) && memode) || ((ct[n][am]&0x800) && xmode) )) || (am>=19 && am!=23)) + { + if(vv[0]&0xff00) { +#ifdef DEBUG_AM +fprintf(stderr, "address mode: %i address: %i\n", am, vv[0]); +#endif + er=E_OVERFLOW; + } + else + t[1]=vv[0]; +/*if(rlt[0]) printf("relocation 1 byte %04x at pc=$%04x, value now =$%04x\n",rlt[0],pc[segment]+1,*vv); */ + if(rlt[0]) u_set(pc[segment]+1, rlt[0], lab[0], 1); + } else + if(((am<14 || am==23) && am!=11) || am==7) + { + if (vv[0]>0xffff) { +#ifdef DEBUG_AM +fprintf(stderr, "address mode: %i address: %i\n", am, vv[0]); +#endif + er=E_OVERFLOW; + } + else { + t[1]=vv[0]&255; + t[2]=(vv[0]>>8)&255; +/*if(rlt[0]) printf("relocation 2 byte %04x at pc=$%04x, value now =$%04x\n",rlt[0],pc[segment]+1,*vv); */ + if(rlt[0]) u_set(pc[segment]+1, rlt[0], lab[0], 2); + } + } else + if(am==11 || am==16) { + if((segment!=SEG_ABS) && (!rlt[0])) { + er=E_ILLPOINTER; + } else { +/*printf("am=11, pc=%04x, vv[0]=%04x, segment=%d\n",pc[segment],vv[0], segment);*/ + v=vv[0]-pc[segment]-le[am]; + if(((v&0xff80)!=0xff80) && (v&0xff80) && (am==11)) + er=E_RANGE; + else { + t[1]=v&255; + t[2]=(v>>8)&255; + } + } + } else + if(am==14) { + if(vv[0]&0xfff8 || vv[1]&0xff00) + er=E_RANGE; + else + if((segment!=SEG_ABS) && (rlt[0] || !rlt[2])) { + er=E_ILLPOINTER; + } else { +/*if(rlt[1]) printf("relocation 1 byte %04x at pc=$%04x, value now =$%04x\n",rlt[1],pc[segment]+1,*vv); */ + if(rlt[1]) u_set(pc[segment]+1, rlt[1], lab[1], 1); + t[0]=t[0]|(vv[0]<<4); + t[1]=vv[1]; + v=vv[2]-pc[segment]-3; + if((v&0xff80) && ((v&0xff80)!=0xff80)) + er=E_OVERFLOW; + else + t[2]=v; + } + } else + if(am==15) + { +/*if(rlt[1]) printf("relocation 1 byte %04x at pc=$%04x, value now =$%04x\n",rlt[1],pc[segment]+1,*vv); */ + if(rlt[1]) u_set(pc[segment]+1, rlt[1], lab[1], 1); + if(vv[0]&0xfff8 || vv[1]&0xff00) + er=E_OVERFLOW; + else + { + t[0]=t[0]|(vv[0]<<4); + t[1]=vv[1]; + } + } else + if(am==17 || am==18) + { + t[1]=vv[0]&255; + t[2]=(vv[0]>>8)&255; + t[3]=(vv[0]>>16)&255; + if(rlt[0]) { + rlt[0]|=A_LONG; + u_set(pc[segment]+1, rlt[0], lab[0], 3); + } + + } else + er=E_SYNTAX; + } + } + + } else + er=E_SYNTAX; + } +#ifdef DEBUG_AM +fprintf(stderr, "-- endof P2\n"); +#endif + pc[segment]+=bl; + if(segment==SEG_TEXT) pc[SEG_ABS]+=bl; + if(segment==SEG_ABS) pc[SEG_TEXT]+=bl; + *al = bl; + return(er); +} + +int b_term(char *s, int *v, int *l, int pc) +{ + static signed char t[MAXLINE]; + int er,i,afl, label; + + if(!(er=t_conv((signed char*)s,t,l,pc,&i,&i,&i,1,NULL))) + { + er=a_term(t,v,&i,pc,&afl,&label,0); + + } + return(er); +} + +/* translate a string into a first-pass sequence of tokens */ +static int t_conv(signed char *s, signed char *t, int *l, int pc, int *nk, + int *na1, int *na2, int af, int *bytep) /* Pass1 von s nach t */ +/* tr. pass1, from s to t */ +{ + static int v,f; + static int operand,o; + int fl,afl; + int p,q,ud,n,ll,mk,er; + int m, uz, byte; + static unsigned char cast; + +/* ich verstehe deutsch, aber verstehen andere leute nicht; so, werde ich + diese bemerkungen uebersetzen ... cameron */ +/* I understand German, but other folks don't, so I'll translate these + comments ... Cameron */ +/* note that I don't write so good tho' ;) */ + + *nk=0; /* comma count */ + *na1=0; /* asc text count */ + *na2=0; /* total bytecount in asc texts */ + ll=0; + er=E_OK; /* error state */ + p=0; + q=0; + ud = uz = byte =0; + mk=0; /* 0 = add'l commas ok */ + fl=0; /* 1 = pass text thru */ + afl=0; /* pointer flag for label */ + + while(s[p]==' ') p++; + + n=T_END; + cast='\0'; + + if(!af) + { + while(s[p]!='\0' && s[p]!=';') + { + + /* is keyword? */ + if(!(er=t_keyword(s+p,&ll,&n))) + break; + + /* valid syntax, but just not a real token? */ + if(er && er!=E_NOKEY) + break; + + /* if so, try to understand as label */ + if((er=l_def((char*)s+p,&ll,&n,&f))) + break; + + p+=ll; + + while(s[p]==' ') p++; + + if(s[p]=='=') + { + t[q++]=T_OP; + t[q++]=n&255; + t[q++]=(n>>8)&255; + t[q++]='='; + p++; + ll=n=0; + break; + } else + if(f && s[p]!='\0' && s[p+1]=='=') + { + t[q++]=T_OP; + t[q++]=n&255; + t[q++]=(n>>8)&255; + t[q++]=s[p]; + p+=2; + ll=n=0; + break; + } else + if(s[p]==':') /* to support label: ... syntax */ + { + p++; + while(s[p]==' ') p++; + l_set(n,pc,segment); /* set as address value */ + n=0; + } else { /* label ... syntax */ + l_set(n,pc,segment); /* set as address value */ + n=0; + } + + } + + if((n & 0xff) <=Lastbef) + mk=1; /* 1= nur 1 Komma erlaubt *//* = only 1 comma ok */ + } + + if(s[p]=='\0' || s[p]==';') + { + er=E_NOLINE; + ll=0; + } else + if(!er) + { + + p+=ll; + if(ll) { + t[q++]= n & 0xff; +/* + if( (n&0xff) == Kmacro) { + t[q++]= (n >> 8) & 0xff; + } +*/ + } + + operand=1; + + while(s[p]==' ') p++; + + if(s[p]=='#') + { + mk=0; + t[q++]=s[p++]; + while(s[p]==' ') p++; + } + +/* + * + * operand processing + * byte = length of operand in bytes to be assembled + * + * + */ + + /* this addresses forced high/low/two byte addressing, but only + for the first operand. Further processing is done in a_term() + */ + +/* FIXIT2 */ + + while(s[p]!='\0' && s[p]!=';' && !er) + { + if(fl) + { + t[q++]=s[p++]; + } else + { + if(operand) + { + /* are we forcing the operand into a particular + addressing mode? !, @, ` operators */ + if(s[p]=='!' || s[p]=='@' || s[p]=='`') + { + cast=s[p]; + operand= -operand+1; + p++; + } else + if(s[p]=='(' || s[p]=='-' || s[p]=='>' || + s[p]=='<' || s[p]=='[') + { + t[q++]=s[p++]; + operand= -operand+1; /* invert to become reinverted */ + } else + if(s[p]=='*') + { + t[q++]=s[p++]; + } else + /* maybe it's a label */ + if(isalpha(s[p]) || s[p]=='_') + { + m=n; + er=l_search((char*)s+p,&ll,&n,&v,&afl); + +/* + if(m==Kglobl || m==Kextzero) { + if(er==E_NODEF) { + er=E_OK; + } + t[q++]=T_LABEL; + t[q++]=n & 255; + t[q++]=(n>>8) & 255; + } else +*/ + + if(!er) + { + if(afl) { + t[q++]=T_POINTER; + t[q++]=afl & 255; + t[q++]=v & 255; + t[q++]=(v>>8) & 255; + } else { + wval(q,v); + } + } else + if(er==E_NODEF) + { +#ifdef DEBUG_AM +fprintf(stderr, "could not find %s\n", (char *)s+p); +#endif + t[q++]=T_LABEL; + t[q++]=n & 255; + t[q++]=(n>>8) & 255; +/* + if(afl==SEG_ZEROUNDEF) uz++; +*/ + ud++; + er=E_OK; + } + p+=ll; + } + else + if(s[p]<='9' && s[p]>='0') + { + tg_dez(s+p,&ll,&v); + p+=ll; + wval(q,v); + } + else + + /* handle encodings: hex, binary, octal, quoted strings */ + switch(s[p]) { + case '$': + tg_hex(s+p+1,&ll,&v); + p+=1+ll; + wval(q,v); + break; + case '%': + tg_bin(s+p+1,&ll,&v); + p+=1+ll; + wval(q,v); + break; + case '&': + tg_oct(s+p+1,&ll,&v); + p+=1+ll; + wval(q,v); + break; + case '\'': + case '\"': + er=tg_asc(s+p,t+q,&q,&p,na1,na2,n); + break; + case ',': + if(mk) + while(s[p]!='\0' && s[p]!=';') + { + while(s[p]==' ') p++; + *nk+=(s[p]==','); + t[q++]=s[p++]; + } + else + { + *nk+=1; + t[q++]=s[p++]; + } + break; + default : + er=E_SYNTAX; + break; + } + operand= -operand+1; + + } else /* operator */ + { + o=0; + if(s[p]==')' || s[p]==']') + { + t[q++]=s[p++]; + operand =-operand+1; + } else + if(s[p]==',') + { + t[q++]=s[p++]; + if(mk) + fl++; + *nk+=1; + } else + switch(s[p]) { + case '+': + o=1; + break; + case '-': + o=2; + break; + case '*': + o=3; + break; + case '/': + o=4; + break; + case '<': + switch (s[p+1]) { + case '<': + o=6; + break; + case '>': + o=12; + break; + case '=': + o=10; + break; + default : + o=7; + break; + } + break; + case '>': + switch (s[p+1]) { + case '>': + o=5; + break; + case '<': + o=12; + break; + case '=': + o=11; + break; + default: + o=8; + break; + } + break; + case '=': + switch (s[p+1]) { + case '<': + o=10; + break; + case '>': + o=11; + break; + default: + o=9; + break; + } + break; + case '&': + if (s[p+1]=='&') + o=16; + else + o=13; + break; + case '|': + if (s[p+1]=='|') + o=17; + else + o=15; + break; + case '^': + o=14; + break; + default: + er=E_SYNTAX; + break; + } + if(o) + { + t[q++]=o; + p+=lp[o]; +#if(0) + uz++; /* disable 8-bit detection */ +#endif + } + operand= -operand+1; + } + + while(s[p]==' ') p++; + } + } + } + if(!er) + { +/* + if(uz==1 && ud==1 && byte!=2) { + byte=1; + } + if(byte == 1) { + t[q++] = T_FBYTE; + } else if(byte == 2) { + t[q++] = T_FADDR; + } +*/ + byte = 0; + t[q++]=T_END; + if(ud > 0) { + er=E_NODEF; + byte = 1; + } + } + /* FIXME: this is an unholy union of two "!" implementations :-( */ + t[q++]='\0'; + t[q++]=cast; + *l=q; + if(bytep) *bytep=byte; + return(er); +} + +static int t_keyword(signed char *s, int *l, int *n) +{ + int i = 0, j = 0, hash; + + if(!isalpha(s[0]) && s[0]!='.' && s[0]!='*' ) + return(E_NOKEY); + + if(isalpha(s[0])) + hash=tolower(s[0])-'a'; + else + hash=26; + + + if(s[0]=='*') { + j=1; + while(s[j] && isspace(s[j])) j++; + if(s[j]=='=') { + i=Kpcdef; + j++; + } + } + if(!i) { + i=ktp[hash]; + hash=ktp[hash+1]; + while(i='0') + val=val*8+(s[i++]-'0'); + + *l=i; + *v=val; +} + +static void tg_hex(signed char *s, int *l, int *v) +{ + int i=0,val=0; + + while((s[i]>='0' && s[i]<='9') || (tolower(s[i])<='f' && tolower(s[i])>='a')) + { + val=val*16+(s[i]<='9' ? s[i]-'0' : tolower(s[i])-'a'+10); + i++; + } + *l=i; + *v=val; +} + +/* + * tokenize a string - handle two delimiter types, ' and " + */ +static int tg_asc(signed char *s, signed char *t, int *q, int *p, int *na1, int *na2,int n) +{ + + int er=E_OK,i=0,j=0; + + signed char delimiter = s[i++]; + +#ifdef DEBUG_AM +fprintf(stderr, "tg_asc token = %i\n", n); +#endif + + t[j++]='"'; /* pass2 token for string */ + j++; /* skip place for length */ + + while(s[i]!='\0' && s[i]!=delimiter) + { + /* do NOT convert for Kbin or Kaasc, or for initial parse */ + if (!n || n == Kbin || n == Kaasc) { + t[j++]=s[i]; + } else if(s[i]!='^') { /* no escape code "^" */ + t[j++]=convert_char(s[i]); + } else { /* escape code */ + signed char payload = s[i+1]; + switch(payload) { + case '\0': + er=E_SYNTAX; + break; + case '\"': + if (payload == delimiter) { + t[j++]=convert_char(payload); + i++; + } else { + er=E_SYNTAX; + } + break; + case '\'': + if (payload == delimiter) { + t[j++]=convert_char(payload); + i++; + } else { + er=E_SYNTAX; + } + break; + case '^': + t[j++]=convert_char('^'); + i++; + break; + default: + t[j++]=convert_char(payload&0x1f); + i++; + break; + } + } + i++; + } + if(j==3) /* optimize single byte string to value */ + { + t[0]=T_VALUE; + t[1]=t[2]; + t[2]=0; + t[3]=0; + j++; + } else + { /* handle as string */ + t[1]=j-2; + *na1 +=1; + *na2 +=j-2; + } + if(s[i]==delimiter) { /* in case of no error */ + i++; /* skip ending delimiter */ + } + *q +=j; + *p +=i; + return(er); +} + diff --git a/xa-2.3.5/src/xat.h b/xa-2.3.5/src/xat.h new file mode 100644 index 0000000..ad7b82b --- /dev/null +++ b/xa-2.3.5/src/xat.h @@ -0,0 +1,28 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __XA65_XAT_H__ +#define __XA65_XAT_H__ + +extern int dsb_len; + +int t_p1(signed char *s, signed char *t, int *ll, int *al); +int t_p2(signed char *t, int *ll, int fl, int *al); +int b_term(char *s, int *v, int *l, int pc); + +#endif /* __XA65_XAT_H__ */ diff --git a/xa-2.3.5/src/xau.c b/xa-2.3.5/src/xau.c new file mode 100644 index 0000000..c8b7560 --- /dev/null +++ b/xa-2.3.5/src/xau.c @@ -0,0 +1,73 @@ +/* xa65 - 65xx/65816 cross-assembler and utility suite + * + * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + * + * Undefined label tracking module (also see xal.c) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include "xad.h" +#include "xau.h" +#include "xah.h" +#include "xal.h" + +/* +static int *ulist = NULL; +static int un = 0; +static int um = 0; +*/ + +int u_label(int labnr) { + int i; +/*printf("u_label: %d\n",labnr);*/ + if(!afile->ud.ulist) { + afile->ud.ulist = malloc(200*sizeof(int)); + if(afile->ud.ulist) afile->ud.um=200; + } + + for(i=0;iud.un;i++) { + if(afile->ud.ulist[i] == labnr) return i; + } + if(afile->ud.un>=afile->ud.um) { + afile->ud.um *= 1.5; + afile->ud.ulist = realloc(afile->ud.ulist, afile->ud.um * sizeof(int)); + if(!afile->ud.ulist) { + fprintf(stderr, "Panic: No memory!\n"); + exit(1); + } + } + afile->ud.ulist[afile->ud.un] = labnr; + return afile->ud.un++; +} + +void u_write(FILE *fp) { + int i, d; + char *s; +/*printf("u_write: un=%d\n",afile->ud.un);*/ + fputw(afile->ud.un, fp); + + for(i=0;iud.un;i++) { + l_vget(afile->ud.ulist[i], &d, &s); + fprintf(fp,"%s", s); + fputc(0,fp); + } + free(afile->ud.ulist); + afile->ud.ulist=NULL; + afile->ud.um = afile->ud.un = 0; +} diff --git a/xa-2.3.5/src/xau.h b/xa-2.3.5/src/xau.h new file mode 100644 index 0000000..aca100a --- /dev/null +++ b/xa-2.3.5/src/xau.h @@ -0,0 +1,24 @@ + +/* + xa65 - 6502 cross assembler and utility suite + Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +extern int u_label(int labnr); +extern void u_write(FILE *fp); + diff --git a/xa-2.3.5/tests/README b/xa-2.3.5/tests/README new file mode 100644 index 0000000..58442e8 --- /dev/null +++ b/xa-2.3.5/tests/README @@ -0,0 +1,24 @@ +This is a directory of test suites for complex or pathological cases that +have been repaired (?) in the current version. It is primarily for internal +testing, but is here for your interest. + +adrm/ Addressing mode test (especially the optimizer and quantity + prefixes) +nonl/ Patryk's no-new-line-on-last-line cases ;) +fordef/ Optimizer warnings for forward defined labels +relocundef/ Tests for the detection of undefined references during a + reloc65 export. +ldoreloc/ Test case for the relocation table reading of ldo when undef'd + refs are involved +comcom/ Comments-with-comments-with-commands-etc. for testing -M +recmac/ Recursive macro evaluation testing +openpp/ Testing of open #if*s in pp +cpp/ Random preprocessor tests, mostly crap +incerr/ 1) .xl/.al should error without -w 2) error should be in + the correct file +binclude/ Binary include code with some weird casing +chppch/ Changing preprocessor characters (-p) +charset/ Tests of when charsets should be honoured and when not + +Cameron Kaiser, André Fachat + diff --git a/xa-2.3.5/tests/adrm/02.asm b/xa-2.3.5/tests/adrm/02.asm new file mode 100644 index 0000000..db2b783 --- /dev/null +++ b/xa-2.3.5/tests/adrm/02.asm @@ -0,0 +1,33 @@ + .word $0400 + * = $0400 + +q = $0005 +fizz +/* these should be optimized to zero page */ + sta $05 + sta $0005 + sta q + dec q + inc q + lda <$0005 +/* these should not */ + stx !$0005 + sta !q + sta $8765 + stx $919c + inc !q + dec $8342 + sta $4431 + inc $79e0 + sty $1b0a + jsr $ffe4 + jmp $fce2 + jmp breadbox + lsr $2020 + bne fizz + rts + +breadbox rts + beq fizz + rts + diff --git a/xa-2.3.5/tests/adrm/816.asm b/xa-2.3.5/tests/adrm/816.asm new file mode 100644 index 0000000..7f1b3e0 --- /dev/null +++ b/xa-2.3.5/tests/adrm/816.asm @@ -0,0 +1,29 @@ + .word $0400 + * = $0400 + +q = $0005 +r = $000005 +fizz +/* these should be optimized to zero page */ + sta $05 + sta $0005 + sta q + sta r +/* 16-bit */ + sta !$0005 + sta !q + sta $8765 +/* 24-bit */ + sta @q + sta @$000005 + sta $876543 + rts + + jmp $fce2 + jmp $99fce2 + jmp breadbox + jmp @breadbox + +breadbox rts + bne fizz + rts diff --git a/xa-2.3.5/tests/adrm/a.o65 b/xa-2.3.5/tests/adrm/a.o65 new file mode 100644 index 0000000000000000000000000000000000000000..218516c921d89e81e10c67e8c7bb4ce449844e2c GIT binary patch literal 50 vcmZQzX=R1LURDMWN^S3FWnchf=hXHDpGSXU9{ri=qr(!T!@`ho;bH;+WtI?j literal 0 HcmV?d00001 diff --git a/xa-2.3.5/tests/adrm/bip.inc b/xa-2.3.5/tests/adrm/bip.inc new file mode 100644 index 0000000..93bb6cb --- /dev/null +++ b/xa-2.3.5/tests/adrm/bip.inc @@ -0,0 +1,28 @@ +/* + +- tbasic.0.asm: if you make vecwri absolute with !, then the branch gets + generated as if it were NOT absolute. works okay without it (and + gets a warning) + +*/ + +test lda !$0095 + bne test + + ldx #13 +lup0 lda @vecwri,x + sta $2005,x + dex + bne lup0 + lda #$00 + sta $0020 + lda #$02 + sta $0021 + lda #$ff + sta $0022 + lda #$13 + sta $0023 + jmp $2003 + +vectors .byt $4c, $5a, $1e, $4c, $a0, $1e, $4c, $00, $01 +vecwri = vectors - 1 diff --git a/xa-2.3.5/tests/adrm/c02.asm b/xa-2.3.5/tests/adrm/c02.asm new file mode 100644 index 0000000..a55556a --- /dev/null +++ b/xa-2.3.5/tests/adrm/c02.asm @@ -0,0 +1,33 @@ + .word $0400 + * = $0400 + +q = $0005 +fizz +/* these should be optimized to zero page */ + sta $05 + sta $0005 + sta q + dec q + inc q +/* these should not */ + stx !$0005 + sta !q + sta $8765 + stx $919c + inc !q + inc; a + dec $8342 + dec; a + sta $4431 + inc $79e0 + sty $1b0a + jsr $ffe4 + jmp $fce2 + jmp breadbox + lsr $2020 + rts + + +breadbox rts + bne fizz + rts diff --git a/xa-2.3.5/tests/adrm/zab.asm b/xa-2.3.5/tests/adrm/zab.asm new file mode 100644 index 0000000..248f0e2 --- /dev/null +++ b/xa-2.3.5/tests/adrm/zab.asm @@ -0,0 +1,4 @@ + .word $4000 + * = $4000 + +#include "bip.inc" diff --git a/xa-2.3.5/tests/adrm/zpa.asm b/xa-2.3.5/tests/adrm/zpa.asm new file mode 100644 index 0000000..ca9bc5f --- /dev/null +++ b/xa-2.3.5/tests/adrm/zpa.asm @@ -0,0 +1,4 @@ + .word $0000 + * = $0000 + +#include "bip.inc" diff --git a/xa-2.3.5/tests/binclude/README.1st b/xa-2.3.5/tests/binclude/README.1st new file mode 100644 index 0000000..57a3ecf --- /dev/null +++ b/xa-2.3.5/tests/binclude/README.1st @@ -0,0 +1,117 @@ +This is the readme for xa, a cross-assembler for the 6502 and 65816 CPUs (and +derivatives). xa is a small, fast, portable two-pass assembler that compiles +under most ANSI C compilers. It is distributed under the GNU Public License +(see COPYING). + +The current version is 2.3.3, which implements several compatibility +improvements on 2.3.2, a bug fix to the 2.3.0 version. + +2.3.0 itself features many compatibility improvements and new man-based +documentation. It also completed the merge of the 65816 and 6502/R65C02 +versions and thus the current xa can generate code for all targets now. + +To install on a generic Unixy thing, you should be able to just type + + % make # to build the executable, and if it works ... + % make install # to install man pages and binaries into the system + +This will create xa along with its various support utilities. Try assembling +the cpk depacker in examples/ as a test. xa also comes with uncpk (a program +for generating cpk archives) and printcbm (a program for listing Commodore +BASIC test) and file65, ldo65 and reloc65 for displaying, linking and +relocating o65 files in Andre's relocatable format (see doc/fileformats.txt). +The loader/ directory also has goodies for managing relocatable binaries. + +Don't forget the man pages in man/. Install these into your MANPATH at your +leisure, or read them with nroff -man (and/or groff -man). + +xa is no longer broadly supported outside of Unix due to my inability to test +it, but has nothing that should impair it from compiling elsewhere. To wit, +DOS compilation is still supported with the GO32 package. You should just be +able to type + + C:\> make dos + +In addition, there are compatibility updates to allow it to compile under +Microsoft Visual Studio and mingw. It should compile under VS2005 as written; +look in the vstudio directory for solution and project files provided by +Fabian Nunez. For mingw, use + + make mingw + +Similarly, Amiga and Atari ST compilation should still also function with +their particular compatible packages. + +xa has a companion disassembler, the dxa package. dxa is not included in the +standard xa distribution, but can be downloaded from the xa home page at + + http://www.floodgap.com/retrotech/xa/ + +Please check by periodically for the latest version of both packages. + +xa was originally written and maintained by Andre Fachat. The current version +is maintained by Cameron Kaiser. + +Please send me your comments at ckaiser@floodgap.com -- Andre's original +readme follows and applies generally to the present version. + +------------------------------------------------------------------------------- + +XA is a 6502 cross compiler: + + - under GNU public license + + - can produce _relocatable_ binaries + + - The full fileformat description and 6502 file loader included. + + - also included relocation and info utilites, as well as linker + + - for any ANSI-C compliant computer (only utilities need 'stat' call + for file size). + + - fast by hashtables + + - Rockwell CMOS opcodes + + - running under DOS and any ANSI C system (Unix, Amiga, Atari ST) + +I developed this cross assembler for the 6502 CPU family quite some time +ago on my Atari ST. The assembler has successfully been ported to Amiga +and Unix computer (ported? just compiled... :-) +Lately I came across the problem to need relocatable 6502 binary files, so +I revised the assembler from version 2.0.7 to 2.1.0, adding a (admittedly +proprietary) 6502 relocatable binary format. But there are not many other +formats around and they didn't fit my needs. I have developed this format +myself and it is under the GNU public license. +With version 2.1.1 the 'official' version of the fileformat is supported. + +To compile it, just type "make" (if you have the GNU gcc. If not, edit the +Makefile for the compiler options). This produces "xa", the cross assembler; +"uncpk", a small packing utility (where the C64 counterpart is in the +examples subdirectory), "printcbm", that lists C64 BASIC files and +'file65' that prints some information about o65 files. The "loader" in +the loader subdirectory is a basic 6502 implementation of a relocating +binary loader. +"file65" prints file information on 'o65' relocatable files. "reloc65" +can relocate 'o65' files. + +If you want to use it under DOS, you have to have the GO32 DOS crosscompiling +tools to compile. Then just type "make dos" and you'll end up with the +appropriate DOS binaries. This has been tested only under i386 Linux, however. +Another archive with the DOS binaries included is provided. + +One problem on the Atari was it's broken "malloc". Therefore I used to +alloc everything in one chunk and divide the memory by hand. So everything +was kind of statically allocated. This is almost gone now. Only the +temporary storage between pass1 and pass2 and the preprocessor are still +allocated in one chunk (size definitions in xah.h). The rest is allocated +as needed. + +The docs are in the 'doc' subdir. There also is a description of the +6502 relocatable binary format. If you think some things could be +expressed in a better way, feel free and mail me to improve my english ;-) +[ The documentation is now maintained in man(1) format in man/ . -- CK ] + +Andre + diff --git a/xa-2.3.5/tests/binclude/test.asm b/xa-2.3.5/tests/binclude/test.asm new file mode 100644 index 0000000..850fb15 --- /dev/null +++ b/xa-2.3.5/tests/binclude/test.asm @@ -0,0 +1,19 @@ + .word $9000 + * = $9000 + + w = 10 + +glorb + +.dsb 9,10 ; +9 +.byt 6, 6, 6, "devil", 'Q' ; +9 => 18 +.bin 0,5,'README.1st' ; +5 => 23 +.bin w,w+256,'README.1st' ; +266 => 289 ($0121) + +gleeb + ; $0123 + jmp glorb + ; should be $9121 (remember the SA) + jmp gleeb + bne * + diff --git a/xa-2.3.5/tests/binclude/test2.asm b/xa-2.3.5/tests/binclude/test2.asm new file mode 100644 index 0000000..4f88763 --- /dev/null +++ b/xa-2.3.5/tests/binclude/test2.asm @@ -0,0 +1,18 @@ + .word $9000 + * = $9000 + + w = 10 + +glorb + +.dsb 9,10 +.byt 6, 6, 6, "devil", 'Q' +.bin 0,109,'README.1st' +.bin w,w+119,'README.1st' + +gleeb + + jmp glorb + jmp gleeb + bne * + diff --git a/xa-2.3.5/tests/chardelimiter/a.o65 b/xa-2.3.5/tests/chardelimiter/a.o65 new file mode 100644 index 0000000..a47bbe1 --- /dev/null +++ b/xa-2.3.5/tests/chardelimiter/a.o65 @@ -0,0 +1 @@ +©A©A1234512345"''" \ No newline at end of file diff --git a/xa-2.3.5/tests/chardelimiter/delimtest.a65 b/xa-2.3.5/tests/chardelimiter/delimtest.a65 new file mode 100644 index 0000000..c7b3a84 --- /dev/null +++ b/xa-2.3.5/tests/chardelimiter/delimtest.a65 @@ -0,0 +1,16 @@ + + *=$033c + + lda #"A" + lda #'A' + + .asc "12345" + .asc '12345' + + .asc "^"" ; ^ is escape character + .asc '^'' + .asc "'" + .asc '"' + + .asc "^1" + diff --git a/xa-2.3.5/tests/charset/README.1st b/xa-2.3.5/tests/charset/README.1st new file mode 100644 index 0000000..0c7994a --- /dev/null +++ b/xa-2.3.5/tests/charset/README.1st @@ -0,0 +1,116 @@ +This is the readme for xa, a cross-assembler for the 6502 and 65816 CPUs (and +derivatives). xa is a small, fast, portable two-pass assembler that compiles +under most ANSI C compilers. It is distributed under the GNU Public License +(see COPYING). + +The current version is 2.3.4, which implements multiple improvements on +2.3.2, a bug fix to the 2.3.0 version. 2.3.0 itself features many +compatibility improvements and new man-based documentation. It also completed +the merge of the 65816 and 6502/R65C02 versions and thus the current xa can +generate code for all targets now. + +To install on a generic Unixy thing, you should be able to just type + + % make # to build the executable, and if it works ... + % make install # to install man pages and binaries into the system + +This will create xa along with its various support utilities. Try assembling +the cpk depacker in examples/ as a test. xa also comes with uncpk (a program +for generating cpk archives) and printcbm (a program for listing Commodore +BASIC test) and file65, ldo65 and reloc65 for displaying, linking and +relocating o65 files in Andre's relocatable format (see doc/fileformats.txt). +The loader/ directory also has goodies for managing relocatable binaries. + +Don't forget the man pages in man/. Install these into your MANPATH at your +leisure, or read them with nroff -man (and/or groff -man). + +xa is no longer broadly supported outside of Unix due to my inability to test +it, but has nothing that should impair it from compiling elsewhere. To wit, +DOS compilation is still supported with the GO32 package. You should just be +able to type + + C:\> make dos + +In addition, there are compatibility updates to allow it to compile under +Microsoft Visual Studio and mingw. It should compile under VS2005 as written; +look in the vstudio directory for solution and project files provided by +Fabian Nunez. For mingw, use + + make mingw + +Similarly, Amiga and Atari ST compilation should still also function with +their particular compatible packages. + +xa has a companion disassembler, the dxa package. dxa is not included in the +standard xa distribution, but can be downloaded from the xa home page at + + http://www.floodgap.com/retrotech/xa/ + +Please check by periodically for the latest version of both packages. + +xa was originally written and maintained by Andre Fachat. The current version +is maintained by Cameron Kaiser. + +Please send me your comments at ckaiser@floodgap.com -- Andre's original +readme follows and applies generally to the present version. + +------------------------------------------------------------------------------- + +XA is a 6502 cross compiler: + + - under GNU public license + + - can produce _relocatable_ binaries + + - The full fileformat description and 6502 file loader included. + + - also included relocation and info utilites, as well as linker + + - for any ANSI-C compliant computer (only utilities need 'stat' call + for file size). + + - fast by hashtables + + - Rockwell CMOS opcodes + + - running under DOS and any ANSI C system (Unix, Amiga, Atari ST) + +I developed this cross assembler for the 6502 CPU family quite some time +ago on my Atari ST. The assembler has successfully been ported to Amiga +and Unix computer (ported? just compiled... :-) +Lately I came across the problem to need relocatable 6502 binary files, so +I revised the assembler from version 2.0.7 to 2.1.0, adding a (admittedly +proprietary) 6502 relocatable binary format. But there are not many other +formats around and they didn't fit my needs. I have developed this format +myself and it is under the GNU public license. +With version 2.1.1 the 'official' version of the fileformat is supported. + +To compile it, just type "make" (if you have the GNU gcc. If not, edit the +Makefile for the compiler options). This produces "xa", the cross assembler; +"uncpk", a small packing utility (where the C64 counterpart is in the +examples subdirectory), "printcbm", that lists C64 BASIC files and +'file65' that prints some information about o65 files. The "loader" in +the loader subdirectory is a basic 6502 implementation of a relocating +binary loader. +"file65" prints file information on 'o65' relocatable files. "reloc65" +can relocate 'o65' files. + +If you want to use it under DOS, you have to have the GO32 DOS crosscompiling +tools to compile. Then just type "make dos" and you'll end up with the +appropriate DOS binaries. This has been tested only under i386 Linux, however. +Another archive with the DOS binaries included is provided. + +One problem on the Atari was it's broken "malloc". Therefore I used to +alloc everything in one chunk and divide the memory by hand. So everything +was kind of statically allocated. This is almost gone now. Only the +temporary storage between pass1 and pass2 and the preprocessor are still +allocated in one chunk (size definitions in xah.h). The rest is allocated +as needed. + +The docs are in the 'doc' subdir. There also is a description of the +6502 relocatable binary format. If you think some things could be +expressed in a better way, feel free and mail me to improve my english ;-) +[ The documentation is now maintained in man(1) format in man/ . -- CK ] + +Andre + diff --git a/xa-2.3.5/tests/charset/a.o65 b/xa-2.3.5/tests/charset/a.o65 new file mode 100644 index 0000000000000000000000000000000000000000..72ae310bd9c52e8770e54416c85064bafc0c4080 GIT binary patch literal 87 zcmZRW5R#EutN=tM8L0|Isfj7MsS0Tbv69r{5~DPK|0KsCxBPsk#3BgWF_8vz>Rwk}Y1OPXb9@PK< literal 0 HcmV?d00001 diff --git a/xa-2.3.5/tests/charset/test.asm b/xa-2.3.5/tests/charset/test.asm new file mode 100644 index 0000000..645b528 --- /dev/null +++ b/xa-2.3.5/tests/charset/test.asm @@ -0,0 +1,16 @@ + .word $9000 + * = $9000 + + w = 10 +.bin 0,10+w,"README.1st" +.bin 0,10,"README.1st" +#include "test2.s" +.byt "FooBar" +.aasc "FooBar" +.asc "FooBar",65,97,10 +.aasc "FooBar" +.bin 0,10,"README.1st" +.aasc "Barfoo",65,97,10 + +lda #'A' +lda #"A" diff --git a/xa-2.3.5/tests/charset/test2.s b/xa-2.3.5/tests/charset/test2.s new file mode 100644 index 0000000..1d6065b --- /dev/null +++ b/xa-2.3.5/tests/charset/test2.s @@ -0,0 +1 @@ +.aasc "test2" diff --git a/xa-2.3.5/tests/chppch/a.o65 b/xa-2.3.5/tests/chppch/a.o65 new file mode 100644 index 0000000..64845fb --- /dev/null +++ b/xa-2.3.5/tests/chppch/a.o65 @@ -0,0 +1 @@ +` \ No newline at end of file diff --git a/xa-2.3.5/tests/chppch/qwerty.h b/xa-2.3.5/tests/chppch/qwerty.h new file mode 100644 index 0000000..28b2b52 --- /dev/null +++ b/xa-2.3.5/tests/chppch/qwerty.h @@ -0,0 +1,4 @@ + rts + +#print 12+12 +~print 10+10 diff --git a/xa-2.3.5/tests/chppch/test.c b/xa-2.3.5/tests/chppch/test.c new file mode 100644 index 0000000..f5b1094 --- /dev/null +++ b/xa-2.3.5/tests/chppch/test.c @@ -0,0 +1,3 @@ +#include "qwerty.h" + +~print 5+5 diff --git a/xa-2.3.5/tests/chppch/test.out b/xa-2.3.5/tests/chppch/test.out new file mode 100644 index 0000000..dd4cdaa --- /dev/null +++ b/xa-2.3.5/tests/chppch/test.out @@ -0,0 +1,10 @@ +# 1 "test.c" +# 1 "qwerty.h" 1 + rts + +#print 12+12 +~print 10+10 +# 1 "test.c" 2 + + +~print 5+5 diff --git a/xa-2.3.5/tests/comcom/a.o65 b/xa-2.3.5/tests/comcom/a.o65 new file mode 100644 index 0000000000000000000000000000000000000000..579a01e5e8fea0a87ceebd78970dfd59876e205f GIT binary patch literal 9 QcmZQLuyV4(rT+;I02TiPjsO4v literal 0 HcmV?d00001 diff --git a/xa-2.3.5/tests/comcom/comcom.asm b/xa-2.3.5/tests/comcom/comcom.asm new file mode 100644 index 0000000..1281528 --- /dev/null +++ b/xa-2.3.5/tests/comcom/comcom.asm @@ -0,0 +1,18 @@ + .word $c000 + * = $c000 + + lda #147 +; depending on the -M option:lda #65 + jsr $ffd2:rts ; maybe some stuff out here:tay + +; there will be:cli +; extra code:sei +; or not:rti +; let's ; be ; tricky : ; does it ; work? :nop:nop: ; let's see! ; rts + +/* hey, + ; what about + now? + : brk */ +// do I make you sexy? ; :brk + rti diff --git a/xa-2.3.5/tests/comcom/scomcom.asm b/xa-2.3.5/tests/comcom/scomcom.asm new file mode 100644 index 0000000..388ade4 --- /dev/null +++ b/xa-2.3.5/tests/comcom/scomcom.asm @@ -0,0 +1,5 @@ +; some comment: *2 = 1 + +start + lda #1: sta 2 + diff --git a/xa-2.3.5/tests/cpp/Makefile b/xa-2.3.5/tests/cpp/Makefile new file mode 100644 index 0000000..6cc4878 --- /dev/null +++ b/xa-2.3.5/tests/cpp/Makefile @@ -0,0 +1,3 @@ +default: + cc -E over.c > over.asm + ../../xa over.asm diff --git a/xa-2.3.5/tests/cpp/over.asm b/xa-2.3.5/tests/cpp/over.asm new file mode 100644 index 0000000..c8cc252 --- /dev/null +++ b/xa-2.3.5/tests/cpp/over.asm @@ -0,0 +1,32 @@ +# 1 "over.c" + + + + + + + + + + + + + lda #1 + jmp buggy + rts + +# 1 "over.h" 1 + + + + +fuzz lda #5 + sta $0400 + bne fuzz + rts + + lda @$c0c0c0 +# 17 "over.c" 2 + + + diff --git a/xa-2.3.5/tests/cpp/over.c b/xa-2.3.5/tests/cpp/over.c new file mode 100644 index 0000000..5990b0c --- /dev/null +++ b/xa-2.3.5/tests/cpp/over.c @@ -0,0 +1,19 @@ +/* #define BUG */ +#ifdef BUG +#define WW AA +#define AA WW +#else +#define CC 1 +#define WW CC +#define AA WW +#endif + +/* This has a .c extension for those cc -E's that won't deal with .asm */ + + lda #AA + jmp buggy + rts + +#include "over.h" + +/* the buggy will force a line number to be printed */ diff --git a/xa-2.3.5/tests/cpp/over.h b/xa-2.3.5/tests/cpp/over.h new file mode 100644 index 0000000..ab2f3ae --- /dev/null +++ b/xa-2.3.5/tests/cpp/over.h @@ -0,0 +1,10 @@ + +#define X W +#define W 5 + +fuzz lda #W + sta $0400 + bne fuzz + rts + + lda @$c0c0c0 diff --git a/xa-2.3.5/tests/fordef/a.o65 b/xa-2.3.5/tests/fordef/a.o65 new file mode 100644 index 0000000000000000000000000000000000000000..74a6e57a95c56467ba1d9f7955292492e76ea1ba GIT binary patch literal 11 ScmZQz>1Aix$mGM$k^le;Spo+D literal 0 HcmV?d00001 diff --git a/xa-2.3.5/tests/fordef/test.asm b/xa-2.3.5/tests/fordef/test.asm new file mode 100644 index 0000000..3df82bb --- /dev/null +++ b/xa-2.3.5/tests/fordef/test.asm @@ -0,0 +1,20 @@ + .word $0400 + * = $0400 + +/* this should generate optimizer warnings */ + + lda forward1 + sta forward2 + +/* this shouldn't */ + + jmp forward3 + +/* and this won't */ + +t1 lda `forward1 + sta `forward2 + rts + +#include "test2.asm" + diff --git a/xa-2.3.5/tests/fordef/test2.asm b/xa-2.3.5/tests/fordef/test2.asm new file mode 100644 index 0000000..9499afb --- /dev/null +++ b/xa-2.3.5/tests/fordef/test2.asm @@ -0,0 +1,5 @@ + +forward1 = $02 +forward2 = $03 + +forward3 rts diff --git a/xa-2.3.5/tests/fordef/test3.asm b/xa-2.3.5/tests/fordef/test3.asm new file mode 100644 index 0000000..0429816 --- /dev/null +++ b/xa-2.3.5/tests/fordef/test3.asm @@ -0,0 +1,21 @@ + .word $0400 + *=$0400 + +/* define this if you want to crash and burn */ + +#ifdef FAIL + jmp `forward3 + bne `forward3 + lda (`forward1),y + lda (`forward3),y +#echo congrats, you have FAILED! +#endif + + sta `forward3 + +/* this looks like it should fail, but won't because there is no ambiguity */ + + lda (forward1),y + jmp forward3 + +#include "test2.asm" diff --git a/xa-2.3.5/tests/incerr/test.6502 b/xa-2.3.5/tests/incerr/test.6502 new file mode 100644 index 0000000..ebec20a --- /dev/null +++ b/xa-2.3.5/tests/incerr/test.6502 @@ -0,0 +1 @@ + .xl diff --git a/xa-2.3.5/tests/incerr/test.s b/xa-2.3.5/tests/incerr/test.s new file mode 100644 index 0000000..6261e7f --- /dev/null +++ b/xa-2.3.5/tests/incerr/test.s @@ -0,0 +1 @@ +#include "test.6502" diff --git a/xa-2.3.5/tests/ldoreloc/1.s b/xa-2.3.5/tests/ldoreloc/1.s new file mode 100644 index 0000000..ce601c7 --- /dev/null +++ b/xa-2.3.5/tests/ldoreloc/1.s @@ -0,0 +1 @@ +jsr bla: loop: jmp loop diff --git a/xa-2.3.5/tests/ldoreloc/2.s b/xa-2.3.5/tests/ldoreloc/2.s new file mode 100644 index 0000000..d350048 --- /dev/null +++ b/xa-2.3.5/tests/ldoreloc/2.s @@ -0,0 +1 @@ +bla: rts diff --git a/xa-2.3.5/tests/ldoreloc/Makefile b/xa-2.3.5/tests/ldoreloc/Makefile new file mode 100644 index 0000000..301ec80 --- /dev/null +++ b/xa-2.3.5/tests/ldoreloc/Makefile @@ -0,0 +1,23 @@ + +all: t + +1.o65: 1.s + xa -R -c -o 1.o65 1.s + hexdump -C 1.o65 > 1.o65.hex + +2.o65: 2.s + xa -R -c -o 2.o65 2.s + hexdump -C 2.o65 > 2.o65.hex + +linked.o65: 1.o65 2.o65 + ldo65 -o linked.o65 1.o65 2.o65 + hexdump -C linked.o65 > linked.o65.hex + +t: linked.o65 + reloc65 -bt 32768 -xt -o t linked.o65 + hexdump -C t > t.hex + diff t t.ok + +clean: + rm -f *.o65 *.hex t + diff --git a/xa-2.3.5/tests/ldoreloc/t.ok b/xa-2.3.5/tests/ldoreloc/t.ok new file mode 100644 index 0000000..683812c --- /dev/null +++ b/xa-2.3.5/tests/ldoreloc/t.ok @@ -0,0 +1 @@ + €L€` \ No newline at end of file diff --git a/xa-2.3.5/tests/ldoreloc/xatestanalysis.txt b/xa-2.3.5/tests/ldoreloc/xatestanalysis.txt new file mode 100644 index 0000000..ba630ba --- /dev/null +++ b/xa-2.3.5/tests/ldoreloc/xatestanalysis.txt @@ -0,0 +1,109 @@ + + +mkdir xatest +cd xatest +echo "jsr bla: loop: jmp loop" > 1.s +echo "bla: rts" > 2.s +xa -R -c -o 1.o65 1.s +xa -R -c -o 2.o65 2.s +ldo65 -o linked.o65 1.o65 2.o65 +reloc65 -bt 32768 -xt -o t linked.o65 +hexdump -C t + +output: + +00000000 20 06 80 4c 03 8c 60 | ..L..`| +00000007 + +The bytes 20 06 80 are correct, it's JSR $8006. But 4c 03 8c means JMP $8c03. What's wrong here? +Do I use it in a wrong way? + +Following procedure leads to the expected code: +echo "jsr bla: loop: jmp loop: bla: rts" > 1.s +xa -R -c -o 1.o65 1.s +ldo65 -o linked.o65 1.o65 +reloc65 -bt 32768 -xt -o t linked.o65 +hexdump -C t + +I use 2.3.0, it comes with Debian Testing. + +============================================================================== +Analysis: + + cat 1.o65.hex +00000000 01 00 6f 36 35 00 00 10 00 10 06 00 00 04 00 00 |..o65...........| +00000010 00 40 00 00 04 00 00 00 00 00 00 20 00 00 4c 03 |.@......... ..L.| +00000020 10 01 00 62 6c 61 00 02 80 00 00 03 82 00 00 01 |...bla..........| +00000030 00 6c 6f 6f 70 00 02 03 10 |.loop....| +00000039 + +file65 -V 1.o65 +1.o65: o65 version 0 object file + mode: 1000 =[object][16bit][byte relocation][CPU 6502][align 1] + text segment @ $1000 - $1006 [$0006 bytes] + data segment @ $0400 - $0400 [$0000 bytes] + bss segment @ $4000 - $4000 [$0000 bytes] + zero segment @ $0004 - $0004 [$0000 bytes] + stack size $0000 bytes (i.e. unknown) +Undefined Labels: 1 +bla +Global Labels: 1 +loop (segID=2 (text), offset=1003) + + +==> text segment start $1000, loop is at $1003 -> ok + undef'd label bla stored as zero (00 00) in opcode -> ok + relocation table entry: $1001, ADR, undef'd, label #0 -> ok + $1004, ADR, text segment -> ok + +--------------------------------------------------- + +cat 2.o65.hex +00000000 01 00 6f 36 35 00 00 10 00 10 01 00 00 04 00 00 |..o65...........| +00000010 00 40 00 00 04 00 00 00 00 00 00 60 00 00 00 00 |.@.........`....| +00000020 01 00 62 6c 61 00 02 00 10 |..bla....| + +file65 -V 2.o65 +2.o65: o65 version 0 object file + mode: 1000 =[object][16bit][byte relocation][CPU 6502][align 1] + text segment @ $1000 - $1001 [$0001 bytes] + data segment @ $0400 - $0400 [$0000 bytes] + bss segment @ $4000 - $4000 [$0000 bytes] + zero segment @ $0004 - $0004 [$0000 bytes] + stack size $0000 bytes (i.e. unknown) +Undefined Labels: 0 +Global Labels: 1 +bla (segID=2 (text), offset=1000) + +=> text segment start $1000 -> ok + no relocation table entries -> ok + global label text segment, at $1000 -> ok + +--------------------------------------------------- + +ldo65 -o linked.o65 1.o65 2.o65 + +linked.o65.hex +00000000 01 00 6f 36 35 00 00 00 00 04 07 00 00 10 00 00 |..o65...........| +00000010 00 40 00 00 02 00 00 00 00 00 00 20 06 04 4c 03 |.@......... ..L.| +00000020 10 60 00 00 02 82 03 82 00 00 02 00 6c 6f 6f 70 |.`..........loop| +00000030 00 02 03 04 62 6c 61 00 02 06 04 |....bla....| + +ile65 -V linked.o65 +linked.o65: o65 version 0 executable file + mode: 0000 =[executable][16bit][byte relocation][CPU 6502][align 1] + text segment @ $0400 - $0407 [$0007 bytes] + data segment @ $1000 - $1000 [$0000 bytes] + bss segment @ $4000 - $4000 [$0000 bytes] + zero segment @ $0002 - $0002 [$0000 bytes] + stack size $0000 bytes (i.e. unknown) +Undefined Labels: 0 +Global Labels: 2 +loop (segID=2 (text), offset=0403) +bla (segID=2 (text), offset=0406) + +=> text segment start $0400 + $0400 jsr $0406 -> RTS -> ok + $0403 jmp $1003 >>>>>>>>>>>>>>>> wrong !!! + should be $0403 + diff --git a/xa-2.3.5/tests/nonl/a.o65 b/xa-2.3.5/tests/nonl/a.o65 new file mode 100644 index 0000000..e69de29 diff --git a/xa-2.3.5/tests/nonl/test.asm b/xa-2.3.5/tests/nonl/test.asm new file mode 100644 index 0000000..0e7fb25 --- /dev/null +++ b/xa-2.3.5/tests/nonl/test.asm @@ -0,0 +1,3 @@ + lda #4 + sta !$0005 + rts \ No newline at end of file diff --git a/xa-2.3.5/tests/nonl/test2.asm b/xa-2.3.5/tests/nonl/test2.asm new file mode 100644 index 0000000..c137904 --- /dev/null +++ b/xa-2.3.5/tests/nonl/test2.asm @@ -0,0 +1 @@ +#include "test.asm" \ No newline at end of file diff --git a/xa-2.3.5/tests/openpp/test.asm b/xa-2.3.5/tests/openpp/test.asm new file mode 100644 index 0000000..8bdb8e9 --- /dev/null +++ b/xa-2.3.5/tests/openpp/test.asm @@ -0,0 +1,27 @@ + .word $c000 + * = $c000 +#if 0 +#error BUGGGGG +#endif + +#if 1 + lda #93 + jsr $ffd2 +#endif + +/* comment this out to stop testing included gaffes */ +#include "test.inc" + +#if 1 +#if 2 + lda #93 + jsr $ffd2 +#endif +#endif + +#ifdef X +/* comment this out for bugs in this file */ +/* +#endif +*/ + diff --git a/xa-2.3.5/tests/openpp/test.inc b/xa-2.3.5/tests/openpp/test.inc new file mode 100644 index 0000000..2669d09 --- /dev/null +++ b/xa-2.3.5/tests/openpp/test.inc @@ -0,0 +1,4 @@ +#ifdef BUGOUTTTTT +/* +#endif +*/ diff --git a/xa-2.3.5/tests/recmac/a.o65 b/xa-2.3.5/tests/recmac/a.o65 new file mode 100644 index 0000000..7cd3c66 --- /dev/null +++ b/xa-2.3.5/tests/recmac/a.o65 @@ -0,0 +1 @@ +© \ No newline at end of file diff --git a/xa-2.3.5/tests/recmac/cpu.inc b/xa-2.3.5/tests/recmac/cpu.inc new file mode 100644 index 0000000..9d5bae2 --- /dev/null +++ b/xa-2.3.5/tests/recmac/cpu.inc @@ -0,0 +1,5 @@ +; +; FILE cpu.inc +; + +#define WDM(v) .byt $42,(v) diff --git a/xa-2.3.5/tests/recmac/test.asm b/xa-2.3.5/tests/recmac/test.asm new file mode 100644 index 0000000..af88314 --- /dev/null +++ b/xa-2.3.5/tests/recmac/test.asm @@ -0,0 +1,31 @@ +#define AA 1 +#define POKE(a,b) lda #b: sta a +#define WW AA + +start + ; next line works + POKE(1, 2) + ; next two lines work + lda #AA + sta 2 + ; next line fails + POKE(AA, 2) + POKE(WW, 2) + lda #WW + sta 2 + +#define WW 94 + POKE(WW, 2) +#define WW 5 +#define AA WW +#define POKE(a,b) lda #a: sta b + POKE(AA, 3) + + bne start + rts + +/* this should bug out */ + +/* + POKE(NOGOOD, 7) +*/ diff --git a/xa-2.3.5/tests/recmac/testi.asm b/xa-2.3.5/tests/recmac/testi.asm new file mode 100644 index 0000000..cd2c5e8 --- /dev/null +++ b/xa-2.3.5/tests/recmac/testi.asm @@ -0,0 +1,13 @@ + +; +; FILE image.a +; + +#include "cpu.inc" + + .text + *=$C000 + +Foo: + WDM(170) + diff --git a/xa-2.3.5/tests/relocundef/Makefile b/xa-2.3.5/tests/relocundef/Makefile new file mode 100644 index 0000000..39eab67 --- /dev/null +++ b/xa-2.3.5/tests/relocundef/Makefile @@ -0,0 +1,25 @@ + +all: a.o65 b.o65 + +a.o65: test1.o65 + echo "********* This should give a warning about an undefined relocation table entry" + reloc65 -X test1.o65 + hexdump -C a.o65 > a.o65.hex + +b.o65: test2.o65 + echo "********* This should NOT give a warning" + reloc65 -X -o b.o65 test2.o65 + hexdump -C b.o65 > b.o65.hex + +test1.o65: test1.a65 + xa -R -Lundefl -Ll2 -o test1.o65 test1.a65 + hexdump -C test1.o65 > test1.o65.hex + +test2.o65: test2.a65 + xa -R -o test2.o65 test2.a65 + hexdump -C test2.o65 > test2.o65.hex + +clean: + rm -f test1.o65 a.o65 test1.o65.hex a.o65.hex + rm -f test2.o65 b.o65 test2.o65.hex b.o65.hex + diff --git a/xa-2.3.5/tests/relocundef/test1.a65 b/xa-2.3.5/tests/relocundef/test1.a65 new file mode 100644 index 0000000..8f6d32b --- /dev/null +++ b/xa-2.3.5/tests/relocundef/test1.a65 @@ -0,0 +1,9 @@ + +// this file defines an undefined label + .text + ldx #1 +l1 lda undefl + dex + bne l1 + rts + diff --git a/xa-2.3.5/tests/relocundef/test2.a65 b/xa-2.3.5/tests/relocundef/test2.a65 new file mode 100644 index 0000000..bd1f74e --- /dev/null +++ b/xa-2.3.5/tests/relocundef/test2.a65 @@ -0,0 +1,11 @@ + +// this file defines an undefined label + .text + ldx #1 +l1 lda undefl + dex + bne l1 + rts + +undefl =1 + diff --git a/xa-2.3.5/vstudio/00readme.txt b/xa-2.3.5/vstudio/00readme.txt new file mode 100644 index 0000000..a075984 --- /dev/null +++ b/xa-2.3.5/vstudio/00readme.txt @@ -0,0 +1,8 @@ +This directory contains a solution and project files that can be used to +build xa and the various tools in the misc directory with the free version +of Microsoft Visual C++ 2005 (AKA Visual Studio 2005 Express). This compiler +and IDE can be dowloaded free from + +http://msdn.microsoft.com/vstudio/express/downloads/default.aspx + +Just run visual studio and load the solution called "vstudio.sln" diff --git a/xa-2.3.5/vstudio/file65.vcproj b/xa-2.3.5/vstudio/file65.vcproj new file mode 100644 index 0000000..83ff040 --- /dev/null +++ b/xa-2.3.5/vstudio/file65.vcproj @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xa-2.3.5/vstudio/ldo65.vcproj b/xa-2.3.5/vstudio/ldo65.vcproj new file mode 100644 index 0000000..a77fc6f --- /dev/null +++ b/xa-2.3.5/vstudio/ldo65.vcproj @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xa-2.3.5/vstudio/printcbm.vcproj b/xa-2.3.5/vstudio/printcbm.vcproj new file mode 100644 index 0000000..514b3b1 --- /dev/null +++ b/xa-2.3.5/vstudio/printcbm.vcproj @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xa-2.3.5/vstudio/uncpk.vcproj b/xa-2.3.5/vstudio/uncpk.vcproj new file mode 100644 index 0000000..a8347d2 --- /dev/null +++ b/xa-2.3.5/vstudio/uncpk.vcproj @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xa-2.3.5/vstudio/vstudio.sln b/xa-2.3.5/vstudio/vstudio.sln new file mode 100644 index 0000000..8edc6a8 --- /dev/null +++ b/xa-2.3.5/vstudio/vstudio.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xa", "xa.vcproj", "{55808FFA-DA3B-44AF-AB9E-D937A531F932}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uncpk", "uncpk.vcproj", "{32CE058C-D818-4756-BAA6-A8AD4378308D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "file65", "file65.vcproj", "{3CB15D2B-3777-4401-BF81-C0BC1B8AE8E6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ldo65", "ldo65.vcproj", "{EFA4F295-A2CD-4B8A-ABE9-DD9DEB670CA6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "printcbm", "printcbm.vcproj", "{26727633-7B9B-40CB-A31A-C6C6FEBA00CA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {55808FFA-DA3B-44AF-AB9E-D937A531F932}.Debug|Win32.ActiveCfg = Debug|Win32 + {55808FFA-DA3B-44AF-AB9E-D937A531F932}.Debug|Win32.Build.0 = Debug|Win32 + {55808FFA-DA3B-44AF-AB9E-D937A531F932}.Release|Win32.ActiveCfg = Release|Win32 + {55808FFA-DA3B-44AF-AB9E-D937A531F932}.Release|Win32.Build.0 = Release|Win32 + {32CE058C-D818-4756-BAA6-A8AD4378308D}.Debug|Win32.ActiveCfg = Debug|Win32 + {32CE058C-D818-4756-BAA6-A8AD4378308D}.Debug|Win32.Build.0 = Debug|Win32 + {32CE058C-D818-4756-BAA6-A8AD4378308D}.Release|Win32.ActiveCfg = Release|Win32 + {32CE058C-D818-4756-BAA6-A8AD4378308D}.Release|Win32.Build.0 = Release|Win32 + {3CB15D2B-3777-4401-BF81-C0BC1B8AE8E6}.Debug|Win32.ActiveCfg = Debug|Win32 + {3CB15D2B-3777-4401-BF81-C0BC1B8AE8E6}.Debug|Win32.Build.0 = Debug|Win32 + {3CB15D2B-3777-4401-BF81-C0BC1B8AE8E6}.Release|Win32.ActiveCfg = Release|Win32 + {3CB15D2B-3777-4401-BF81-C0BC1B8AE8E6}.Release|Win32.Build.0 = Release|Win32 + {EFA4F295-A2CD-4B8A-ABE9-DD9DEB670CA6}.Debug|Win32.ActiveCfg = Debug|Win32 + {EFA4F295-A2CD-4B8A-ABE9-DD9DEB670CA6}.Debug|Win32.Build.0 = Debug|Win32 + {EFA4F295-A2CD-4B8A-ABE9-DD9DEB670CA6}.Release|Win32.ActiveCfg = Release|Win32 + {EFA4F295-A2CD-4B8A-ABE9-DD9DEB670CA6}.Release|Win32.Build.0 = Release|Win32 + {26727633-7B9B-40CB-A31A-C6C6FEBA00CA}.Debug|Win32.ActiveCfg = Debug|Win32 + {26727633-7B9B-40CB-A31A-C6C6FEBA00CA}.Debug|Win32.Build.0 = Debug|Win32 + {26727633-7B9B-40CB-A31A-C6C6FEBA00CA}.Release|Win32.ActiveCfg = Release|Win32 + {26727633-7B9B-40CB-A31A-C6C6FEBA00CA}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/xa-2.3.5/vstudio/xa.vcproj b/xa-2.3.5/vstudio/xa.vcproj new file mode 100644 index 0000000..8c53ed1 --- /dev/null +++ b/xa-2.3.5/vstudio/xa.vcproj @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +