; FILEIO.MAC - 1/4/86 - Buffered file I/O. ; Two files may be open at once: one for read, one for write. .z80 maclib TNC.LIB entry @openn,@openr,@openw,@opena,@closew,rcv entry inibak,getbuf,copfil,kilfil,renfil,hidfil entry bkm1,bkm2,bkm3,bkm4,bkm5,bkm6,bkm7,bkm8 entry tobuf,fmbuf,@ntobuf,@ctobuf,rdcmd,rdstr,rdmstr entry setb7,rfcb,wfcb,ioini,ckdrv,ckro,defdrv,maxdrv external @mcmd,@cmpcmd,$memry,@outch,numb,bindec external eofs,leofs,addcr0,opt2,cmd,cmdlen external getdat,erexst,ercant,erdone,ername,erwhat,erfind external div8,memtop,ckname,fcb2,fcb3,@prtx,@fill asciictl bdosdef tncdefs dseg ro: ds 1 ; Read only drives. online: ds 1 ; Drives on line (logged in). maxdrv: ds 1 ; Highest drive online. defdrv: ds 1 ; Default drive. rb: ds 2 ; Address of read buffer. rbptr: ds 2 ; Read buffer pointer. rbcnt: ds 2 ; # bytes remaining in read buffer. rbeof: ds 1 ; True if eof read. rbsec: ds 1 ; # sectors read. rfcb: ds fcbsize ; Read file control block. wb: ds 2 ; Address of write buffer. wbptr: ds 2 ; Write buffer pointer. wbcnt: ds 2 ; # Bytes left in write buffer. wbsec: ds 1 ; # sectors in write buffer. wfcb: ds fcbsize ; Write file control block. wfcbrc equ wfcb+15 ; FCB: record count. wfcbcr equ wfcb+32 ; FCB: current record. wfcbrn equ wfcb+33 ; FCB: random record number. copst: ds 1 copbs: ds 2 copbp: ds 2 copbc: ds 2 copin: ds 2 tfcb: ds 2 curdrv: ds 1 cseg dosrc: ld (tfcb),hl dodosa setdma,defdma ld hl,(tfcb) ld a,(hl) ld (curdrv),a ld (hl),0 ld bc,12 add hl,bc ld (hl),0 or a ; Drive specified? jr z,dosrca ; No dec a ld e,a dodosa seldsk ; Make this drive the default dosrca: ld de,(tfcb) dodosa search push psw ld hl,(tfcb) ld a,(curdrv) ld (hl),a ld a,(defdrv) ld e,a dodosa seldsk ; Put back original default pop psw inc a ret ; Return in hl the number of sectors of free memory. getbuf: ld de,($memry) ; Start of free mem ld hl,(memtop) ; End of free mem or a ; Clear carry sbc hl,de ; Bytes available ld e,128 call div8 ld h,0 ret ; Read a bunch of sectors into buffer copr: movw copbp,$memry ; Point to start of buffer space movw copbc,copbs ; Init avail buffer count lxim copin,0 ; Init count of records read copra: ld de,(copbp) dodosa setdma ; Point IO to buffer dodosa read,fcb2 ; Read record ld (copst),a ; Save read status or a ; Done? ret nz ; Yes inxm copin ; Count records in buffer ld hl,(copbp) ld de,128 add hl,de ld (copbp),hl ; Point to next buffer dcxm copbc ; One less available ld a,l or h ; Any left jr nz,copra ; Yes, read next ret ; Write the bunch of sectors from buffer. copw: movw copbp,$memry ld hl,(copin) copwa: ld a,l or h ; Done? ret z ; Yes ld de,(copbp) dodosa setdma dodosa write,fcb3 ; Write record ld hl,(copbp) ld de,128 add hl,de ld (copbp),hl dcxm copin jr copwa ; The "copy file" command. copfil: ckname fcb2 ; Input name legal? jp z,ername ; No ld a,(fcb2) call ckdrv ; Input drive on line? jp nc,erfind ; No ckname fcb3 ; Output name legal? jp z,ername ; No ld a,(fcb3) call ckdrv ; Output drive on line? jp nc,erfind ; No ld a,(fcb3) call ckro ; Can we write on it? jp c,ercant ; No ld hl,fcb3 call dosrc ; Output file already exist? jp nz,erexst ; Yes dodosa open,fcb2 ; Open the input file inc a jp z,erfind ; No input file dodosa make,fcb3 ; Create the output file call copyf ; Do the copy jp erdone ; Copy file from fcb2 to fcb3. copyf: call getbuf ld (copbs),hl ; Sectors of buffer available copfa: call copr ; Read a bunch call copw ; Write em ld a,(copst) or a ; Done? jr z,copfa ; No dodosa close,fcb3 ret ; Delete a file. kilfil: ckname fcb2 ; Legal file name? jp z,ername ; No ld a,(fcb2) call ckdrv ; Drive on line? jp nc,erfind ; No ld a,(fcb2) call ckro ; Can we write on it? jp c,ercant ; No dodosa delete,fcb2 ; Delete the file inc a jp z,erfind ; Delete failed jp erdone ; Toggle $sys attribute on file. hidfil: ckname fcb2 ; File name legal? jp z,ername ; No ld a,(fcb2) call ckdrv ; Drive on line? jp nc,erfind ; No ld a,(fcb2) call ckro ; Can we write on it? jp c,ercant ; No ld hl,fcb2 call dosrc ; Get dir entry into buffer jp z,erfind ; No such file dec a add a,a add a,a add a,a add a,a add a,a ld hl,defdma ld e,a ld d,0 add hl,de ; (HL) point to dir entry ld a,(fcb2) ld (hl),a ; Set drive in dir entry push hl ld bc,10 ld a,(opt2) cp 'N' ; Toggle new attribute? jr nz,hidfa ; No, do $sys ld bc,4 hidfa: add hl,bc ld a,(hl) xor 80h ; Toggle the bit ld (hl),a pop hl ex de,hl ; (DE) point to dir entry dodosa setatr jp erdone ; Rename a file. renfil: ckname fcb2 ; Old name legal? jp z,ername ; No ckname fcb3 ; New name legal? jp z,ername ; No ld a,(fcb2) call ckdrv ; This drive on line? jp nc,erfind ; No ld a,(fcb2) call ckro ; Can we write on it? jp c,ercant ; No zmov fcb2+16,fcb3,12 ld hl,fcb3 call dosrc ; New already exist? jp nz,erexst ; Yes dodosa rename,fcb2 ; Rename the sucker inc a jp z,ercant ; Could not rename jp erdone ; Set attribute at (HL) setb7: ld a,(hl) or 80h ld (hl),a ret ; Initialize for backup. ; fcb2 contains file to be backed up (.DAT) ; fcb3 contains file to backup to (.BAK) ; Return zero cleared if success, set if failure. dseg renfcb: ds fcbsize cseg bkm1: ds 2 bkm2: ds 2 bkm3: ds 2 bkm4: ds 2 bkm5: ds 2 bkm6: ds 2 bkm7: ds 2 bkm8: ds 2 ; Check if .DAT exists. inibak: ld hl,fcb2 call dosrc ; Is a .DAT file? ld hl,(bkm1) jp z,inibe ; Tell no file dodosa close,fcb2 ; Close file. ; Delete .BAK prtx bkm5 dodosa delete,fcb3 ld hl,fcb3+10 call setb7 ; Set $sys attribute ; Rename or copy .DAT to .BAK ld a,(fcb3) ld hl,fcb2 cp m ; DAT and BAK on same device? jr z,inibb ; Yes ; Different devices, must copy fill fcb2+12,fcbsize-12,0 dodosa open,fcb2 fill fcb3+12,fcbsize-12,0 dodosa make,fcb3 prtx bkm7 call copyf dodosa delete,fcb2 ; Delete .DAT jr inibc ; Same device, can rename. inibb: prtx bkm6 zmov renfcb,fcb2,16 zmov renfcb+16,fcb3,16 dodosa rename,renfcb dec a ld hl,(bkm2) ; Point to msg jr z,inibd ; Tell error ; Make the new .DAT inibc: prtx bkm8 fill fcb2+12,fcbsize-12,0 ld hl,fcb2+10 call setb7 ; Set $sys attribute dodosa make,fcb2 ; Open .BAK fill fcb3+12,fcbsize-12,0 dodosa open,fcb3 inc a ld hl,(bkm3) ; Point to msg jr z,inibd ; Tell error retnz ; Return zero cleared, success inibd: call @prtx ; Tell what happened... ld hl,(bkm4) ; and is serious inibe: call @prtx retz ; Return zero set, failure ; Initialize buffered file I/O. ; Allocate fixed memory space. ioini: movw rb,$memry ld de,secsize*bufsize add hl,de ld (wb),hl add hl,de ld ($memry),hl ; Find out what drives available. dodosv ckdsks ld a,l ld (online),a ; Drives online ld c,9 fndmax: dec c rlca jr nc,fndmax ld a,c ld (maxdrv),a dodosv getro ld a,l ld (ro),a ; Read only drives dodosv ckcur ld (defdrv),a ; Current default drive ret ; Check if drive in (A) is online. ; Carry set if ok, cleared if not online. ckdrv: or a scf ; Good status ret z ; Default ld hl,maxdrv ld b,(hl) inc b cp b ret nc ; No more than maxdrv drives ld c,a ld a,(online) ckdrva: dec c rrca jr nz,ckdrva ret ; Check if drive in (A) is R/O. ; Carry set if R/O, cleared if R/W. ckro: or a ret z ; Default, carry cleared=R/W ld c,a ld a,(ro) ckroa: dec c rrca jr nz,ckroa ret ; Check if drive exists and is R/W ; Return zero set if not. ckwrt: ld a,(wfcb) call ckdrv jr nc,ckwrta ld a,(wfcb) call ckro jr c,ckwrta retnz ckwrta: retz ; Write the buffers to file. ; Return zero set if error. wtbuf: push bc push de call wtbufa pop de pop bc ret wtbufa: ld de,(wbcnt) ; Bytes unused in buffer ld hl,secsize*bufsize or a ; Clear carry sbc hl,de ld de,secsize-1 add hl,de ; Divide (hl)=(hl)/128 ld a,l rla ld a,h rla ld (wbsec),a ; Sectors used in buffer lxim wbcnt,secsize*bufsize movw wbptr,wb ex de,hl wtbufb: push de dodosa setdma dodosa write,wfcb pop de or a jr z,wtbufc retz ; Error wtbufc: ld hl,secsize add hl,de ex de,hl ld hl,wbsec dec (hl) jr nz,wtbufb retnz ; Buffered read - fill the buffer. ; Return zero set if error. rdbuf: push bc push de call rdbufa pop de pop bc ret rdbufa: cmpm rbeof,true ret z mvim rbsec,bufsize movw rbptr,rb ex de,hl rdbufb: push de dodosa setdma dodosa read,rfcb pop de or a jr z,rdbufc mvim rbeof,true dtz rbcnt ret rdbufc: ld bc,secsize ld hl,(rbcnt) add hl,bc ld (rbcnt),hl ld hl,secsize add hl,de ex de,hl ld hl,rbsec dec (hl) jr nz,rdbufb retnz ; Put character in C into buffer. ; Return zero set if error. tobuf: push hl dtz wbcnt call z,wtbuf pop hl ret z push hl ld hl,(wbptr) ld (hl),c inc hl ld (wbptr),hl dcxm wbcnt pop hl retnz ; Get char from file buffer, return in C. ; Return zero set if error/eof. fmbuf: push hl dtz rbcnt call z,rdbuf pop hl ret z push hl ld hl,(rbptr) ld c,(hl) inc hl ld (rbptr),hl dcxm rbcnt pop hl retnz ; (B) characters from (HL) to file buffer. @ntobuf: ld c,(hl) call tobuf inc hl dec b jr nz,@ntobuf ret ; Copy characters starting at (HL) to buffer until CR. ; Add LF after CR. @ctobuf: ld c,(hl) call tobuf inc hl ld a,c cp cr jr nz,@ctobuf ld c,lf call tobuf ret crlf: ld c,cr call tobuf ret z ld c,lf call tobuf ret ; Collect a text file from the current console device. rcv: call getdat ckcmd rcv,rcve,rcve cmpm cmd,eof ; Just a ^Z? jr z,rcvd ; Yes ld a,(cmdlen) or a ; Just a CR? jr z,rcvb ; Yes ld b,a ld hl,cmd rcva: ld a,(hl) cp eof jr z,rcvc ld c,a call tobuf jr z,rcve ; Error inc hl dec b jr nz,rcva rcvb: call crlf jr z,rcve jr rcv rcvc: call crlf jr z,rcve rcvd: ld c,eof call tobuf jr z,rcve ; Error closew ret rcve: dodosa close,wfcb dodosa delete,wfcb retz ; Open file for buffered read. ; Return zero set if error. @openr: call openny ret z mvim rbeof,false lxim rbcnt,0 retnz ; Open file. If drive not specified, look on all drives. openny: ld a,(rfcb) or a jr nz,opn3 opn1: inc a ld (rfcb),a call opn3 ret nz opn2: ld a,(rfcb) ld hl,maxdrv cp (hl) jr nz,opn1 ret opn3: ld a,(rfcb) call ckdrv jr c,opn4 retz opn4: dodosa open,rfcb inc a ret ; Open file for write, if it does not exist. ; Return zero set if not ok, cleared if ok. @openn: call ckwrt jr z,opnwnb ld hl,wfcb call dosrc ; File exist? jr nz,opnwna ; Yes call opnwa jr z,opnwnb retnz opnwna: call erexst retz opnwnb: call ercant retz ; Open file for buffered write. Delete existing. ; Return zero set if error. @openw: call ckwrt ret z dodosa delete,wfcb opnwa: dodosa make,wfcb inc a ret z lxim wbcnt,secsize*bufsize movw wbptr,wb retnz ; Open file for append. Make file if not exist. ; Return zero set if error. @opena: call ckwrt ret z dodosa open,wfcb inc a jr nz,opna dodosa make,wfcb inc a ret z movw wbptr,wb lxim wbcnt,secsize*bufsize retnz opna: dodosa getend,wfcb dcxm wfcbrn ld de,(wb) dodosa setdma dodosa rrec,wfcb ld hl,(wb) ld c,secsize ld a,eof opnb: cp (hl) jr z,opnc inc hl dec c jr nz,opnb ld hl,(wb) ld c,secsize jr opnd ; Found eof, want to rewrite this record. opnc: ld a,(wfcbrc) dec a ld (wfcbrc),a opnd: ld (wbptr),hl ld hl,secsize*(bufsize-1) ld b,0 add hl,bc ld (wbcnt),hl retnz ; Close file opened for buffered write. ; Return zero set if error. @closew: call wtbuf ; Drain the final buffer dodosa close,wfcb ; Close the file inc a ; Set flags ret ; Read a line from a file into the command line buffer. ; Return zero set if error/eof. rdcmd: ld hl,cmdlen ; Point to byte count ld (hl),0 ; Clear it ld b,cmdmax ; Max length of string ld de,cmd ; Point to buffer rdcmda: call fmbuf ; Get byte from I/O buffer ret z ; I/O error ld a,c cp lf ; Was LF? jr z,rdcmda ; Yes, ignore it cp cr ; Was CR? jr z,rdcmdc ; Yes, end of string rdcmdb: cp eof ; Was end of file? ret z ; Yes, error (no CR) ld (de),a ; Store the byte inc de ; Point where next byte goes inc (hl) ; Incremement byte count dec b ; Decrement # bytes left jr nz,rdcmda ; Some left rdcmdc: retnz ; Overflowed buffer, truncate string ; Read a line from a file into allocated memory. ; Add CR,0 at end. Return (HL)=start of line. ; Return zero set if I/O error. rdstr: call rdcmd ; Read the line ret z ; I/O error or eof ld hl,($memry) ; Address to put string push hl ; Save it movcmd ,0,cmdmax ; Move string there call addcr0 ; Put CR,0 at end of string inc hl ld ($memry),hl ; Next available byte pop hl ; Return start of string retnz ; and zero cleared ; Read multiple lines from a file into allocated memory. ; Return (HL)=start of first line. ; Return zero set if I/O error. ; rdmstr: ld hl,($memry) ; Where to put the string push hl ; Save it ld (hl),0 ; Mark end of string inc hl ; Allocate it ld ($memry),hl ; Next available byte rdma: dcxm $memry ; Back up over the 0 call rdstr ; Read a line into memory jr z,rdmb ; Error push hl ; Save string start cmpcmd eofs,leofs pop hl ; Restore string start jr nz,rdma ; Not *** EOF ld (hl),0 ; Mark end of string inc hl ; Next byte in memory ld ($memry),hl ; Is next available xor a ; Set zero flag inc a ; and clear it rdmb: pop hl ; Return start of string ret end