commit ba946c39a28b4987c2d0d8bea2fc2a6bc07f9f5e Author: Scott Duensing Date: Tue Apr 23 17:17:48 2013 -0500 Initializing vm6502 repository 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 0000000..07cbbe7 Binary files /dev/null and b/Krusader/krusader13.pdf differ 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 #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) 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 0000000..8170f61 Binary files /dev/null and b/xa-2.3.5/loader/ex2 differ diff --git a/xa-2.3.5/loader/example2 b/xa-2.3.5/loader/example2 new file mode 100644 index 0000000..e4bfa99 Binary files /dev/null and b/xa-2.3.5/loader/example2 differ 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 0000000..218516c Binary files /dev/null and b/xa-2.3.5/tests/adrm/a.o65 differ 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 0000000..72ae310 Binary files /dev/null and b/xa-2.3.5/tests/charset/a.o65 differ 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 0000000..579a01e Binary files /dev/null and b/xa-2.3.5/tests/comcom/a.o65 differ 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 0000000..74a6e57 Binary files /dev/null and b/xa-2.3.5/tests/fordef/a.o65 differ 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +