; Copyright (C) Reston Publishing Company, Inc. 1983 ; #start IOVars -- these should be included at the top of the ; program, between PROLOG and MAIN:. The subroutines herein ; reference these names. ; #start InVars -- used by SetUpInput and GetChar -------------- InFCB ds 35 ; Input FCB InIndex db 0 ; Index over CpmBuffer ; #end InVars ; #start OutVars -- used by SetUpOutput, PutChar, DumpOut ------ ; Output file variables: OutFCB ds 35 ; Output FCB OutType ds 3 ; hold for real output filetype ; OutBuffer dw 0 ; address of OutBuffer OutIndex dw 0 ; index over OutBuffer OutLimit dw 0 ; negative of size of OutBuffer DollarType db '$$$' ; type of an output workfile ; #end OutVars ; #start FileMessages -- abort-messages for utility I/O -------- MsgNoName db 'An input filename is required.$' MsgNoFile db 'Input file not found.$' MsgNoBuffer db 'Not enough room for an output buffer.$' MsgAmbig db 'The output file may not be ambiguous.$' MsgNoMake db 'Can''t create the work file.$' MsgWrite db 'Error writing the work file.$' MsgNoClose db 'Error closing the work file.$' MsgRename db 'Error renaming work file to proper name.$' ; #end FileMessages ; #end IOVars ; #start IOSubs -- Shout, Shift, GetChar, PutChar, DumpOut ; #start Shift ------------------------------------------------- ; Shout: write the left byte of the pair B, C. Then do the ; actions of Shift, by falling into Shift's code. ;--------------------------------------------------------------- Shout: mov a,b call PutChar ;--------------------------------------------------------------- ; Shift moves the two-byte text window right over the input ; text. The new byte that enters C is returned in A as well. ; preserves -- DE, HL ; returns -- A = C = next byte of input ; B = prior byte from entry C ;--------------------------------------------------------------- Shift: call GetChar ; A := next byte mov b,c mov c,a ret ; #end Shift ; #start GetChar ----------------------------------------------- ; GetChar: return the next byte of input in A. The default ; buffer at 80h..FFh is used. Since we are dealing with ASCII ; files, CpmEof (^Z) is the end of file mark. When it appears, ; don't advance the index past it, so further calls will keep on ; returning CpmEof indefinitely. ; preserves -- BC, DE, HL ;--------------------------------------------------------------- GetChar: push h ; save a work register lda InIndex inr a ; InIndex := InIndex+1 jnz GetChar2 ; ; InIndex was FFh, the buffer is empty. Put EOF in the buffer ; so that if physical end of file occurs, we will see it as ; logical end of file instead. ; push d push b lxi h,CpmBuffer+CpmBase mvi m,CpmEof ; put CpmEof in buffer xchg ; DE-->buffer mvi c,BdosSetBuffer call Bdos ; set buffer address lxi d,InFCB mvi c,BdosRead call Bdos ; read a record pop b pop d mvi a,80h ; InIndex := 80h ; ; At this point there is either data or CpmEof at the ; location addressed by InIndex = A. ; GetChar2: sta InIndex mvi h,CpmBasePage ; form an address mov l,a mov a,m ; A := data cpi CpmEof ; If it's EOF, JRNZ GetChar3 lxi h,InIndex dcr m ; ..InIndex := Inindex-1 GetChar3: pop h ret ; #end GetChar ; #start PutChar (and DumpOut) --------------------------------- ; PutChar: write the byte in A into the output buffer. If the ; buffer is full, (OutIndex=OutLimit), then dump it first. ; preserves -- all ;--------------------------------------------------------------- PutChar: push h push d lhld OutIndex xchg ; DE = OutIndex lhld OutLimit dad d ; carry := OutIndex=OutLimit JRNC PutChar2 ; call DumpOut ; dump the buffer to the file lxi d,0 ; OutIndex := 0 ; PutChar2: lhld OutBuffer dad d ; HL --> next output byte spot mov m,a inx d xchg shld OutIndex ; OutIndex := OutIndex+1 pop d pop h ret ; #start DumpOut ----------------------------------------------- ; DumpOut: write buffered output data to the work file. Called ; in two cases: (1) when the buffer is full (OutIndex=OutLimit) ; and (2) at the end of the program when OutIndex is ; unpredictable and might even be zero. The buffer must be a ; multiple of 128 bytes, but needn't be on any special boundary. ; preserves -- all ;--------------------------------------------------------------- DumpOut: push psw push h push d push b lhld OutIndex mov a,h ora l ; is there any data at all? JRZ DumpOut9 ; ..no data? do nothing. ; ; When the buffer is full, OutIndex mod 128 is zero. At the ; end of the run, though, OutIndex could be in mid-record. In ; that case, fill the last record with EOF marks. ; xchg ; DE = OutIndex lhld OutBuffer dad d ; HL --> next output byte DumpOut2: mov a,e ani 128-1 ; test OutIndex mod 128 JRZ DumpOut3 ; (it was zero) mvi m,CpmEof ; ..stick in an EOF inx d ; ..step OutIndex inx h ; ..and step storage pointer JMPR DumpOut2 ; ; The buffer now contains a whole number of 128-byte records. ; Write them all. Keep the limit index in BC, and the current ; record's index in DE. ; DumpOut3: mov b,d ! mov c,e ; BC := index of last record dcx b ; ..less one lxi d,0 ; DE := starting index of 0 DumpOut4: lhld OutBuffer dad d xchg ; DE-->record, HL has index SERVICE BdosSetBuffer SERVICE BdosWrite,OutFCB ora a ; check for disk full, etc. JRNZ DumpOutError ; lxi d,128 dad d ; step index (in HL) xchg ; DE := index to next record call CmpBD ; set flags for BC vs DE JRNC DumpOut4 ; continue if limit <= index ; DumpOut9: pop b pop d pop h pop psw ret ; DumpOutError: ABORT MsgWrite ; #include arithlib.inc,CmpBD ; #end DumpOut ; #end PutChar ; #end IOSubs ; #start IOSetup -- SetUpInput, SetUpOutput ; #start SetUpInput -------------------------------------------- ; SetUpInput: initialize the input file for utility I/O with ; GetChar. Requires definition of InFCB, InIndex (InVars). ; preserves -- all ;--------------------------------------------------------------- SetUpInput: push psw push b push d push h ; ; Prepare the input file for reading ; Zero the input FCB ; lxi d,InFCB lxi b,35 call FillZero ; ; if input filename omitted, abort ; lxi h,CpmFCB+1 ; the filename is omitted if call Delimiter ; it starts with any delimiter JRNZ SUI2 ABORT MsgNoName ; ; get an input drivecode for the FCB ; SUI2: dcx h ! mov a,m ; A := given drivecode ora a ; was it omitted? JRNZ SUI3 SERVICE BdosDrive ; get e.g. A=00, B=01 inr a ; make it A=01, B=02, etc. SUI3: sta InFCB ; explicit drivecode in FCB ; ; move the input filename and filetype to the FCB ; inx h ; HL-->filename (source) lxi d,InFCB+1 ; DE-->FCB (destination) lxi b,8+3 ; B = length of name and type call MoveHtoD ; move-characters routine ; ; open the input FCB ; SERVICE BdosOpen,InFCB inr a ; if no file exists, abort JRNZ SUI4 ABORT MsgNoFile ; ; InIndex := FFh ; SUI4: mvi a,0FFh sta InIndex ; pop h pop d pop b pop psw ret ; #include textlib.inc,FillZero (and MoveHtoD) ; #include textlib.inc,Delimiter ; #end SetUpInput ; #start SetUpOutput (and FinishOutput) ------------------------ ; SetUpOutput: prepare the utility output mechanism. ; input -- BC--> a default FCB for missing fileref parts ; DE--> start of output buffer ; HL--> end of output buffer (as PROLOG gives it) ; CpmFcb2 contains output fileref ; preserves -- all ; Note: this code assumes that the OutVars and FileMessages ; units from this file have been included. ; It aborts the program under these conditions: ; MsgAmbigOut -- if the output fileref is ambiguous ; MsgNoBuffer -- if the output buffer is less than 256 bytes ; MsgNoMake -- if "outname.$$$" can't be defined ; MsgNoClose -- if "outname.$$$" doesn't close successfully ; MsgNoRename -- if the rename to change the type doesn't work ;--------------------------------------------------------------- SetUpOutput: push psw push b push d push h push b ; save -->default fileref ; ; prepare the buffer address, index, and index limit. The limit ; is the 2s complement of the buffer-size -- see DumpOut for use ; xchg ; HL-->start of buffer mov a,l ; ensure buffer starts on 128-multiple dcr a ori 128-1 mov l,a inx h shld OutBuffer xchg ; DE-->start, HL-->end mov a,d ! cma ! mov d,a mov a,e ! cma ! mov e,a inx d ; DE := 2's complement of buffer start dad d ; HL := buffer size mov a,h ; ..which we want to ensure is at ora a ; least 256 bytes... JRNZ SUO2 ; (it is) ABORT MsgNoBuffer SUO2: mov a,l ; ensure buffer size is a multiple ani 128 ; ..of 128 bytes, too cma ! mov l,a mov a,h ! cma ! mov h,a inx h shld OutLimit; OutLimit := 2's complement of size lxi h,0 shld OutIndex; OutIndex := 0 ; ; put the default fileref, complete, in the output fcb ; pop h ; HL-->default FCB lxi d,OutFCB lxi b,12 ; move the fileref: d:filenametyp call MoveHtoD ; ; make the rest of the FCB zeroes ; lxi b,35-12 call FillZero ; ; Get an output drivecode for the FCB ; lda CpmFCB2 ; drivecode from 2nd operand ora a JRZ SUO3 ; (none given) sta OutFCB ; ; get an output filename for the FCB, and make sure that ; the name given is explicit (has no question marks). ; SUO3: lxi b,8 lxi d,OutFCB+1 ; DE-->destination lxi h,CpmFCB2+1 ; HL-->2nd operand filename call Delimiter ; was it omitted? cnz MoveHtoD ; if not, copy over default lxi h,OutFCB+1 ; HL-->the final filename mvi a,'?' ; BC still has the length call ScanForA ; look for "?" in name JRNZ SUO4 ; (there are none) ABORT MsgAmbig ; ; get an output filetype and save it. Make sure it isn't ; ambiguous, either. ; SUO4: lxi b,3 ; BC = length of filetype lxi d,OutType ; DE-->destination (not FCB) lxi h,CpmFCB2+9 ; HL-->2nd operand filetype mov a,m cpi AsciiBlank ; was one given? JRNZ SUO5 ; (yes, move it) lxi h,OutFCB+9 ; ..no, use default filetype SUO5: call MoveHtoD ; move filetype to OutType lxi h,OutType ; ..then check the chosen type mvi a,'?' call ScanForA ; look for "?" in type JRNZ SUO6 ; (none found) ABORT MsgAmbig ; ; Prepare a workfile for output ; move '$$$' to the output FCB filetype ; SUO6: lxi h,DollarType ; "$$$" for use in work file lxi d,OutFCB+9 lxi b,3 ; length of filetype call MoveHtoD ; ; erase any existing workfile of that name ; (note: if drive is R/O, we die here) ; SERVICE BdosErase,OutFCB ; ; make a file of the workfile's name ; SERVICE BdosMake,OutFCB inr a ; if the make fails, abort JRNZ SUO7 ABORT MsgNoMake ; SUO7: pop h pop d pop b pop psw ret ;--------------------------------------------------------------- ; FinishOutput: close the work file and rename to proper type. ; preserves -- all ;--------------------------------------------------------------- FinishOutput: push psw push b push d push h ; call DumpOut ; write any buffered output ; lxi d,OutFCB ; then close the work file mvi c,BdosClose call Bdos inr a ; FFh signals failure JRNZ FinO2 ; (wasn't that) ABORT MsgNoClose ; ; OutFCB presently names "y:outname.$$$". Stuff in the type ; of the real output file, and erase that file. This is where, ; if the output fileref had any question marks, we could do a ; lot of damage! ; FinO2: lhld OutType lda OutType+2 shld OutFCB+9 sta OutFCB+9+2 lxi d,OutFCB mvi c,BdosErase call Bdos ; return code is irrelevant ; ; Now set the FCB up so that it reads: ; OutFCB+0: y outname $$$ ; OutFCB+16: - outname typ ; and do the rename function to rename the work file. ; lxi h,OutFcb lxi d,OutFCB+16 lxi b,16 call MoveHtoD lhld Dollartype mov a,h shld OutFCB+9 sta OutFCB+9+2 lxi d,OutFCB mvi c,BdosRename call Bdos inr a ; FFh means failure JRNZ FinO3 ; (not the case) ABORT MsgRename ; FinO3: pop h pop d pop b pop psw ret ; #include textlib.inc,FillZero (and MoveHtoD) ; #include textlib.inc,ScanForA ; #end SetUpOutput ; #end IOSetup sw ret ; #include