GMW@psuvm.BITNET (06/05/85)
.TITLE Hxload - Load ASCII hex files in KIM format
; For Commodore 64 with 1541 disk
; adapted from Apple version of Antonino N. J. Mione
; By Dave Dermott, Eric Lavitsky
; C64 Kernal entry points
chrin = $ffcf ; input a character
chrout = $ffd2 ; ouput a byte to current channel
chkin = $ffc6 ; change Kernal input channel
setlfs = $ffba ; set open parameters
setnam = $ffbd ; set file name
open = $ffc0 ; open a channel
close = $ffc3 ; close a channel
clall = $ffe7 ; Close all channels
ioinit = $ff84 ; Initialize I/O devices
restoi = $ff8a ; Restore default I/O vectors
stop = $ffe1 ; Scan for stop key
clrchn = $ffcc ; clear channel
readst = $ffb7 ; read the status word
getin = $ffe4 ; input a character
dos = $a002 ; BASIC NMI vector
buff = $200 ; Buffer for disk message
space = $20 ; <sp>
comma = $2c ; ','
semi = $3b ; ';'
cr = $0d ; <cr>
spc = $20 ; <sp>
stat = $90 ;
laddr = $fb ; Zero page pointer for load
saddr = $fd ; Zero page pointer for prstr
count = $c3
lind = $17
hold = $19
staddr = $c000 ; Address for high loader
;staddr = $0810 ; Address for low loader
.ifeq <staddr-$0810>
; When starting at $0810 include BASIC calling sequence
; 10 SYS(2064)
.= $0801
.byte $0d,$08,$0a,$00 ; Line 10 in BASIC
.byte $9e ;SYS
.ascii /(2064)/
.byte $00,$00,$00 ;End of line
.byte $00
.endc
.= staddr
dxst: jmp start ; Jump to start of code
; Working storage areas
chksum: .byte $00,$00
add2: .byte $00,$00
sav2: .byte $00
fnerc: .byte 'I,'0
fnam: .byte $00
.=.+17
len: .byte $00
hxwrk1: .byte $00
hxwrk2: .byte $00
hxwrk3: .byte $00
; Message area
mess1: .byte $0d
.ascii /HEX OBJECT LOADER/
.byte $0d
.asciz /OBJECT FILE NAME? /
mess2: .byte $0d
.asciz /CHECKSUM ERROR/
mess3: .byte $0d
.asciz /DISK ERROR/
mess4: .byte $0d
.asciz /ABORT/
mess5: .asciz / [OK]/
eol: .byte $0d
.asciz /END OF LOAD/
; Macros
; Macro to load address in X,Y
.macro ldadr adr
ldx #adr\ ;load low addr.
ldy #adr^ ;load high
.endm
;Macro to open file
.macro openm lun,dev,scadr,name,len
lda lun ;Log. unit no.
ldx dev ;Dev. no.
ldy scadr ;Sec. address
jsr setlfs
ldadr name ;File-name
lda len ;No. of bytes in file-name
jsr setnam
jsr open
.endm
start: jsr clall ; close all channels
lda #$00 ; clear status
sta stat ; ...
ldadr mess1 ; Print start message
jsr prstr ; ...
ldx #0 ; Clear len of file name
stx len ; ...
gtfnm: jsr chrin ; Get a character from keyboard
cmp #cr ; Stop on cr
beq gtfn2 ; ...
cmp #spc ; Ignore spaces
beq gtfnm ; ...
sta fnam,x ; Put in fnam
stx len ;
inx ;
bne gtfnm ;
gtfn2: ldx len ;
inx ;
lda #comma ;
sta fnam,x ; put in comma
inx ;
lda #'S ; put in S
sta fnam,x ;
inx ;
stx len ; final length
lda #cr ;
jsr chrout ; print a cr
lda #cr
jsr chrout
openm #15,#8,#15,fnerc,#2 ; open error channel
jsr readst ; check IO status
beq dskok ;
cmp #$40 ;
bne dskerr ;
dskok: openm #7,#8,#7,fnam,len ; open input file
jsr readst ; check IO status
beq dok2 ; no error
cmp #$40 ; 64
beq dok2 ;
dskerr: ldadr mess3 ; IO error
jmp errors ;
dok2: jsr rddst ; Read disk status
ldx #$07 ; Open channel 7 for disk input
jsr chkin ; ...
inloop: jsr stop ; stop key pressed?
bne receve ; no
ldadr mess4 ; yes, set 'abort' message
jmp errors ; abort the load
receve: jsr fgetc ; get a character from disk
jmp clsit ; Close the file and leave
cmp #semi ; Is it the beginning of a line?
bne inloop ; keep looking
lda #cr ;
jsr chrout ;
lda #semi ; Print a semi-colon
jsr chrout ; ...
jsr doline ; Otherwise read a line of ascii hex
jmp receve ; If no error or done, go back for next line
clsit: jsr closem ; end
jmp (dos) ; Restart BASIC
;
; Doline routine
;
doline: ldy #$00 ; Zero the
sty lind ; load point index
sty chksum ; and the checksum
sty chksum+1 ; ...
dolin1: jsr getbyt ; Get a hex byte
pha ; Save it on the stack
jsr by2hx ; Display it on the screen
pla ; Fetch it back
cmp #$00 ; Is it zero?
bne norskp ; If not, continue gobbling line
jmp rskp ; Otherwise, return with a skip
norskp: pha ; Save it on the stack
lda #space ; Get a space
jsr chrout ; And print it
pla ; Fetch the character back
jsr chksad ; Add it to the checksum
sta count ; The first byte is a count, store it
jsr getbyt ; Get the next hex byte
jsr chksad ; Add it to the checksum
sta laddr+1 ; That is the H.O. byte of the load address
jsr getbyt ; Get the next byte
jsr chksad ; Add that into the checksum
sta laddr ; That is the L.O. byte of the load address
jsr prnyb ; Print the load address on the screen
dolin3: dec count ; Decrement the count
ldx count ; Load it into X
bmi dolchk ; If we are done, do the checksum stuff
jsr getbyt ; Get a byte
ldy lind ; Get the load point index
sty len ; Save it for end of program pointer
jsr chksad ; Add the data into the checksum
sta (laddr),y ; Stuff the data where it belongs
jsr by2hx ; Print it
inc lind ; Up the load point index once
jmp dolin3 ; Loop for more data
dolchk: jsr getbyt ; Get another byte from disk
pha ; Stuff it on the stack
jsr by2hx ; Print it
pla ; Fetch back the character
cmp chksum+1 ; Compare that with the checksum
bne chkerr ; If they don't match, we have problems
lda chksum+1 ; Get the H.O. byte of the checksum
jsr by2hx ; Print it as hex
lda #space ; Get a space
jsr chrout ; and print it
jsr getbyt ; Get another byte from disk
pha ; Hold it on the stack
jsr by2hx ; Print it
pla ; Fetch the data back
cmp chksum ; Compare that to the L.O. byte of the checksum
bne chkerr ; If they don't match, tell the user
lda chksum ; Fetch the L.O. byte of the checksum
jsr by2hx ; Print that as hex
ldx #mess5\ ; Get the address of the 'OK' message
ldy #mess5^ ; ...
jsr prstr ; Print that text
lda #cr ; Print a cr
jsr chrout ; ...
rts ; Return, this line is OK
chksad: pha ; save the accumulator accross this call
clc ; clear the carry flag for addition
adc chksum ; add in the checksum
sta chksum ; stuff that back
lda chksum+1 ; adjust the h.o. byte of the checksum
adc #$00 ; by whatever is in the carry
sta chksum+1 ; ...
pla ; restore the AC
rts ; return
chkerr: ldadr mess2 ; get the address of the 'checksum error' mess.
jsr prstr ; print that error text
rts ; Return
errors: jsr prstr ; print error message
closem: lda #7 ; close the disk file
jsr close ; ...
lda #15 ; and the error channel
jsr close ; ...
clc ;
lda len ;
adc laddr ; form last load address
sta laddr ;
lda laddr+1 ;
adc #$00 ;
sta laddr+1 ;
lda #cr ; Print a <cr>
jsr chrout ; ...
jsr prnyb ; Print laddr
ldadr eol ; Get the address of the message
jsr prstr ; Print end of load
jsr clall ; Close all channels
brk ; Give control back to whoever is
; Servicing interrupts (BASIC,Supermon etc.)
getbyt: jsr getnib ; Get the first nibble of the two
bcc gterr ; Error
sta hold ; Stuff that here
jsr getnib ; Get the second nibble
bcc gterr ;
sta hold+1 ; Hold that here
lda hold ; Get the first of the two
asl a ; Shift it to the left side
asl a ; ...
asl a ; ...
asl a ; ...
ora hold+1 ; Combine that with the second nibble
sec ; Set no error
rts ; and return
gterr: clc ; ...
rts ; Error return
;
; Getnib routine
;
getnib: jsr fgetc ; Read a character from the file
jmp clsit ; Error!
getnb: cmp #$30 ; is it in the proper range?
bcc badchr ; 0<=char<=f
cmp #$3a ; ...
bcc getnb3 ; This is good, no special adjustment
cmp #$41 ; ...
bcc badchr ; ...
cmp #$47 ; ...
bcc getnb2 ; This is good, but adjust it down a bit
jmp badchr ; Bad character in input
getnb2: sec ; Set the carry flag
sbc #$07 ; Adjust the letters into the proper range
getnb3: sec ; Set the carry flag
sbc #$30 ; Make the character numeric
sec ; Set C for no error
rts ; and return
badchr: pla ;
pla ;
pla ;
pla ;
pla ;
pla ;
jmp receve ;
fgetc: jsr chrin ; Get char.
pha ; Save it
jsr readst ; Check status
and #$bf ; ...
beq getok ; No error
ldadr mess3 ; Print error message
rts ; Return
getok: pla ; Retrieve char
jmp rskp ; Skip return
prnyb: lda laddr ; Save low order
pha ; on stack
lda laddr+1 ; Get high order
jsr by2hx ; Print high order
pla ; Retreive low order
by2hx: pha ; Save byte
lsr a ;
lsr a ;
lsr a ;
lsr a ;
jsr ny2hx ; High nyble
tax ; to X
pla ; Get back
and #$0f ; Low nyble
jsr ny2hx ;
pha ;
txa ;
jsr chrout ; Print first nyble
pla ;
jmp chrout ; Print second nyble
ny2hx: clc ; Translate nyble to hex
adc #$f6 ;
bcc ny2h2 ;
adc #$06 ;
ny2h2: adc #$3a ;
rts ;
; Print to screen
prstr: stx saddr ;
sty saddr+1 ;
ldy #$00 ;
prst1: lda (saddr),y ;
beq prn3 ; Stop on null
jsr chrout ; Output the character
iny ;
bne prst1 ;
prn3: rts ;
; Read disk status
rddst: ldx #15 ;
jsr chkin ;
ldy #0 ;
rdds1: jsr getin ; Set byte from disk status
cmp #cr ; End at cr
beq rdds2 ;
sta buff,y ; Save in buff
iny ;
bne rdds1 ;
rdds2: lda #0 ;
sta buff,y ;
lda buff ; Get 1st digit
sec ;
sbc #$30 ; Convert to binary
beq rddex ; If error=0 exit
pla ;
pla ; Restore stack
ldadr buff ; Point to disk error
jmp errors ;
rddex: rts ;
;
; Rskp - do a skip return
;
rskp: sta hxwrk1 ; Hold the accumulator
stx hxwrk2 ; Hold x
sty hxwrk3 ; Hold y
pla ; Fetch the L.O. byte of the return address
tax ; Put that in X
pla ; Get the H.O. byte of the return address
tay ; Stuff that in Y
txa ; Get the L.O. byte back again
clc ; Clear the carry flag
adc #$04 ; Add in 4 (3 byte inst. + 1 for not doing rts)
bcc rskp2 ; If there was no carry, continue
iny ; Increment the H.O. byte
rskp2: sta saddr ; Store the L.O. byte
sty saddr+1 ; and the H.O. byte
lda hxwrk1 ; Restore the accumulator
ldx hxwrk2 ; Restore x
ldy hxwrk3 ; Restore y
jmp (saddr) ; Jump to the new return address
.end