1540 lines
68 KiB
NASM
1540 lines
68 KiB
NASM
; ********************************
|
|
; * SUPERMON+ 64 JIM BUTTERFIELD *
|
|
; * V1.2 AUGUST 20 1985 *
|
|
; ********************************
|
|
|
|
; Reformatted and annotated in late 2016/early 2017 by J.B. Langston.
|
|
;
|
|
; I've made the minimum necessary changes to this code to get it to assemble
|
|
; with 64tass. Specifically, I changed the following directives from PAL
|
|
; that 64tass doesn't support:
|
|
; - .ASC => .TEXT
|
|
; - *=*+X => .FILL X
|
|
;
|
|
; Aside from this, I have adopted a strict whitespace and comments only
|
|
; policy so that I preserve code exactly as Jim Butterfield wrote it.
|
|
;
|
|
; I think my comments are correct but I don't guarantee I haven't made
|
|
; any errors. Sadly Jim isn't around to ask anymore. If you spot any
|
|
; misunderstanings or errors in my comments, please report them.
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; temporary pointers
|
|
TMP0 = $C1 ; used to return input, often holds end address
|
|
TMP2 = $C3 ; usually holds start address
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; kernal variables
|
|
SATUS = $90 ; kernal i/o status word
|
|
FNLEN = $B7 ; length of current filename
|
|
SADD = $B9 ; current secondary address (official name SA)
|
|
FA = $BA ; current device number
|
|
FNADR = $BB ; pointer to current filename
|
|
NDX = $C6 ; number of characters in keyboard buffer
|
|
KEYD = $0277 ; keyboard buffer
|
|
BKVEC = $0316 ; BRK instruction vector (official name CBINV)
|
|
|
|
*= $0100 ; store variables in tape error buffer
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; variables
|
|
ACMD .FILL 1 ; addressing command
|
|
LENGTH .FILL 1 ; length of operand
|
|
MNEMW .FILL 3 ; 3 letter mnemonic buffer
|
|
SAVX .FILL 1 ; 1 byte temp storage, often to save X register
|
|
OPCODE .FILL 1 ; current opcode for assembler/disassembler
|
|
UPFLG .FILL 1 ; flag: count up (bit 7 clear) or down (bit 7 set)
|
|
DIGCNT .FILL 1 ; digit count
|
|
INDIG .FILL 1 ; numeric value of single digit
|
|
NUMBIT .FILL 1 ; numeric base of input
|
|
STASH .FILL 2 ; 2-byte temp storage
|
|
U0AA0 .FILL 10 ; work buffer
|
|
U0AAE =* ; end of work buffer
|
|
STAGE .FILL 30 ; staging buffer for filename, search, etc.
|
|
ESTAGE =* ; end of staging buffer
|
|
|
|
*= $0200 ; store more variables in basic line editor buffer
|
|
|
|
INBUFF .FILL 40 ; 40-character input buffer
|
|
ENDIN =* ; end of input buffer
|
|
|
|
; the next 7 locations are used to store the registers when
|
|
; entering the monitor and restore them when exiting.
|
|
|
|
PCH .FILL 1 ; program counter high byte
|
|
PCL .FILL 1 ; program counter low byte
|
|
SR .FILL 1 ; status register
|
|
ACC .FILL 1 ; accumulator
|
|
XR .FILL 1 ; X register
|
|
YR .FILL 1 ; Y register
|
|
SP .FILL 1 ; stack pointer
|
|
|
|
STORE .FILL 2 ; 2-byte temp storage
|
|
CHRPNT .FILL 1 ; current position in input buffer
|
|
SAVY .FILL 1 ; temp storage, often to save Y register
|
|
U9F .FILL 1 ; index into assembler work buffer
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; kernal entry points
|
|
SETMSG = $FF90 ; set kernel message control flag
|
|
SECOND = $FF93 ; set secondary address after LISTEN
|
|
TKSA = $FF96 ; send secondary address after TALK
|
|
LISTEN = $FFB1 ; command serial bus device to LISTEN
|
|
TALK = $FFB4 ; command serial bus device to TALK
|
|
SETLFS = $FFBA ; set logical file parameters
|
|
SETNAM = $FFBD ; set filename
|
|
ACPTR = $FFA5 ; input byte from serial bus
|
|
CIOUT = $FFA8 ; output byte to serial bus
|
|
UNTLK = $FFAB ; command serial bus device to UNTALK
|
|
UNLSN = $FFAE ; command serial bus device to UNLISTEN
|
|
CHKIN = $FFC6 ; define input channel
|
|
CLRCHN = $FFCC ; restore default devices
|
|
INPUT = $FFCF ; input a character (official name CHRIN)
|
|
CHROUT = $FFD2 ; output a character
|
|
LOAD = $FFD5 ; load from device
|
|
SAVE = $FFD8 ; save to device
|
|
STOP = $FFE1 ; check the STOP key
|
|
GETIN = $FFE4 ; get a character
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; set up origin
|
|
|
|
.WEAK
|
|
ORG = $9519
|
|
.ENDWEAK
|
|
|
|
* = ORG
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; initial entry point
|
|
SUPER LDY #MSG4-MSGBAS ; display "..SYS "
|
|
JSR SNDMSG
|
|
LDA SUPAD ; store entry point address in tmp0
|
|
STA TMP0
|
|
LDA SUPAD+1
|
|
STA TMP0+1
|
|
JSR CVTDEC ; convert address to decimal
|
|
LDA #0
|
|
LDX #6
|
|
LDY #3
|
|
JSR NMPRNT ; print entry point address
|
|
JSR CRLF
|
|
LDA LINKAD ; set BRK vector
|
|
STA BKVEC
|
|
LDA LINKAD+1
|
|
STA BKVEC+1
|
|
LDA #$80 ; disable kernel control messages
|
|
JSR SETMSG ; and enable error messages
|
|
BRK
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; BRK handler
|
|
BREAK LDX #$05 ; pull registers off the stack
|
|
BSTACK PLA ; order: Y,X,A,SR,PCL,PCH
|
|
STA PCH,X ; store in memory
|
|
DEX
|
|
BPL BSTACK
|
|
CLD ; disable bcd mode
|
|
TSX ; store stack pointer in memory
|
|
STX SP
|
|
CLI ; enable interupts
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; display registers [R]
|
|
DSPLYR LDY #MSG2-MSGBAS ; display headers
|
|
JSR SNDCLR
|
|
LDA #$3B ; prefix registers with "; " to allow editing
|
|
JSR CHROUT
|
|
LDA #$20
|
|
JSR CHROUT
|
|
LDA PCH ; print 2-byte program counter
|
|
JSR WRTWO
|
|
LDY #1 ; start 1 byte after PC high byte
|
|
DISJ LDA PCH,Y ; loop through rest of the registers
|
|
JSR WRBYTE ; print 1-byte register value
|
|
INY
|
|
CPY #7 ; there are a total of 5 registers to print
|
|
BCC DISJ
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; main loop
|
|
STRT JSR CRLF ; new line
|
|
LDX #0 ; point at start of input buffer
|
|
STX CHRPNT
|
|
SMOVE JSR INPUT ; CHRIN kernal call to input a character
|
|
STA INBUFF,X ; store in input buffer
|
|
INX
|
|
CPX #ENDIN-INBUFF ; error if buffer is full
|
|
BCS ERROR
|
|
CMP #$0D ; keep reading until CR
|
|
BNE SMOVE
|
|
LDA #0 ; null-terminate input buffer
|
|
STA INBUFF-1,X ; (replacing the CR)
|
|
ST1 JSR GETCHR ; get a character from the buffer
|
|
BEQ STRT ; start over if buffer is empty
|
|
CMP #$20 ; skip leading spaces
|
|
BEQ ST1
|
|
S0 LDX #KEYTOP-KEYW ; loop through valid command characters
|
|
S1 CMP KEYW,X ; see if input character matches
|
|
BEQ S2 ; command matched, dispatch it
|
|
DEX ; no match, check next command
|
|
BPL S1 ; keep trying until we've checked them all
|
|
; then fall through to error handler
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; handle error
|
|
ERROR LDY #MSG3-MSGBAS ; display "?" to indicate error and go to new line
|
|
JSR SNDMSG
|
|
JMP STRT ; back to main loop
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; dispatch command
|
|
S2 CPX #$13 ; last 3 commands in table are load/save/validate
|
|
BCS LSV ; which are handled by the same subroutine
|
|
CPX #$0F ; next 4 commands are base conversions
|
|
BCS CNVLNK ; which are handled by the same subroutine
|
|
TXA ; remaining commands dispatch through vector table
|
|
ASL A ; multiply index of command by 2
|
|
TAX ; since table contains 2-byte addresses
|
|
LDA KADDR+1,X ; push address from vector table onto stack
|
|
PHA ; so that the RTS from GETPAR will jump there
|
|
LDA KADDR,X
|
|
PHA
|
|
JMP GETPAR ; get the first parameter for the command
|
|
LSV STA SAVY ; handle load/save/validate
|
|
JMP LD
|
|
CNVLNK JMP CONVRT ; handle base conversion
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; exit monitor [X]
|
|
EXIT JMP ($A002) ; jump to warm-start vector to reinitialize BASIC
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; display memory [M]
|
|
DSPLYM BCS DSPM11 ; start from previous end addr if no address given
|
|
JSR COPY12 ; save start address in TMP2
|
|
JSR GETPAR ; get end address in TMP0
|
|
BCC DSMNEW ; did user specify one?
|
|
DSPM11 LDA #$0B ; if not, show 12 lines by default
|
|
STA TMP0
|
|
BNE DSPBYT ; always true, but BNE uses 1 byte less than JMP
|
|
DSMNEW JSR SUB12 ; end addr given, calc bytes between start and end
|
|
BCC MERROR ; error if start is after end
|
|
LDX #3 ; divide by 8 (shift right 3 times)
|
|
DSPM01 LSR TMP0+1
|
|
ROR TMP0
|
|
DEX
|
|
BNE DSPM01
|
|
DSPBYT JSR STOP ; check for stop key
|
|
BEQ DSPMX ; exit early if pressed
|
|
JSR DISPMEM ; display 1 line containing 8 bytes
|
|
LDA #8 ; increase start address by 8 bytes
|
|
JSR BUMPAD2
|
|
JSR SUBA1 ; decrement line counter
|
|
BCS DSPBYT ; show another line until it's < 0
|
|
DSPMX JMP STRT ; back to main loop
|
|
MERROR JMP ERROR ; handle error
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; alter registers [;]
|
|
ALTR JSR COPY1P ; store first parameter in PC
|
|
LDY #0 ; init counter
|
|
ALTR1 JSR GETPAR ; get value for next register
|
|
BCS ALTRX ; exit early if no more values given
|
|
LDA TMP0 ; store in memory, offset from SR
|
|
STA SR,Y ; these locations will be transferred to the
|
|
INY ; actual registers before exiting the monitor
|
|
CPY #$05 ; have we updated all 5 yet?
|
|
BCC ALTR1 ; if not, get next
|
|
ALTRX JMP STRT ; back to main loop
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; alter memory [>]
|
|
ALTM BCS ALTMX ; exit if no parameter provided
|
|
JSR COPY12 ; copy parameter to start address
|
|
LDY #0
|
|
ALTM1 JSR GETPAR ; get value for next byte of memory
|
|
BCS ALTMX ; if none given, exit early
|
|
LDA TMP0 ; poke value into memory at start address + Y
|
|
STA (TMP2),Y
|
|
INY ; next byte
|
|
CPY #8 ; have we read 8 bytes yet?
|
|
BCC ALTM1 ; if not, read the next one
|
|
ALTMX LDA #$91 ; move cursor up
|
|
JSR CHROUT
|
|
JSR DISPMEM ; re-display line to make ascii match hex
|
|
JMP STRT ; back to main loop
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; goto (run) [G]
|
|
GOTO LDX SP ; load stack pointer from memory
|
|
TXS ; save in SP register
|
|
GOTO2 JSR COPY1P ; copy provided address to PC
|
|
SEI ; disable interrupts
|
|
LDA PCH ; push PC high byte on stack
|
|
PHA
|
|
LDA PCL ; push PC low byte on stack
|
|
PHA
|
|
LDA SR ; push status byte on stack
|
|
PHA
|
|
LDA ACC ; load accumulator from memory
|
|
LDX XR ; load X from memory
|
|
LDY YR ; load Y from memory
|
|
RTI ; return from interrupt (pops PC and SR)
|
|
|
|
; jump to subroutine [J]
|
|
JSUB LDX SP ; load stack pointer from memory
|
|
TXS ; save value in SP register
|
|
JSR GOTO2 ; same as goto command
|
|
STY YR ; save Y to memory
|
|
STX XR ; save X to memory
|
|
STA ACC ; save accumulator to memory
|
|
PHP ; push processor status on stack
|
|
PLA ; pull processor status into A
|
|
STA SR ; save processor status to memory
|
|
JMP DSPLYR ; display registers
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; display 8 bytes of memory
|
|
DISPMEM JSR CRLF ; new line
|
|
LDA #">" ; prefix > so memory can be edited in place
|
|
JSR CHROUT
|
|
JSR SHOWAD ; show address of first byte on line
|
|
LDY #0
|
|
BEQ DMEMGO ; SHOWAD already printed a space after the address
|
|
DMEMLP JSR SPACE ; print space between bytes
|
|
DMEMGO LDA (TMP2),Y ; load byte from start address + Y
|
|
JSR WRTWO ; output hex digits for byte
|
|
INY ; next byte
|
|
CPY #8 ; have we output 8 bytes yet?
|
|
BCC DMEMLP ; if not, output next byte
|
|
LDY #MSG5-MSGBAS ; if so, output : and turn on reverse video
|
|
JSR SNDMSG ; before displaying ascii representation
|
|
LDY #0 ; back to first byte in line
|
|
DCHAR LDA (TMP2),Y ; load byte at start address + Y
|
|
TAX ; stash in X
|
|
AND #$BF ; clear 6th bit
|
|
CMP #$22 ; is it a quote (")?
|
|
BEQ DDOT ; if so, print . instead
|
|
TXA ; if not, restore character
|
|
AND #$7F ; clear top bit
|
|
CMP #$20 ; is it a printable character (>= $20)?
|
|
TXA ; restore character
|
|
BCS DCHROK ; if printable, output character
|
|
DDOT LDA #$2E ; if not, output '.' instaed
|
|
DCHROK JSR CHROUT
|
|
INY ; next byte
|
|
CPY #8 ; have we output 8 bytes yet?
|
|
BCC DCHAR ; if not, output next byte
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; compare memory [C]
|
|
COMPAR LDA #0 ; bit 7 clear signals compare
|
|
.BYTE $2C ; absolute BIT opcode consumes next word (LDA #$80)
|
|
|
|
; transfer memory [T]
|
|
TRANS LDA #$80 ; bit 7 set signals transfer
|
|
STA SAVY ; save compare/transfer flag in SAVY
|
|
LDA #0 ; assume we're counting up (bit 7 clear)
|
|
STA UPFLG ; save direction flag
|
|
JSR GETDIF ; get two addresses and calculate difference
|
|
; TMP2 = source start
|
|
; STASH = source end
|
|
; STORE = length
|
|
BCS TERROR ; carry set indicates error
|
|
JSR GETPAR ; get destination address in TMP0
|
|
BCC TOKAY ; carry set indicates error
|
|
TERROR JMP ERROR ; handle error
|
|
TOKAY BIT SAVY ; transfer or compare?
|
|
BPL COMPAR1 ; high bit clear indicates compare
|
|
LDA TMP2 ; if it's a transfer, we must take steps
|
|
CMP TMP0 ; to avoid overwriting the source bytes before
|
|
LDA TMP2+1 ; they have been transferred
|
|
SBC TMP0+1 ; compare source (TMP2) to destination (TMP0)
|
|
BCS COMPAR1 ; and count up if source is before than desitnation
|
|
LDA STORE ; otherwise, start at end and count down...
|
|
ADC TMP0 ; add length (STORE) to desintation (TMP0)
|
|
STA TMP0 ; to calculate end of destination
|
|
LDA STORE+1
|
|
ADC TMP0+1
|
|
STA TMP0+1
|
|
LDX #1 ; change source pointer from beginning to end
|
|
TDOWN LDA STASH,X ; TMP2 = source end (STASH)
|
|
STA TMP2,X
|
|
DEX
|
|
BPL TDOWN
|
|
LDA #$80 ; high bit set in UPFLG means count down
|
|
STA UPFLG
|
|
COMPAR1 JSR CRLF ; new line
|
|
LDY #0 ; no offset from pointer
|
|
TCLOOP JSR STOP ; check for stop key
|
|
BEQ TEXIT ; exit if pressed
|
|
LDA (TMP2),Y ; load byte from source
|
|
BIT SAVY ; transfer or compare?
|
|
BPL COMPAR2 ; skip store if comparing
|
|
STA (TMP0),Y ; otherwise, store in destination
|
|
COMPAR2 CMP (TMP0),Y ; compare to destination
|
|
BEQ TMVAD ; don't show address if equal
|
|
JSR SHOWAD ; show address
|
|
TMVAD BIT UPFLG ; counting up or down?
|
|
BMI TDECAD ; high bit set means we're counting down
|
|
INC TMP0 ; increment destination low byte
|
|
BNE TINCOK
|
|
INC TMP0+1 ; carry to high byte if necessary
|
|
BNE TINCOK
|
|
JMP ERROR ; error if high byte overflowed
|
|
TDECAD JSR SUBA1 ; decrement destination (TMP0)
|
|
JSR SUB21 ; decrement source (TMP2)
|
|
JMP TMOR
|
|
TINCOK JSR ADDA2 ; increment source (TMP2)
|
|
TMOR JSR SUB13 ; decrement length
|
|
BCS TCLOOP ; loop until length is 0
|
|
TEXIT JMP STRT ; back to main loop
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; hunt memory [H]
|
|
HUNT JSR GETDIF ; get start (TMP2) and end (TMP0) of haystack
|
|
BCS HERROR ; carry indicates error
|
|
LDY #0
|
|
JSR GETCHR ; get a single character
|
|
CMP #"'" ; is it a single quote?
|
|
BNE NOSTRH ; if not, input needle as hex bytes
|
|
JSR GETCHR ; if so, input needle as string
|
|
CMP #0
|
|
BEQ HERROR ; error if needle isn't at least one byte
|
|
HPAR STA STAGE,Y ; save char in staging area
|
|
INY
|
|
JSR GETCHR ; get another char
|
|
BEQ HTGO ; if it's null start searching
|
|
CPY #ESTAGE-STAGE ; have we filled up the needle staging area?
|
|
BNE HPAR ; if not, get another character
|
|
BEQ HTGO ; if so, start searching
|
|
NOSTRH JSR RDPAR ; read hex bytes if string not indicated
|
|
HLP LDA TMP0 ; save last read byte in staging area
|
|
STA STAGE,Y
|
|
INY ; get another hex byte
|
|
JSR GETPAR
|
|
BCS HTGO ; if there is none, start searching
|
|
CPY #ESTAGE-STAGE ; have we filled up the needle staging area?
|
|
BNE HLP ; if not, get another byte
|
|
HTGO STY SAVY ; save length of needle
|
|
JSR CRLF ; new line
|
|
HSCAN LDY #0
|
|
HLP3 LDA (TMP2),Y ; get first byte in haystack
|
|
CMP STAGE,Y ; compare it to first byte of needle
|
|
BNE HNOFT ; if it doesn't match, we haven't found anything
|
|
INY ; if it does, check the next byte
|
|
CPY SAVY ; have we reached the end of the needle?
|
|
BNE HLP3 ; if not, keep comparing bytes
|
|
JSR SHOWAD ; match found, show address
|
|
HNOFT JSR STOP ; no match, check for stop key
|
|
BEQ HEXIT ; exit prematurely if pressed
|
|
JSR ADDA2 ; increment haystack pointer
|
|
JSR SUB13 ; decrement haystack length
|
|
BCS HSCAN ; still more haystack? keep searching
|
|
HEXIT JMP STRT ; back to main loop
|
|
HERROR JMP ERROR ; handle error
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; load, save, or verify [LSV]
|
|
LD LDY #1 ; default to reading from tape, device #1
|
|
STY FA
|
|
STY SADD ; default to secondary address #1
|
|
DEY
|
|
STY FNLEN ; start with an empty filename
|
|
STY SATUS ; clear status
|
|
LDA #>STAGE ; set filename pointer to staging buffer
|
|
STA FNADR+1
|
|
LDA #<STAGE
|
|
STA FNADR
|
|
L1 JSR GETCHR ; get a character
|
|
BEQ LSHORT ; no filename given, try load or verify from tape
|
|
CMP #$20 ; skip leading spaces
|
|
BEQ L1
|
|
CMP #$22 ; error if filename doesn't start with a quote
|
|
BNE LERROR
|
|
LDX CHRPNT ; load current char pointer into index reg
|
|
L3 LDA INBUFF,X ; load current char from buffer to accumulator
|
|
BEQ LSHORT ; no filename given, try load or verify from tape
|
|
INX ; next char
|
|
CMP #$22 ; is it a quote?
|
|
BEQ L8 ; if so, we've reached the end of the filename
|
|
STA (FNADR),Y ; if not, save character in filename buffer
|
|
INC FNLEN ; increment filename length
|
|
INY
|
|
CPY #ESTAGE-STAGE ; check whether buffer is full
|
|
BCC L3 ; if not, get another character
|
|
LERROR JMP ERROR ; if so, handle error
|
|
L8 STX CHRPNT ; set character pointer to the current index
|
|
JSR GETCHR ; eat separator between filename and device #
|
|
BEQ LSHORT ; no separator, try to load or verify from tape
|
|
JSR GETPAR ; get device number
|
|
BCS LSHORT ; no device # given, try load or verify from tape
|
|
LDA TMP0 ; set device number for kernal routines
|
|
STA FA
|
|
JSR GETPAR ; get start address for load or save in TMP0
|
|
BCS LSHORT ; no start address, try to load or verify
|
|
JSR COPY12 ; transfer start address to TMP2
|
|
JSR GETPAR ; get end address for save in TMP0
|
|
BCS LDADDR ; no end address, try to load to given start addr
|
|
JSR CRLF ; new line
|
|
LDX TMP0 ; put low byte of end address in X
|
|
LDY TMP0+1 ; put high byte of end address in Y
|
|
LDA SAVY ; confirm that we're doing a save
|
|
CMP #"S"
|
|
BNE LERROR ; if not, error due to too many params
|
|
LDA #0
|
|
STA SADD ; set secondary address to 0
|
|
LDA #TMP2 ; put addr of zero-page pointer to data in A
|
|
JSR SAVE ; call kernal save routine
|
|
LSVXIT JMP STRT ; back to mainloop
|
|
LSHORT LDA SAVY ; check which command we received
|
|
CMP #"V"
|
|
BEQ LOADIT ; we're doing a verify so don't set A to 0
|
|
CMP #"L"
|
|
BNE LERROR ; error due to not enough params for save
|
|
LDA #0 ; 0 in A signals load, anything else is verify
|
|
LOADIT JSR LOAD ; call kernal load routine
|
|
LDA SATUS ; get i/o status
|
|
AND #$10 ; check bit 5 for checksum error
|
|
BEQ LSVXIT ; if no error go back to mainloop
|
|
LDA SAVY ; ?? not sure what these two lines are for...
|
|
BEQ LERROR ; ?? SAVY will never be 0, so why check?
|
|
LDY #MSG6-MSGBAS ; display "ERROR" if checksum didn't match
|
|
JSR SNDMSG
|
|
JMP STRT ; back to mainloop
|
|
LDADDR LDX TMP2 ; load address low byte in X
|
|
LDY TMP2+1 ; load address high byte in Y
|
|
LDA #0 ; 0 in A signals load
|
|
STA SADD ; secondary addr 0 means load to addr in X and Y
|
|
BEQ LSHORT ; execute load
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; fill memory [F]
|
|
FILL JSR GETDIF ; start in TMP2, end in STASH, length in STORE
|
|
BCS AERROR ; carry set indicates error
|
|
JSR GETPAR ; get value to fill in TMP0
|
|
BCS AERROR ; carry set indicates error
|
|
JSR GETCHR ; any more characters triggers an error
|
|
BNE AERROR
|
|
LDY #0 ; no offset
|
|
FILLP LDA TMP0 ; load value to fill in accumulator
|
|
STA (TMP2),Y ; store fill value in current address
|
|
JSR STOP ; check for stop key
|
|
BEQ FSTART ; if pressed, back to main loop
|
|
JSR ADDA2 ; increment address
|
|
JSR SUB13 ; decrement length
|
|
BCS FILLP ; keep going until length reaches 0
|
|
FSTART JMP STRT ; back to main loop
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; assemble [A.]
|
|
|
|
; read in mnemonic
|
|
ASSEM BCS AERROR ; error if no address given
|
|
JSR COPY12 ; copy address to TMP2
|
|
AGET1 LDX #0
|
|
STX U0AA0+1 ; clear byte that mnemonic gets shifted into
|
|
STX DIGCNT ; clear digit count
|
|
AGET2 JSR GETCHR ; get a char
|
|
BNE ALMOR ; proceed if the character isn't null
|
|
CPX #0 ; it's null, have read a mnemonic yet?
|
|
BEQ FSTART ; if not, silently go back to main loop
|
|
ALMOR CMP #$20 ; skip leading spaces
|
|
BEQ AGET1
|
|
STA MNEMW,X ; put character in mnemonic buffer
|
|
INX
|
|
CPX #3 ; have we read 3 characters yet?
|
|
BNE AGET2 ; if not, get next character
|
|
|
|
; compress mnemonic into two bytes
|
|
ASQEEZ DEX ; move to previous char
|
|
BMI AOPRND ; if we're done with mnemonic, look for operand
|
|
LDA MNEMW,X ; get current character
|
|
SEC ; pack 3-letter mnemonic into 2 bytes (15 bits)
|
|
SBC #$3F ; subtract $3F from ascii code so A-Z = 2 to 27
|
|
LDY #$05 ; letters now fit in 5 bits; shift them out
|
|
ASHIFT LSR A ; into the first two bytes of the inst buffer
|
|
ROR U0AA0+1 ; catch the low bit from accumulator in right byte
|
|
ROR U0AA0 ; catch the low bit from right byte in left byte
|
|
DEY ; count down bits
|
|
BNE ASHIFT ; keep looping until we reach zero
|
|
BEQ ASQEEZ ; unconditional branch to handle next char
|
|
AERROR JMP ERROR ; handle error
|
|
|
|
; parse operand
|
|
AOPRND LDX #2 ; mnemonic is in first two bytes so start at third
|
|
ASCAN LDA DIGCNT ; did we find address digits last time?
|
|
BNE AFORM1 ; if so, look for mode chars
|
|
JSR RDVAL ; otherwise, look for an address
|
|
BEQ AFORM0 ; we didn't find an address, look for characters
|
|
BCS AERROR ; carry flag indicates error
|
|
LDA #"$"
|
|
STA U0AA0,X ; prefix addresses with $
|
|
INX ; next position in buffer
|
|
LDY #4 ; non-zero page addresses are 4 hex digits
|
|
LDA NUMBIT ; check numeric base in which address was given
|
|
CMP #8 ; for addresses given in octal or binary
|
|
BCC AADDR ; use only the high byte to determine page
|
|
CPY DIGCNT ; for decimal or hex, force non-zero page addressing
|
|
BEQ AFILL0 ; if address was given with four digits or more
|
|
AADDR LDA TMP0+1 ; check whether high byte of address is zero
|
|
BNE AFILL0 ; non-zero high byte means we're not in zero page
|
|
LDY #2 ; if it's in zero page, addr is 2 hex digits
|
|
AFILL0 LDA #$30 ; use 0 as placeholder for each hex digit in addr
|
|
AFIL0L STA U0AA0,X ; put placeholder in assembly buffer
|
|
INX ; move to next byte in buffer
|
|
DEY ; decrement number of remaining digits
|
|
BNE AFIL0L ; loop until all digits have been placed
|
|
AFORM0 DEC CHRPNT ; non-numeric input; back 1 char to see what it was
|
|
AFORM1 JSR GETCHR ; get next character
|
|
BEQ AESCAN ; if there is none, we're finished scanning
|
|
CMP #$20 ; skip spaces
|
|
BEQ ASCAN
|
|
STA U0AA0,X ; store character in assembly buffer
|
|
INX ; move to next byte in buffer
|
|
CPX #U0AAE-U0AA0 ; is instruction buffer full?
|
|
BCC ASCAN ; if not, keep scanning
|
|
BCS AERROR ; error if buffer is full
|
|
|
|
; find matching opcode
|
|
AESCAN STX STORE ; save number of bytes in assembly buffer
|
|
LDX #0 ; start at opcode $00 and check every one until
|
|
STX OPCODE ; we find one that matches our criteria
|
|
ATRYOP LDX #0
|
|
STX U9F ; reset index into work buffer
|
|
LDA OPCODE
|
|
JSR INSTXX ; look up instruction format for current opcode
|
|
LDX ACMD ; save addressing command for later
|
|
STX STORE+1
|
|
TAX ; use current opcode as index
|
|
LDA MNEMR,X ; check right byte of compressed mnemonic
|
|
JSR CHEKOP
|
|
LDA MNEML,X ; check left byte of compressed mnemonic
|
|
JSR CHEKOP
|
|
LDX #6 ; 6 possible characters to check against operand
|
|
TRYIT CPX #3 ; are we on character 3?
|
|
BNE TRYMOD ; if not, check operand characters
|
|
LDY LENGTH ; otherwise, check number of bytes in operand
|
|
BEQ TRYMOD ; if zero, check operand characters
|
|
TRYAD LDA ACMD ; otherwise, look for an address
|
|
CMP #$E8 ; special case for relative addressing mode
|
|
; since it's specified with 4 digits in assembly
|
|
; but encoded with only 1 byte in object code
|
|
LDA #$30 ; '0' is the digit placeholder we're looking for
|
|
BCS TRY4B ; ACMD >= $E8 indicates relative addressing
|
|
JSR CHEK2B ; ACMD < $E8 indicates normal addressing
|
|
DEY ; consume byte
|
|
BNE TRYAD ; check for 2 more digits if not zero-page
|
|
TRYMOD ASL ACMD ; shift a bit out of the addressing command
|
|
BCC UB4DF ; if it's zero, skip checking current character
|
|
LDA CHAR1-1,X
|
|
JSR CHEKOP ; otherwise first character against operand
|
|
LDA CHAR2-1,X ; get second character to check
|
|
BEQ UB4DF ; if it's zero, skip checking it
|
|
JSR CHEKOP ; otherwise check it against hte operand
|
|
UB4DF DEX ; move to next character
|
|
BNE TRYIT ; repeat tests
|
|
BEQ TRYBRAN
|
|
TRY4B JSR CHEK2B ; check for 4 digit address placeholder
|
|
JSR CHEK2B ; by checking for 2 digits twice
|
|
TRYBRAN LDA STORE ; get number of bytes in assembly buffer
|
|
CMP U9F ; more bytes left to check?
|
|
BEQ ABRAN ; if not, we've found a match; build instruction
|
|
JMP BUMPOP ; if so, this opcode doesn't match; try the next
|
|
|
|
; convert branches to relative address
|
|
ABRAN LDY LENGTH ; get number of bytes in operand
|
|
BEQ A1BYTE ; if none, just output the opcode
|
|
LDA STORE+1 ; otherwise check the address format
|
|
CMP #$9D ; is it a relative branch?
|
|
BNE OBJPUT ; if not, skip relative branch calculation
|
|
LDA TMP0 ; calculate the difference between the current
|
|
SBC TMP2 ; address and the branch target (low byte)
|
|
TAX ; save it in X
|
|
LDA TMP0+1 ; borrow from the high byte if necessary
|
|
SBC TMP2+1
|
|
BCC ABBACK ; if result is negative, we're branching back
|
|
BNE SERROR ; high bytes must be equal when branching forward
|
|
CPX #$82 ; difference between low bytes must be < 130
|
|
BCS SERROR ; error if the address is too far away
|
|
BCC ABRANX
|
|
ABBACK TAY ; when branching backward high byte of target must
|
|
INY ; be 1 less than high byte of current address
|
|
BNE SERROR ; if not, it's too far away
|
|
CPX #$82 ; difference between low bytes must be < 130
|
|
BCC SERROR ; if not, it's too far away
|
|
ABRANX DEX ; adjust branch target relative to the
|
|
DEX ; instruction following this one
|
|
TXA
|
|
LDY LENGTH ; load length of operand
|
|
BNE OBJP2 ; don't use the absolute address
|
|
|
|
; assemble machine code
|
|
OBJPUT LDA TMP0-1,Y ; get the operand
|
|
OBJP2 STA (TMP2),Y ; store it after the opcode
|
|
DEY
|
|
BNE OBJPUT ; copy the other byte of operand if there is one
|
|
A1BYTE LDA OPCODE ; put opcode into instruction
|
|
STA (TMP2),Y
|
|
JSR CRLF ; carriage return
|
|
LDA #$91 ; back up one line
|
|
JSR CHROUT
|
|
LDY #MSG7-MSGBAS ; "A " prefix
|
|
JSR SNDCLR ; clear line
|
|
JSR DISLIN ; disassemble the instruction we just assembled
|
|
INC LENGTH ; instruction length = operand length + 1 byte
|
|
LDA LENGTH ; for the opcode
|
|
JSR BUMPAD2 ; increment address by length of instruction
|
|
LDA #"A" ; stuff keyboard buffer with next assemble command:
|
|
STA KEYD ; "A XXXX " where XXXX is the next address
|
|
LDA #" " ; after the previously assembled instruction
|
|
STA KEYD+1
|
|
STA KEYD+6
|
|
LDA TMP2+1 ; convert high byte of next address to hex
|
|
JSR ASCTWO
|
|
STA KEYD+2 ; put it in the keyboard buffer
|
|
STX KEYD+3
|
|
LDA TMP2 ; convert low byte of next address to hex
|
|
JSR ASCTWO
|
|
STA KEYD+4 ; put it in the keyboard buffer
|
|
STX KEYD+5
|
|
LDA #7 ; set number of chars in keyboard buffer
|
|
STA NDX
|
|
JMP STRT ; back to main loop
|
|
SERROR JMP ERROR ; handle error
|
|
|
|
; check characters in operand
|
|
CHEK2B JSR CHEKOP ; check two bytes against value in accumulator
|
|
CHEKOP STX SAVX ; stash X
|
|
LDX U9F ; get current index into work buffer
|
|
CMP U0AA0,X ; check whether this opcode matches the buffer
|
|
BEQ OPOK ; matching so far, check the next criteria
|
|
PLA ; didn't match, so throw away return address
|
|
PLA ; on the stack because we're starting over
|
|
BUMPOP INC OPCODE ; check the next opcode
|
|
BEQ SERROR ; error if we tried every opcode and none fit
|
|
JMP ATRYOP ; start over with new opcode
|
|
OPOK INC U9F ; opcode matches so far; check the next criteria
|
|
LDX SAVX ; restore X
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; disassemble [D]
|
|
DISASS BCS DIS0AD ; if no address was given, start from last address
|
|
JSR COPY12 ; copy start address to TMP2
|
|
JSR GETPAR ; get end address in TMP0
|
|
BCC DIS2AD ; if one was given, skip default
|
|
DIS0AD LDA #$14 ; disassemble 14 bytes by default
|
|
STA TMP0 ; store length in TMP0
|
|
BNE DISGO ; skip length calculation
|
|
DIS2AD JSR SUB12 ; calculate number of bytes between start and end
|
|
BCC DERROR ; error if end address is before start address
|
|
DISGO JSR CLINE ; clear the current line
|
|
JSR STOP ; check for stop key
|
|
BEQ DISEXIT ; exit early if pressed
|
|
JSR DSOUT1 ; output disassembly prefix ". "
|
|
INC LENGTH
|
|
LDA LENGTH ; add length of last instruction to start address
|
|
JSR BUMPAD2
|
|
LDA LENGTH ; subtract length of last inst from end address
|
|
JSR SUBA2
|
|
BCS DISGO
|
|
DISEXIT JMP STRT ; back to mainloop
|
|
DERROR JMP ERROR
|
|
|
|
DSOUT1 LDA #"." ; output ". " prefix to allow edit and reassemble
|
|
JSR CHROUT
|
|
JSR SPACE
|
|
|
|
DISLIN JSR SHOWAD ; show the address of the instruction
|
|
JSR SPACE ; insert a space
|
|
LDY #0 ; no offset
|
|
LDA (TMP2),Y ; load operand of current instruction
|
|
JSR INSTXX ; get mnemonic and addressing mode for opcode
|
|
PHA ; save index into mnemonic table
|
|
LDX LENGTH ; get length of operand
|
|
INX ; add 1 byte for opcode
|
|
DSBYT DEX ; decrement index
|
|
BPL DSHEX ; show hex for byte being disassembled
|
|
STY SAVY ; save index
|
|
LDY #MSG8-MSGBAS ; skip 3 spaces
|
|
JSR SNDMSG
|
|
LDY SAVY ; restore index
|
|
JMP NXBYT
|
|
DSHEX LDA (TMP2),Y ; show hex for byte
|
|
JSR WRBYTE
|
|
|
|
NXBYT INY ; next byte
|
|
CPY #3 ; have we output 3 bytes yet?
|
|
BCC DSBYT ; if not, loop
|
|
PLA ; restore index into mnemonic table
|
|
LDX #3 ; 3 letters in mnemonic
|
|
JSR PROPXX ; print mnemonic
|
|
LDX #6 ; 6 possible address mode character combos
|
|
PRADR1 CPX #3 ; have we checked the third combo yet?
|
|
BNE PRADR3 ; if so, output the leading characters
|
|
LDY LENGTH ; get the length of the operand
|
|
BEQ PRADR3 ; if it's zero, there's no operand to print
|
|
PRADR2 LDA ACMD ; otherwise, get the addressing mode
|
|
CMP #$E8 ; check for relative addressing
|
|
PHP ; save result of check
|
|
LDA (TMP2),Y ; get the operand
|
|
PLP ; restore result of check
|
|
BCS RELAD ; handle a relative address
|
|
JSR WRTWO ; output digits from address
|
|
DEY
|
|
BNE PRADR2 ; repeat for next byte of operand, if there is one
|
|
PRADR3 ASL ACMD ; check whether addr mode uses the current char
|
|
BCC PRADR4 ; if not, skip it
|
|
LDA CHAR1-1,X ; look up the first char in the table
|
|
JSR CHROUT ; print first char
|
|
LDA CHAR2-1,X ; look up the second char in the table
|
|
BEQ PRADR4 ; if there's no second character, skip it
|
|
JSR CHROUT ; print second char
|
|
PRADR4 DEX ; next potential address mode character
|
|
BNE PRADR1 ; loop if we haven't checked them all yet
|
|
RTS ; back to caller
|
|
RELAD JSR UB64D ; calculate absolute address from relative
|
|
CLC
|
|
ADC #1 ; adjust address relative to next instruction
|
|
BNE RELEND ; don't increment high byte unless we overflowed
|
|
INX ; increment high byte
|
|
RELEND JMP WRADDR ; print address
|
|
|
|
UB64D LDX TMP2+1 ; get high byte of current address
|
|
TAY ; is relative address positive or negative?
|
|
BPL RELC2 ; if positive, leave high byte alone
|
|
DEX ; if negative, decrement high byte
|
|
RELC2 ADC TMP2 ; add relative address to low byte
|
|
BCC RELC3 ; if there's no carry, we're done
|
|
INX ; if there's a carry, increment the high byte
|
|
RELC3 RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; get opcode mode and length
|
|
|
|
; Note: the labels are different, but the code of this subroutine is almost
|
|
; identical to the INSDS2 subroutine of the Apple Mini-Assembler on page 78 of
|
|
; the Apple II Red Book. I'm not sure exactly where this code originated
|
|
; (MOS or Apple) but it's clear that this part of Supermon64 and the
|
|
; Mini-Asssembler share a common heritage. The comments showing the way the
|
|
; opcodes are transformed into indexes for the mnemonic lookup table come
|
|
; from the Mini-Assembler source.
|
|
|
|
INSTXX TAY ; stash opcode in accumulator in Y for later
|
|
LSR A ; is opcode even or odd?
|
|
BCC IEVEN
|
|
LSR A
|
|
BCS ERR ; invalid opcodes XXXXXX11
|
|
CMP #$22
|
|
BEQ ERR ; invalid opcode 10001001
|
|
AND #$07 ; mask bits to 10000XXX
|
|
ORA #$80
|
|
IEVEN LSR A ; LSB determines whether to use left/right nybble
|
|
TAX ; get format index using remaining high bytes
|
|
LDA MODE,X
|
|
BCS RTMODE ; look at left or right nybble based on carry bit
|
|
LSR A ; if carry = 0, use left nybble
|
|
LSR A
|
|
LSR A
|
|
LSR A
|
|
RTMODE AND #$0F ; if carry = 1, use right nybble
|
|
BNE GETFMT
|
|
ERR LDY #$80 ; substitute 10000000 for invalid opcodes
|
|
LDA #0
|
|
GETFMT TAX
|
|
LDA MODE2,X ; lookup operand format using selected nybble
|
|
STA ACMD ; save for later use
|
|
AND #$03 ; lower 2 bits indicate number of bytes in operand
|
|
STA LENGTH
|
|
TYA ; restore original opcode
|
|
AND #$8F ; mask bits to X000XXXX
|
|
TAX ; save it
|
|
TYA ; restore original opcode
|
|
LDY #3
|
|
CPX #$8A ; check if opcode = 1XXX1010
|
|
BEQ GTFM4
|
|
GTFM2 LSR A ; transform opcode into index for mnemonic table
|
|
BCC GTFM4
|
|
LSR A ; opcodes transformed as follows:
|
|
GTFM3 LSR A ; 1XXX1010->00101XXX
|
|
ORA #$20 ; XXXYYY01->00111XXX
|
|
DEY ; XXXYYY10->00111XXX
|
|
BNE GTFM3 ; XXXYY100->00110XXX
|
|
INY ; XXXXX000->000XXXXX
|
|
GTFM4 DEY
|
|
BNE GTFM2
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; extract and print packed mnemonics
|
|
PROPXX TAY ; use index in accumulator to look up mnemonic
|
|
LDA MNEML,Y ; and place a temporary copy in STORE
|
|
STA STORE
|
|
LDA MNEMR,Y
|
|
STA STORE+1
|
|
PRMN1 LDA #0 ; clear accumulator
|
|
LDY #$05 ; shift 5 times
|
|
PRMN2 ASL STORE+1 ; shift right byte
|
|
ROL STORE ; rotate bits from right byte into left byte
|
|
ROL A ; rotate bits from left byte into accumulator
|
|
DEY ; next bit
|
|
BNE PRMN2 ; loop until all bits shifted
|
|
ADC #$3F ; calculate ascii code for letter by adding to '?'
|
|
JSR CHROUT ; output letter
|
|
DEX ; next letter
|
|
BNE PRMN1 ; loop until all 3 letters are output
|
|
JMP SPACE ; output space
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; read parameters
|
|
RDPAR DEC CHRPNT ; back up one char
|
|
GETPAR JSR RDVAL ; read the value
|
|
BCS GTERR ; carry set indicates error
|
|
JSR GOTCHR ; check previous character
|
|
BNE CKTERM ; if it's not null, check if it's a valid separator
|
|
DEC CHRPNT ; back up one char
|
|
LDA DIGCNT ; get number of digits read
|
|
BNE GETGOT ; found some digits
|
|
BEQ GTNIL ; didn't find any digits
|
|
CKTERM CMP #$20 ; space or comma are valid separators
|
|
BEQ GETGOT ; anything else is an error
|
|
CMP #","
|
|
BEQ GETGOT
|
|
GTERR PLA ; encountered error
|
|
PLA ; get rid of command vector pushed on stack
|
|
JMP ERROR ; handle error
|
|
GTNIL SEC ; set carry to indicate no parameter found
|
|
.BYTE $24 ; BIT ZP opcode consumes next byte (CLC)
|
|
GETGOT CLC ; clear carry to indicate paremeter returned
|
|
LDA DIGCNT ; return number of digits in A
|
|
RTS ; return to address pushed from vector table
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; read a value in the specified base
|
|
RDVAL LDA #0 ; clear temp
|
|
STA TMP0
|
|
STA TMP0+1
|
|
STA DIGCNT ; clear digit counter
|
|
TXA ; save X and Y
|
|
PHA
|
|
TYA
|
|
PHA
|
|
RDVMOR JSR GETCHR ; get next character from input buffer
|
|
BEQ RDNILK ; null at end of buffer
|
|
CMP #$20 ; skip spaces
|
|
BEQ RDVMOR
|
|
LDX #3 ; check numeric base [$+&%]
|
|
GNMODE CMP HIKEY,X
|
|
BEQ GOTMOD ; got a match, set up base
|
|
DEX
|
|
BPL GNMODE ; check next base
|
|
INX ; default to hex
|
|
DEC CHRPNT ; back up one character
|
|
GOTMOD LDY MODTAB,X ; get base value
|
|
LDA LENTAB,X ; get bits per digit
|
|
STA NUMBIT ; store bits per digit
|
|
NUDIG JSR GETCHR ; get next char in A
|
|
RDNILK BEQ RDNIL ; end of number if no more characters
|
|
SEC
|
|
SBC #$30 ; subtract ascii value of 0 to get numeric value
|
|
BCC RDNIL ; end of number if character was less than 0
|
|
CMP #$0A
|
|
BCC DIGMOR ; not a hex digit if less than A
|
|
SBC #$07 ; 7 chars between ascii 9 and A, so subtract 7
|
|
CMP #$10 ; end of number if char is greater than F
|
|
BCS RDNIL
|
|
DIGMOR STA INDIG ; store the digit
|
|
CPY INDIG ; compare base with the digit
|
|
BCC RDERR ; error if the digit >= the base
|
|
BEQ RDERR
|
|
INC DIGCNT ; increment the number of digits
|
|
CPY #10
|
|
BNE NODECM ; skip the next part if not using base 10
|
|
LDX #1
|
|
DECLP1 LDA TMP0,X ; stash the previous 16-bit value for later use
|
|
STA STASH,X
|
|
DEX
|
|
BPL DECLP1
|
|
NODECM LDX NUMBIT ; number of bits to shift
|
|
TIMES2 ASL TMP0 ; shift 16-bit value by specified number of bits
|
|
ROL TMP0+1
|
|
BCS RDERR ; error if we overflowed 16 bits
|
|
DEX
|
|
BNE TIMES2 ; shift remaining bits
|
|
CPY #10
|
|
BNE NODEC2 ; skip the next part if not using base 10
|
|
ASL STASH ; shift the previous 16-bit value one bit left
|
|
ROL STASH+1
|
|
BCS RDERR ; error if we overflowed 16 bits
|
|
LDA STASH ; add shifted previous value to current value
|
|
ADC TMP0
|
|
STA TMP0
|
|
LDA STASH+1
|
|
ADC TMP0+1
|
|
STA TMP0+1
|
|
BCS RDERR ; error if we overflowed 16 bits
|
|
NODEC2 CLC
|
|
LDA INDIG ; load current digit
|
|
ADC TMP0 ; add current digit to low byte
|
|
STA TMP0 ; and store result back in low byte
|
|
TXA ; A=0
|
|
ADC TMP0+1 ; add carry to high byte
|
|
STA TMP0+1 ; and store result back in high byte
|
|
BCC NUDIG ; get next digit if we didn't overflow
|
|
RDERR SEC ; set carry to indicate error
|
|
.BYTE $24 ; BIT ZP opcode consumes next byte (CLC)
|
|
RDNIL CLC ; clear carry to indicate success
|
|
STY NUMBIT ; save base of number
|
|
PLA ; restore X and Y
|
|
TAY
|
|
PLA
|
|
TAX
|
|
LDA DIGCNT ; return number of digits in A
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; print address
|
|
SHOWAD LDA TMP2
|
|
LDX TMP2+1
|
|
|
|
WRADDR PHA ; save low byte
|
|
TXA ; put high byte in A
|
|
JSR WRTWO ; output high byte
|
|
PLA ; restore low byte
|
|
|
|
WRBYTE JSR WRTWO ; output byte in A
|
|
|
|
SPACE LDA #$20 ; output space
|
|
BNE FLIP
|
|
|
|
CHOUT CMP #$0D ; output char with special handling of CR
|
|
BNE FLIP
|
|
CRLF LDA #$0D ; load CR in A
|
|
BIT $13 ; check default channel
|
|
BPL FLIP ; if high bit is clear output CR only
|
|
JSR CHROUT ; otherwise output CR+LF
|
|
LDA #$0A ; output LF
|
|
FLIP JMP CHROUT
|
|
|
|
FRESH JSR CRLF ; output CR
|
|
LDA #$20 ; load space in A
|
|
JSR CHROUT
|
|
JMP SNCLR
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; output two hex digits for byte
|
|
WRTWO STX SAVX ; save X
|
|
JSR ASCTWO ; get hex chars for byte in X (lower) and A (upper)
|
|
JSR CHROUT ; output upper nybble
|
|
TXA ; transfer lower to A
|
|
LDX SAVX ; restore X
|
|
JMP CHROUT ; output lower nybble
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; convert byte in A to hex digits
|
|
ASCTWO PHA ; save byte
|
|
JSR ASCII ; do low nybble
|
|
TAX ; save in X
|
|
PLA ; restore byte
|
|
LSR A ; shift upper nybble down
|
|
LSR A
|
|
LSR A
|
|
LSR A
|
|
|
|
; convert low nybble in A to hex digit
|
|
ASCII AND #$0F ; clear upper nibble
|
|
CMP #$0A ; if less than A, skip next step
|
|
BCC ASC1
|
|
ADC #6 ; skip ascii chars between 9 and A
|
|
ASC1 ADC #$30 ; add ascii char 0 to value
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; get prev char from input buffer
|
|
GOTCHR DEC CHRPNT
|
|
|
|
; get next char from input buffer
|
|
GETCHR STX SAVX
|
|
LDX CHRPNT ; get pointer to next char
|
|
LDA INBUFF,X ; load next char in A
|
|
BEQ NOCHAR ; null, :, or ? signal end of buffer
|
|
CMP #":"
|
|
BEQ NOCHAR
|
|
CMP #"?"
|
|
NOCHAR PHP
|
|
INC CHRPNT ; next char
|
|
LDX SAVX
|
|
PLP ; Z flag will signal last character
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; copy TMP0 to TMP2
|
|
COPY12 LDA TMP0 ; low byte
|
|
STA TMP2
|
|
LDA TMP0+1 ; high byte
|
|
STA TMP2+1
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; subtract TMP2 from TMP0
|
|
SUB12 SEC
|
|
LDA TMP0 ; subtract low byte
|
|
SBC TMP2
|
|
STA TMP0
|
|
LDA TMP0+1
|
|
SBC TMP2+1 ; subtract high byte
|
|
STA TMP0+1
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; subtract from TMP0
|
|
SUBA1 LDA #1 ; shortcut to decrement by 1
|
|
SUBA2 STA SAVX ; subtrahend in accumulator
|
|
SEC
|
|
LDA TMP0 ; minuend in low byte
|
|
SBC SAVX
|
|
STA TMP0
|
|
LDA TMP0+1 ; borrow from high byte
|
|
SBC #0
|
|
STA TMP0+1
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; subtract 1 from STORE
|
|
SUB13 SEC
|
|
LDA STORE
|
|
SBC #1 ; decrement low byte
|
|
STA STORE
|
|
LDA STORE+1
|
|
SBC #0 ; borrow from high byte
|
|
STA STORE+1
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; add to TMP2
|
|
ADDA2 LDA #1 ; shortcut to increment by 1
|
|
BUMPAD2 CLC
|
|
ADC TMP2 ; add value in accumulator to low byte
|
|
STA TMP2
|
|
BCC BUMPEX
|
|
INC TMP2+1 ; carry to high byte
|
|
BUMPEX RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; subtract 1 from TMP2
|
|
SUB21 SEC
|
|
LDA TMP2 ; decrement low byte
|
|
SBC #1
|
|
STA TMP2
|
|
LDA TMP2+1 ; borrow from high byte
|
|
SBC #0
|
|
STA TMP2+1
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; copy TMP0 to PC
|
|
COPY1P BCS CPY1PX ; do nothing if parameter is empty
|
|
LDA TMP0 ; copy low byte
|
|
LDY TMP0+1 ; copy high byte
|
|
STA PCL
|
|
STY PCH
|
|
CPY1PX RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; get start/end addresses and calc difference
|
|
GETDIF BCS GDIFX ; exit with error if no parameter given
|
|
JSR COPY12 ; save start address in TMP2
|
|
JSR GETPAR ; get end address in TMP0
|
|
BCS GDIFX ; exit with error if no parameter given
|
|
LDA TMP0 ; save end address in STASH
|
|
STA STASH
|
|
LDA TMP0+1
|
|
STA STASH+1
|
|
JSR SUB12 ; subtract start address from end address
|
|
LDA TMP0
|
|
STA STORE ; save difference in STORE
|
|
LDA TMP0+1
|
|
STA STORE+1
|
|
BCC GDIFX ; error if start address is after end address
|
|
CLC ; clear carry to indicate success
|
|
.BYTE $24 ; BIT ZP opcode consumes next byte (SEC)
|
|
GDIFX SEC ; set carry to indicate error
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; convert base [$+&%]
|
|
CONVRT JSR RDPAR ; read a parameter
|
|
JSR FRESH ; next line and clear
|
|
LDA #"$" ; output $ sigil for hex
|
|
JSR CHROUT
|
|
LDA TMP0 ; load the 16-bit value entered
|
|
LDX TMP0+1
|
|
JSR WRADDR ; print it in 4 hex digits
|
|
JSR FRESH
|
|
LDA #"+" ; output + sigil for decimal
|
|
JSR CHROUT
|
|
JSR CVTDEC ; convert to BCD using hardware mode
|
|
LDA #0 ; clear digit counter
|
|
LDX #6 ; max digits + 1
|
|
LDY #3 ; bits per digit - 1
|
|
JSR NMPRNT ; print result without leading zeros
|
|
JSR FRESH ; next line and clear
|
|
LDA #"&" ; print & sigil for octal
|
|
JSR CHROUT
|
|
LDA #0 ; clear digit counter
|
|
LDX #8 ; max digits + 1
|
|
LDY #2 ; bits per digit - 1
|
|
JSR PRINUM ; output number
|
|
JSR FRESH ; next line and clear
|
|
LDA #"%" ; print % sigil for binary
|
|
JSR CHROUT
|
|
LDA #0 ; clear digit counter
|
|
LDX #$18 ; max digits + 1
|
|
LDY #0 ; bits per digit - 1
|
|
JSR PRINUM ; output number
|
|
JMP STRT ; back to mainloop
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; convert binary to BCD
|
|
|
|
CVTDEC JSR COPY12 ; copy value from TMP0 to TMP2
|
|
LDA #0
|
|
LDX #2 ; clear 3 bytes in work buffer
|
|
DECML1 STA U0AA0,X
|
|
DEX
|
|
BPL DECML1
|
|
LDY #16 ; 16 bits in input
|
|
PHP ; save status register
|
|
SEI ; make sure no interrupts occur with BCD enabled
|
|
SED
|
|
DECML2 ASL TMP2 ; rotate bytes out of input low byte
|
|
ROL TMP2+1 ; .. into high byte and carry bit
|
|
LDX #2 ; process 3 bytes
|
|
DECDBL LDA U0AA0,X ; load current value of byte
|
|
ADC U0AA0,X ; add it to itself plus the carry bit
|
|
STA U0AA0,X ; store it back in the same location
|
|
DEX ; decrement byte counter
|
|
BPL DECDBL ; loop until all bytes processed
|
|
DEY ; decrement bit counter
|
|
BNE DECML2 ; loop until all bits processed
|
|
PLP ; restore processor status
|
|
RTS
|
|
|
|
; load the input value and fall through to print it
|
|
PRINUM PHA ; save accumulator
|
|
LDA TMP0 ; copy input low byte to work buffer
|
|
STA U0AA0+2
|
|
LDA TMP0+1 ; copy input high byte to work buffer
|
|
STA U0AA0+1
|
|
LDA #0 ; clear overflow byte in work buffer
|
|
STA U0AA0
|
|
PLA ; restore accumulator
|
|
|
|
; print number in specified base without leading zeros
|
|
NMPRNT STA DIGCNT ; number of digits in accumulator
|
|
STY NUMBIT ; bits per digit passed in Y register
|
|
DIGOUT LDY NUMBIT ; get bits to process
|
|
LDA #0 ; clear accumulator
|
|
ROLBIT ASL U0AA0+2 ; shift bits out of low byte
|
|
ROL U0AA0+1 ; ... into high byte
|
|
ROL U0AA0 ; ... into overflow byte
|
|
ROL A ; ... into accumulator
|
|
DEY ; decrement bit counter
|
|
BPL ROLBIT ; loop until all bits processed
|
|
TAY ; check whether accumulator is 0
|
|
BNE NZERO ; if not, print it
|
|
CPX #1 ; have we output the max number of digits?
|
|
BEQ NZERO ; if not, print it
|
|
LDY DIGCNT ; how many digits have we output?
|
|
BEQ ZERSUP ; skip output if digit is 0
|
|
NZERO INC DIGCNT ; increment digit counter
|
|
ORA #$30 ; add numeric value to ascii '0' to get ascii char
|
|
JSR CHROUT ; output character
|
|
ZERSUP DEX ; decrement number of leading zeros
|
|
BNE DIGOUT ; next digit
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; disk status/command [@]
|
|
DSTAT BNE CHGDEV ; if device address was given, use it
|
|
LDX #8 ; otherwise, default to 8
|
|
.BYTE $2C ; absolute BIT opcode consumes next word (LDX TMP0)
|
|
CHGDEV LDX TMP0 ; load device address from parameter
|
|
CPX #4 ; make sure device address is in range 4-31
|
|
BCC IOERR
|
|
CPX #32
|
|
BCS IOERR
|
|
STX TMP0
|
|
LDA #0 ; clear status
|
|
STA SATUS
|
|
STA FNLEN ; empty filename
|
|
JSR GETCHR ; get next character
|
|
BEQ INSTAT1 ; null, display status
|
|
DEC CHRPNT ; back up 1 char
|
|
CMP #"$" ; $, display directory
|
|
BEQ DIRECT
|
|
LDA TMP0 ; command specified device to listen
|
|
JSR LISTEN
|
|
LDA #$6F ; secondary address 15 (only low nybble used)
|
|
JSR SECOND
|
|
|
|
; send command to device
|
|
DCOMD LDX CHRPNT ; get next character from buffer
|
|
INC CHRPNT
|
|
LDA INBUFF,X
|
|
BEQ INSTAT ; break out of loop if it's null
|
|
JSR CIOUT ; otherwise output it to the serial bus
|
|
BCC DCOMD ; unconditional loop: CIOUT clears carry before RTS
|
|
|
|
; get device status
|
|
INSTAT JSR UNLSN ; command device to unlisten
|
|
INSTAT1 JSR CRLF ; new line
|
|
LDA TMP0 ; load device address
|
|
JSR TALK ; command device to talk
|
|
LDA #$6F ; secondary address 15 (only low nybble used)
|
|
JSR TKSA
|
|
RDSTAT JSR ACPTR ; read byte from serial bus
|
|
JSR CHROUT ; print it
|
|
CMP #$0D ; if the byte is CR, exit loop
|
|
BEQ DEXIT
|
|
LDA SATUS ; check status
|
|
AND #$BF ; ignore EOI bit
|
|
BEQ RDSTAT ; if no errors, read next byte
|
|
DEXIT JSR UNTLK ; command device to stop talking
|
|
JMP STRT ; back to mainloop
|
|
IOERR JMP ERROR ; handle error
|
|
|
|
; get directory
|
|
DIRECT LDA TMP0 ; load device address
|
|
JSR LISTEN ; command device to listen
|
|
LDA #$F0 ; secondary address 0 (only low nybble used)
|
|
JSR SECOND
|
|
LDX CHRPNT ; get index of next character
|
|
DIR2 LDA INBUFF,X ; get next character from buffer
|
|
BEQ DIR3 ; break if it's null
|
|
JSR CIOUT ; send character to device
|
|
INX ; increment characer index
|
|
BNE DIR2 ; loop if it hasn't wrapped to zero
|
|
DIR3 JSR UNLSN ; command device to unlisten
|
|
JSR CRLF ; new line
|
|
LDA TMP0 ; load device address
|
|
PHA ; save on stack
|
|
JSR TALK ; command device to talk
|
|
LDA #$60 ; secondary address 0 (only low nybble used)
|
|
JSR TKSA
|
|
LDY #3 ; read 3 16-bit values from device
|
|
DIRLIN STY STORE ; ignore the first 2; 3rd is file size
|
|
DLINK JSR ACPTR ; read low byte from device
|
|
STA TMP0 ; store it
|
|
LDA SATUS ; check status
|
|
BNE DREXIT ; exit if error or eof occurred
|
|
JSR ACPTR ; read high byte from device
|
|
STA TMP0+1 ; store it
|
|
LDA SATUS ; check status
|
|
BNE DREXIT ; exit if error or eof cocurred
|
|
DEC STORE ; decrement byte count
|
|
BNE DLINK ; loop if bytes remain
|
|
JSR CVTDEC ; convert last 16-bit value to decimal
|
|
LDA #0 ; clear digit count
|
|
LDX #6 ; max 6 digits
|
|
LDY #3 ; 3 bits per digit
|
|
JSR NMPRNT ; output number
|
|
LDA #" " ; output space
|
|
JSR CHROUT
|
|
DNAME JSR ACPTR ; get a filename character from the device
|
|
BEQ DMORE ; if it's null, break out of loop
|
|
LDX SATUS ; check for errors or eof
|
|
BNE DREXIT ; if found exit early
|
|
JSR CHROUT ; output character
|
|
CLC
|
|
BCC DNAME ; unconditional branch to read next char
|
|
DMORE JSR CRLF
|
|
JSR STOP ; check for stop key
|
|
BEQ DREXIT ; exit early if pressed
|
|
JSR GETIN ; pause if a key was pressed
|
|
BEQ NOPAWS
|
|
PAWS JSR GETIN ; wait until another key is pressed
|
|
BEQ PAWS
|
|
NOPAWS LDY #2
|
|
BNE DIRLIN ; unconditional branch to read next file
|
|
DREXIT JSR UNTLK ; command device to untalk
|
|
PLA ; restore accumulator
|
|
JSR LISTEN ; command device to listen
|
|
LDA #$E0 ; secondary address 0 (only low nybble is used)
|
|
JSR SECOND
|
|
JSR UNLSN ; command device to unlisten
|
|
JMP STRT ; back to mainloop
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; print and clear routines
|
|
CLINE JSR CRLF ; send CR+LF
|
|
JMP SNCLR ; clear line
|
|
SNDCLR JSR SNDMSG
|
|
SNCLR LDY #$28 ; loop 40 times
|
|
SNCLP LDA #$20 ; output space character
|
|
JSR CHROUT
|
|
LDA #$14 ; output delete character
|
|
JSR CHROUT
|
|
DEY
|
|
BNE SNCLP
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; display message from table
|
|
SNDMSG LDA MSGBAS,Y ; Y contains offset in msg table
|
|
PHP
|
|
AND #$7F ; strip high bit before output
|
|
JSR CHOUT
|
|
INY
|
|
PLP
|
|
BPL SNDMSG ; loop until high bit is set
|
|
RTS
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; message table; last character has high bit set
|
|
MSGBAS =*
|
|
MSG2 .BYTE $0D ; header for registers
|
|
.TEXT " PC SR AC XR YR SP V1.2"
|
|
.BYTE $0D+$80
|
|
MSG3 .BYTE $1D,$3F+$80 ; syntax error: move right, display "?"
|
|
MSG4 .TEXT "..SYS" ; SYS call to enter monitor
|
|
.BYTE $20+$80
|
|
MSG5 .BYTE $3A,$12+$80 ; ":" then RVS ON for memory ASCII dump
|
|
MSG6 .TEXT " ERRO" ; I/O error: display " ERROR"
|
|
.BYTE "R"+$80
|
|
MSG7 .BYTE $41,$20+$80 ; assemble next instruction: "A " + addr
|
|
MSG8 .TEXT " " ; pad non-existent byte: skip 3 spaces
|
|
.BYTE $20+$80
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; addressing mode table - nybbles provide index into MODE2 table
|
|
; for opcodes XXXXXXY0, use XXXXXX as index into table
|
|
; for opcodes WWWXXY01 use $40 + XX as index into table
|
|
; use right nybble if Y=0; use left nybble if Y=1
|
|
|
|
MODE .BYTE $40,$02,$45,$03 ; even opcodes
|
|
.BYTE $D0,$08,$40,$09
|
|
.BYTE $30,$22,$45,$33
|
|
.BYTE $D0,$08,$40,$09
|
|
.BYTE $40,$02,$45,$33
|
|
.BYTE $D0,$08,$40,$09
|
|
.BYTE $40,$02,$45,$B3
|
|
.BYTE $D0,$08,$40,$09
|
|
.BYTE $00,$22,$44,$33
|
|
.BYTE $D0,$8C,$44,$00
|
|
.BYTE $11,$22,$44,$33
|
|
.BYTE $D0,$8C,$44,$9A
|
|
.BYTE $10,$22,$44,$33
|
|
.BYTE $D0,$08,$40,$09
|
|
.BYTE $10,$22,$44,$33
|
|
.BYTE $D0,$08,$40,$09
|
|
.BYTE $62,$13,$78,$A9 ; opcodes ending in 01
|
|
|
|
; addressing mode format definitions indexed by nybbles from MODE table
|
|
|
|
; left 6 bits define which characters appear in the assembly operand
|
|
; left 3 bits are before the address; next 3 bits are after
|
|
|
|
; right-most 2 bits define length of binary operand
|
|
|
|
; index 654 321
|
|
; 1st character $(# ,),
|
|
; 2nd character $$ X Y length format idx mode
|
|
MODE2 .BYTE $00 ; 000 000 00 0 error
|
|
.BYTE $21 ; 001 000 01 #$00 1 immediate
|
|
.BYTE $81 ; 100 000 01 $00 2 zero-page
|
|
.BYTE $82 ; 100 000 10 $0000 3 absolute
|
|
.BYTE $00 ; 000 000 00 4 implied
|
|
.BYTE $00 ; 000 000 00 5 accumulator
|
|
.BYTE $59 ; 010 110 01 ($00,X) 6 indirect,X
|
|
.BYTE $4D ; 010 011 01 ($00),Y 7 indirect,Y
|
|
.BYTE $91 ; 100 100 01 $00,X 8 zero-page,X
|
|
.BYTE $92 ; 100 100 10 $0000,X 9 absolute,X
|
|
.BYTE $86 ; 100 001 10 $0000,Y A absolute,Y
|
|
.BYTE $4A ; 010 010 10 ($0000) B indirect
|
|
.BYTE $85 ; 100 001 01 $00,Y C zero-page,Y
|
|
.BYTE $9D ; 100 111 01 $0000* D relative
|
|
|
|
; * relative is special-cased so format bits don't match
|
|
|
|
|
|
; character lookup tables for the format definitions in MODE2
|
|
|
|
CHAR1 .BYTE $2C,$29,$2C ; "," ")" ","
|
|
.BYTE $23,$28,$24 ; "#" "(" "$"
|
|
|
|
CHAR2 .BYTE $59,$00,$58 ; "Y" 0 "X"
|
|
.BYTE $24,$24,$00 ; "$" "$" 0
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; 3-letter mnemonics packed into two bytes (5 bits per letter)
|
|
|
|
; left 8 bits
|
|
; XXXXX000 opcodes
|
|
MNEML .BYTE $1C,$8A,$1C,$23 ; BRK PHP BPL CLC
|
|
.BYTE $5D,$8B,$1B,$A1 ; JSR PLP BMI SEC
|
|
.BYTE $9D,$8A,$1D,$23 ; RTI PHA BVC CLI
|
|
.BYTE $9D,$8B,$1D,$A1 ; RTS PLA BVS SEI
|
|
.BYTE $00,$29,$19,$AE ; ??? DEY BCC TYA
|
|
.BYTE $69,$A8,$19,$23 ; LDY TAY BCS CLV
|
|
.BYTE $24,$53,$1B,$23 ; CPY INY BNE CLD
|
|
.BYTE $24,$53,$19,$A1 ; CPX INX BEQ SED
|
|
; XXXYY100 opcodes
|
|
.BYTE $00,$1A,$5B,$5B ; ??? BIT JMP JMP
|
|
.BYTE $A5,$69,$24,$24 ; STY LDY CPY CPX
|
|
; 1XXX1010 opcodes
|
|
.BYTE $AE,$AE,$A8,$AD ; TXA TXS TAX TSX
|
|
.BYTE $29,$00,$7C,$00 ; DEX ??? NOP ???
|
|
; XXXYYY10 opcodes
|
|
.BYTE $15,$9C,$6D,$9C ; ASL ROL LSR ROR
|
|
.BYTE $A5,$69,$29,$53 ; STX LDX DEC INC
|
|
; XXXYYY01 opcodes
|
|
.BYTE $84,$13,$34,$11 ; ORA AND EOR ADC
|
|
.BYTE $A5,$69,$23,$A0 ; STA LDA CMP SBC
|
|
|
|
; right 7 bits, left justified
|
|
; XXXXX000 opcodes
|
|
MNEMR .BYTE $D8,$62,$5A,$48 ; BRK PHP BPL CLC
|
|
.BYTE $26,$62,$94,$88 ; JSR PLP BMI SEC
|
|
.BYTE $54,$44,$C8,$54 ; RTI PHA BVC CLI
|
|
.BYTE $68,$44,$E8,$94 ; RTS PLA BVS SEI
|
|
.BYTE $00,$B4,$08,$84 ; ??? DEY BCC TYA
|
|
.BYTE $74,$B4,$28,$6E ; LDY TAY BCS CLV
|
|
.BYTE $74,$F4,$CC,$4A ; CPY INY BNE CLD
|
|
.BYTE $72,$F2,$A4,$8A ; CPX INX BEQ SED
|
|
; XXXYY100 opcodes
|
|
.BYTE $00,$AA,$A2,$A2 ; ??? BIT JMP JMP
|
|
.BYTE $74,$74,$74,$72 ; STY LDY CPY CPX
|
|
; 1XXX1010 opcodes
|
|
.BYTE $44,$68,$B2,$32 ; TXA TXS TAX TSX
|
|
.BYTE $B2,$00,$22,$00 ; DEX ??? NOP ???
|
|
; XXXYYY10 opcodes
|
|
.BYTE $1A,$1A,$26,$26 ; ASL ROL LSR ROR
|
|
.BYTE $72,$72,$88,$C8 ; STX LDX DEC INC
|
|
; XXXYYY01 opcodes
|
|
.BYTE $C4,$CA,$26,$48 ; ORA AND EOR ADC
|
|
.BYTE $44,$44,$A2,$C8 ; STA LDA CMP SBC
|
|
.BYTE $0D,$20,$20,$20
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; single-character commands
|
|
KEYW .TEXT "ACDFGHJMRTX@.>;"
|
|
HIKEY .TEXT "$+&%LSV"
|
|
KEYTOP =*
|
|
|
|
; vectors corresponding to commands above
|
|
KADDR .WORD ASSEM-1,COMPAR-1,DISASS-1,FILL-1
|
|
.WORD GOTO-1,HUNT-1,JSUB-1,DSPLYM-1
|
|
.WORD DSPLYR-1,TRANS-1,EXIT-1,DSTAT-1
|
|
.WORD ASSEM-1,ALTM-1,ALTR-1
|
|
|
|
; -----------------------------------------------------------------------------
|
|
MODTAB .BYTE $10,$0A,$08,02 ; modulo number systems
|
|
LENTAB .BYTE $04,$03,$03,$01 ; bits per digit
|
|
|
|
LINKAD .WORD BREAK ; address of brk handler
|
|
SUPAD .WORD SUPER ; address of entry point
|