452 lines
13 KiB
ArmAsm
452 lines
13 KiB
ArmAsm
; CHIBI PC-09 Machine Language Monitor -- BUZBEE
|
|
; Copyright (c) 2025 Gale Faraday
|
|
; Licensed under MIT
|
|
|
|
INCLUDE "bbhash.inc"
|
|
INCLUDE "buzbee.inc"
|
|
INCLUDE "hardware.inc"
|
|
INCLUDE "serial.inc"
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; BUZBEE Variables
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
BBIN_DEPTH EQU $80 ; Input buffer depth
|
|
BBTOKENS_DEPTH EQU $40 ; Token buffer depth
|
|
|
|
SECTION BBVARS,bss
|
|
|
|
tagbbvar STRUCT
|
|
input rmb BBIN_DEPTH ; Input buffer
|
|
cchinput rmd 1 ; Input buffer char count
|
|
tokens rmb BBTOKENS_DEPTH ; Token buffer
|
|
cbtokens rmd 1 ; Token buffer byte count
|
|
scratch rmd 1 ; Scratch word
|
|
ENDSTRUCT
|
|
|
|
ORG $0200
|
|
BBVAR tagbbvar
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; BUZBEE Main Loop
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
SECTION BUZBEE
|
|
|
|
EXPORT BUZBEE
|
|
|
|
BUZBEE
|
|
lbsr NEWLINE ; Setup the new input line and handle display.
|
|
bsr INPLOOP ; Fill input buffer.
|
|
cmpy #0 ; No data?
|
|
beq BUZBEE ; Try again...
|
|
lbsr TOKENIZE ; Try to tokenize the input buffer
|
|
lbsr RUNIF ; Execute token buffer, handling any errors
|
|
bra BUZBEE ; Repeat
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; BUZBEE Input Buffering and Handling Routines
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; Fills input buffer with characters from the serial console. Sets Y to the
|
|
; offset in the input buffer of the last char read.
|
|
INPLOOP
|
|
jsr PINCHAR ; Try to read a char
|
|
cmpd #0 ; If no char keep waitin'
|
|
beq INPLOOP
|
|
bitb #UARTF_LSR_DR ; Is there a char in A?
|
|
beq NOCHAR@
|
|
cmpa #$1B ; ESC?
|
|
beq HESC@ ; Handle ESC
|
|
cmpa #$08 ; BS?
|
|
beq HBACKSPC@ ; Backup a char
|
|
cmpa #$0D ; CR?
|
|
beq HCR@ ; Then terminate and parse input buffer
|
|
cmpy #BBIN_DEPTH ; Are we at the end of the input buffer?
|
|
beq FULLBUF@ ; Handle the buffer being full
|
|
ECHO@
|
|
jsr POUTCHAR ; Echo char back, this includes BS chars
|
|
sta BBVAR.input,y ; Add it to the input buffer
|
|
leay 1,y
|
|
; Fall through
|
|
NOCHAR@
|
|
; Check for error condition, work based on The Serial Port release 19
|
|
bitb #(UARTF_LSR_FIFO|UARTF_LSR_BI|UARTF_LSR_FE|UARTF_LSR_PE|UARTF_LSR_OE)
|
|
beq INPLOOP
|
|
bitb #UARTF_LSR_OE ; Check for overrun error
|
|
beq NOOVER@
|
|
PZSTR EM_OVERRUN
|
|
NOOVER@
|
|
bitb #UARTF_LSR_PE ; Check for parity error
|
|
beq NOPARITY@
|
|
PZSTR EM_PARITY
|
|
NOPARITY@
|
|
bitb #UARTF_LSR_FE ; Check for framing error
|
|
beq NOFRAM@
|
|
PZSTR EM_FRAMING
|
|
NOFRAM@
|
|
bitb #UARTF_LSR_FIFO ; Check for FIFO error
|
|
beq INPLOOP ; Loop over
|
|
PZSTR EM_FIFO
|
|
bra INPLOOP ; Loop over
|
|
HCR@
|
|
sty BBVAR.cchinput ; Store buffer length
|
|
rts
|
|
HESC@
|
|
lda #'^ ; Print a char that signifies that ESC was pressed
|
|
jsr POUTCHAR
|
|
ldy #0 ; On return we cmpy #0 and if eq then newline
|
|
rts
|
|
HBACKSPC@
|
|
clrb ; Clear last char
|
|
leay -1,y
|
|
stb BBVAR.input
|
|
bra ECHO@ ; Echo the char in A
|
|
FULLBUF@
|
|
lda #$07 ; ASCII BEL
|
|
jsr POUTCHAR
|
|
lbra INPLOOP
|
|
|
|
; Handle new lines; prints a new prompt and then clears the input buffer
|
|
NEWLINE
|
|
PZSTR PROMPTLINE ; Print prompt line
|
|
CLRIN ; Label to just clear input buffer without newline
|
|
clra ; Init A and X
|
|
ldx #0
|
|
NEXT@
|
|
sta BBVAR.input,x ; Clear input buffer
|
|
leax 1,x
|
|
cmpx #BBIN_DEPTH
|
|
blo NEXT@
|
|
ldy #0 ; Reset buffer fill pointer
|
|
rts
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; Hex Conversion and Printing Routines
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; Prints a byte in A
|
|
; @param A: byte to print
|
|
PBYTE
|
|
pshs b
|
|
tfr a,b ; Transfer lower nybble into B
|
|
andd #$F00F ; Mask and adjsut
|
|
asra
|
|
asra
|
|
asra
|
|
asra
|
|
anda #$0F
|
|
ora #'0 ; Add '0' to offset to digits
|
|
cmpa #$3A ; Hex? ':' '9'+1
|
|
blo SKIPA@
|
|
adca #6 ; Hex offset
|
|
SKIPA@
|
|
jsr POUTCHAR ; Print char in A
|
|
andb #$0F
|
|
orb #'0 ; Add '0' to offset to digits
|
|
cmpb #$3A ; Hex? ':' '9'+1
|
|
blo SKIPB@
|
|
adcb #6 ; Hex offset
|
|
SKIPB@
|
|
tfr b,a ; Print char in B
|
|
jsr POUTCHAR
|
|
puls b ; Restore B
|
|
rts
|
|
|
|
; Converts a hexadecimal sequence into a byte value
|
|
; @param D: hex representation of a byte with A: MSD B: LSD
|
|
; @return A: returned byte value
|
|
; NOTE: Conversion logic credit to Wozniak. This code unrolled for speed.
|
|
HEX2BYT
|
|
eora #$B0 ; Map digits to 0-9
|
|
cmpa #9+1 ; Is it a decimal?
|
|
blo AISDEC@ ; Yes? skip hex adjust
|
|
adca #$88 ; Map 'A'-'F' to $FA-$FF
|
|
cmpa #$FA ; Hex char?
|
|
blo BADHEX@ ; No? Print error
|
|
AISDEC@
|
|
asla ; Shift MSD into upper nybble of A
|
|
asla
|
|
asla
|
|
asla
|
|
; Do B part
|
|
eorb #$B0 ; Map digits to 0-9
|
|
cmpa #9+1 ; Is it a decimal?
|
|
blo BISDEC@ ; Yes? skip hex adjust
|
|
adcb #$88 ; Map 'A'-'F' to $FA-$FF
|
|
andb #$0F ; Mask high nybble
|
|
cmpb #$0A ; Hex char?
|
|
blo BADHEX@ ; No? Print error
|
|
BISDEC@
|
|
stb BBVAR.scratch ; Store low nybble in scratch buffer
|
|
ora BBVAR.scratch ; Combine the nybbles
|
|
rts
|
|
BADHEX@
|
|
PZSTR EM_BADHEX ; Print an error message
|
|
clrd ; Prevent RUNIF from executing
|
|
std BBVAR.cbtokens
|
|
rts
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; BUZBEE Tokenizing Routines
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; Attempts to parse the input buffer into tokens depending on the command
|
|
TOKENIZE
|
|
ldd BBVAR.cchinput ; Do we have input to work with?
|
|
cmpd #4 ; Do we have even enough space for a string command
|
|
blo TOKFAIL@ ; No? GTFO
|
|
ldy #0 ; Initialize Y; used to track current position in BBVAR.input
|
|
ldx #0 ; Initialize X; used to track position in BBVAR.tokens
|
|
bsr SKIPTONEXTC ; Get the next non-whitespace char
|
|
bsr MKCMDSUM ; Hash the first four non-whitespace chars
|
|
bsr HASH2TOKEN ; Try to turn that hash into a proper token
|
|
bcs TOKFAIL@
|
|
bra STTOK@ ; Store token
|
|
NEXTHEX@ ; Next hex token
|
|
bsr SKIPTONEXTC ; Skip to next whitespace
|
|
ldd BBVAR.input,y ; Get hex value (two digits)
|
|
bsr HEX2BYT ; Convert hex value to byte value
|
|
STTOK@
|
|
sta BBVAR.tokens,x ; Store curent token
|
|
leax 1,x ; Advance to next token
|
|
cmpx #BBTOKENS_DEPTH ; Is this next token in bounds?
|
|
beq FULLBUF@ ; No? handle a full buffer
|
|
bra NEXTHEX@ ; Try to turn the next character into a hex value
|
|
FULLBUF@
|
|
PZSTR EM_FULLTOKBUF ; Print an error message
|
|
clrd ; Say we wrote no tokens
|
|
std BBVAR.cbtokens
|
|
rts
|
|
TOKFAIL@
|
|
PZSTR EM_TOKFAIL ; Print tokenization fail
|
|
clrd ; Say we wrote no tokens
|
|
std BBVAR.cbtokens
|
|
rts
|
|
|
|
; Converts a runtime command hash into a portable token. Command tokens are
|
|
; indexes into BBCHT, which is generated at compile-time
|
|
; @param A: runtime hash
|
|
; @return A: output token
|
|
; @return CC.C: set if error state, cleared otherwise
|
|
HASH2TOKEN
|
|
pshs x ; Preserve & init X; other routines in this group use it
|
|
ldx #0
|
|
NEXTHASH@
|
|
cmpa BBCHT,x ; Is this hash our hash?
|
|
beq THISHASH@ ; Yes? turn it into a token
|
|
leax 1,x ; Begin considering next hash
|
|
cmpx BBCHTC ; Is the next hash even in the table?
|
|
blo NEXTHASH@ ; Yes? try this next hash, No? fall through
|
|
PZSTR EM_BADHASH CALL; Print an error message to the user
|
|
puls x
|
|
orcc #1 ; Set CC.C to indicate error
|
|
rts
|
|
THISHASH@
|
|
puls x
|
|
andcc #$FE ; Clear CC.C to indicate success
|
|
rts
|
|
|
|
; Makes a hash of four chars in BBIN starting at offset X
|
|
; @param Y: offset in BBIN to read the four chars from
|
|
; @return A: resulting hash
|
|
; @return Y: offset after hash processing
|
|
MKCMDSUM
|
|
pshs b
|
|
ldb #4 ; Loop over four chars
|
|
clra ; Initialize accumulator
|
|
NEXTC@
|
|
suba BBVAR.input,y ; Subtract current char from accumulator
|
|
leax 1,y ; Next char
|
|
decb ; Reduce count
|
|
cmpb #0 ; Are we at the end?
|
|
bne NEXTC@ ; No? loop
|
|
puls b
|
|
rts
|
|
|
|
; Skips "whitespace" to the next semantic char
|
|
; @param Y: current index in text buffer
|
|
; @return Y: resulting index in text buffer
|
|
SKIPTONEXTC
|
|
lda BBVAR.input,y ; Get our char?
|
|
cmpa #$20 ; SPACE or control char?
|
|
bhi EXIT@ ; Yes? End
|
|
leay 1,y ; Iterate next char
|
|
bra SKIPTONEXTC
|
|
EXIT@
|
|
rts
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; BUZBEE IF Handling Functions
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; Executes a command based on the initial token
|
|
; @corrupts B
|
|
; @return X: command data table index
|
|
RUNIF
|
|
clrd ; Do we have any tokens?
|
|
cmpd BBVAR.cbtokens
|
|
beq NOTOK@
|
|
ldx #0 ; Counting up from zero
|
|
lda BBVAR.tokens ; Load token
|
|
NEXTHASH@
|
|
cmpa BBCHT,x ; Is this hash our hash?
|
|
beq CALCPTR@ ; Yes? skip to next step to put ptr in x
|
|
leax 1,x ; Begin considering next hash
|
|
cmpx BBCHTC ; Is this the last byte?
|
|
blo NEXTHASH@ ; No? try next hash, Yes? fall through
|
|
PZSTR EM_BADHASH ; Print an error message
|
|
lbra IFHELP ; Proceed to call "HELP"
|
|
CALCPTR@
|
|
tfr x,d ; Swap into d to do a cheap multiply
|
|
asld ; Cheaply << to get *2, pointer size
|
|
tfr d,x ; Restore x from d and jump to function at index
|
|
jmp [IFPTRTBL,x]
|
|
NOTOK@
|
|
rts
|
|
|
|
; IF pointer table
|
|
IFPTRTBL
|
|
fdb IFCALL
|
|
fdb IFHELP
|
|
fdb IFPEEK
|
|
fdb IFPOKE
|
|
fdb IFSREC
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; BUZBEE Internal Command Functions
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; Call a pointer
|
|
IFCALL
|
|
ldd BBVAR.cbtokens ; Only jump if three tokens given
|
|
cmpd #3
|
|
lbne INVALIDARG
|
|
jsr [BBVAR.tokens+1] ; Make our jump (doesn't implicitly return)
|
|
rts
|
|
|
|
; Print out a help message
|
|
IFHELP
|
|
PZSTR HELP_MSG
|
|
rts
|
|
|
|
; Peek memory
|
|
IFPEEK
|
|
ldd BBVAR.cbtokens ; One 16-bit token given, single peek
|
|
cmpd #3
|
|
lblo INVALIDARG ; Not enough args given
|
|
bhi SELTYPE@ ; Select forward or backward peek
|
|
SINGLE@
|
|
lda [BBVAR.tokens+1] ; Get byte
|
|
lbsr PBYTE ; Print peek byte
|
|
rts
|
|
SELTYPE@
|
|
ldd BBVAR.cbtokens ; Have enough tokens?
|
|
cmpd #5
|
|
lbne INVALIDARG ; No? bounce out
|
|
ldd BBVAR.tokens+1 ; Are we forwards (BASE < HIGH)?
|
|
cmpd BBVAR.tokens+3
|
|
beq SINGLE@ ; Gracefully handle BASE == HIGH
|
|
bhi INVALIDARG ; Malformed if BASE > HIGH
|
|
ldx BBVAR.tokens+1 ; Get BASE
|
|
NEXT@
|
|
lda ,x+ ; Get the next byte
|
|
lbsr PBYTE ; Print our byte
|
|
cmpx BBVAR.tokens+3 ; Are we at <HIGH> yet?
|
|
bne NEXT@ ; No? loop
|
|
rts
|
|
|
|
; Poke bytes into memory
|
|
; NOTE: Blocks could also use 6309 TFM instruction
|
|
IFPOKE
|
|
ldx BBVAR.cbtokens ; Make sure we have enough tokens
|
|
cmpx #4
|
|
lblo INVALIDARG
|
|
leax -3,x ; Get count of bytes to write into X
|
|
ldy #0 ; Setup Y for indexing
|
|
NEXTB@
|
|
lda BBVAR.tokens+3,y ; Get source byte to copy
|
|
sta [BBVAR.tokens+1,y] ; Copy byte
|
|
leay +1,y ; Increment memory indexer
|
|
leax -1,x ; Decement byte count
|
|
cmpx #0 ; Are we at our last byte?
|
|
bhi NEXTB@ ; No? loop
|
|
rts
|
|
|
|
; Placeholder function labels to make assembler happy before git commit
|
|
IFSREC
|
|
rts
|
|
|
|
; Invalid argument IF tail
|
|
INVALIDARG
|
|
PZSTR EM_BADARG
|
|
rts
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; BUZBEE Strings and Fixed Data
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
HELP_MSG
|
|
fcc "-- BUZBEE HELP --"
|
|
fcb $0D,$0A
|
|
fcc "Available Commands:"
|
|
fcb $0D,$0A
|
|
fcc "CALL <PTR> - Call the pointer given in <PTR> as a subroutine."
|
|
fcb $0D,$0A
|
|
fcc "HELP [CMD] - Display help, command optional."
|
|
fcb $0D,$0A
|
|
fcc "PEEK <BASE> [<HIGH>] - Read memory at <BASE> to <HIGH>."
|
|
fcb $0D,$0A
|
|
fcc "POKE <ADDR> <BYTES> - Overwrite memory with <BYTES> starting at <BASE>."
|
|
fcb $0D,$0A
|
|
fcc "SREC - Enter Motorola S-Record entry mode."
|
|
fcb $0D,$0A
|
|
fcb $00
|
|
|
|
PROMPTLINE
|
|
fcb $0D,$0A,$25,$00 ; CR LF '%' NUL
|
|
|
|
EM_OVERRUN
|
|
fcc "!!! UART Overrun Error !!!"
|
|
fcb $0D,$0A,$00
|
|
EM_PARITY
|
|
fcc "!!! UART Parity Error !!!"
|
|
fcb $0D,$0A,$00
|
|
EM_FRAMING
|
|
fcc "!!! UART Framing Error !!!"
|
|
fcb $0D,$0A,$00
|
|
EM_FIFO
|
|
fcc "!!! UART FIFO Error !!!"
|
|
fcb $0D,$0A,$00
|
|
EM_BADHASH
|
|
fcc "!!! Bad Command Hash !!!"
|
|
fcb $0D,$0A,$00
|
|
EM_BADARG
|
|
fcc "!!! Malformed Arguments !!!"
|
|
fcb $0D,$0A,$00
|
|
EM_TOKFAIL
|
|
fcc "!!! Tokenization Failure !!!"
|
|
fcb $0D,$0A,$00
|
|
EM_BADHEX
|
|
fcc "!!! Malformed Hex Value !!!"
|
|
fcb $0D,$0A,$00
|
|
EM_FULLTOKBUF
|
|
fcc "!!! Token Buffer Overrun !!!"
|
|
fcb $0D,$0A,$00
|