py65/examples/65Org16.boot.asm

237 lines
8.4 KiB
NASM

.LIST
; bootrom for py65 monitor in 65Org16 mode
; based on
; Intel Hex format loader by Ross Archer (9 February 2001, freeware)
; from http: http://www.6502.org/source/monitors/intelhex/intelhex.htm
;
; use this monitor like this:
; py65mon -m 65org16
; load bootrom.bin fe00
; goto fe00
START = $FFFFFe00
; I/O is memory-mapped in py65:
PUTC = $f001
GETC = $f004
; Note that Hex format for 65Org16 uses ';' not ':' as the start of record mark
; also note that some fields are now composed of 16-bit elements:
; previously:
; length offset type data checksum
; :/08/E008/00/08090A0B0C0D0E0F/xx
; now
; ;/10/E008/00/00080009000A000B000C000D000E000F/xx
; Zero-page storage
DPL = $00 ; data pointer (two bytes) used by PUTSTRI
DPH = $01 ; high of data pointer
RECLEN = $02 ; record length in bytes
START_LO = $03
START_HI = $04
RECTYPE = $05
CHKSUM = $06 ; record checksum accumulator
DLFAIL = $07 ; flag for download failure
TEMP = $08 ; save hex value
TMPHEX = $09 ; save another hex value
; where the RAM program MUST have its first instruction
ENTRY_POINT = $0200
.ORG START
sei ; disable interrupts
cld ; binary mode arithmetic
ldx #$1FFFF ; Set up the stack pointer
txs ; "
; Download Intel hex. The program you download MUST have its entry
; instruction (even if only a jump to somewhere else) at ENTRY_POINT.
HEXDNLD lda #0
sta START_HI ; store all programs in bank 0 (page 0) for now
sta DLFAIL ;Start by assuming no D/L failure
jsr PUTSTRI
.byte 13,10,13,10
.byte "Send 65Org16 code in"
.byte " variant Intel Hex format"
.byte " at 19200,n,8,1 ->"
.byte 13,10
.byte 0 ; Null-terminate unless you prefer to crash.
HDWRECS jsr GETSER ; Wait for start of record mark ';'
cmp #';'
bne HDWRECS ; not found yet
; Start of record marker has been found
lda #0
sta CHKSUM
jsr GETHEX ; Get the record length
sta RECLEN ; save it
jsr GET4HX ; Get the 16-bit offset
sta START_LO
jsr GETHEX ; Get the record type
sta RECTYPE ; & save it
bne HDER1 ; end-of-record
ldx RECLEN ; number of data bytes to write to memory
ldy #0 ; start offset at 0
HDLP1 jsr GET4HX ; Get the first/next/last data word
sta (START_LO),y ; Save it to RAM
iny ; update data pointer
dex ; decrement character count
dex ; ... twice
bne HDLP1
jsr GETHEX ; get the checksum
lda CHKSUM
bne HDDLF1 ; If failed, report it
; Another successful record has been processed
lda #'#' ; Character indicating record OK = '#'
sta PUTC ; write it out but don't wait for output
jmp HDWRECS ; get next record
HDDLF1 lda #'F' ; Character indicating record failure = 'F'
sta DLFAIL ; download failed if non-zero
sta PUTC ; write it to transmit buffer register
jmp HDWRECS ; wait for next record start
HDER1 cmp #1 ; Check for end-of-record type
beq HDER2
jsr PUTSTRI ; Warn user of unknown record type
.byte 13,10,13,10
.byte "Unknown record type $"
.byte 0 ; null-terminate unless you prefer to crash!
lda RECTYPE ; Get it
sta DLFAIL ; non-zero --> download has failed
jsr PUTHEX ; print it
lda #13 ; but we'll let it finish so as not to
jsr PUTSER ; falsely start a new d/l from existing
lda #10 ; file that may still be coming in for
jsr PUTSER ; quite some time yet.
jmp HDWRECS
; We've reached the end-of-record record
HDER2 jsr GETHEX ; get the checksum
lda CHKSUM ; Add previous checksum accumulator value
beq HDER3 ; checksum = 0 means we're OK!
jsr PUTSTRI ; Warn user of bad checksum
.byte 13,10,13,10
.byte "Bad record checksum!",13,10
.byte 0 ; Null-terminate or 6502 go bye-bye
jmp START
HDER3 lda DLFAIL
beq HDEROK
;A download failure has occurred
jsr PUTSTRI
.byte 13,10,13,10
.byte "Download Failed",13,10
.byte "Aborting!",13,10
.byte 0 ; null-terminate every string yada yada.
jmp START
HDEROK jsr PUTSTRI
.byte 13,10,13,10
.byte "Download Successful!",13,10
.byte "Jumping to location $"
.byte 0 ; by now, I figure you know what this is for. :)
lda #HI(ENTRY_POINT) ; Print the entry point in hex
jsr PUTHEX
lda #LO(ENTRY_POINT)
jsr PUTHEX
jsr PUTSTRI
.byte 13,10
.byte 0 ; stop lemming-like march of the program ctr. thru data
jmp ENTRY_POINT ; jump to canonical entry point
; For py65, the input routine will block until a character arrives
GETSER lda GETC
beq GETSER
rts
; get four ascii chars, adding both octets into the checksum
GET4HX jsr GETHEX
asl a
asl a
asl a
asl a
asl a
asl a
asl a
asl a
sta TMPHEX
jsr GETHEX
ora TMPHEX
rts
; get two ascii chars, add into the checksum
GETHEX jsr GETSER
jsr MKNIBL ; Convert to 0..F numeric
asl a
asl a
asl a
asl a ; This is the upper nibble
and #$F0
sta TEMP
jsr GETSER
jsr MKNIBL
ora TEMP
sta TEMP
clc
adc CHKSUM ; Add in the checksum
and #$ff
sta CHKSUM ;
lda TEMP
rts ; return with the nibble received
; Convert the ASCII nibble to numeric value from 0-F:
MKNIBL cmp #'9'+1 ; See if it's 0-9 or 'A'..'F' (no lowercase yet)
bcc MKNNH ; If we borrowed, we lost the carry so 0..9
sbc #7+1 ; Subtract off extra 7 (sbc subtracts off one less)
; If we fall through, carry is set unlike direct entry at MKNNH
MKNNH sbc #'0'-1 ; subtract off '0' (if carry clear coming in)
and #$0F ; no upper nibble no matter what
rts ; and return the nibble
; Put byte in A as hexydecascii
PUTHEX pha ;
lsr a
lsr a
lsr a
lsr a
jsr PRNIBL
pla
PRNIBL and #$0F ; strip off the low nibble
cmp #$0A
bcc NOTHEX ; if it's 0-9, add '0' else also add 7
adc #6 ; Add 7 (6+carry=1), result will be carry clear
NOTHEX adc #'0' ; If carry clear, we're 0-9
; Write the character in A as ASCII:
PUTSER sta PUTC
rts
;Put the string following in-line until a NULL out to the console
PUTSTRI pla ; Get the low part of "return" address (data start address)
sta DPL
pla
sta DPH ; Get the high part of "return" address
; (data start address)
; Note: actually we're pointing one short
PSINB ldy #1
lda (DPL),y ; Get the next string character
inc DPL ; update the pointer
bne PSICHO ; if not, we're pointing to next character
inc DPH ; account for page crossing
PSICHO ora #0 ; Set flags according to contents of Accumulator
beq PSIX1 ; don't print the final NULL
jsr PUTSER ; write it out
jmp PSINB ; back around
PSIX1 inc DPL ;
bne PSIX2 ;
inc DPH ; account for page crossing
PSIX2 jmp (DPL) ; return to byte following final NULL
;
; Dummy interrupt handlers
GOIRQ
GONMI RTI
; vectors
.ORG $FFFFFFFA
NMIENT .word GONMI
RSTENT .word START
IRQENT .word GOIRQ
.end ; finally. das Ende.