#include "rom.h" #include "common.h" ; ROM code header .WORD CMN_CD, _END_CMN_CD - CMN_CD ; beginning of ROM code * = CMN_CD _CMN .( ; common function interpreter JSR _SAV ; save registers PLA STA _PCL ; set program counter from return address PLA STA _PCH INC _PCL ; advance the program counter BNE _1 INC _PCH _1 JSR _2 ; interpret and execute one common instruction JMP _1 _2 LDY #0 LDA (_PC),Y ; get operand INC _PCL ; advance the program counter BNE _3 INC _PCH _3 TAX ; save operand for later AND #$F0 BEQ _4 ; go to 0X instructions CMP #$F0 ; check for FX functions BEQ _5 ; go to FX instructions LSR ; get offset to XR instructions LSR LSR TAY DEY DEY LDA FN_XR+1,Y ; push high address PHA LDA FN_XR,Y ; push low address PHA TXA ; restore operand AND #$F ; mask to get register ASL ; shift to get offset to register ASL TAX ; back to index RTS ; "return" to routine _4 TXA ; get operand ASL ; shift to get offset to 0X instructions TAY LDA FN_0X+1,Y ; push high address PHA LDA FN_0X,Y ; push low address PHA TXA ; restore operand RTS ; "return" to routine _5 TXA ; get operand AND #$F ; mask to get index ASL ; shift to get offset to FX instructions TAY LDA FN_FX+1,Y ; push high address PHA LDA FN_FX,Y ; push low address PHA TXA ; restore operand RTS ; "return" to routine .) _INI .( ; initialize common LDA #0 ; initialize RSI STA _RSI ; copy system functions (TODO) ; load program (TODO) JMP (_PC) ; go to last loaded block .) _SAV .( ; save the registers prior to entering common STA _ACC STX _IDX STY _IDY PHP PLA STA _PS CLD RTS .) _RES .( ; restore the registers prior to leaving common LDA _PS PHA LDA _ACC LDX _IDX LDY _IDY PLP RTS .) _SET .( ; SET r aabbcc.dd 1r dd cc bb aa Rr <- aabbcc.dd - set register LDY #0 LDA (_PC),Y ; transfer four bytes over STA _R0,X INY LDA (_PC),Y STA _R0+1,X INY LDA (_PC),Y STA _R0+2,X INY LDA (_PC),Y STA _R0+3,X LDA #4 ; update program counter CLC ADC _PCL STA _PCL BCC _1 INC _PCH _1 RTS ; done .) _PSH .( ; PSH r 2r RS <- Rr - push onto stack LDY _RSI ; get register stack index CPY #_RSS ; compare against limit BCC _1 ; still room, all okay BRK ; next push will cause a stack overflow, abort and call exception handler (TODO) _1 LDA _R0,X ; transfer four bytes over STA _RS,Y INY LDA _R0+1,X STA _RS,Y INY LDA _R0+2,X STA _RS,Y INY LDA _R0+3,X STA _RS,Y INY STY _RSI ; update register stack index RTS .) _POP .( ; POP r 3r Rr <- RS - pop from stack LDY _RSI ; get register stack index BNE _1 ; all good, something can be popped off the stack BRK ; next pop will cause a stack underflow, abort and call exception handler (TODO) _1 DEY ; transfer four bytes over LDA _RS,Y STA _R0+3,X DEY LDA _RS,Y STA _R0+2,X DEY LDA _RS,Y STA _R0+1,X DEY LDA _RS,Y STA _R0,X STY _RSI ; update register stack index RTS .) _EXC .( ; EXC r 4r Rr <-> RS - exchange Rr with stack LDY _RSI ; RS to RD LDA _RS-1,Y STA _RD+3 LDA _RS-2,Y STA _RD+2 LDA _RS-3,Y STA _RD+1 LDA _RS-4,Y STA _RD LDA _R0,X ; copy Rr to RS STA _RS-4,Y LDA _R0+1,X STA _RS-3,Y LDA _R0+2,X STA _RS-2,Y LDA _R0+3,X STA _RS-1,Y LDA _RD ; copy RD to Rr STA _R0,X LDA _RD+1 STA _R0+1,X LDA _RD+2 STA _R0+2,X LDA _RD+3 STA _R0+3,X RTS .) _ADDRD .( ; add RD to register indexed by X LDA _R0+3,X AND #_MSK_O ; check for existing overflow condition BEQ _4 EOR #_MSK_O BNE _3 ; existing overflow, skip decrement operation _4 CLC ; adding RD LDA _RD ADC _R0,X STA _R0,X LDA _RD+1 ADC _R0+1,X STA _R0+1,X LDA _RD+2 ADC _R0+2,X STA _R0+2,X LDA _RD+3 ADC _R0+3,X STA _R0+3,X AND #_MSK_O ; check for overflow BEQ _2 EOR #_MSK_O BEQ _2 _3 LDA _F ; set overflow ORA #_F_O STA _F BNE _5 _2 LDA _F ; clear overflow AND #_F_O^$FF STA _F _5 RTS .) _INR .( ; INR r 5r Rr <- Rr + 1.0 - increment register LDA #0 ; set RD to plus one STA _RD LDA #_PLS_1 STA _RD+1 LDA #0 STA _RD+2 STA _RD+3 BEQ _ADDRD .) _DCR .( ; DCR r 6r Rr <- Rr - 1.0 - decrement register LDA #0 ; set RD to minus one STA _RD LDA #_MNS_1 STA _RD+1 LDA #$FF STA _RD+2 STA _RD+3 BNE _ADDRD .) _TST .( ; TST r 7r F <- Rr <=> 0.0 - test register LDA _F AND #_MSK_T ; clear TST bits STA _F LDA _R0+3,X ; check highest byte BMI _1 ; is negative ORA _R0+2,X ; could be positive or zero, OR with all other bytes ORA _R0+1,X ORA _R0,X BNE _2 ; is positive LDA #_F_Z ; set zero flag BNE _3 _1 LDA #_F_N ; set negative flag BNE _3 _2 LDA #_F_P ; set positive flag _3 ORA _F STA _F RTS .) _DEC .( ; DEC r 8r Rr <- dec(Rr) - convert Rr from hex aabbcc.dd to decimal ######.## RTS .) _HEX .( ; HEX r 9r Rr <- hex(Rr) - convert Rr from decimal ######.## to hex aabbcc.dd RTS .) _GETPQ .( ; sets X as p register and Y as q register, checks for overflow in the operands, advances PC LDY #0 LDA (_PC),Y ; get source registers LSR LSR AND #_MSK_R ; p register TAX LDA (_PC),Y ASL ASL AND #_MSK_R ; q register TAY LDA _R0+3,X AND #_MSK_O ; check for existing overflow condition BEQ _1 ; sign and overflow are both clear EOR #_MSK_O BEQ _1 ; sign and overflow are both set BRK ; an operand is in an overflow condition, abort and call exception handler (TODO) _1 LDA _R0+3,Y AND #_MSK_O ; check for existing overflow condition BEQ _2 ; sign and overflow are both clear EOR #_MSK_O BEQ _2 ; sign and overflow are both set BRK ; an operand is in an overflow condition, abort and call exception handler (TODO) _2 INC _PCL ; advance PC BNE _3 INC _PCH _3 RTS .) _RETZD .( ; clears register D, clears underflow, falls through to _TRFDR LDA #0 STA _RD STA _RD+1 STA _RD+2 STA _RD+3 LDA _F ; clear underflow AND #_F_U^$FF STA _F .) _TRFDR .( ; pulls X, transfers RD to X as r register, updates overflow flag PLA TAX LDA _RD ; transfer result to Rr STA _R0,X LDA _RD+1 STA _R0+1,X LDA _RD+2 STA _R0+2,X LDA _RD+3 STA _R0+3,X AND #_MSK_O ; check for overflow BEQ _4 EOR #_MSK_O BEQ _4 _3 LDA _F ; set overflow ORA #_F_O STA _F BNE _5 _4 LDA _F ; clear overflow AND #_F_O^$FF STA _F _5 RTS .) _ADD .( ; ADD r pq ar pq Rr <- Rp + Rq - addition TXA PHA ; save r register for later JSR _GETPQ CLC ; set RD to Rp + Rq LDA _R0,X ADC _R0,Y STA _RD LDA _R0+1,X ADC _R0+1,Y STA _RD+1 LDA _R0+2,X ADC _R0+2,Y STA _RD+2 LDA _R0+3,X ADC _R0+3,Y STA _RD+3 JMP _TRFDR ; pull X, transfer RD to r register, let it handle the return .) _SUB .( ; SUB r pq br pq Rr <- Rp - Rq - subtraction TXA PHA ; save r register for later JSR _GETPQ SEC ; set RD to Rp - Rq LDA _R0,X SBC _R0,Y STA _RD LDA _R0+1,X SBC _R0+1,Y STA _RD+1 LDA _R0+2,X SBC _R0+2,Y STA _RD+2 LDA _R0+3,X SBC _R0+3,Y STA _RD+3 JMP _TRFDR ; pull X, transfer RD to r register, let it handle the return .) _TRFQD .( ; transfers Y as q register to RD LDA _R0,Y STA _RD LDA _R0+1,Y STA _RD+1 LDA _R0+2,Y STA _RD+2 LDA _R0+3,Y STA _RD+3 RTS .) _NEGRY .( ; negates register at Y SEC LDA #0 SBC _R0,Y STA _R0,Y LDA #0 SBC _R0+1,Y STA _R0+1,Y LDA #0 SBC _R0+2,Y STA _R0+2,Y LDA #0 SBC _R0+3,Y STA _R0+3,Y RTS .) _ABSRY .( ; sets register at Y to absolute value LDA _R0+3,Y BMI _NEGRY RTS .) _MUL .( ; MUL r pq cr pq Rr <- Rp * Rq - multiplication TXA ; adapted from http://www.6502.org/source/integers/32muldiv.htm PHA ; save r register for later JSR _GETPQ LDA _R0,X ; check for zero argument ORA _R0+1,X ORA _R0+2,X ORA _R0+3,X BNE _1 ; p is non-zero JMP _RETZD ; p is zero, return zero _1 LDA _R0,Y ; check for zero argument ORA _R0+1,Y ORA _R0+2,Y ORA _R0+3,Y BNE _2 ; q is non-zero JMP _RETZD ; q is zero, return zero _2 LDA _R0+3,X ; save sign of register p AND #_MSK_O PHA EOR _R0+3,Y AND #_MSK_O ; save sign of product PHA JSR _TRFQD TXA TAY ; absolute value of register p JSR _ABSRY LDY #_RD-_R0 ; absolute value of register q saved in D JSR _ABSRY LDA #0 STA _RB+4 ; clear upper half of product STA _RB+5 STA _RB+6 STA _RB+7 LDY #34 ; thirty bit multiply and four bit shift _3 LSR _RD+3 ; shift operand ROR _RD+2 ROR _RD+1 ROR _RD BCC _4 ; skip adding in product if bit is zero CLC LDA _RB+4 ; add in p register ADC _R0,X STA _RB+4 LDA _RB+5 ADC _R0+1,X STA _RB+5 LDA _RB+6 ADC _R0+2,X STA _RB+6 LDA _RB+7 ADC _R0+3,X _4 ROR ; shift the product STA _RB+7 ROR _RB+6 ROR _RB+5 ROR _RB+4 ROR _RB+3 ROR _RB+2 ROR _RB+1 ROR _RB DEY BNE _3 ; repeat until bits are done LDA _RB+1 ; copy result to RD STA _RD LDA _RB+2 STA _RD+1 LDA _RB+3 STA _RD+2 LDA _RB+4 STA _RD+3 AND #_MSK_O ; consider the overflow bits ORA _RB+5 ; check all the other bytes ORA _RB+6 ORA _RB+7 BEQ _5 ; all zeroes means no overflow LDA _RD+3 ; overflow situation, set accordingly AND #_MSK_O^$FF ; set overflow ORA #_F_O STA _RD+3 BNE _6 _5 LDA _RD ; check for underflow ORA _RD+1 ORA _RD+2 ORA _RD+3 BNE _6 ; non-zero result means no underflow LDA _F ; we checked earlier for zero operands, so a zero result means underflow, set underflow ORA #_F_U STA _F BNE _7 _6 LDA _F ; clear underflow AND #_F_U^$FF STA _F _7 PLA ; set the sign of the product BEQ _8 LDY #_RD-_R0 ; negate register D JSR _NEGRY _8 PLA ; reset the sign of register p BEQ _9 TXA TAY JSR _NEGRY _9 JMP _TRFDR ; pull X, transfer RD to r register, let it handle the return .) _CMPDC .( ; compare D to C, return result in status LDA _RD+3 CMP _RC+3 BCC _1 ; definitely less BNE _1 ; definitely greater LDA _RD+2 CMP _RC+2 BCC _1 ; definitely less BNE _1 ; definitely greater LDA _RD+1 CMP _RC+1 BCC _1 ; definitely less BNE _1 ; definitely greater LDA _RD CMP _RC _1 RTS .) _DIV .( ; DIV r pq dr pq Rr <- Rp / Rq - division TXA PHA ; save r register for later JSR _GETPQ LDA _R0,X ; check for zero argument ORA _R0+1,X ORA _R0+2,X ORA _R0+3,X BNE _1 ; p is non-zero JMP _RETZD ; p is zero, return zero _1 LDA _R0,Y ; check for zero argument ORA _R0+1,Y ORA _R0+2,Y ORA _R0+3,Y BNE _2 ; q is non-zero BRK ; q is zero, abort and call exception handler (TODO) _2 LDA _R0,X ; copy p to RD STA _RD LDA _R0+1,X STA _RD+1 LDA _R0+2,X STA _RD+2 LDA _R0+3,X STA _RD+3 LDA _R0,Y ; copy q to RC STA _RC LDA _R0+1,Y STA _RC+1 LDA _R0+2,Y STA _RC+2 LDA _R0+3,Y STA _RC+3 LDA #0 ; set RB to 1 STA _RB LDA #_PLS_1 STA _RB+1 LDA #0 STA _RB+2 STA _RB+3 PLA ; restore r register TAX LDA #0 ; set r to 0 STA _R0,X STA _R0+1,X STA _R0+2,X STA _R0+3,X LDA _RD+3 ; save sign of quotient EOR _RC+3 AND #_MSK_O PHA LDY #_RD-_R0 ; absolute value of register p saved in D JSR _ABSRY LDY #_RC-_R0 ; absolute value of register q saved in C JSR _ABSRY _3 JSR _CMPDC ; is D < C? BCC _4 ; yes, continue BEQ _5 ; D = C ASL _RC ; RC *= 2 ROL _RC+1 ROL _RC+2 ROL _RC+3 ASL _RB ; RB *= 2 ROL _RB+1 ROL _RB+2 ROL _RB+3 BCC _3 ; carry is set, means a real overflow condition LDA #$FF ; set to the maximum STA _R0,X STA _R0+1,X STA _R0+2,X LDA #_MAX_V|_F_O STA _R0+3,X JMP _9 _4 LDA _RB ; is RB > 0? ORA _RB+1 ORA _RB+2 ORA _RB+3 BEQ _7 ; no, done JSR _CMPDC ; is D >= C? BCC _6 ; no, skip subtraction _5 SEC ; RD -= RC LDA _RD SBC _RC STA _RD LDA _RD+1 SBC _RC+1 STA _RD+1 LDA _RD+2 SBC _RC+2 STA _RD+2 LDA _RD+3 SBC _RC+3 STA _RD+3 CLC ; RX += RB LDA _R0,X ADC _RB STA _R0,X LDA _R0+1,X ADC _RB+1 STA _R0+1,X LDA _R0+2,X ADC _RB+2 STA _R0+2,X LDA _R0+3,X ADC _RB+3 STA _R0+3,X LDA _RD ; is RD > 0? ORA _RD+1 ORA _RD+2 ORA _RD+3 BEQ _7 ; no, done _6 CLC ; RC /= 2 ROR _RC+3 ROR _RC+2 ROR _RC+1 ROR _RC CLC ; RB /= 2 ROR _RB+3 ROR _RB+2 ROR _RB+1 ROR _RB JMP _4 _7 LDA _R0,X ; check for underflow ORA _R0+1,X ORA _R0+2,X ORA _R0+3,X BNE _8 ; non-zero result means no underflow LDA _F ; we checked earlier for zero operands, so a zero result means underflow, set underflow ORA #_F_U STA _F BNE _A _8 LDA _F ; clear underflow AND #_F_U^$FF STA _F _9 LDA _R0+3,X ; check for overflow AND #_MSK_O BEQ _A ; all zero, no overflow LDA _F ; set overflow ORA #_F_O STA _F BNE _B _A LDA _F ; clear overflow AND #_F_O^$FF STA _F _B PLA ; set the sign of quotient BEQ _C TXA TAY JSR _NEGRY _C RTS .) _MOD .( ; MOD r pq er pq Rr <- Rp % Rq - modulus RTS .) _ESC .( ; ESC 00 - escape back into regular assembler PLA ; discard the COMMON _1 return address PLA JSR _RES ; restore the registers JMP (_PC) ; get back in the code .) _RTN .( ; RTN 01 - return from subroutine RTS .) _BRS .( ; BRS xxyy 02 yy xx PC <- PC + xxyy - branch to subroutine RTS .) _BRA .( ; BRA xxyy 03 yy xx PC <- PC + xxyy - branch always RTS .) _BRX .( ; generic branch testing AND _F ; check the bit BNE _BRA ; if set, branch CLC ; not set, advance the program counter over the xxyy offset LDA #2 ADC _PCL STA _PCL LDA #0 ADC _PCH STA _PCH RTS .) _BRE .( ; BRE xxyy 04 yy xx PC <- PC + xxyy - branch if Rp = Rq (after CMP) LDA #_F_E BNE _BRX .) _BRG .( ; BRG xxyy 05 yy xx PC <- PC + xxyy - branch if Rp > Rq (after CMP) LDA #_F_G BNE _BRX .) _BRL .( ; BRL xxyy 06 yy xx PC <- PC + xxyy - branch if Rp < Rq (after CMP) LDA #_F_L BNE _BRX .) _BRZ .( ; BRZ xxyy 07 yy xx PC <- PC + xxyy - branch if Rr = 0.0 (after TST) LDA #_F_Z BNE _BRX .) _BRP .( ; BRP xxyy 08 yy xx PC <- PC + xxyy - branch if Rr > 0.0 (after TST) LDA #_F_P BNE _BRX .) _BRN .( ; BRN xxyy 09 yy xx PC <- PC + xxyy - branch if Rr < 0.0 (after TST) LDA #_F_N BNE _BRX .) _BRO .( ; BRO xxyy 0a yy xx PC <- PC + xxyy - branch if overflow (after arithmetic operations) LDA #_F_O BNE _BRX .) _BRU .( ; BRU xxyy 0b yy xx PC <- PC + xxyy - branch if underflow (after arithmetic operations) LDA #_F_U BNE _BRX .) _CPR .( ; CPR pq 0c pq Rp <- Rq - copy register RTS .) _LDI .( ; LDI pq 0d pq Rp <- (Rq:bbcc) - load indirect from memory RTS .) _SVI .( ; SVI pq 0e pq (Rp:bbcc) <- Rq - save indirect to memory RTS .) _CMR .( ; CMR pq 0f pq F <- Rp <=> Rq - compare registers RTS .) _END_CMN_CD ; ROM data header .WORD CMN_DT, _END_CMN_DT - CMN_DT ; beginning of ROM data * = CMN_DT FN_0X .WORD _ESC-1, _RTN-1, _BRS-1, _BRA-1, _BRE-1, _BRG-1, _BRL-1, _BRZ-1, .WORD _BRP-1, _BRN-1, _BRO-1, _BRU-1, _CPR-1, _LDI-1, _SVI-1, _CMR-1 FN_XR .WORD _SET-1, _POP-1, _PSH-1, _EXC-1, _INR-1, _DCR-1, _TST-1, .WORD _DEC-1, _HEX-1, _ADD-1, _SUB-1, _MUL-1, _DIV-1, _MOD-1 _END_CMN_DT ; 6502 addresses .WORD ADDR, 6 ; 6502 NMI, Reset and IRQ * = $FFFA ADDR .WORD 0, _INI, 0