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