title Build HEX From COM File name ('UNLOAD') ; ; DASMed version of UNLOAD.COM ; ; Origin from MICROCode Consulting ; .z80 aseg org 0100h BDOS equ 0005h FCB equ 005ch CCP equ 0080h DMA equ 0080h TPA equ 0100h .string equ 9 .open equ 15 .close equ 16 .rdseq equ 20 .wrseq equ 21 .make equ 22 .setdma equ 26 .usrcod equ 32 .drv equ 1 .nam equ 8 .ext equ 3 _DIR equ 16 _CR equ 32 FCBlen equ 35 reclng equ 128 WrRecs equ 128 tab equ 09h lf equ 0ah cr equ 0dh eof equ 1ah eot equ '$' MAXUSR equ 31 ; CHANGE TO 15 FOR CP/M 3 HEXlen equ 32 Inplen equ 80 NoMSB equ 01111111b HiMask equ 11110000b LoMask equ 00001111b sbc a,a ; Verify Z80 ld de,NoZ80 ld c,.string jp pe,BDOS ; Nope ld sp,LocStk ; Get local stack ld de,PRGMSG call String ; Tell what we are ld hl,CCP ld e,(hl) ; Get length of command line inc hl ld d,0 add hl,de ld (hl),d ; Close line ld hl,CCP+1 call SkpSpc ; Skip spaces jr z,NoCommand ; End of line ld de,FCB call Parse ; Parse file jr nc,COM2HEX ; Ok NoCommand: ld de,HELPMSG ; Give help LastMsg: call String rst 0 ; ; Give error message and exit ; ErrMsg: call String ld de,ENDMSG jr LastMsg ; ; Print string ^DE on console ; String: ld c,.string jp BDOS ; ; Process conversion ; COM2HEX: push hl ld e,c ; Get user ld c,.usrcod call BDOS ; Select it ld hl,FCB ld de,FOT ld bc,FCBlen ldir ; Copy source file ld hl,FOT+.drv+.nam ld (hl),'H' ; Set extension of destination inc hl ld (hl),'E' inc hl ld (hl),'X' ld hl,FCB+.drv+.nam ld a,(hl) cp ' ' ; Test source file extension jr nz,reset ; Got it ld (hl),'C' ; Set default extension inc hl ld (hl),'O' inc hl ld (hl),'M' reset: ld de,FCB ld c,.open call BDOS ; Open source file inc a ld de,OPENMSG jp z,ErrMsg ; Not found ld de,FOT ld c,.open call BDOS ; Open destination file inc a jr nz,Exist ; Already on disk ld de,FOT ld c,.make call BDOS ; Create destination file inc a ld de,CREATEMSG jp z,ErrMsg ; Creation error Exist: ld (RdFlag),a ; Set file mode - 0 is read in progress ld hl,DMA+reclng-1 ld (RdPtr),hl ; Force read ld hl,FODMAptr ld (WrPtr),hl ; Init buffer pointer ld hl,WrRecs*reclng ld (WrLen),hl ; Init buffer length pop hl call SkpSpc ; Skip spaces ; ; Conversion routine ; ld de,TPA ; Set default address jr z,COM2HEXgo ; End of line ex de,hl ld h,l ; Clear a bit ld b,h NxtAdr: ld a,h and HiMask ; Mask hi jr nz,IllAdr ; Overflow if already defined add hl,hl ; * 2 add hl,hl ; * 4 add hl,hl ; * 8 add hl,hl ; *16 ld a,(de) ; Get digit inc de sub '0' ; Strip off offset jr c,HexErr ; Invalid cp 9+1 ; Test decimal jr c,BldStrt ; Yeap sub 'A'-'0'-10 ; Verify hex jr c,HexErr ; Invalid cp 15+1 jr c,BldStrt HexErr: ld de,HEXERMSG ; Invalid hex .ErrMsg: jp ErrMsg IllAdr: ld de,ADRERRMSG jr .ErrMsg BldStrt: ld c,a add hl,bc ; Insert digit ld a,(de) or a ; Test end jr nz,NxtAdr ; Nope, get next ex de,hl ; Get address ; ; >>>> MAIN JOB <<<< ; COM2HEXgo: ld b,HEXlen ; Set count ld hl,COMbuff ; Init binary buffer COMfill: call fget ; Read byte from file jr c,LastHEX ; End of file ld (hl),a ; Unpack into internal buffer inc hl djnz COMfill LastHEX: push af ; Save end of file flag ld a,':' call fput ; Put indicator to file ld a,HEXlen sub b ; Test all bytes processed jr z,LineFilled ; Nope, must be end ld b,a ; Set length ld c,0 ; Init checksum call fputbyte ; Output length ld a,d call fputbyte ; Output address ld a,e call fputbyte xor a call fputbyte ; Output zero ld h,0 ld l,b add hl,de ; Fix address ex de,hl ld hl,COMbuff ; Init buffer push bc HEXfill: ld a,(hl) ; Get byte call fputbyte ; Put to file inc hl djnz HEXfill ld a,c ; Get checksum pop bc neg ; Negate it call fputbyte ; Put to file ld a,cr call fput ; Close ld a,lf call fput pop af ; Get back end of file flag jr nc,COM2HEXgo ; Still more ; ld a,':' call fput ; Give final delimiter LineFilled: xor a ; Clear byte ld c,a ; Clear checksum call fputbyte ld a,1 call fputbyte ; Output final sequence xor a call fputbyte ld a,1 call fputbyte ld a,c neg call fputbyte ld a,cr call fput ; Close line ld a,lf call fput ld a,eof call fput ; Close file ld hl,(WrPtr) ; Get buffer pointer ld de,FODMAptr or a sbc hl,de ; Test buffer emptied jr z,NoBuffer ; Yeap ld de,reclng-1 add hl,de add hl,hl ld b,h call WrBuff NoBuffer: ld de,FOT ld c,.close call BDOS ; Close destination file ld de,CLOSEMSG inc a jp z,ErrMsg ; Cannot close file ld de,RDYMSG jp LastMsg ; Tell done and exit ; ; Put ASCII byte to file updating checksum ; fputbyte: push af add a,c ; Add to checksum ld c,a pop af push af rlca ; Extract hi bits rlca rlca rlca call fputnyb ; Convert to ASCII pop af fputnyb: and LoMask ; Mask bits cp 9+1 ; Test decimal jr c,fputdec add a,'A'-'0'-10 ; Fix for hex fputdec: add a,'0' ; Make ASCII ; ; Put character in Accu to file ; fput: push hl push de push bc WrPtr equ $+1 ld hl,$-$ ; Load buffer pointer WrLen equ $+1 ld bc,$-$ ; Load buffer length ld (hl),a ; Store character inc hl ; Advance buffer dec bc ; Count down length ld a,b or c ; Test any free space jr nz,StBuffer ; Yeap ld b,WrRecs call WrBuff ; Write buffer to file ld hl,FODMAptr ld bc,WrRecs*reclng StBuffer: ld (WrLen),bc ; Set buffer length ld (WrPtr),hl ; Set buffer pointer pop bc pop de pop hl ret ; ; Write buffer to file with record count in reg B ; WrBuff: ld hl,FODMAptr ; Init base buffer address WrNext: push hl push bc ex de,hl ld c,.setdma call BDOS ; Set disk buffer call WrRec ; Write record pop bc pop hl ld de,reclng add hl,de ; Advance buffer djnz WrNext ; Fill entire buffer ret ; ; Write record to file ; WrRec: ld de,FOT ld c,.wrseq call BDOS ; Write record to destination file or a ; Test success ret z ; Ok ld de,DIRMSG jp ErrMsg ; Tell write error ; ; Read byte from file - C set indicates end of file ; fget: push hl push de push bc RdPtr equ $+1 ld hl,$-$ ; Load buffer [0080..00FF] inc l ; Advance it jr nz,fgbuff ; Still in range ld de,DMA ld c,.setdma call BDOS ; Set disk buffer ld de,FCB ld c,.rdseq call BDOS ; Read from source file or a ; Test end ld hl,DMA ; Init base buffer jr z,fgetok ; Read successfully RdFlag equ $+1 ld a,$-$ ; Get read progress flag ld de,LENERMSG or a jp nz,ErrMsg ; Zero length file scf ; Set end of file jr fgetend fgetok: xor a ld (RdFlag),a ; Set read in progress fgbuff: ld (RdPtr),hl ; Save current buffer ld a,(hl) ; Get byte fgetend: pop bc pop de pop hl ret ; ; Parse file ^DE from ^HL ; Return user in reg C ; Carry set on error ; Parse: push hl push de ld hl,FCB.NULL ld bc,_DIR ldir ; Preset FCB ld c,_CR-_DIR ex de,hl add hl,bc ld (hl),b ; Clear current record pop de pop hl push de push hl inc de ld bc,256*.nam+11111111b ; ; Control byte in reg C: ; ; Bit:: 7.6.5.4.3.2.1.0 ; 0.x.x.x.x.x.x.x User selected ; x.0.x.x.x.x.x.x File name selected ; x.x.0.x.x.x.x.x Extension selected ; ; ParseFCB: ld a,(hl) ; Get character and NoMSB ; Less attribute inc hl cp ' '+1 ; Test valid character jr c,FCBend ; Nope cp '*' ; Test wild card jr z,Wildcard cp ':' ; Test drive delimiter jr z,ParseDel cp ';' jr z,ParseDel ; Or other cp '.' ; Test extension follows jr nz,FCB.store bit 5,c ; Test extension already selected jp z,ParseErr ; Yeap, error dec hl ; Fix pointer ld a,' ' ; Blank remainder FCB.store: ld (de),a ; Unpack character to FCB inc de djnz ParseFCB ParseNext: ld a,(hl) ; Get character inc hl cp ' '+1 ; Test valid one jr c,FCBend ; Yeap cp '.' ; Test extension delimiter jp nz,ParseErr ; Nope, should be bit 5,c ; Test extension already selected jp z,ParseErr ; Yeap, error res 5,c ; Indicate extension in progress ld b,.ext ; Set new count jr ParseFCB ; Sample extension FCBend: ld a,c and MAXUSR ; Mask user bit 7,c ; Test valid user jr z,ParseUser ; Yeap ld a,11111111b ; Set no user ParseUser: ld c,a dec hl ld a,(hl) cp ' ' jr z,ParseEnd cp tab jr z,ParseEnd or a jr z,ParseEnd ParseErr: scf ; Set error ex (sp),hl ParseEnd: ex (sp),hl pop hl pop de ret ; ; Process wildcard ; Wildcard: ld a,'?' StWild: ld (de),a inc de djnz StWild jr ParseNext ; ; Process delimiter ; ParseDel: ld a,c ; Get state and 11100000b ; Mask bits xor 11100000b ; Test any cleared jr nz,ParseErr ; Yeap, error ld (de),a ; Unpack ld a,b ; Get count cp .nam ; Test name in progress jp z,ParseFN cp 5 jr c,ParseErr pop ix pop de push de push ix ex de,hl inc hl ld a,(hl) ; Get character sub 'A' ; Make binary jr c,ParseNodrv cp 'P'-'A'+1 ; Verify in range jr nc,.ParseErr ; Nope, error inc a ; Make 1 relative dec hl ld (hl),a ; Save drive inc hl ld (hl),' ' ; Clear next inc hl ParseNodrv: ld a,(hl) ; Get next ld (hl),' ' ; Clear it or a ; Test end jr z,.ParseFN ; Yeap sub '0' ; Strip off offset jr c,.ParseErr ; Not a digit, error cp 9+1 jr nc,.ParseErr ld b,a ; Save digit inc hl ld a,(hl) ; Get next ld (hl),' ' ; Clear this one inc hl ld (hl),' ' ; And next one or a ; Was end? jr z,GotUser ; Yeap sub '0' ; Strip off offset jr c,.ParseErr ; Not a digit, error cp 9+1 jr nc,.ParseErr ld c,a ; Save 2nd one ld a,b add a,a ; * 2 ld b,a add a,a ; * 4 add a,a ; * 8 add a,b ; *10 ld b,c GotUser: add a,b ; Add digit cp MAXUSR+1 jr nc,.ParseErr ; Out of range or 00100000b ; Indicate user number ld c,a .ParseFN: ex de,hl ParseFN: res 6,c ; Indicate filename follows pop ix pop de push de push ix inc de ld b,.nam ; Reset count jp ParseFCB .ParseErr: ex de,hl jp ParseErr ; FCB.NULL: db 0,' ',0,0,0,0 ; ; Skip white spaces ^HL - Z set indicates end of line ; SkpSpc: dec hl .SkpSpc: inc hl ld a,(hl) cp ' ' jr z,.SkpSpc cp tab jr z,.SkpSpc or a ret ; PRGMSG: db cr,lf db 'UNLOAD v1.1 (c) 1987 MICROCode Consulting' db cr,lf,lf,eot RDYMSG: db 'Done.' db cr,lf,eot HELPMSG: db 'Usage: UNLOAD filename[.ext] [StartAddr]' db cr,lf db ' Creates a .HEX file with the same name where' db cr,lf db ' values in brackets [] are optional.' db cr,lf db ' Assumes .COM input extension if none given and' db cr,lf db ' Start Address of 0100H if none given.' db cr,lf,eot OPENMSG: db '### Missing source file',eot CREATEMSG: db '### Error creating .HEX file',eot CLOSEMSG: db '### Cannot close .HEX file',eot DIRMSG: db '### Error writing .HEX file',eot LENERMSG: db '### File is zero size',eot NoZ80: db '### Z80?' ENDMSG: db ' ###',cr,lf,eot ADRERRMSG: db '### Start addr overflow',eot HEXERMSG: db '### Bad hex digit',eot ; ds 2*32 LocStk equ $ ; COMbuff: ds HEXlen ; Binary buffer FOT equ COMbuff+Inplen FODMAptr equ FOT+FCBlen end