title Tab and Blank expander name ('EXPAND') ; Program EXPAND ; Copyright (C) Werner Cirsovius ; Hohe Weide 44 ; D-2000 Hamburg 20 ; Tel.:+49/040 4223247 ; Version 1.0, January 1989 ; Exchange tabs against spaces or vice versa ; How to call: ; EXPAND {-u} {d:}Infile.ext {{d:}Outfile.ext} ; -U exchanges spaces against tabs FALSE equ 0 TRUE equ NOT FALSE GERMAN equ TRUE ; Select German messages ;GERMAN equ FALSE ; Select English messages OS equ 0000h BDOS equ 0005h TPAtop equ BDOS+1 FCB equ 005ch CCP equ 0080h .condir equ 6 .string equ 9 .vers equ 12 .open equ 15 .close equ 16 .srcfrs equ 17 .delete equ 19 .rdseq equ 20 .wrseq equ 21 .make equ 22 .rename equ 23 .setdma equ 26 .parse equ 152 CPMv equ 30h .drv equ 1 .nam equ 8 .ext equ 3 _DIR equ 16 fcblen equ 36 reclng equ 128 tab equ 09h lf equ 0ah cr equ 0dh eof equ 1ah eot equ '$' MAXPAR equ 3 UPPMASK equ 01011111b COLMASK equ 00000111b NOMSB equ 01111111b STKDEP equ 32 ; ; Simple help ; $HELP: IF GERMAN db 'Call it:' db cr,lf,lf,tab db 'EXPAND {-u} {d:}Eingabe.typ {{d:}Ausgabe.typ}' db cr,lf,lf db 'Wechselt Tabulatoren gegen Leerzeichen oder umgekehrt (-u)' db cr,lf,eot ELSE db 'Call it:' db cr,lf,lf,tab db 'EXPAND {-u} {d:}Infile.ext {{d:}Outfile.ext}' db cr,lf,lf db 'Exchange tabs against spaces or vice versa (-u)' db cr,lf,eot ENDIF ; GERMAN ; ; Enter the tool ; _MAIN: ld sp,LocStk ; Load local stack ld c,.vers call BDOS ; Get OS version cp CPMv ; Verify CP/M Plus or higher ld de,$ILLOS jp c,EndMsg ld a,(CCP) ; Get length of command line or a ld de,$HELP jp z,EndMsg ; Give help if empty call IniMem ; Set up memory call GetParams ; Build parameter list call ProcOpt ; Process option call ParseSRC ; Parse source file call PrepDST ; Set up destination file call PrepFiles ; Prepare files call EXPAND ; Un-Expand file call CloseFiles ; Close files call UpdateDST ; Update destination file jp OS ; ; Set up memory ; IniMem: ld de,Heap ; Get first data address ld hl,(TPAtop) ; Get last data address or a sbc hl,de ; Calculate free space ld a,h ld (FreePag),a ; Save free pages srl h ; Halve free space rrc l ld (SRCbase),de ; Save base of source buffer add hl,de ld (DSTbase),hl ; Save base of destination buffer ret ; ; Build parameter list ; GetParams: ld de,CCP ; Point to command line ld a,(de) ; Fetch length ld l,a xor a ld h,a inc de add hl,de ; Position to end of line ld (hl),a ; Close line ld hl,ARGV1 ; Init parameter field ld b,MAXPAR ; Set max parameters ParamLoop: call SkpSpc ; Skip blanks ld (hl),e ; Save address of parameter inc hl ld (hl),d inc hl call SkpItem ; Skip parameter ld a,(de) ; Test end of command line or a ret z ; Yeap xor a ld (de),a ; Close parameter inc de djnz ParamLoop ; ; Skip white space ; SkpSpc: ld a,(de) cp ' ' ; Either blank jr z,GotSpc cp tab ; Or tab ret nz GotSpc: inc de jr SkpSpc ; ; Skip item ; SkpItem: ld a,(de) cp ' '+1 ; Test still a item ret c ; Nope inc de jr SkpItem ; ; Process option ; ProcOpt: ld hl,(ARGV1) ; Get pointer to 1st parameter ld a,(hl) cp '-' ; Test ption ld a,TRUE jr nz,DefOpt ; Nope, set default expand inc hl ld a,(hl) and UPPMASK cp 'U' ; Verify unexpand ld de,$ILLOPT jp nz,EndMsg ld hl,(ARGV2) ; Shift parameters ld (ARGV1),hl ld hl,(ARGV3) ld (ARGV2),hl ld a,FALSE DefOpt: ld (SelOpt),a ; Set mode and a ld hl,$EXPANDED jr nz,OptSet ld hl,$COMPRESSED OptSet: ld (MessPtr),hl ; Set mode string ret ; ; Parse source file ; ParseSRC: ld hl,(ARGV1) ; Get pointer to input file ld de,FCB call ParseFile ; Parse file ret ; ; Set up destination file ; PrepDST: ld hl,FCB ld de,DST.FCB ld bc,fcblen ldir ; Unpack FCB ld hl,(ARGV2) ; Get 2nd parameter ld a,l ; Test defined or h jr z,NoDST ; Nope, got no destination file ld de,DST.FCB call ParseFile ; Parse file ld a,FALSE ld (DSTfile),a ; Set destination file ret NoDST: ld a,TRUE ld (DSTfile),a ; Indicate no destination file ld hl,DST.FCB+.drv+.nam ; ; Set extension .$$$ into ^HL ; Set$$$: ld a,'$' ld (hl),a ; Simple one inc hl ld (hl),a inc hl ld (hl),a ret ; ; Parse file ^DE from name ^HL ; ParseFile: ld (ParsePB),hl ; Save name ld (ParsePB+2),de ; Save FCB ld de,ParsePB ld c,.parse call BDOS ; Parse it ret ; ; Prepare files ; PrepFiles: ld de,EndMsg push de ld de,DST.FCB call create ; Create destination file ld de,$NODIR inc a ; Verify success ret z ; Error ld de,DST.FCB call open ; Open destination file ld de,$NODST inc a ; Verify success ret z ; Error ld de,FCB call open ; Open source file ld de,$NOSRC inc a ; Verify success ret z ; Error pop de ret ; ; Un-Expand file ; EXPAND: xor a ld (Allowed),a ; Enable Un-Expand ld (BlnkCount),a ; Clear blank count ld (SrcRecords),a ; Clear source record count ld (DstRecords),a ; Clear destination record count ld hl,(SRCbase) ; Get base of source buffer ld (SRCbuff),hl ld hl,(DSTbase) ; Get base of destination buffer ld (DSTbuff),hl ld (TabCol),a ; Clear column ld hl,0 ld (DSTptr),hl ; Clear destination pointer RdFile: ld de,$RDIN call string ; Tell reading from source file RdBuff: ld hl,(SRCbuff) ; Get source buffer ex de,hl ld c,.setdma call BDOS ; Set disk buffer ld de,FCB call DskRed ; Read record from file or a ; Test end of file jr nz,EndFile ; Yeap ld hl,(SRCbuff) ; Get source buffer ld de,reclng add hl,de ; Advance it ld (SRCbuff),hl ld a,(SrcRecords) ; Get source record count inc a ; Advance it ld (SrcRecords),a ld b,a ld a,(FreePag) ; Get free pages cp b ; Test memory filled jr nz,RdBuff ; Nope EndFile: ld hl,0 ld (SRCptr),hl ; Clear source pointer ld hl,(SRCbase) ; Get base of source buffer ld (SRCbuff),hl ld a,(SrcRecords) ; Get source record count and a ; Test any read jr z,NoRead ; Nope call PrDecNum ld de,$SECRD call string ; Tell sectors read NoRead: ld a,(SrcRecords) ; Get source record count and a ; Test any read jp z,FillEOF ; Nope, end of file ld de,(SRCptr) ; Get source pointer ld hl,(SRCbuff) ; Get source buffer add hl,de ; Build memory address ld a,(hl) cp eof ; Test end of source file jp z,FillEOF ; Yeap call TstChar ; Test character ld hl,(SRCptr) inc hl ; Advance source pointer ld (SRCptr),hl ld a,l and a ; Test within record boundary jp p,NoRead ; Yeap ld hl,0 ld (SRCptr),hl ; Reset source pointer ld hl,(SRCbuff) ; Get source buffer ld de,reclng add hl,de ; Advance it ld (SRCbuff),hl ld a,(SrcRecords) ; Get source record count dec a ; Decrement it ld (SrcRecords),a jr nz,NoRead ; Still more ld hl,(SRCbase) ; Get base of source buffer ld (SRCbuff),hl jp RdFile ; ; Suppress Un-Expand ; Suppress: ld (Allowed),a ; Suppress Un-Expand ret ; ; Test character ; TstChar: ld b,a ; Save character cp ';' ; Test comment signe call z,Suppress ; Yeap cp '''' ; Test string sign call z,Suppress ; Yeap cp '"' ; Test alternate string sign call z,Suppress ; Yeap ld a,(SelOpt) ; Get option and a jr z,Spc2Tab ; Selected -U ; ; Exchange tabs against spaces ; ld a,b cp tab ; Test tab jr nz,fput ; Nope Tab2Spc: ld a,' ' call fput ; Expand tabs to blanks ld a,(TabCol) ; Get column and COLMASK jr nz,Tab2Spc jr ChrRes ; ; Exchange spaces against tabs -U ; Spc2Tab: ld a,(Allowed) ; Test Un-Expand allowed and a ld a,b jr nz,fput ; Nope cp ' ' ; Test blank jr nz,fput ; Nope ld a,(BlnkCount) ; Get blank count inc a ; Advance it ld (BlnkCount),a ld a,(TabCol) ; Get column inc a ; Advance it ld (TabCol),a and COLMASK ; Test column boundary ret nz ; Nope ld a,(TabCol) ; Get column dec a ; Decrement it ld (TabCol),a xor a ld (BlnkCount),a ; Clear blank count ld a,tab call fput ; Put tab to destination file ChrRes: xor a inc a ret ; ; Put character in Accu with leading blanks to destination file ; fput: push af ; Save character putloop: ld a,(BlnkCount) ; Get blank count and a ; Test any jr z,noblank ; Nope dec a ; Decrement it ld (BlnkCount),a ld a,(TabCol) ; Get column dec a ; Decrement it ld (TabCol),a ld a,' ' call fputc ; Put blank to file jr putloop noblank: pop af ; ; Put character in Accu to destination file ; fputc: ld de,(DSTbuff) ; Get destination buffer ld hl,(DSTptr) ; Get destination pointer add hl,de ; Build address and NOMSB ld (hl),a ; Store character ld hl,(DSTptr) ; Get destination pointer inc hl ; Advance it ld (DSTptr),hl push af ld a,(TabCol) ; Get column inc a ; Advance it ld (TabCol),a ld a,l and a ; Test within record boundary jp p,tstNL ; Yeap ld hl,(DSTbuff) ; Get destination buffer ld de,reclng add hl,de ; Advance it ld (DSTbuff),hl ld hl,0 ld (DSTptr),hl ; Clear destination pointer ld a,(DstRecords) ; Get destination record count inc a ; Advance it ld (DstRecords),a ld b,a ld a,(FreePag) ; Get free pages cp b ; Test buffer filled call z,WrBuff ; Yeap, write to file tstNL: pop af cp cr ; Test end of line call z,ResCounts ; Reset counts if so cp lf call z,ResCounts ret ; ; Write buffer to file ; WrBuff: ld a,(DstRecords) ; Get destination record count ld (CpyRecords),a ; Save it and a ; Test any in buffer ret z ; Nope ld de,$WROUT call string ; Tell writing to file ld hl,(DSTbase) ; Get base of destination buffer ld (DSTbuff),hl Buffloop: ld de,(DSTbuff) ; Get destination buffer ld c,.setdma call BDOS ; Set disk buffer ld de,DST.FCB call DskWrt ; Write record to file ld de,$NOSPACE or a ; Test success call nz,EndMsg ; Nope, error ld hl,(DSTbuff) ; Get destination buffer ld de,reclng add hl,de ; Advance it ld (DSTbuff),hl ld a,(DstRecords) ; Get destination record count dec a ; Decrement it ld (DstRecords),a jr nz,Buffloop ld de,(SRCbase) ; Get base of source buffer ld c,.setdma call BDOS ; Set disk buffer ld hl,0 ld (DSTptr),hl ; Clear destination pointer ld hl,(DSTbase) ; Get base of destination buffer ld (DSTbuff),hl ld a,(CpyRecords) ; Get records and a call PrDecNum ; Tell sectors written ld de,$SECWR call string ret ; ; Reset counts ; ResCounts: push af xor a ld (TabCol),a ; Clear column ld (BlnkCount),a ; Clear blank count ld (Allowed),a ; Enable Un-Expand pop af ret ; ; Print number in Accu as decimal value ; PrDecNum: ld l,a ; Expand number ld h,0 Hex2Dec: push af push bc push de push hl ld bc,-10 ; Init divisor ld de,-1 ; Init result SubConst: add hl,bc ; Divide number inc de ; Fix quotient jr c,SubConst ld bc,10 add hl,bc ; Make number > 0 ex de,hl ld a,l ; Get quotient or h ; Test zero call nz,Hex2Dec ; Nope, call recursively ld a,e ; Get remainder add a,'0' ; Make ASCII ld e,a ld c,.condir call BDOS ; Put to console pop hl pop de pop bc pop af ret ; ; Perform end of source file ; FillEOF: ld a,eof call fputc ; Write EOF to file ld hl,(DSTptr) ; Get destination pointer ld a,l and a ; Test any in buffer jr nz,FillEOF ; Yeap, empty it call WrBuff ; Write buffer to file ld de,(MessPtr) ; Get mode string call string ; Tell final message ret ; ; Give error message and exit ; EndMsg: call string ; Give message jp OS ; Give up ; ; Close files ; CloseFiles: ld de,FCB call close ; Close source file ld de,$CLOSERR inc a ; Test success jr z,EndMsg ; Error ld de,DST.FCB call close ; Close destination file ld de,$CLOSERR inc a ; Test success jr z,EndMsg ; Error ret ; ; Print string ^DE on console ; string: ld c,.string jp BDOS ; Put to console ; ; Update destination file ; UpdateDST: ld a,(DSTfile) ; Test destination file or a ret z ; Yeap ld de,FCB call delete ; Delete source file ld de,$DELERR inc a ; Test success jr z,EndMsg ; Error ld hl,FCB ld de,FCB+_DIR ld bc,_DIR ldir ; Unpack filename ld hl,FCB+.drv+.nam call Set$$$ ; Set extension .$$$ ld de,FCB call rename ; Rename file ld de,$RENERR inc a ; Test success jr z,EndMsg ; Error ret ; ; Open file ^DE ; open: ld c,.open jp BDOS ; Open it ; ; Close file ^DE ; close: ld c,.close jp BDOS ; Close it ; ; Delete file ^DE ; delete: ld c,.delete jp BDOS ; Delete it ; ; Read record from file ^DE ; DskRed: ld c,.rdseq jp BDOS ; Read it ; ; Write record to file ^DE ; DskWrt: ld c,.wrseq jp BDOS ; Write it ; ; Create file ^DE ; create: push de call delete ; Delete file pop de ld c,.make jp BDOS ; Create new one ; ; Rename file ^DE ; rename: ld c,.rename jp BDOS ; Rename it ; IF GERMAN $ILLOS: db cr,lf db 'Programm benoetigt CP/M 3.x' db cr,lf,eot $NOSRC: db cr,lf db 'Keine Eingabedatei' db cr,lf,eot $NODST: db cr,lf db 'Keine Ausgabedatei',eot $NODIR: db cr,lf db 'Kein Verzeichnisplatz',eot $NOSPACE: db cr,lf db 'Kein Diskettenplatz',eot $CLOSERR: db cr,lf db 'Fehler bei Schliessen der Datei',eot $DELERR: db cr,lf db 'Fehler bei Loeschen der Datei',eot $RENERR: db cr,lf db 'Fehler bei Umbenennen der Datei',eot $ILLOPT: db cr,lf db 'Falsche Option gewaehlt.' db cr,lf,eot $RDIN: db 'Eingabedatei wird gelesen... ',eot $WROUT: db 'Ausgabedatei wird geschrieben... ',eot $SECRD: db ' Sektoren gelesen' db cr,lf db 'Gelesene Daten werden bearbeitet...',eot $SECWR: db ' Sektoren geschrieben' db cr,lf,eot $EXPANDED: db 'Daten expandiert.' db cr,lf,eot $COMPRESSED: db 'Daten verdichtet.' db cr,lf,eot ELSE $ILLOS: db cr,lf db 'Requires CP/M 3.x' db cr,lf,eot $NOSRC: db cr,lf db 'No source file' db cr,lf,eot $NODST: db cr,lf db 'No destination file',eot $NODIR: db cr,lf db 'No directory space',eot $NOSPACE: db cr,lf db 'Out of data space',eot $CLOSERR: db cr,lf db 'Close file failure',eot $DELERR: db cr,lf db 'Delete file failure',eot $RENERR: db cr,lf db 'Rename file failure',eot $ILLOPT: db cr,lf db 'Invalid option selected.' db cr,lf,eot $RDIN: db 'Reading input file... ',eot $WROUT: db 'Writing output file... ',eot $SECRD: db ' sectors read' db cr,lf db 'Manipulating data read...',eot $SECWR: db ' sectors written' db cr,lf,eot $EXPANDED: db 'Data expanded.' db cr,lf,eot $COMPRESSED: db 'Data compressed.' db cr,lf,eot ENDIF ; GERMAN ; ARGV1: dw 0 ; Parameter 1 ARGV2: dw 0 ; Parameter 2 ARGV3: dw 0 ; Parameter 3 ; DST.FCB: ds fcblen ; Destination FCB DSTfile: db FALSE ; Destination file flag CpyRecords: db 0 ; Copy of destination record count SRCptr: dw 0 ; Source file pointer DSTptr: dw 0 ; Destination file pointer SRCbuff: dw 0 ; Source buffer DSTbuff: dw 0 ; Destination buffer SrcRecords: db 0 ; Source record count DstRecords: db 0 ; Destination record count TabCol: db 0 ; Column BlnkCount: db 0 ; Blank count Allowed: db 0 ; Un-Expand flag SelOpt: db FALSE ; Selected option (TRUE is default) MessPtr: dw 0 ; String mode pointer ParsePB: ds 2*2 ; Parse parameter block ; ; Local stack ; ds 2*STKDEP LocStk equ $ ; SRCbase: dw 0 ; Base of source buffer DSTbase: dw 0 ; Base of destination buffer FreePag: db 0 ; Free pages ; Heap equ $ end _MAIN