diff --git a/EhBASIC/basic-v6502.asm b/EhBASIC/basic-v6502.asm index 9f2c9b4..b113cb0 100644 --- a/EhBASIC/basic-v6502.asm +++ b/EhBASIC/basic-v6502.asm @@ -420,7 +420,7 @@ LAB_SKFE = LAB_STAK+$FE LAB_SKFF = LAB_STAK+$FF ; flushed stack address -ccflag = $1200 ; BASIC CTRL-C flag, 00 = enabled, 01 = dis +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 @@ -437,13 +437,13 @@ 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) +Ram_base = $0300 ; start of user RAM (set as needed, should be page aligned) +Ram_top = $C700 ; 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 + .dsb ($C700-*),0 ; pad to new PC with zeros + *= $C700 ; BASIC cold start entry point @@ -498,6 +498,12 @@ TabLoop STA g_step ; save it LDX #des_sk ; descriptor stack start STX next_s ; set descriptor stack pointer + + ; Memory Hack. Don't ask us the size. Just do it. + LDA #$00 ; get temporary integer low byte + LDY #$C7 ; get temporary integer high byte + JMP LAB_2DB6 + JSR LAB_CRLF ; print CR/LF LDA #LAB_MSZM ; point to memory size message (high addr) @@ -544,8 +550,8 @@ LAB_2DAA ; (no range check) LAB_2DB6 - LDA Itempl ; get temporary integer low byte - LDY Itemph ; get temporary integer high byte +; LDA Itempl ; get temporary integer low byte +; LDY Itemph ; get temporary integer high byte CPY # $BASE.js -cp $BASE.js ../../vm/Virtual\ Computer/software/. +../xa-2.3.5/xa -M -e $BASE.error.txt -l $BASE.label.txt -o $BASE.out $BASE.asm +../65b2js $BASE.out > $BASE.js +cp $BASE.js ../vm/software/. diff --git a/Krusader/Krusader 1.3 65C02.asm b/Krusader/Krusader 1.3 65C02.asm deleted file mode 100644 index da26a33..0000000 --- a/Krusader/Krusader 1.3 65C02.asm +++ /dev/null @@ -1,3023 +0,0 @@ -; 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 deleted file mode 100644 index b8a39be..0000000 --- a/Krusader/krusader 1.3.asm +++ /dev/null @@ -1,3038 +0,0 @@ -; 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 deleted file mode 100644 index 07cbbe7..0000000 Binary files a/Krusader/krusader13.pdf and /dev/null differ diff --git a/vm/README.txt b/vm/README.txt index 4302d52..04b93d9 100644 --- a/vm/README.txt +++ b/vm/README.txt @@ -36,22 +36,21 @@ Notes: 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 + MM_IRQ_VECTOR = 0xfffe; + MM_RESET_VECTOR = 0xfffc; + MM_NMI_VECTOR = 0xfffa; + MM_CLOCK = 0xfff9; + MM_PRNG = 0xfff8; + MM_KEYBOARD_META = 0xfff7; + MM_KEYBOARD_CHARACTER = 0xfff6; + MM_DISPLAY_COMMAND = 0xffa1; + MM_DISPLAY_DATA = 0xffa0; + MM_DISPLAY_ADAPTER = 0xf000; + MM_HEAP_END = 0xefff; + MM_HEAP_START = 0x0200; + MM_STACK = 0x0100; + MM_ZERO_PAGE = 0x0000; + Display Adapter: @@ -131,7 +130,20 @@ 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()". +By default, when the CPU starts, it begins executing the code pointed to +by the reset vector. This address must be filled in before calling cpu.reset. + +--- + +NOTES: + +Abstraction with util.js is poor. Same with memory map. +Video display sucks. +- Needs real graphics. +- Needs to parse special symbols in strings to clear screen, set colors, move cursor, bell, etc. +Save / Load machine snapshots. +Sound. +Networking? +128k? + +640x400 diff --git a/vm/VGAFont.png b/vm/VGAFont.png new file mode 100644 index 0000000..214c07f Binary files /dev/null and b/vm/VGAFont.png differ diff --git a/vm/computer/clock.js b/vm/computer/clock.js index c784bde..d87d026 100644 --- a/vm/computer/clock.js +++ b/vm/computer/clock.js @@ -22,21 +22,24 @@ function clock() { // --- Private variables. - // Memory location for PRNG value. + // Memory location for clock value. var MEMORY = null; var MEMORY_START = 0; var CLOCK_ADDRESS = 0; // Clock values. - var MILLISECONDS = 0; - var CLOCK = 0; + var MILLISECONDS = 0; + var CLOCK = 0; + var CLOCK_RUNNING = true; // --- Private methods. var clockTick = function() { - MEMORY.writeByte(CLOCK_ADDRESS, CLOCK++); - if (CLOCK > 255) CLOCK = 0; + if (CLOCK_RUNNING) { + MEMORY.writeByte(CLOCK_ADDRESS, CLOCK++); + if (CLOCK > 255) CLOCK = 0; + } setTimeout(function(){ clockTick(); }, MILLISECONDS); @@ -53,5 +56,31 @@ function clock() { MILLISECONDS = newMilliseconds; clockTick(); }; + + // Deserialize clock state. + this.deserialize = function(state) { + CLOCK = state.C; + CLOCK_RUNNING = state.R; + }; + // Pause the clock. + this.pause = function() { + console.log("Pausing Clock."); + CLOCK_RUNNING = false; + }; + + // Start the clock. + this.run = function() { + console.log("Starting Clock."); + CLOCK_RUNNING = true; + }; + + // Serialize clock state. + this.serialize = function() { + var state = { + C: CLOCK, + R: CLOCK_RUNNING + }; + return state; + }; } diff --git a/vm/computer/cpu6502.js b/vm/computer/cpu6502.js index 0a92075..d73de3b 100644 --- a/vm/computer/cpu6502.js +++ b/vm/computer/cpu6502.js @@ -18,25 +18,24 @@ */ // 6502 CPU Core - based on http://www.6502asm.com +// Lots of good ideas swiped from http://skilldrick.github.io/easy6502/ function cpu6502() { // --- Private variables. var self = this; + var TRACING = false; // 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; + var REG_PC = 0; + var REG_SP = 0xff; // Internal CPU Status. var CODE_RUNNING = false; @@ -46,8 +45,49 @@ function cpu6502() { // Instruction Function "Pointers". var INSTRUCTION_POINTERS = null; - + // Opcode names for debugging. (Ugly. Generated programmatically.) + var OPCODE_NAMES = ["BRK", "ORA", undefined, undefined, undefined, "ORA", + "ASL", undefined, "PHP", "ORA", "ASL", undefined, + undefined, "ORA", "ASL", undefined, "BPL", "ORA", + undefined, undefined, undefined, "ORA", "ASL", undefined, + "CLC", "ORA", undefined, undefined, undefined, "ORA", + "ASL", undefined, "JSR", "AND", undefined, undefined, + "BIT", "AND", "ROL", undefined, "PLP", "AND", "ROL", + undefined, "BIT", "AND", "ROL", undefined, "BMI", "AND", + undefined, undefined, undefined, "AND", "ROL", undefined, + "SEC", "AND", undefined, undefined, undefined, "AND", + "ROL", undefined, "RTI", "EOR", undefined, undefined, + undefined, "EOR", "LSR", undefined, "PHA", "EOR", "LSR", + undefined, "JMP", "EOR", "LSR", undefined, "BVC", "EOR", + undefined, undefined, undefined, "EOR", "LSR", undefined, + "CLI", "EOR", undefined, undefined, undefined, "EOR", + "LSR", undefined, "RTS", "ADC", undefined, undefined, + undefined, "ADC", "ROR", undefined, "PLA", "ADC", "ROR", + undefined, "JMP", "ADC", "ROR", undefined, "BVS", "ADC", + undefined, undefined, undefined, "ADC", "ROR", undefined, + "SEI", "ADC", undefined, undefined, undefined, "ADC", + "ROR", undefined, undefined, "STA", undefined, undefined, + "STY", "STA", "STX", undefined, "DEY", undefined, "TXA", + undefined, "STY", "STA", "STX", undefined, "BCC", "STA", + undefined, undefined, "STY", "STA", "STX", undefined, + "TYA", "STA", "TXS", undefined, undefined, "STA", + undefined, undefined, "LDY", "LDA", "LDX", undefined, + "LDY", "LDA", "LDX", undefined, "TAY", "LDA", "TAX", + undefined, "LDY", "LDA", "LDX", undefined, "BCS", "LDA", + undefined, undefined, "LDY", "LDA", "LDX", undefined, + "CLV", "LDA", "TSX", undefined, "LDY", "LDA", "LDX", + undefined, "CPY", "CMP", undefined, undefined, "CPY", + "CMP", "DEC", undefined, "INY", "CMP", "DEX", undefined, + "CPY", "CMP", "DEC", undefined, "BNE", "CMP", undefined, + undefined, undefined, "CMP", "DEC", undefined, "CLD", + "CMP", undefined, undefined, undefined, "CMP", "DEC", + undefined, "CPX", "SBC", undefined, undefined, "CPX", + "SBC", "INC", undefined, "INX", "SBC", "NOP", undefined, + "CPX", "SBC", "INC", undefined, "BEQ", "SBC", undefined, + undefined, undefined, "SBC", "INC", undefined, "SED", + "SBC", undefined, undefined, undefined, "SBC", "INC"]; + // --- Public variables, enums. this.E_HALT = 0; @@ -58,21 +98,63 @@ function cpu6502() { // --- 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; - } + var BIT = function(value) { + if (value & 0x80) + REG_P |= 0x80; + else + REG_P &= 0x7f; + if (value & 0x40) + REG_P |= 0x40; + else + REG_P &= ~0x40; + if (REG_A & value) + REG_P &= 0xfd; + else + REG_P |= 0x02; + }; + + var carrySet = function() { + return REG_P & 1; + }; + + // Clear Carry + var CLC = function() { + REG_P &= 0xfe; + }; + + // Clear Overflow + var CLV = function() { + REG_P &= 0xbf; + }; + + var DEC = function(addr) { + var value = MEMORY.readByte(addr); + value--; + value &= 0xff; + MEMORY.writeByte(addr, value); + setNVflags(value); + }; + + var decimalMode = function() { + return REG_P & 8; + }; + + var doCompare = function(reg, val) { + if (reg >= val) + SEC(); + else + CLC(); + val = (reg - val); + setNVflags(val); + }; + + var INC = function(addr) { + var value = MEMORY.readByte(addr); + value++; + value &= 0xff; + MEMORY.writeByte(addr, value); + setNVflags(value); + }; // Might as well Jump... JUMP! var jumpBranch = function(offset) { @@ -82,6 +164,14 @@ function cpu6502() { REG_PC = (REG_PC + offset); }; + var negativeSet = function() { + return REG_P & 0x80; + }; + + var overflowSet = function() { + return REG_P & 0x40; + }; + // Fetch the next byte pointed to by the program counter. var popByte = function() { return (MEMORY.readByte(REG_PC++) & 0xff); @@ -94,254 +184,270 @@ function cpu6502() { // 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); + if (CODE_RUNNING) { + var instructions = 100; + while ((instructions-- > 0) && (CODE_RUNNING)) + self.execute(); + setTimeout(runBlock, 0); + } + }; + + // Set Carry + var SEC = function() { + REG_P |= 1; + }; + + var setCarryFlagFromBit0 = function(value) { + REG_P = (REG_P & 0xfe) | (value & 1); + }; + + var setCarryFlagFromBit7 = function(value) { + REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); + }; + + // Set Zero and Negative processor flags. + var setNVflags = function(value) { + if (value) + REG_P &= 0xfd; + else + REG_P |= 0x02; + if (value & 0x80) + REG_P |= 0x80; + else + REG_P &= 0x7f; + }; + + var setOverflow = function() { + REG_P |= 0x40; }; // 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; + REG_SP++; + if (REG_SP > 0xff) { + REG_SP &= 0xff; CALLBACK(self.E_STACK_UNDERFLOW); - return 0; } + var value = MEMORY.readByte(REG_SP + 0x100); + return value; }; // 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; + MEMORY.writeByte(REG_SP + 0x100, value & 0xff); + REG_SP--; + if (REG_SP < 0) { + REG_SP &= 0xff; CALLBACK(self.E_STACK_OVERFLOW); } }; + + var testADC = function(value) { + var tmp; + if ((REG_A ^ value) & 0x80) + CLV(); + else + setOverflow(); + if (decimalMode()) { + tmp = (REG_A & 0xf) + (value & 0xf) + carrySet(); + if (tmp >= 10) + tmp = 0x10 | ((tmp + 6) & 0xf); + tmp += (REG_A & 0xf0) + (value & 0xf0); + if (tmp >= 160) { + SEC(); + if (overflowSet() && tmp >= 0x180) CLV(); + tmp += 0x60; + } else { + CLC(); + if (overflowSet() && tmp < 0x80) CLV(); + } + } else { + tmp = REG_A + value + carrySet(); + if (tmp >= 0x100) { + SEC(); + if (overflowSet() && tmp >= 0x180) CLV(); + } else { + CLC(); + if (overflowSet() && tmp < 0x80) CLV(); + } + } + REG_A = tmp & 0xff; + setNVflags(REG_A); + }; 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 tmp; + var w; + if ((REG_A ^ value) & 0x80) + setOverflow(); + else + CLV(); + if (decimalMode()) { + tmp = 0xf + (REG_A & 0xf) - (value & 0xf) + carrySet(); + if (tmp < 0x10) { + w = 0; + tmp -= 6; + } else { + w = 0x10; + tmp -= 0x10; + } + w += 0xf0 + (REG_A & 0xf0) - (value & 0xf0); + if (w < 0x100) { + CLC(); + if (overflowSet() && w < 0x80) CLV(); + w -= 0x60; + } else { + SEC(); + if (overflowSet() && w >= 0x180) CLV(); + } + w += tmp; + } else { + w = 0xff + REG_A - value + carrySet(); + if (w < 0x100) { + CLC(); + if (overflowSet() && w < 0x80) CLV(); + } else { + SEC(); + if (overflowSet() && w >= 0x180) CLV(); + } + } + REG_A = w & 0xff; + setNVflags(REG_A); + }; + + var zeroSet = function() { + return REG_P & 0x02; + }; - 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. + // BRK (Break) var i00 = function() { CODE_RUNNING = false; }; + // ORA (bitwise OR with Accumulator) Indirect,X var i01 = function() { - var addr = popByte() + REG_X; - var value = MEMORY.readByte(addr) + (MEMORY.readByte(addr + 1) << 8); + var zp = (popByte() + REG_X) & 0xff; + var addr = MEMORY.readWord(addr); + 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; + setNVflags(REG_A); }; + // ORA (bitwise OR with Accumulator) Zero Page 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; + setNVflags(REG_A); }; + // ASL (Arithmetic Shift Left) Zero Page var i06 = function() { var zp = popByte(); var value = MEMORY.readByte(zp); - REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); - value = value << 1; + setCarryFlagFromBit7(value); + value = (value << 1) & 0xff; MEMORY.writeByte(zp, value); - if (value) REG_P &= 0xfd; else REG_P |= 0x02; - if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + setNVflags(value); }; + // PHP (PusH Processor status) var i08 = function() { - stackPush(REG_P); + stackPush(REG_P | 0x30); }; + // ORA (bitwise OR with Accumulator) Immediate 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; + setNVflags(REG_A); }; + // ASL (Arithmetic Shift Left) Accumulator 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; + setCarryFlagFromBit7(REG_A); + REG_A = (REG_A << 1) & 0xff; + setNVflags(REG_A); }; + // ORA (bitwise OR with Accumulator) Absolute 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; + setNVflags(REG_A); }; + // ASL (Arithmetic Shift Left) Absolute var i0e = function() { var addr = popWord(); var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); - value = value << 1; + setCarryFlagFromBit7(value); + value = (value << 1) & 0xff; MEMORY.writeByte(addr, value); - if (value) REG_P &= 0xfd; else REG_P |= 2; - if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + setNVflags(value); }; - + + // BPL (Branch on PLus) var i10 = function() { var offset = popByte(); - if ((REG_P & 0x80) == 0) jumpBranch(offset); + if (!negativeSet()) jumpBranch(offset); }; + // ORA (bitwise OR with Accumulator) Indirect,Y var i11 = function() { var zp = popByte(); - var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + var value = MEMORY.readWord(zp) + 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; + setNVflags(REG_A); }; + // ORA (bitwise OR with Accumulator) Zero Page,X 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; + setNVflags(REG_A); }; + // ASL (Arithmetic Shift Left) Zero Page,X 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; + setCarryFlagFromBit7(value); + value = (value << 1) & 0xff; MEMORY.writeByte(addr, value); - if (value) REG_P &= 0xfd; else REG_P |= 0x02; - if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + setNVflags(value); }; - + + // CLC (CLear Carry) var i18 = function() { - REG_P &= 0xfe; + CLC(); }; + // ORA (bitwise OR with Accumulator) Absolute,Y 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; + setNVflags(REG_A); }; + // ORA (bitwise OR with Accumulator) Absolute,X 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; + setNVflags(REG_A); }; + // ASL (Arithmetic Shift Left) Absolute,X var i1e = function() { var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); - value = value << 1; + setCarryFlagFromBit7(value); + value = (value << 1) & 0xff; MEMORY.writeByte(addr, value); - if (value) REG_P &= 0xfd; else REG_P |= 0x02; - if (value & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + setNVflags(value); }; + // JSR (Jump to SubRoutine) Absolute var i20 = function() { var addr = popWord(); var currAddr = REG_PC - 1; @@ -350,879 +456,915 @@ function cpu6502() { REG_PC = addr; }; + // AND (bitwise AND with accumulator) Indirect,X var i21 = function() { var addr = (popByte() + REG_X) & 0xff; - var value = MEMORY.readByte(addr) + (MEMORY.readByte(addr + 1) << 8); + var value = MEMORY.readWord(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; + setNVflags(REG_A); }; + // BIT (test BITs) Zero Page 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); + BIT(value); }; + // AND (bitwise AND with accumulator) Zero Page 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; + setNVflags(REG_A); }; + // ROL (ROtate Left) Zero Page var i26 = function() { - var sf = (REG_P & 1); + var sf = carrySet(); var addr = popByte(); var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); - value = value << 1; + setCarryFlagFromBit7(value); + value = (value << 1) & 0xff; 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; + setNVflags(value); }; + // PLP (PuLl Processor status) var i28 = function() { - REG_P = stackPop() | 0x20; + REG_P = stackPop() | 0x30; }; + // AND (bitwise AND with accumulator) Immediate 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; + setNVflags(REG_A); }; + // ROL (ROtate Left) Accumulator var i2a = function() { - var sf = (REG_P & 1); - REG_P = (REG_P & 0xfe) | ((REG_A >> 7) & 1); - REG_A = REG_A << 1; + var sf = carrySet(); + setCarryFlagFromBit7(REG_A); + REG_A = (REG_A << 1) & 0xff; REG_A |= sf; - if (REG_A) REG_P &= 0xfd; else REG_P |= 0x02; - if (REG_A & 0x80) REG_P |= 0x80; else REG_P &= 0x7f; + setNVflags(REG_A); }; + // BIT (test BITs) Absolute 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); + BIT(value); }; + // AND (bitwise AND with accumulator) Absolute 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; + setNVflags(REG_A); }; + // ROL (ROtate Left) Absolute var i2e = function() { - var sf = REG_P & 1; + var sf = carrySet(); var addr = popWord(); var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); - value = value << 1; + setCarryFlagFromBit7(value); + value = (value << 1) & 0xff; 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; + setNVflags(value); }; + // BMI (Branch on MInus) var i30 = function() { var offset = popByte(); - if (REG_P & 0x80) jumpBranch(offset); + if (negativeSet()) jumpBranch(offset); }; + // AND (bitwise AND with accumulator) Indirect,Y var i31 = function() { var zp = popByte(); - var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + var value = MEMORY.readWord(zp) + 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; + setNVflags(REG_A); }; + // AND (bitwise AND with accumulator) Zero Page,X 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 zp = (popByte() + REG_X) & 0xff; + REG_A &= MEMORY.readByte(zp); + setNVflags(REG_A); }; + // ROL (ROtate Left) Zero Page,X var i36 = function() { - var sf = REG_P & 1; + var sf = carrySet(); var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); - value = value << 1; + setCarryFlagFromBit7(value); + value = (value << 1) & 0xff; 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; + setNVflags(value); }; + // SEC (SEt Carry) var i38 = function() { - REG_P |= 1; + SEC(); }; + // AND (bitwise AND with accumulator) Absolute,Y 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; + setNVflags(REG_A); }; + // AND (bitwise AND with accumulator) Absolute,X 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; + setNVflags(REG_A); }; + // ROL (ROtate Left) Absolute,X var i3e = function() { - var sf = REG_P & 1; + var sf = carrySet(); var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | ((value >> 7) & 1); - value = value << 1; + setCarryFlagFromBit7(value); + value = (value << 1) & 0xff; 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; + setNVflags(value); }; + // RTI (ReTurn from Interrupt) var i40 = function() { + REG_P = stackPop() | 0x30; + REG_PC = stackPop() | (stackPop() << 8); }; + // EOR (bitwise Exclusive OR) Indirect,X var i41 = function() { var zp = (popByte() + REG_X) & 0xff; - var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + var value = MEMORY.readWord(zp); 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; + setNVflags(REG_A); }; + // EOR (bitwise Exclusive OR) Zero Page var i45 = function() { - var addr = (popByte() + REG_X) & 0xff; + var addr = popByte() & 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; + setNVflags(REG_A); }; + // LSR (Logical Shift Right) Zero Page var i46 = function() { var addr = popByte() & 0xff; var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | (value & 1); + setCarryFlagFromBit0(value); 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; + setNVflags(value); }; + // PHA (PusH Accumulator) var i48 = function() { stackPush(REG_A); }; - + + // EOR (bitwise Exclusive OR) Immediate 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; + setNVflags(REG_A); }; + // LSR (Logical Shift Right) Accumulator var i4a = function() { - REG_P = (REG_P & 0xfe) | (REG_A & 1); + setCarryFlagFromBit0(REG_A); 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; + setNVflags(REG_A); }; + // JMP (JuMP) Absolute var i4c = function() { REG_PC = popWord(); }; + // EOR (bitwise Exclusive OR) Absolute 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; + setNVflags(REG_A); }; + // LSR (Logical Shift Right) Absolute var i4e = function() { var addr = popWord(); var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | (value & 1); + setCarryFlagFromBit0(value); 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; + setNVflags(value); }; + // BVC (Branch on oVerflow Clear) var i50 = function() { var offset = popByte(); - if ((REG_P & 0x40) == 0) jumpBranch(offset); + if (!overflowSet()) jumpBranch(offset); }; + // EOR (bitwise Exclusive OR) Indirect,Y var i51 = function() { var zp = popByte(); - var value = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + var value = MEMORY.readWord(zp) + 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; + setNVflags(REG_A); }; + // EOR (bitwise Exclusive OR) Zero Page,X 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; + setNVflags(REG_A); }; + // LSR (Logical Shift Right) Zero Page,X var i56 = function() { var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | (value & 1); + setCarryFlagFromBit0(value); 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; + setNVflags(value); }; + // CLI (CLear Interrupt) var i58 = function() { + REG_P &= ~0x04; + // Interrupts not supported yet. }; + // EOR (bitwise Exclusive OR) Absolute,Y 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; + setNVflags(REG_A); }; + // EOR (bitwise Exclusive OR) Absolute,X 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; + setNVflags(REG_A); }; + // LSR (Logical Shift Right) Absolute,X var i5e = function() { var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | (value & 1); + setCarryFlagFromBit0(value); 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; + setNVflags(value); }; + // RTS (ReTurn from Subroutine) var i60 = function() { - REG_PC = (stackPop() + 1) | (stackPop() << 8); + REG_PC = (stackPop() | (stackPop() << 8)) + 1; }; + // ADC (ADd with Carry) Indirect,X var i61 = function() { var zp = (popByte() + REG_X) & 0xff; - var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + var addr = MEMORY.readWord(zp); value = MEMORY.readByte(addr); testADC(value); }; + // ADC (ADd with Carry) Zero Page var i65 = function() { var addr = popByte(); var value = MEMORY.readByte(addr); testADC(value); }; + // ROR (ROtate Right) Zero Page var i66 = function() { - var sf = REG_P & 1; + var sf = carrySet(); var addr = popByte(); var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | (value & 1); + setCarryFlagFromBit0(value); 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; + setNVflags(value); }; + // PLA (PuLl Accumulator) 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; + setNVflags(REG_A); }; + // ADC (ADd with Carry) Immediate var i69 = function() { var value = popByte(); testADC(value); }; + // ROR (ROtate Right) Accumulator var i6a = function() { - var sf = REG_P & 1; - REG_P = (REG_P & 0xfe) | (REG_A & 1); + var sf = carrySet(); + setCarryFlagFromBit0(REG_A); 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; + setNVflags(REG_A); }; + // JMP (JuMP) Indirect var i6c = function() { + var addr = popWord(); + REG_PC = MEMORY.readWord(addr); }; + // ADC (ADd with Carry) Absolute var i6d = function() { var addr = popWord(); var value = MEMORY.readByte(addr); testADC(value); }; + // ROR (ROtate Right) Absolute var i6e = function() { - var sf = REG_P & 1; + var sf = carrySet(); var addr = popWord(); var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | (value & 1); + setCarryFlagFromBit0(value); 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; + setNVflags(value); }; + // BVS (Branch on oVerflow Set) var i70 = function() { var offset = popByte(); - if (REG_P & 0x40) jumpBranch(offset); + if (overflowSet()) jumpBranch(offset); }; + // ADC (ADd with Carry) Indirect,Y var i71 = function() { var zp = popByte(); - var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + var addr = MEMORY.readWord(zp); var value = MEMORY.readByte(addr + REG_Y); testADC(value); }; + // ADC (ADd with Carry) Zero Page,X var i75 = function() { var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | (value & 1); testADC(value); }; + // ROR (ROtate Right) Zero Page,X var i76 = function() { - var sf = (REG_P & 1); + var sf = carrySet(); var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | (value & 1); + setCarryFlagFromBit0(value); 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; + setNVflags(value); }; + // SEI (SEt Interrupt) var i78 = function() { + REG_P |= 0x04; + // Interrupts not supported yet. }; + // ADC (ADd with Carry) Absolute,Y var i79 = function() { var addr = popWord(); var value = MEMORY.readByte(addr + REG_Y); testADC(value); }; + // ADC (ADd with Carry) Absolute,X var i7d = function() { var addr = popWord(); var value = MEMORY.readByte(addr + REG_X); testADC(value); }; + // ROR (ROtate Right) Absolute,X var i7e = function() { - //var sf = REG_P & 1; + var sf = carrySet(); var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | (value & 1); + setCarryFlagFromBit0(value); value = value >> 1; - if (value) value |= 0x80; + 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; + setNVflags(value); }; + // STA (STore Accumulator) Indirect,X var i81 = function() { var zp = (popByte() + REG_X) & 0xff; - var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + var addr = MEMORY.readWord(zp); MEMORY.writeByte(addr, REG_A); }; + // STY (STore Y register) Zero Page var i84 = function() { MEMORY.writeByte(popByte(), REG_Y); }; + // STA (STore Accumulator) Zero Page var i85 = function() { MEMORY.writeByte(popByte(), REG_A); }; + // STX (STore X register) Zero Page var i86 = function() { MEMORY.writeByte(popByte(), REG_X); }; + // DEY (DEcrement Y) 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; + setNVflags(REG_Y); }; + // TXA (Transfer X to A) 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; + setNVflags(REG_A); }; + // STY (STore Y register) Absolute var i8c = function() { MEMORY.writeByte(popWord(), REG_Y); }; + // STA (STore Accumulator) Absolute var i8d = function() { MEMORY.writeByte(popWord(), REG_A); }; + // STX (STore X register) Absolute var i8e = function() { MEMORY.writeByte(popWord(), REG_X); }; + // BCC (Branch on Carry Clear) var i90 = function() { var offset = popByte(); - if ((REG_P & 1) == 0) jumpBranch(offset); + if (!carrySet()) jumpBranch(offset); }; + // STA (STore Accumulator) Indirect,Y var i91 = function() { var zp = popByte(); - var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + var addr = MEMORY.readWord(zp) + REG_Y; MEMORY.writeByte(addr, REG_A); }; - + + // STY (STore Y register) Zero Page,X var i94 = function() { - MEMORY.writeByte(popByte() + REG_X, REG_Y); + MEMORY.writeByte((popByte() + REG_X) & 0xff, REG_Y); }; + // STA (STore Accumulator) Zero Page,X var i95 = function() { - MEMORY.writeByte(popByte() + REG_X, REG_A); + MEMORY.writeByte((popByte() + REG_X) & 0xff, REG_A); }; + // STX (STore X register) Zero Page,Y var i96 = function() { - MEMORY.writeByte(popByte() + REG_Y, REG_X); + MEMORY.writeByte((popByte() + REG_Y) & 0xff, REG_X); }; + // TYA (Transfer Y to A) 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; + setNVflags(REG_A); }; + // STA (STore Accumulator) Absolute,Y var i99 = function() { MEMORY.writeByte(popWord() + REG_Y, REG_A); }; + // TXS (Transfer X to Stack ptr) var i9a = function() { REG_SP = REG_X & 0xff; }; + // STA (STore Accumulator) Absolute,X var i9d = function() { var addr = popWord(); MEMORY.writeByte(addr + REG_X, REG_A); }; + // LDY (LoaD Y register) Immediate 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; + setNVflags(REG_Y); }; + // LDA (LoaD Accumulator) Indirect,X var ia1 = function() { var zp = (popByte() + REG_X) & 0xff; - var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + var addr = MEMORY.readWord(zp); 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; + setNVflags(REG_A); }; + // LDX (LoaD X register) Immediate 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; + setNVflags(REG_X); }; + // LDY (LoaD Y register) Zero Page 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; + setNVflags(REG_Y); }; + // LDA (LoaD Accumulator) Zero Page 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; + setNVflags(REG_A); }; + // LDX (LoaD X register) Zero Page 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; + setNVflags(REG_X); }; + // TAY (Transfer A to Y) 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; + setNVflags(REG_Y); }; - + + // LDA (LoaD Accumulator) Immediate 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; + setNVflags(REG_A); }; + // TAX (Transfer A to X) 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; + setNVflags(REG_X); }; + // LDY (LoaD Y register) Absolute 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; + setNVflags(REG_Y); }; + // LDA (LoaD Accumulator) Absolute 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; + setNVflags(REG_A); }; + // LDX (LoaD X register) Absolute 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; + setNVflags(REG_X); }; - + + // BCS (Branch on Carry Set) var ib0 = function() { var offset = popByte(); - if (REG_P & 1) jumpBranch(offset); + if (carrySet()) jumpBranch(offset); }; + // LDA (LoaD Accumulator) Indirect,Y var ib1 = function() { var zp = popByte(); - var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + var addr = MEMORY.readWord(zp) + 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; + setNVflags(REG_A); }; + // LDY (LoaD Y register) Zero Page,X 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; + REG_Y = MEMORY.readByte((popByte() + REG_X) & 0xff); + setNVflags(REG_Y); }; + // LDA (LoaD Accumulator) Zero Page,X 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; + setNVflags(REG_A); }; + // LDX (LoaD X register) Zero Page,Y 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; + REG_X = MEMORY.readByte((popByte() + REG_Y) & 0xff); + setNVflags(REG_X); }; + // CLV (CLear oVerflow) var ib8 = function() { - REG_P &= 0xbf; + CLV(); }; + // LDA (LoaD Accumulator) Absolute,Y 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; + setNVflags(REG_A); }; + // TSX (Transfer Stack ptr to X) var iba = function() { REG_X = REG_SP & 0xff; + setNVflags(REG_X); }; + // LDY (LoaD Y register) Absolute,X 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; + setNVflags(REG_Y); }; + // LDA (LoaD Accumulator) Absolute,X 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; + setNVflags(REG_A); }; + // LDX (LoaD X register) Absolute,Y 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; + setNVflags(REG_X); }; + // CPY (ComPare Y register) Immediate 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; + doCompare(REG_Y, value); }; + // CMP (CoMPare accumulator) Indirect,X var ic1 = function() { - var zp = popByte(); - var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + var zp = (popByte() + REG_X) & 0xff; + var addr = MEMORY.readWord(zp); var value = MEMORY.readByte(addr); doCompare(REG_A, value); }; - + + // CPY (ComPare Y register) Zero Page var ic4 = function() { var value = MEMORY.readByte(popByte()); doCompare(REG_Y, value); }; + // CMP (CoMPare accumulator) Zero Page var ic5 = function() { var value = MEMORY.readByte(popByte()); doCompare(REG_A, value); }; - + + // DEC (DECrement memory) Zero Page 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; + DEC(zp); }; - + + // INY (INcrement Y) 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; + setNVflags(REG_Y); }; - + + // CMP (CoMPare accumulator) Immediate var ic9 = function() { var value = popByte(); doCompare(REG_A, value); }; - + + // DEX (DEcrement X) 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; + setNVflags(REG_X); }; - + + // CPY (ComPare Y register) Absolute var icc = function() { var value = MEMORY.readByte(popWord()); doCompare(REG_Y, value); }; + // CMP (CoMPare accumulator) Absolute var icd = function() { var value = MEMORY.readByte(popWord()); doCompare(REG_A, value); }; - /* + // DEC (DECrement memory) Absolute 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; + DEC(addr); }; - */ - + + // BNE (Branch on Not Equal) var id0 = function() { var offset = popByte(); - if ((REG_P & 2) == 0) jumpBranch(offset); + if (!zeroSet()) jumpBranch(offset); }; + // CMP (CoMPare accumulator) Indirect,Y var id1 = function() { var zp = popByte(); - var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8) + REG_Y; + var addr = MEMORY.readWord(zp) + REG_Y; var value = MEMORY.readByte(addr); doCompare(REG_A, value); }; + // CMP (CoMPare accumulator) Zero Page,X var id5 = function() { - var value = MEMORY.readByte(popByte() + REG_X); + var value = MEMORY.readByte((popByte() + REG_X) & 0xff); doCompare(REG_A, value); }; + // DEC (DECrement memory) Zero Page,X 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 addr = (popByte() + REG_X) & 0xff; + DEC(addr); }; + // CLD (CLear Decimal) var id8 = function() { REG_P &= 0xf7; }; + // CMP (CoMPare accumulator) Absolute,Y var id9 = function() { var addr = popWord() + REG_Y; var value = MEMORY.readByte(addr); doCompare(REG_A, value); }; + // CMP (CoMPare accumulator) Absolute,X var idd = function() { var addr = popWord() + REG_X; var value = MEMORY.readByte(addr); doCompare(REG_A, value); }; + // DEC (DECrement memory) Absolute,X 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; + DEC(addr); }; - + + // CPX (ComPare X register) Immediate var ie0 = function() { var value = popByte(); doCompare(REG_X, value); }; + // SBC (SuBtract with Carry) Indirect,X var ie1 = function() { - var zp = (popByte()+REG_X)&0xff; - var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + var zp = (popByte() + REG_X) & 0xff; + var addr = MEMORY.readWord(zp); var value = MEMORY.readByte(addr); testSBC(value); }; + // CPX (ComPare X register) Zero Page var ie4 = function() { var value = MEMORY.readByte(popByte()); doCompare(REG_X, value); }; + // SBC (SuBtract with Carry) Zero Page var ie5 = function() { var addr = popByte(); var value = MEMORY.readByte(addr); testSBC(value); }; + // INC (INCrement memory) Zero Page 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; + INC(zp); }; + // INX (INcrement X) 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; + setNVflags(REG_X); }; + // SBC (SuBtract with Carry) Immediate var ie9 = function() { var value = popByte(); testSBC(value); }; + // NOP (No OPeration) var iea = function() { + // Nothing! }; + // CPX (ComPare X register) Absolute var iec = function() { var value = MEMORY.readByte(popWord()); doCompare(REG_X, value); }; + // SBC (SuBtract with Carry) Absolute var ied = function() { var addr = popWord(); var value = MEMORY.readByte(addr); testSBC(value); }; + // INC (INCrement memory) Absolute 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; + INC(addr); }; + // BEQ (Branch on EQual) var if0 = function() { var offset = popByte(); - if (REG_P & 2) jumpBranch(offset); + if (zeroSet()) jumpBranch(offset); }; + // SBC (SuBtract with Carry) Indirect,Y var if1 = function() { var zp = popByte(); - var addr = MEMORY.readByte(zp) + (MEMORY.readByte(zp + 1) << 8); + var addr = MEMORY.readWord(zp); var value = MEMORY.readByte(addr + REG_Y); testSBC(value); }; + // SBC (SuBtract with Carry) Zero Page,X var if5 = function() { var addr = (popByte() + REG_X) & 0xff; var value = MEMORY.readByte(addr); - REG_P = (REG_P & 0xfe) | (value & 1); testSBC(value); }; + // INC (INCrement memory) Zero Page,X 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 addr = (popByte() + REG_X) & 0xff; + INC(addr); }; + // SED (SEt Decimal) var if8 = function() { REG_P |= 8; }; + // SBC (SuBtract with Carry) Absolute,Y var if9 = function() { var addr = popWord(); var value = MEMORY.readByte(addr + REG_Y); testSBC(value); }; + // SBC (SuBtract with Carry) Absolute,X var ifd = function() { var addr = popWord(); var value = MEMORY.readByte(addr + REG_X); testSBC(value); }; + // INC (INCrement memory) Absolute,X 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; + INC(addr); }; + // Unknown Opcode var ierr = function() { CODE_RUNNING = false; CALLBACK(self.E_UNKNOWN_OPCODE); @@ -1232,9 +1374,8 @@ function cpu6502() { // --- Public methods. // Attach the CPU to the VM. - this.attach = function(newMemory, newPC, newCallback) { + this.attach = function(newMemory, newCallback) { MEMORY = newMemory; - DEFAULT_PC = newPC; CALLBACK = newCallback; INSTRUCTION_POINTERS = [ i00, //00 @@ -1443,7 +1584,7 @@ function cpu6502() { ierr, //cb icc, //cc icd, //cd - ierr, //ce + ice, //ce ierr, //cf id0, //d0 id1, //d1 @@ -1497,11 +1638,36 @@ function cpu6502() { this.reset(); }; + // Deserialize CPU state. + this.deserialize = function(state) { + REG_A = state.A; + REG_X = state.X; + REG_Y = state.Y; + REG_P = state.P; + REG_PC = state.PC; + REG_SP = state.SP; + if (state.R) this.run(); + }; + // 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)); + + /* + if ((REG_PC == 0xb77f + 1) || TRACING) { + TRACING = true; + console.log("PC = 0x" + (REG_PC - 1).toString(16) + " OP = " + OPCODE_NAMES[opcode] + " (0x" + opcode.toString(16) + ")"); + if (OPCODE_NAMES[opcode] == 'JMP') console.log ("0x" + MEMORY.readByte(REG_PC).toString(16) + " 0x" + MEMORY.readByte(REG_PC+1).toString(16)); + } + + var details = " PC = 0x" + (REG_PC - 1).toString(16) + " OP = " + OPCODE_NAMES[opcode] + " (0x" + opcode.toString(16) + ")"; + if ((REG_A < 0) || (REG_A > 0xff)) console.log("Error! REG_A = " + REG_A + details); + if ((REG_X < 0) || (REG_X > 0xff)) console.log("Error! REG_X = " + REG_X + details); + if ((REG_Y < 0) || (REG_Y > 0xff)) console.log("Error! REG_Y = " + REG_Y + details); + if ((REG_P < 0) || (REG_P > 0xff)) console.log("Error! REG_P = " + REG_P + details); + */ + INSTRUCTION_POINTERS[opcode](); if ((REG_PC == 0) || (!CODE_RUNNING)) { CODE_RUNNING = false; @@ -1514,20 +1680,54 @@ function cpu6502() { return REG_PC; }; + // Pause CPU. + this.pause = function() { + if (CODE_RUNNING) { + console.log("Pausing CPU."); + CODE_RUNNING = false; + } + }; + // Reset CPU. this.reset = function() { REG_A = 0; REG_X = 0; REG_Y = 0; REG_P = 0; - REG_PC = DEFAULT_PC; + REG_PC = MEMORY.readWord(0xfffc); REG_SP = 0x100; }; // Run until we die. this.run = function() { - console.log("Starting CPU."); - CODE_RUNNING = true; - setTimeout(runBlock, 0); + if (!CODE_RUNNING) { + console.log("Starting CPU."); + CODE_RUNNING = true; + setTimeout(runBlock, 0); + } + }; + + // Are we running? + this.running = function() { + return CODE_RUNNING; + }; + + // Serialize CPU state. + this.serialize = function() { + var state = { + A: REG_A, + X: REG_X, + Y: REG_Y, + P: REG_P, + PC: REG_PC, + SP: REG_SP, + R: CODE_RUNNING + }; + return state; + }; + + // Set PC. + this.setPC = function(newPC) { + REG_PC = newPC; }; } diff --git a/vm/computer/keyboard.js b/vm/computer/keyboard.js index da89444..a1c4fcd 100644 --- a/vm/computer/keyboard.js +++ b/vm/computer/keyboard.js @@ -1,17 +1,17 @@ /* - * 6502 Based Virtual Computer + * 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. @@ -19,9 +19,9 @@ // Our keyboard object. function keyboard() { - + // OMFGWTFBBQ - http://unixpapa.com/js/key.html - + // --- Private variables. // Memory location for keyboard status. @@ -29,54 +29,7 @@ function keyboard() { 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; + var RUNNING = false; // --- Public methods. @@ -87,65 +40,67 @@ function keyboard() { 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; + }; + + // Keyboard handler for use with Meteor. + this.handleEvent = function(event) { + // Process the key press. + var code = event.which || window.event.keyCode; + var k = { + code: code, + shift: event.shiftKey, + alt: event.altKey, + ctrl: event.ctrlKey + }; + var processIt = false; + if (event.type == "keydown") { + if ((code == 8) || + (code == 9) || + (code == 27)) + processIt = true; } - if (IS_KONQUEROR) { - KEY_MAP[0] = 45; - KEY_MAP[127] = 46; - KEY_MAP[45] = 95; + if (event.type == "keypress") { + processIt = true; } - // 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; // ' - } + //console.log(code + " " + event.type + " " + processIt); + if (processIt) { // 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); } - }; + } + return !processIt; + }; + + // Deserialize keyboard state. + this.deserialize = function(state) { + RUNNING = state.R; }; // Fetch how much RAM the current settings will require. this.getMemoryNeeded = function() { return 2; // Complicated, huh? }; + + // Pause the keyboard. + this.pause = function() { + console.log("Pausing Keyboard."); + RUNNING = false; + }; + + // Start the keyboard. + this.run = function() { + console.log("Starting Keyboard."); + RUNNING = true; + }; + + // Serialize keyboard state. + this.serialize = function() { + var state = { + R: RUNNING + }; + return state; + }; } diff --git a/vm/computer/prng.js b/vm/computer/prng.js index 17bf9a4..52b6bb8 100644 --- a/vm/computer/prng.js +++ b/vm/computer/prng.js @@ -22,6 +22,9 @@ function prng() { // --- Private variables. + var MAX = Math.pow(2, 32); + var SEED = Math.round(Math.random() * MAX); + // Memory location for PRNG value. var MEMORY = null; var MEMORY_START = 0; @@ -32,13 +35,31 @@ function prng() { // Attach PRNG to DOM. this.attach = function(newMemory, newStartPosition) { + // We create our own PRN to ensure recreatability between sessions and browsers. + // http://weblog.bocoup.com/random-numbers/ https://gist.github.com/Protonk/5367430 MEMORY = newMemory; MEMORY_START = newStartPosition; PRNG_ADDRESS = MEMORY_START; + LAST_RANDOM = Math.random(); 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)); + // On read, change the random value. Based on WebKit2's invertible mapping generator. + SEED += (SEED * SEED) | 5; + var value = (SEED >>> 32) / MAX; + MEMORY.writeByte(address, Math.floor(value * 256)); }); }; + // Deserialize PRNG state. + this.deserialize = function(state) { + SEED = state.S; + }; + + // Serialize PRNG state. + this.serialize = function() { + var state = { + S: SEED + }; + return state; + }; + } diff --git a/vm/computer/ram.js b/vm/computer/ram.js index f703c83..eed2dc7 100644 --- a/vm/computer/ram.js +++ b/vm/computer/ram.js @@ -1,17 +1,17 @@ /* * 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. @@ -19,17 +19,17 @@ // 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); @@ -37,8 +37,27 @@ function ram(newSize) { var READ_EVENTS = new Array(); var EVENTS_ENABLED = true; var EVENTS_ENABLED_COUNTER = 0; - - + var CANVAS_HEIGHT = 0; + var CANVAS_WIDTH = 0; + var CANVAS_DATA = null; + var CANVAS_CONTEXT = null; + + + // --- Private methods. + + var drawByte = function(location, value) { + var index = (MEMORY.length - location) * 4; + CANVAS_DATA.data[index] = value; + CANVAS_DATA.data[index + 1] = value; + CANVAS_DATA.data[index + 2] = value; + CANVAS_DATA.data[index + 3] = 255; + } + + var updateCanvas = function() { + CANVAS_CONTEXT.putImageData(CANVAS_DATA, 0, 0); + } + + // --- Public methods. // Add callback for memory read event. @@ -62,13 +81,38 @@ function ram(newSize) { WRITE_EVENTS.push(event); console.log("Write event " + WRITE_EVENTS.length + " added from 0x" + newStart.toString(16) + " to 0x" + newEnd.toString(16)); }; - + + // Optionally attach to a div for visual display of memory contents. + this.attach = function(divId) { + //***TODO*** Assumes 64k + CANVAS_HEIGHT = 256; + CANVAS_WIDTH = 256; + var canvas = document.createElement('canvas'); + canvas.id = "MemoryDisplay"; + canvas.width = CANVAS_WIDTH; + canvas.height = CANVAS_HEIGHT; + CANVAS_CONTEXT = canvas.getContext("2d"); + document.getElementById(divId).appendChild(canvas); + CANVAS_DATA = CANVAS_CONTEXT.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); + window.setInterval(updateCanvas.bind(this), 500); + }; + // Are we performing callbacks? this.callbacksEnabled = function(trueFalse) { EVENTS_ENABLED_COUNTER += (trueFalse ? -1 : 1); EVENTS_ENABLED = (EVENTS_ENABLED_COUNTER == 0); }; - + + // Deserialize RAM state. + this.deserialize = function(state) { + for (var x=0; x 0) && (EVENTS_ENABLED)) { for (var x=0; x= event.startAddress) && (address <= event.endAddress)) + if ((address >= event.startAddress) && (address <= event.endAddress)) { event.callback(address, MEMORY[address] & 0xff, event.anyData); + } } } return MEMORY[address] & 0xff; } }; - + + // Read a word and return it. + this.readWord = function(address) { + return (this.readByte(address) + (this.readByte(address + 1) << 8)); + }; + // 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 (CANVAS_DATA != null) { + drawByte(address, value & 0xff); + } if ((WRITE_EVENTS.length > 0) && (EVENTS_ENABLED)) { for (var x=0; x + * + * 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 textDisplayCanvas() { + + // 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. + + // Font texture is 144x256 pixels. + // Characters are 9x16 + + + // --- 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 atlas size. + var ATLAS_WIDTH = 16; + + // Character cell size. + var CELL_WIDTH = 9; + var CELL_HEIGHT = 16; + + // Default display size. + var WIDTH = 80; + var HEIGHT = 25; + + // Is this a color display? + var IS_COLOR = true; + + // 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 COLOR_HEX = ["#000000", "#8B0000", "#006400", "#8B8B00", + "#00008B", "#8B008B", "#008B8B", "#808080", + "#696969", "#FF0000", "#00FF00", "#FFFF00", + "#0000FF", "#FF00FF", "#00FFFF", "#FFFFFF"]; + var COLOR_RGB = new Array(); + + // Cursor position, blink state, and ASCII symbol. + var CURSOR_X = 0; + var CURSOR_Y = 0; + var CURSOR_B = false; + var CURSOR_C = 177; + + // Current color attribute. Boring white on black. (LSN=FG, MSN=BG) + var COLOR_VALUE = 7; + + // The actual DOM canvases. + var CANVAS = null; + var CANVAS_CONTEXT = null; + var FONT = null; + var FONT_CONTEXT = null; + + + // --- Private methods. + + var blinkCursor = function() { + // Change cursor state first so it is correct for the rest of the code. + CURSOR_B = !CURSOR_B; + if (CURSOR_B) { + renderCell(CURSOR_X, CURSOR_Y, CURSOR_C, 15, 0, true); + } else { + self.refresh(CURSOR_X, CURSOR_Y); + } + }; + + // Clear the screen to the current color attributes. + var clearDisplay = function() { + var byte = MEMORY_START; + for (var y=0; y> 4; + CANVAS_CONTEXT.fillStyle = COLOR_HEX[MSN]; + CANVAS_CONTEXT.fillRect(0, 0, CANVAS.width, CANVAS.height); + }; + + // Move the cursor to a new location. + var moveCursor = function(x, y) { + // Be sure old character cell has been restored if the cursor is currently visible. + if (CURSOR_B) self.refresh(CURSOR_X, CURSOR_Y); + // Move it. + CURSOR_X = x; + CURSOR_Y = y; + }; + + // Draw one character to the display & update cursor position. + var drawCharacter = function(c) { + var color = (IS_COLOR ? 2 : 1); + var byte = MEMORY_START + (CURSOR_Y * WIDTH + CURSOR_X) * color; + var x = CURSOR_X; + var y = CURSOR_Y; + if (c == 8) { + // Backspace + if (x > 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++; + } + } + for (var i=0; i> 4; + CANVAS_CONTEXT.fillStyle = COLOR_HEX[MSN]; + CANVAS_CONTEXT.fillRect(0, CANVAS.height - CELL_HEIGHT, CANVAS.width, CELL_HEIGHT); + } + // Reposition cursor. + moveCursor(x, y); + }; + + // Draw a zero-terminated string to the display & update cursor position. + var drawString = function(address) { + var byte = address; + do { + var c = MEMORY.readByte(byte++); + if (c != 0) drawCharacter(c); + } while (c != 0); + }; + + // Finish attaching this display to the VM. + var finishAttaching = function(newMemory, newStartPosition, divId) { + MEMORY = newMemory; + MEMORY_START = newStartPosition; + // Pre-convert our hex colors to RGB values. + for (c=0; c= 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 / WIDTH); + var x = offset - y * WIDTH; + self.refresh(x, y); + } + MEMORY.callbacksEnabled(true); + }); + }; + + var renderCell = function(x, y, c, f, b, t) { + // Find pixel offset in display canvas. + var cx = x * CELL_WIDTH; + var cy = y * CELL_HEIGHT; + // Locate this character in the font atlas. + var ax = (c % ATLAS_WIDTH) * CELL_WIDTH; + var ay = Math.floor(c / ATLAS_WIDTH) * CELL_HEIGHT; + // Render character. + var cell = CANVAS_CONTEXT.getImageData(cx, cy, CELL_WIDTH, CELL_HEIGHT); + var pixelColor = null; + for (rx=0; rx