Files
6502-cpu-monitor/sbcmon.asm

600 lines
21 KiB
NASM
Executable File

;---------------------------------------------------------------------
; Modified for RC2014
; Changes are copyright Ben Chong and freely licensed to the community
;
;---------------------------------------------------------------------
; Original version information:
; SBC Firmware V5.1.1, 7-4-13, by Daryl Rictor
; 5.1.1 Lite Version - removed List and Mini-Assembler & Help
;
;
CR = 13
LF = 10
ESC = 27 ; ESC to exit
;input_char = $ff03 ; wait for input character
;check_input = $ff06 ; scan for input (no wait), C=1 char, C=0 no character
;output_char = $ff09 ; send 1 character
;*********************************************************************
; local Zero-page variables
; Modified from Daryl's code so that we overlap with EhBASIC as little
; as possible. This was needed to debug EhBASIC during the porting process
ysav = $e0 ; 1 byte
rowcount = $e1 ; 1 byte
Startaddr = $e2 ; 2 bytes
Startaddr_H = $e3
Hexdigits = $e4 ; 2 bytes
Hexdigits_H = $e5
strptr = $e6
strptrh = $e7 ; temporary string pointer (not preserved across calls)
; Local Non-Zero Page Variables
; Unchange from Daryl's code except for addition of the interrupt vectors
buffer = $0300 ; keybd input buffer (127 chrs max)
PCH = $03e0 ; hold program counter (need PCH next to PCL for Printreg routine)
PCL = $03e1 ; ""
ACC = $03e2 ; hold Accumulator (A)
XREG = $03e3 ; hold X register
YREG = $03e4 ; hold Y register
SPTR = $03e5 ; hold stack pointer
PREG = $03e6 ; hold status register (P)
irq_vector = $03e8 ; Interrupt vector
nmi_vector = $03ea ; NMI vector
;
;
;
; *************************************************************************
; kernal commands
; *************************************************************************
; PrintRegCR - subroutine prints a CR, the register contents, CR, then returns
; PrintReg - same as PrintRegCR without leading CR
; Print2Byte - prints AAXX hex digits
; Print1Byte - prints AA hex digits
; PrintDig - prints A hex nibble (low 4 bits)
; Print_CR - prints a CR (ASCII 13)and LF (ASCII 10)
; PrintXSP - prints # of spaces in X Reg
; Print2SP - prints 2 spaces
; Print1SP - prints 1 space
; Input_Char - get one byte from input port, waits for input
; Output_char - send one byte to the console
; *************************************************************************
;
RegData .byte" PC= A= X= Y= S= P= (NVRBDIZC)="
;
; Prints a CR, the register contents, CR, then returns
;
PrintReg Jsr Print_CR ; Lead with a CR
ldx #$ff ;
ldy #$ff ;
Printreg1 iny ;
lda Regdata,y ;
jsr Output_char ;
cmp #$3D ; "="
bne Printreg1 ;
Printreg2 inx ;
cpx #$07 ;
beq Printreg3 ; done with first 6
lda PCH,x ;
jsr Print1Byte ;
cpx #$00 ;
bne Printreg1 ;
beq Printreg2 ;
Printreg3 dex ;
lda PCH,x ; get Preg
ldx #$08 ;
Printreg4 rol ;
tay ;
lda #$31 ;
bcs Printreg5 ;
sbc #$00 ; clc implied:subtract 1
Printreg5 jsr Output_char ;
tya ;
dex ;
bne Printreg4 ;
; fall into the print CR routine
;---------------------------------------------------------------------
; Print CR/LF
; Preserves A
Print_CR PHA ; Save Acc
LDA #$0D ; "cr"
JSR OUTPUT_char ; send it
LDA #$0A ; "lf"
JSR OUTPUT_char ; send it
PLA ; Restore Acc
RTS ;
;---------------------------------------------------------------------
; Prints AAXX hex digits
;
Print2Byte JSR Print1Byte ; prints AAXX hex digits
TXA ;
;---------------------------------------------------------------------
; Prints AA hex digits
; A=byte
Print1Byte PHA ; Save A on stack
LSR ; MOVE UPPER NIBBLE TO LOWER
LSR ;
LSR ;
LSR ;
JSR PrintDig ; Print nibble
PLA ; Restore A
;---------------------------------------------------------------------
; Print lower nibble of A
;
PrintDig sty ysav ; prints A hex nibble (low 4 bits)
AND #$0F ;
TAY ;
LDA Hexdigdata,Y ;
ldy ysav ;
jmp output_char ;
PrintXSP1 JSR Print1SP ;
dex ;
PrintXSP cpx #$00 ;
bne PrintXSP1 ;
rts ;
;---------------------------------------------------------------------
; Print 2 spaces
; Exit: A is changed
Print2SP jsr Print1SP ; print 2 SPACES
;---------------------------------------------------------------------
; Print 1 space
; Exit: A is changed
Print1SP LDA #$20 ; print 1 SPACE
JMP OUTPUT_char ;
;---------------------------------------------------------------------
;Print the string starting at (AX) until we encounter a NULL
;string can be in RAM or ROM. It's limited to <= 255 bytes.
;
PrintStrAX sta strptr+1
stx strptr
tya
pha
ldy #0
PrintStrAXL1 lda (strptr),y
beq PrintStrAXX1 ; quit if NULL
jsr output_char
iny
bne PrintStrAXL1 ; quit if > 255
PrintStrAXX1 pla
tay
rts
;---------------------------------------------------------------------
;
; Break Handler
;
BRKroutine sta ACC ; save A Monitor"s break handler
stx Xreg ; save X
sty Yreg ; save Y
pla ;
sta Preg ; save P
pla ; PCL
tay
pla ; PCH
tax
tya
sec ;
sbc #$02 ;
sta PCL ; backup to BRK cmd
bcs Brk2 ;
dex ;
Brk2 stx PCH ; save PC
TSX ; get stack pointer
stx SPtr ; save stack pointer
jsr PrintReg ; dump register contents
ldx #$FF ;
txs ; clear stack
cli ; enable interrupts again
jmp Monitor ; start the monitor
;*************************************************************************
;
; Monitor Program
;
;**************************************************************************
MonitorBoot
JSR Version ;
SYSjmp ; Added for EhBASIC
; Primary monitor entry point
Monitor
LDX #$FF ;
TXS ; Init the stack
monitor_loop
jsr print_cr
; lda #'>' ; Print prompt
; jsr output_char
lda #>monitorprompt
ldx #<monitorprompt
jsr printstrax
; Init monitor variables
lda #$00 ;
sta Hexdigits ; holds parsed hex
sta Hexdigits_H ;
jsr input_char ; Get user input; blocking
cmp #32 ; Control character?
bcc ml_not_printable ; not printable
jsr output_char ; Echo
ml_not_printable
; cmp #':' ; HEX upload?
; bne nothex
; jmp HexUpLd
;nothex
cmp #'?'
bne nothelp
jmp go_help
nothelp
and #$5f ; Convert to upper case
cmp #'G' ; Go?
bne notgo
jmp go_exec ; Jump to user program
notgo cmp #'E'
bne notmem
jmp go_mem
notmem
cmp #'D'
bne notdump
jmp go_dump
notdump
cmp #'U'
bne notupload
jmp hexupld
notupload
jmp monitor ;_loop ; Reloop if invalid command
;---------------------------------------------------------------------
; Go and run a program...
go_exec
jsr print1sp ; Space out
jsr get_word ; Grab address
bcc ge_0 ; All hex chars
cmp #$0d ; Is the last char RETURN?
bne ge_end ; No, then quit
ge_0
; cpy #$00 ; Did we grab a whole word?
; bne ge_end
; Print out where we are going to jump to
lda #>ge_string
ldx #<ge_string
jsr PrintStrAX
lda hexdigits_h
ldx hexdigits
jsr print2byte
jsr print_cr
sei ; Disable interrupt
; Set the monitor return address on stack
; This is so that user program can do an rts to return to monitor
; Entry point to monitor is $ff00. Stack value is 1 less than actual
lda #$fe
pha
lda #$ff
pha
; Indirect jump to user program
cli ; Reenable interrupt
jmp (Hexdigits)
ge_end
jmp monitor ; return to Monitor, resetting stack pointer
ge_string .byte " PC=",0
;---------------------------------------------------------------------
; Get 8-bit byte
; Y is the nibble count
; Exit: Y=0 if we got all the nibbles
get_byte
ldy #$02
jmp get_byte_start
;---------------------------------------------------------------------
; Get 16-bit word
; Exit: Y=0 if we got all the nibbles
; CY=1 if the last char is non-hex
get_word
ldy #$04
get_byte_start ; Y contains nibble count
lda #$00 ; Init hex buffer
sta Hexdigits ; Holds parsed hex
sta Hexdigits_H
gw_loop0
jsr get_hex_char
bcs gw_end ; If non hex char, abort
; Build a byte with the first nibble already in A
; Entry: A = first hex nibble
; Y = 2
build_byte
LDX #$04 ;
gw_loop1 ; Insert nibble
ASL Hexdigits ;
ROL Hexdigits_H ;
DEX ;
BNE gw_loop1 ;
ora Hexdigits ;
sta Hexdigits
dey
bne gw_loop0 ; Loop until we get all in
clc ; Clear carry
gw_end
rts
;---------------------------------------------------------------------
; Compare Result N Z C
; A, X, or Y < Memory * 0 0
; A, X, or Y = Memory 0 1 1
; A, X, or Y > Memory * 0 1
;---------------------------------------------------------------------
; Get hex char
; C=0 if valid
; C=1 if invalid
get_hex_char
jsr input_char ; Get character, blocking
cmp #32 ; Control character?
bcc ghc_not_printable ; not printable
jsr output_char ; Echo character
ghc_not_printable
; Character in A
; Check if it is a hex
check_hex_char
cmp #'0'
bcc ghc_abort ; A<'0'
cmp #('9'+1)
bcs ghc_notnumber ; A>=('9'+1)
; Number
sec
sbc #'0'
jmp ghc_done
ghc_notnumber
and #$5f ; convert to upper
cmp #'A'
bcc ghc_abort ; A<'A'
cmp #('F'+1)
bcs ghc_abort ; A>=('F'+1)
sec
sbc #('A'-10)
ghc_done
clc
rts
ghc_abort
sec ; Set carry
rts
;---------------------------------------------------------------------
helptxt
.byte $0d,$0a,"6502 Monitor RC2014 v0.1.3"
.byte CR,LF,"? Print this help"
.byte CR,LF,"D XXXX Dump memory from XXXX"
.byte CR,LF,"E XXXX Edit memory from XXXX"
.byte CR,LF,"G XXXX Go execute from XXXX"
.byte CR,LF,"U Upload Intel HEX file",0
helptxt2:
.byte CR,LF," ESC to quit when upload is done"
.byte $0d, $0a
.byte $00
;---------------------------------------------------------------------
go_help
lda #CR
ldx #$ff ; set txt pointer
gh_loop
inx ;
jsr Output_char ; put character to Port
lda helptxt,x ; get message text
bne gh_loop ;
tax ; Init x = 0
gh_loop2
lda helptxt2, x
beq gh_end
jsr output_char
inx
bne gh_loop2
gh_end
jmp monitor_loop
;---------------------------------------------------------------------
; Display byte at an address
; Change byte at the address
; ENTER to go to next address
; ESC to escape back to monitor
; enter hex value to replace current value and skip to next address
go_mem
lda #$00
sta Startaddr
sta Startaddr_H
jsr print1sp ; Space out
; Get address
jsr get_word
bcc gm_0 ; All hex chars
cmp #$0d ; Is the last char RETURN?
bne gm_end ; No, then quit
gm_0
; cpy #$00 ; Did we grab a whole word?
; bne gm_end
; Copy the word to the address pointer
lda Hexdigits
sta Startaddr
lda Hexdigits_H
sta Startaddr_H
gm_loop0
jsr print_cr ; Go to next line
gm_loop1
; Print address
lda Startaddr_H
ldx Startaddr
jsr print2byte
jsr print1sp ; 1 Space
lda #':'
jsr output_char
jsr print1sp
; Print byte at address
ldy #$00
lda (Startaddr), y ; get byte at address
jsr Print1Byte ; Print byte
jsr print1sp
; Grab command
; Next is either ESC (quit), ENTER (next) or HEX value
gm_loop
; Init hex buffer first
lda #$00
sta Hexdigits
sta Hexdigits_H
; Get command or hex
jsr input_char ; Get a char
cmp #27 ; ESC
beq gm_end ; ESC so abort
cmp #$0d ; ENTER
bne gm_not_enter ; Not ENTER, data entry mode?
;
; ENTER, increment Startaddr to next memory address
;
gm_next_address
inc Startaddr
bne gm_inc_done
inc Startaddr_H
gm_inc_done
jmp gm_loop0 ; Get next byte and display it
; Check if hex
; A=first character
gm_not_enter
tay ; Save char
; Char in A
jsr check_hex_char
bcs gm_loop0 ; Not hex, reprint line
pha ; Save hex value
tya ; Get original char
jsr output_char ; Echo it
pla ; Restore hex value
ldy #$02
jsr build_byte ; Get the full byte
cpy #$00 ; Are we done?
bne gm_loop0
sta (Startaddr), y ; Store byte at address
jsr print1sp ; Print space
ldy #$00
lda (Startaddr), y ; get byte at address
jsr Print1Byte ; Print byte at address
jmp gm_next_address ; Next
gm_end
jmp monitor_loop
;---------------------------------------------------------------------
go_dump
dump_mem
lda #$00
sta Startaddr
sta Startaddr_H
jsr print1sp ; Space out
; Get address
jsr get_word
bcc dm_0 ; All hex chars
cmp #$0d ; Is the last char RETURN?
bne dm_end ; No, then quit
dm_0
; cpy #$00 ; Did we grab a whole word?
; bne dm_end
; Copy the word to the address pointer
lda Hexdigits
sta Startaddr
lda Hexdigits_H
sta Startaddr_H
dm_loopx
lda #16
sta rowcount
dm_loop0
jsr print_cr ; Go to next line
dm_loop1
; Print address
lda Startaddr_H
ldx Startaddr
jsr print2byte
jsr print2sp ; 1 Space
; Read 16 bytes
ldy #$00
ldx #$10
dm_loop2
lda (Startaddr), y
jsr print1byte
jsr print1sp
iny
dex
bne dm_loop2
; Next section
lda #';'
jsr output_char
jsr print1sp
ldy #$00
ldx #$10
dm_loop3
lda (Startaddr), y ; Get byte
cmp #32 ; Control character?
bcc not_printable ; not printable
cmp #127 ; non-ascii characters?
bcs not_printable
dm_xx
jsr output_char ; Print character
jmp dm_next0
not_printable
lda #'.' ; Print . instead
jmp dm_xx
dm_next0 ; Incrememt address pointer
inc startaddr
bne dm_next1
inc startaddr_h
dm_next1 ; Have we done 16 bytes?
dex
bne dm_loop3 ; Reloop if not
jsr print_cr ; Go to next row
dec rowcount ; Have we done 16 rows?
bne dm_loop1 ; Next row if not
lda #>dm_prompt ; Print prompt
ldx #<dm_prompt
jsr $ff0c
jsr input_char ; Get user input
cmp #27 ; Escape?
bne dm_loopx ; Next 16 lines
dm_end
jmp monitor_loop
dm_prompt .byte "Press any key to continue, ESC to abort",0
;---------------------------------------------------------------------
; Print version
Version jsr Print_CR ;
lda #>buildtxt
ldx #<buildtxt
jsr PrintStrAX
rts ;
;
;-----------DATA TABLES ------------------------------------------------
;
Hexdigdata .byte "0123456789ABCDEF";hex char table
;---------------------------------------------------------------------
;
buildtxt
.byte $0d, $0a
.byte "6502 CPU for RC2014"
.byte $0d, $0a
.byte $00
monitorprompt .byte "Monitor >"
.byte $00
;end of file