Couple things first. I'm still very much in the learning processes with 6502 assembly, so I'm sure I'm doing some stuff the hard way. For instance, other than initializing it, I use no stack instructions, although I'm sure I'd benefit from them. Still getting to that!
While I'm not specifically looking for critique of my methods, I do welcome it.
The one thing that I think might be bad is how much happens inside the ISR for reading the keyboard. the ISR itself is short, but it calls a routine that includes my entire command recognition code. The ACIA is the only interrupt generator, but I suppose it still might be a thing.
My problems are, lock-ups (often), garbage on the screen (less often), and other strange behaviors.
Here's the code: (formating sure doesn't look as nice on here as it does on my editor)
Code: Select all
;WOPRjr OS
.ORG $0000
ACIA_DAT = $5000
ACIA_STA = ACIA_DAT+1
ACIA_CON = ACIA_DAT+3
ACIA_COM = ACIA_DAT+2
STRING_PTR .DS $02 ;pointer to current output message string
STRING_INDEX .DS $01 ;index into input buffer
LINE_CNT .DS $01 ;clear screen line count
CMD_INDEX .DS $02 ;index into command string list
SELECTED_CMD: .DS $02 ;pointer to slected command routine
.ORG $0200
ACIA_RXBUF .DS $100 ;input buffer
.ORG $8000
;command strings
commands:
cmd_empty: .BYTE $0D, $00 ;empty:user pressed enter with no characters
cmd_write: .BYTE "write", $00 ;command to write to memory
cmd_read: .BYTE "read", $00 ;command to read from memory
cmd_test: .BYTE "test", $00 ;command for system self-test
cmd_list_end: .BYTE $FF ;marker for end of list
;list of pointers to command strings
cmd_indices:
empty_index: .WORD cmd_empty
write_index: .WORD cmd_write
read_index: .WORD cmd_read
test_index: .WORD cmd_test
;list of pointers to command routines
cmd_routines:
empty_rtn_ptr: .WORD empty_rtn
write_rtn_ptr: .WORD write_rtn
read_rtn_ptr: .WORD read_rtn
test_rtn_ptr: .WORD test_rtn
no_cmd_rtn_ptr: .WORD no_cmd_rtn
strings:
prompt: .BYTE $0D, $0A, "WOPRjr:" ,$00 ;commandline prompt
empty_msg .BYTE $0D, $0A, $00 ;empty string
message: .BYTE $0D, $0A, "You entered: ", $00 ;input response message
write_msg: .BYTE $0D, $0A, "writing", $00 ;temp write message
read_msg: .BYTE $0D, $0A, "reading", $00 ;temp read message
test_msg: .BYTE $0D, $0A, "testing", $00 ;temp test message
no_cmd_msg: .BYTE $0D, $0A, "unknown command", $00 ;default message for unknown string
start: LDX #$FF
TXS ;Set stack pointer to top of page 1
STZ STRING_INDEX ;initialize string index to 0
JSR uartinit ;initialize ACIA
CLI ;turn on interrupts
LDX #$1A
clr_loop: LDA ACIA_STA ;get ACIA status register
AND #@00010000 ;check Tx buffer empty. 1 is empty, 0 is not empty
BEQ clr_loop ;keep waiting until Tx buffer is empty
LDA #$0A
STA ACIA_DAT
DEX
BNE clr_loop
STA ACIA_DAT
LDA #$0D
STA ACIA_DAT
LDA #<prompt ;get lower byte of prompt message
STA STRING_PTR ;store it in first byte of the string pointer
LDA #>prompt; ;get upper byte of prompt message
STA STRING_PTR+1 ;store it in second byte of string pointer
JSR terminal_w ;display prompt
inf_loop: NOP
JMP inf_loop
uartinit:
STZ ACIA_STA ;write a zero to ACIA status regidter to reset chip
LDA #@00011110 ;ACIA control register setting for 1 stop,, 8 data, 9600 baud
STA ACIA_CON ;set ACIA control register
LDA #@00001001 ;no parity, no echo, no Tx IRQ, RTSB LOW, Rx IRQ enabled, DTRB LOW
STA ACIA_COM ;set ACIA command register
RTS
terminal_w: LDY #$00 ;set index to zero
write_loop: LDA ACIA_STA ;get ACIA status register
AND #@00010000 ;check Tx buffer empty. 1 is empty, 0 is not empty
BEQ write_loop ;keep waiting until Tx buffer is empty
LDA (STRING_PTR),Y ;put current character A
BEQ tw_end
STA ACIA_DAT ;send character to ACIA
INY ;move to next character
BNE write_loop ;if not NULL keep going
tw_end: RTS
terminal_r: LDA ACIA_DAT ;get data from ACIA Rx
CMP #$0D ;check if enter was pressed
BEQ process_cmd ;if enter pressed, write string to screen
STA ACIA_DAT
LDX STRING_INDEX ;get string index
STA ACIA_RXBUF,X ;place recieved data into input buffer
INX ;increment it
STX STRING_INDEX ;put incremented version back in memory
RTS
process_cmd:
STZ ACIA_RXBUF,X ;put NULL on end of string
STZ STRING_INDEX ;reset index
JSR parse_cmd ;parse the command
JMP (SELECTED_CMD) ;run routine for selected command
rtn_to_tr: LDA #<prompt ;get lower byte of prompt message
STA STRING_PTR ;store it in first byte of the string pointer
LDA #>prompt; ;get upper byte of prompt message
STA STRING_PTR+1 ;store it in second byte of string pointer
JSR terminal_w ;display prompt
RTS
parse_cmd: LDX #$00 ;reset X register
cmd_list_lp: LDA cmd_routines,X ;get address of currently being checked command's routine
STA SELECTED_CMD ;store it in first byte of command routine pointer
LDA cmd_indices,X ;get address of next command string
STA CMD_INDEX ;store it in zero page so it can used as a pointer
INX ;increment x so we can get next byte of command string address
LDA cmd_routines,X ;get second byte of current command routine pointer
STA SELECTED_CMD+1 ;put byte in secdond half of 0 page command routine pointer
LDA cmd_indices,X ;get next byte of command string address
STA CMD_INDEX+1 ;put byte in zero page to form the completed address
INX ;increment X in case we need to check next command string
LDY #$00 ;reset Y register
LDA #$FF ;$FF is marker for end of command string list
CMP (CMD_INDEX),Y ;check if we are at end of command string list
BEQ no_match ;got to no match routine
string_cmp: LDA ACIA_RXBUF,Y ;get current character of input string
CMP (CMD_INDEX),Y ;compare character to same char position in command string being checked
BNE cmd_list_lp ;if no match, try next command string
INY ;incrementY to index of next character in case we need to loop again
CMP #$00 ;check if current character is NULL (end of string)
BNE string_cmp ;if not NULL, check next characters for match
RTS ;if IS NULL, we have a match at this point. Proper routine's pointer will be in SELECTED_CMD
no_match: LDA #<no_cmd_rtn ;get lower byte of Unknown Command rotine's address
STA SELECTED_CMD ;put in first byte of selcected command pointer
LDA #>no_cmd_rtn ;get upper byte
STA SELECTED_CMD+1 ;put in second byte of pointer
RTS ;return. Unknown Command routine is in sELECTED_CMD
empty_rtn: STZ STRING_INDEX ;reset index
LDA #<empty_msg ;get lower byte of canned output response
STA STRING_PTR ;put it in first byte of the string pointer
LDA #>empty_msg ;get upper byte
STA STRING_PTR+1 ;store it in second byte of string pointer
JSR terminal_w ;output canned message
JMP rtn_to_tr ;back to command handler
write_rtn: STZ STRING_INDEX ;reset index
LDA #<write_msg ;get lower byte of canned output response
STA STRING_PTR ;put it in first byte of the string pointer
LDA #>write_msg ;get upper byte
STA STRING_PTR+1 ;store it in second byte of string pointer
JSR terminal_w ;output canned message
JMP rtn_to_tr ;back to command handler
read_rtn: STZ STRING_INDEX ;reset index
LDA #<read_msg ;get lower byte of canned output response
STA STRING_PTR ;put it in first byte of the string pointer
LDA #>read_msg ;get upper byte
STA STRING_PTR+1 ;store it in second byte of string pointer
JSR terminal_w ;output canned message
JMP rtn_to_tr ;back to command handler
test_rtn: STZ STRING_INDEX ;reset index
LDA #<test_msg ;get lower byte of canned output response
STA STRING_PTR ;put it in first byte of the string pointer
LDA #>test_msg ;get upper byte
STA STRING_PTR+1 ;store it in second byte of string pointer
JSR terminal_w ;output canned message
JMP rtn_to_tr ;back to command handler
no_cmd_rtn: STZ STRING_INDEX ;reset index
LDA #<no_cmd_msg ;get lower byte of canned output response
STA STRING_PTR ;put it in first byte of the string pointer
LDA #>no_cmd_msg ;get upper byte
STA STRING_PTR+1 ;store it in second byte of string pointer
JSR terminal_w ;output canned message
JMP rtn_to_tr ;back to command handler
isr: LDA ACIA_STA ;get ACIA status register
BPL end_isr ;if no IRQ, return from ISR
JSR terminal_r
end_isr: RTI
.ORG $FFFA ;6502 NMI, RST, and IRQ vectors
.WORD start ;NMI
.WORD start ;RST
.WORD isr ;IRQ