title Disk Utility name ('DISKUTIL') ; Initiated by: ; DU.ASM V8.8 Revised 12/10/87 ; Disk Utility - Written by Ward Christensen 08/06/78 ; ; (See DU.DOC for description and detailed instructions.) ; ; DASMed version v8.8 of DU.COM - I don't knowe where I got it from ; On SIG-M, Volume 086, DU.COM will be found but as version 7.7 ; ; Code slightly changed and modified for JOYCE screen control ; ; Mods: Immediate messages replaced by address pointers ; Only CP/M Plus (3.x) accepted ; Labels by Ward Christensen ; Many comments by me, Werner Cirsovius FALSE equ 0 TRUE equ NOT FALSE NIL equ 0 OS equ 0000h BDOS equ 0005h TPAtop equ BDOS+1 FCB equ 005ch DMA equ 0080h CCP equ DMA CMPv equ 31h ; CP/M version required ; ; BDOS functions ; .conin equ 1 .conout equ 2 .lstout equ 5 .condir equ 6 .rdkbd equ 10 .consta equ 11 .vers equ 12 .resdsk equ 13 .open equ 15 .close equ 16 .srcfrs equ 17 .srcnxt equ 18 .delete equ 19 .dskwr equ 21 .make equ 22 .retdsk equ 25 .setdma equ 26 .usrcod equ 32 .errmod equ 45 .scb equ 49 .bios equ 50 .conmod equ 109 _get equ -1 _CIN equ 0fdh notab equ 0100b noctrlc equ 1000b ; ; SCB indices ; _CONWID equ 1ah _CONPOS equ 1bh ; ; BIOS functions ; _HOME equ 8 _SELDSK equ 9 _SETTRK equ 10 _SETSEC equ 11 _SETDMA equ 12 _READ equ 13 _WRITE equ 14 _SECTRN equ 16 ; ; Relative pointers within Disk Parameter Header (DPH) ; _DPB equ 12 _TIME equ 021h ; Time/date indicator _FREE equ 0e5h ; Free file slot indicator .drv equ 1 .name equ 8 .ext equ 3 _ex equ 12 _S2 equ 14 _DIR equ 32 FCBlen equ 36 RecLng equ 128 SecSize equ 32*RecLng ; Size for sector buffer _WID1 equ 28 ; Width for file output _WID2 equ 32 null equ 00h tab equ 09h lf equ 0ah cr equ 0dh eof equ 1ah esc equ 1bh DEL equ 7fh MAXUSR equ 15 COMLEN equ 128 ; Length of command line DEFSLP equ 10 ; Default sleep value NOMSB equ 01111111b LOMASK equ 00001111b COL1MSK equ 00000011b COL2MSK equ 00000111b UPPMASK equ 11011111b TABCOL equ 8 _PAUSE equ 1 ; Give some new lines and pause _NL equ 2 ; CR LF control Delay1 equ 8000 ; Approximately .1 second at 2 MHz Delay2 equ 16000 ; Approximately .1 second at 4 MHz Delay3 equ 24000 ; Approximately .1 second at 6 MHz Clock: ; CPU clock db 1 ; 0=2mhz, 1=4mhz, 2=6mhz ; db 'DU.COM v8.8 12/10/87',eof ; ; Enter DISKUTIL ; PastCk: ld hl,(TPAtop) ; Get top of memory ld l,0 ; Set page boundary ld sp,hl ; Get for stack ld (SetStk),hl ; Save for stack reload ld c,.vers call BDOS ; Get version ld a,h or a ; Verify correct machine jp nz,NoPlus ld a,l cp CMPv jp nz,NoPlus ld de,_get ld c,.conmod call BDOS ; Get console mode ld (ConMode),hl ; Save it ld a,l or notab+noctrlc ; No tab expansion, no ^C abort ld e,a ld d,h ld c,.conmod call BDOS ; Set new mode ld c,.errmod ld e,_get call BDOS ; Get error mode ld a,_CONWID call LdSCB ; Get console width add a,4 ; Fix it push af ld b,_WID1 call DivAB ; Divide for display columns ld (DispCol1),a ; Save pop af ; Get back width ld b,_WID2 call DivAB ; Divide again for display columns ld (DispCol2),a ; Save ld hl,(EndAdr) ; Get base heap ld (SectBuff),hl ; Save for sector buffer ld de,SecSize add hl,de ; Allow some space ld (DMABase),hl ; Set DMA address ld l,0 ; Get page boundary inc h ; Allow some space inc h ld (YnkBase),hl ; Set base yank address ld (YnkAdr),hl ; Set current yank address call IlPrt ; Give some intro info dw $ID call GetStp ; Initialize environment ld hl,CCP ld a,(hl) ; Test any command given or a jr z,Prmptr ; Nope ; ; Got initial command, set it up ; ld b,a ; Save length dec b jr z,Prmptr ld de,InBuf+2 ; Point to command line inc hl ; Skip length inc hl ; Skip first blank call Move ; Unpack line ld a,cr ld (de),a ; Close line ld hl,InBuf+2 ; Init command line pointer jr Prmpti ; ; Re-enter command input ; Prmptr: xor a ld (QFlag),a ; Set not quiet call RdBuf ; Read buffer Prmpti: ld a,TRUE ld (ToGo),a ; Set loop count for "/" ld (ToGo+1),a ; ; Re-enter ; Prompt: ld sp,(SetStk) ; Reload stack pointer push hl xor a ld (TwoUp),a ; Clear file count ld a,-1 ld (Ftsw),a ; Set first read ld hl,DMA+RecLng ld (BufAd),hl ; Init buffer address pop hl call CtlCs ; Test abort jr z,Prmptr ; Yeap ; ; Do we have to position in directory after find? ; ld a,(FindFl) or a ; Test files found jp nz,PosDir ; Yeap, position in directory call EndLine ; Test end of line inc hl jr nz,ScrCmd ; Nope cp cr ; Test real end jr z,Prmptr ; Yeap jr Prompt ScrCmd: call UpCase ; Convert to upper case ld (DumTyp),a ; Save for dump type (A,D,H) ; ; Command dispatcher ; push hl ld hl,CMD$TAB ; Point to command table SrcCmd: ld b,(hl) ; Get character from table inc hl cp b ; Compare jr z,ExeCmd ; Got it inc b ; Test end of table jr z,Whatp ; Yeap inc hl ; Skip address inc hl jr SrcCmd ExeCmd: ld c,(hl) ; Fetch address from table inc hl ld b,(hl) pop hl ; Get back line pointer push bc ; Set execution address ret ; Go Whatp: pop hl What: xor a ld (QFlag),a ; Set not quiet ld a,'?' call Type ; Indicate error jp Prmptr ; Restart ; ; ( Toggles the map display to show/not show erased files. ; TogEra: ld a,(Toge) cpl ; Toggle it ld (Toge),a jp Prompt ; ; Give message and exit to CP/M ; NoPlus: call IlPrt ; Tell invalid OS dw NO$PLUS ; ; X Exit back to CP/M ; Xit: ld c,.resdsk call BDOS ; Reset disk system ld a,(LogDsk) ; Get logged disk call BDOS50 ; Select thru BIOS ld c,.errmod ld e,0 call BDOS ; Set compatibility error mode ld hl,(ConMode) ; Get console mode ex de,hl ld c,.conmod call BDOS ; Reset it jp OS ; Exit DISKUTIL ; ; Memory full error ; MemFul: call IlPrtQ ; Tell out of memory dw MEM$FUL jp Prmptr ; ; # the disk parameters. ; Stats: push hl ; Save line pointer call IlPrt dw DRV$STAT ; Tell statistic follows ld a,(Drive) ; Get logged drive add a,'A' ; Make ASCII call Type ; Print it ld a,':' call Type call IlPrt ; Tell tracks dw $TRACKS ld hl,(MaxTrk) ; Get max track inc hl call DecHex ; Print value call IlPrt ; Tell system tracks dw SYS$TRACKS ld hl,(SysTrk) ; Get number of reserved tracks call DecHex ; Print value ld hl,(Spt00) ; Get fixed number of 128-byte records per track ld de,(Spt) ; Get number of 128-byte records per track call SubDE ; HL:=HL-DE jr c,Stat1 call IlPrt ; Tell sectors/track dw SEC$TRACK jr Stat2 Stat1: call IlPrt ; Tell sectors/track 0 dw SEC$TRACK0 ld hl,(Spt00) ; Get fixed number of 128-byte records per track call DecHex ; Print value call IlPrt ; Tell sectors/track in other tracks dw SEC$NOTRACK Stat2: ld hl,(Spt) ; Get number of 128-byte records per track call DecHex ; Print value call IlPrt ; Tell groups dw $GROUPS ld hl,(DSM) ; Get number of blocks on disc push hl call Dec ; Tell decimal ld a,tab call Type ; Give delimiter pop bc call Hexb ; Put hex call IlPrt ; Tell directory groups dw DIR$GROUPS ld hl,(DirGrp) ; Get directory allocation count call DecHex ; Print values call IlPrt ; Tell sectors/group dw SEC$GROUP ld a,(BLM) ; Get block mask inc a ld l,a ; Build word ld h,0 call DecHex ; Tell values call IlPrt ; Tell directory entries dw DIR$ENTRIES ld hl,(DRM) ; Get number of directory entries inc hl call DecHex ; Print values call crlf ; Give new line pop hl ; Get back line pointer jp Prompt ; ; Print decimal and hex value separated by tab ; DecHex: push hl call Dec ; Give decimal ld a,tab call Type ; Print delimiter pop bc call Hexz ; Then give hex ret ; ; N Resets CP/M via BDOS ; NewDSk: push hl ld c,.resdsk call BDOS ; Reset disk system ld a,(Drive) ; Get logged drive pop hl call Select ; Select it jp Prompt ; ; Q Quiet -- preceeding any command suppresses CRT output ; Quiet: ld a,'Q' ld (QFlag),a ; Set quiet jp Prompt ; ; / Repeats entire command -- defaults to "forever" ; /nn Repeat nn times -- nn may be 2 to 65535 ; Repeat: call Decin ; Get possible input ld a,d ; Test any or e jr z,nnn ; Nope ld hl,(ToGo) ; Get value inc hl ld a,h ; Test first time or l jr nz,nnn ; Nope ld (ToGo),de ; Set new value nnn: ld de,(ToGo) ; Get current value ld hl,InBuf+2 ; Point to command line inc de ld a,d ; Test for endless or e jp z,Prompt ; Yeap, restart the command dec de ; Count down dec de ld (ToGo),de ; Save new value ld a,d ; Test still any remaining or e jp nz,Prompt ; Yeap, restart command jp Prmptr ; Get next command ; ; Ux Logs user 'x' for next 'F' command ; User: call Decin ; Get user number ld a,e cp MAXUSR+1 ; Verify correct range jp nc,What ld a,d or a jp nz,What ld c,.usrcod push hl call BDOS ; Set user pop hl jp Prompt ; ; P Toggles the printer on/off ; Prntff: ld a,(PFlag) ; Get printer flag cpl ; Toggle it ld (PFlag),a jp Prompt ; ; Z[nn] Sleep [nn tenths] ; Sleep: call Hexin ; Get hex count ld a,e or a ; Test any jr nz,Sleplp ld e,DEFSLP ; Set default Sleplp: ld bc,Delay1 ld a,(Clock) ; Fetch clock or a ; Test 2mhz jr z,Sleep2 ; Yeap ld bc,Delay2 cp 1 ; Test 4mhz jr z,Sleep2 ; Yeap ld bc,Delay3 Sleep2: dec bc ; Count inner loop down ld a,b or c jr nz,Sleep2 ; Loop till done push de call CtlCs ; Test abort pop de jp z,Prmptr ; Yeap dec e ; Count down ticks jr nz,Sleplp jp Prompt ; ; Test abortion - Z set says abort ; CtlCs: call Const ; Get state of console or a ; Test key pressed jr nz,GetC ; Yeap or 1 ; Indicate no abort ret GetC: call Conin ; Get character from console and NOMSB ; Less hi bit cp 'S'-'@' ; Test stop call z,Conin ; Get from console if so cp 'C'-'@' ; Set state for abort ret ; ; Log disk and initialize a bit ; GetStp: ld c,.retdsk call BDOS ; Return current disk ld (LogDsk),a ; Save it jr Select ; Select it ; ; L Re-logs in the current drive ; Lx Logs in disk 'x', range A to P ; Login: call DoLog ; Do the log job jp Prompt ; ; Log disk ; DoLog: call EndLine ; Test end of line ld de,0 jp z,Lgnodk ; Yeap call UpCase ; Convert to upper case inc hl sub 'A' ; Make binary ; ; Enter with binary drive in Accu ; Select: push hl ld (Drive),a ; Save drive call BDOS50 ; Select thru BIOS ld (DPH.ADR),hl ; Save address of DPH ld a,h ; Test valid or l jp z,What ; Nope ld e,(hl) ; Get the sector table pointer inc hl ld d,(hl) ld (SecTbl),de ; Save it ld de,_DPB-1 add hl,de ; Point to DPB ld e,(hl) ; Get it inc hl ld d,(hl) ex de,hl call LogIt ; Log it ld de,(SysTrk) ; Get number of reserved tracks call SetTrk ; Set track number ld de,1 call SetSec ; Set sector number ld hl,(PhySec) ; Get physical sector ld a,h or l ld (First0),a ; Set physical 0 flag call ClcSub ; Calculate group pop hl Lgnodk: call Norite ; Disable write ret ; ; Read in the disk directory ; RedDir: push hl call Norite ; Disable write ld hl,(SysTrk) ; Get number of reserved tracks ld (CurTrk),hl ; Save into current track ld hl,1 ld (CurSec),hl ; Init current sector ld hl,(DRM) ; Get number of directory entries inc hl ; Make 1 relative call RotrHL ; Divide by 4 call RotrHL ; (4 names/sector) ld b,h ; Save length ld c,l ld de,(DMABase) ; Get DMA address RdirLp: push bc push de ld b,d ; Get DMA address ld c,e ld a,(TPAtop+1) dec a cp d ; Check enough memory jp c,MemFul ; Nope, memory full error call SetDMA ; Set disk buffer ld de,(CurTrk) ; Get current track call SetTrk ; Set track number ld de,(CurSec) ; Get current sector call SetSec ; Set sector number call Read ; Read sector call NxtSec ; Go to next sector pop de pop bc ld hl,RecLng add hl,de ; Advance buffer ex de,hl dec bc ; Count down ld a,b or c jr nz,RdirLp ; Loop on reading ld bc,DMA call SetDMA ; Set default disk buffer pop hl ret ; ; M Dumps a map of the group allocation for files ; Mn Shows which file is allocated to group 'n' ; Map: xor a ld (DlmReq),a ; Reset delimiter required call Hexin ; Get hex group push hl ld hl,(CurTrk) ; Get current track ld (SavTrk),hl ; Save it ld hl,(CurSec) ; Get current sector ld (SavSec),hl ; Save it ld a,e ; Test input given or d jr z,MapRd ; Nope ld hl,(DirGrp) ; Get directory allocation count call SubDE ; HL:=HL-DE jr nc,MapRd ; Start >= default ld hl,(DSM) ; Get number of blocks on disc call SubDE ; HL:=HL-DE jp c,OutLim ; Start exceeds max, so abort ex de,hl jr MapRdDif MapRd: ld hl,(DirGrp) ; Get directory allocation count MapRdDif: ld a,(PFlag) ; Get printer flag or a ; Test enabled ld a,2 jr nz,MapRd1 ; Yeap ld a,(DSM+1) ; Get number of blocks on disc or a ld a,(DispCol2) ; Get 1st width jr nz,MapRd1 ld a,(DispCol1) ; Get 2nd width MapRd1: ld (CurColumn),a ; Unpack or set it call RedDir ; Read directory ld b,h ld c,l MapDf: ld a,(DlmReq) ; Test delimiter required or a call nz,Delim ; Yeap, do it call Hexb ; Put hex ld a,'-' call Type ; Give delimiter ld a,' ' ld (DupFl1),a ; Set flag call GetGrp ; Get group MapCnt: inc bc ; Next group push hl ld hl,(DSM) ; Get number of blocks on disc inc hl ; Fix for comparision ld a,l ; Compare cp c jr nz,MapC1 ld a,h cp b MapC1: pop hl jr z,MapEnd ; End if disk capacity reached push hl call GetGrp ; Get group pop de call CtlCs ; Test abort jr z,MapNd2 ; Yeap ld a,d ; Test same group cp h jr nz,MapDif ; Nope ld a,e cp l jr z,MapCnt ; ; Different file encountered ; MapDif: dec bc call Hexb ; Put hex inc bc ex de,hl call MapNam ; Print file name jr MapDf ; ; End of map ; MapEnd: dec bc call Hexb ; Put hex call MapNam ; Print file name ; ; End of map - reposition to previous group ; MapNd2: call crlf ; Give new line ld de,(SysTrk) ; Get number of reserved tracks ld hl,(SavTrk) ; Get current saved track call SubDE ; HL:=HL-DE jr nc,MapNd3 ; Within group ld hl,(SavSec) ; Get saved sector ld (CurTrk),hl ; Set current track ex de,hl call SetSec ; Set sector number ld hl,(SavTrk) ; Get current saved track ld (CurTrk),hl ; Set current track ex de,hl call SetTrk ; Set track number call Read ; Read sector xor a ld (NotPos),a ; Set positioned pop hl jp Inq MapNd3: ld hl,(Group) ; Get group ex de,hl jp PosGp2 ; ; Print file name pointed to by HL ; MapNam: call Space ; Print space ld a,h ; Test any or l jr z,NoName ; Nope ld a,(hl) ; Get entry cp _FREE ; Test free ld a,' ' jr nz,MpNsp1 ; Nope ld a,'(' MpNsp1: call Type ; Indicate state push hl ld a,(hl) ; Get entry call Hex ; Print as hex call Space ; Print space inc hl push bc ld b,.name call MapN2 ; Print name of file ld a,'.' call Type ; Give delimiter ld b,.ext call MapN2 ; Print extension ld a,(DupFl1) ; Get delimiter call Type ; Print pop bc ld a,(hl) ; Get extent call Hex ; Print as hex pop hl ; Get back pointer ld a,(hl) ; Get state cp _FREE ; Test if it was free ld a,' ' jr nz,MpNsp2 ; Nope ld a,')' MpNsp2: call Type ; Indicate closure jr Flip NoName: call IlPrt ; Tell group is free dw $FREE Flip: ld a,(TwoUp) add a,1 ; Advance file count ld (TwoUp),a ld a,-1 ld (DlmReq),a ; Set delimiter required ret ; ; Give delimiter ; Delim: push bc ld a,(CurColumn) ; Get column width ld b,a ld a,(TwoUp) ; Get file count sub b ; Test line filled pop bc jr nz,Delim1 ; Nope ld (TwoUp),a ; Clear file count call crlf ; Give new line jr Delim2 Delim1: ld a,':' call Type ; Print deliter call Space ; Print space Delim2: xor a ld (DlmReq),a ; Reset delimiter required ret ; ; Print name, length in B ; MapN2: ld a,(hl) ; Get character and NOMSB ; No attributr inc hl cp ' ' ; Test control jr c,MapN2h cp '~'+1 ; Test printable jr c,MapN2a MapN2h: call bhex ; Dump as hex jr MapN2z MapN2a: call Type ; Print character MapN2z: djnz MapN2 ret ; ; Find which file group (BC) belongs to ; GetGrp: ld hl,(DRM) ; Get number of directory entries inc hl ; Make 1 relative ld (FileCt),hl ; Save entry count ld hl,NIL ld (MfPtr),hl ; Clear allocation address ld hl,(DMABase) ; Get DMA address GetGlp: push hl ; Save pointer to name push hl ld a,(Toge) ; Get map display or a jr nz,SkEra inc hl ; Force _FREE to be skipped SkEra: ld a,(hl) ; Get byte pop hl cp _FREE ; Test free jr z,GetGnf ; Yeap, skip record ld a,(hl) ; Get byte back cp _TIME ; Test time/date jr z,GetGnf ; Ignore it ld de,_S2 add hl,de ; Point to S2 portion ld a,(hl) and LOMASK ; Mask bits ld e,a inc hl ld a,(hl) ; Get RC or e ; Test 0 jr z,GetGnf ; Skip if so ld e,16 ; Set up for 8 bit groups ld a,(DSM+1) ; Get number of blocks on disc or a ; Test state jr z,SmalGp ld e,16/2 ; Change for big groups SmalGp: ld d,a GetGl2: inc hl call GrpCmp ; Compare BC group # against 1 dm field jr nz,NotGot ; Not found ; ; Found the file ; push hl ld hl,(MfPtr) ; Get allocation address ld a,h or l pop hl ex (sp),hl ; Get entry start and save pointer jr z,MpFrst ld a,'*' ld (DupFl1),a ; Change delimiter MpFrst: ld (MfPtr),hl ; Set up allocation address ex (sp),hl NotGot: dec e ; Count down jr nz,GetGl2 GetGnf: pop hl ld de,_DIR add hl,de ; Point to next entry ld de,(FileCt) ; Get entry count dec de ; Count down ld (FileCt),de ld a,d or e ; Test end jr nz,GetGlp ; ; Get the allocation address, if any ; ld hl,(MfPtr) ; Get allocation address ret ; ; Y "Yank" the current sector into sequential memory. ; Yank: ld a,(TPAtop+1) ; Get top page ld b,a ld a,(YnkAdr+1) ; Get yank address cp b ; Verify in range jr nc,YMfull ; Buffer full ld a,(WrFlg) ; Get write flag or a jp z,BadW ; Not allowed push hl ld de,(YnkAdr) ; Get yank address ld hl,DMA ld b,RecLng call Move ; Move into yank memory call IlPrt ; Tell last address dw LAST$ADR ld hl,(YnkAdr) ; Get yank address ld bc,RecLng add hl,bc ; Advance it ld (YnkAdr),hl dec hl ld a,h call Hex ; Print last address as hex ld a,l call Hex call crlf ; Give new line pop hl jp Prompt YMfull: call IlPrtQ ; Tell yank memory full dw YANK$FULL jp Prompt ; ; < Saves current sector in a 'save' buffer ; Save: ld a,(WrFlg) ; Get write flag or a jp z,BadW ; Not allowed push hl ld hl,DMA ld de,SavBuf ld b,RecLng call Move ; Copy into buffer ld a,1 ld (SaveFl),a ; Indicate buffer filled pop hl jp Prompt ; ; > Gets saved buffer. ; Restor: ld a,(SaveFl) ; Test buffer filled or a jr z,NoSave ; Nope push hl ld hl,SavBuf ld de,DMA ld b,RecLng call Move ; Copy from buffer pop hl jp Prompt NoSave: call IlPrtQ ; Tell missing command dw NO$SAVE jp Prmptr ; ; Move B bytes from ^HL to ^DE ; Move: ld a,(hl) ; Get byte ld (de),a ; Unpack inc hl inc de djnz Move ret ; ; Disable write for now ; Norite: xor a ld (WrFlg),a ; Clear write flag ret ; ; No match in search, try next character ; SrNomt: pop hl call CtlCs ; Test abort jr nz,Search ; Nope ld hl,InBuf+2 ; Point to command line ld (hl),cr ; Set end jp ClcGrp ; Show where stopped ; ; =string ASCII search, starting at current sector. ; Search: push hl ; Save string pointer Srchl: call RdByte ; Read a byte ld b,a ; Save it ld a,(hl) ; Get next character cp '<' ; Will it be hex? ld a,b jr z,Srchl1 ; Yeap and NOMSB ; Next character is ASCII, strip off hi bit Srchl1: push af call GetVal ; Get search value ld b,a pop af cp b ; Test match jr nz,SrNomt ; Nope inc hl call EndLine ; Test end of line jr nz,Srchl ; Nope ; ; Got match ; call IlPrtQ ; Tell where we found it dw $AT ld a,(BufAd) ; Get buffer index and NOMSB call Hex ; Print as hex call crlf ; Give new line jp ClcGrp ; ; Get value from input buffer ; GetVal: ld a,(hl) cp '<' ; Test hex escape ret nz ; Nope ; ; "<<" means one "<" ; inc hl ld a,(hl) cp '<' ; Test 2nd ret z ; That's all ; ; Got hex ; push de call Hexin ; Get value cp '>' ; Test proper delimiter ld a,e ; Get value pop de jp nz,What ; Error if not proper ret ; ; Read a byte at a time ; RdByte: push hl ld a,(Ftsw) ; Test first read or a jr nz,Read1 ; Yeap ld hl,(BufAd) ; Get buffer address ld a,l or a ; Test in buffer jp m,NoRd ; Yeap, skip read ; ; Have to read ; call NxtSec ; Go to next sector Read1: xor a ld (Ftsw),a ; Set not first read ld de,(CurSec) ; Get current sector call SetSec ; Set sector number ld de,(CurTrk) ; Get current track call SetTrk ; Set track number call Read ; Read sector call ClcSub ; Calculate group ld hl,DMA NoRd: ld a,(hl) ; Get from buffer inc hl ld (BufAd),hl ; Set buffer index pop hl ret ; ; V Views current sector -- assumes ASCII data ; Vnn Views 'nn' sectors ; View: ld a,(WrFlg) ; Get write flag or a jp z,BadDmp ; Not allowed call Hexin ; Get 'nn' if any push hl ld a,e or a ; Test any jr nz,ViewLp ; Yeap inc e ; Set default of 1 ViewLp: ld hl,DMA ; Init address VewChr: call CtlCs ; Test abort jr z,VewEnd ; Yeap ld a,(hl) ; Get character cp eof ; Test end of file jr z,VewEOF ; Yeap bit 7,a ; Test printable jr z,ViewHx ; Yeap call HiOn ; Give hi lite ld a,(hl) ; Get byte call Type1 ; Dump it call HiOff ; Set normal attribute jr ViewNp ViewHx: call Type1 ; Dump character ViewNp: inc l ; Go thru buffer jr nz,VewChr dec e ; Test all done jr z,VewEnd ; Yeap push de call NxtSec ; Go to next sector ld de,(CurSec) ; Get current sector call SetSec ; Set sector number ld de,(CurTrk) ; Get current track call SetTrk ; Set track number call Read ; Read sector pop de jr ViewLp ; View next VewEOF: call IlPrt ; Tell end of file dw $EOF VewEnd: pop hl call crlf ; Give new line jp Prompt ; ; Dump character hex or ascii ; Type1: and NOMSB ; Strip off hi bit cp DEL ; Filter controls jr z,TypeH cp ' ' jr nc,TypeQ cp cr jr z,TypeQ cp lf jr z,TypeQ cp tab jr z,TypeQ TypeH: ld a,(hl) call bhex ; Dump as hex ret TypeQ: call Type ; Print ASCII ret ; ; A Dump a sector, ASCII only ; D Dump sector, hex and ASCII ; H Dump sector, hex only ; Dump: ld a,(WrFlg) ; Get write flag or a jr nz,DumpOk ; Allowed BadDmp: call IlPrtQ ; Cannot dump if nothing read dw CANT$DUMP Expl: call IlPrtQ ; Give some hint dw USE$G jp Prmptr DumpOk: call EndLine ; Test end of line jr nz,DmpNdf ; Nope ; ; Use default ; ld bc,DMA ; Use default if so ld de,DMA+RecLng-1 jr Dump1 DmpNdf: call Disp ; Get address ld b,d ; Set start ld c,e call EndLine1 ; Test end of line jr z,Dump1 ; Yeap inc hl ; Skip comma call Disp ; Get end address ; ; BC = start, DE = end ; Dump1: push hl ; Save command pointer ld h,b ; Copy start ld l,c DumpLp: ld a,l ; Get start address and NOMSB ; Fetch record index call Hex ; Print as hex call Space ; Print space call Space ; Print space ld a,(DumTyp) ; Get dump type cp 'A' ; Test ASCII only jr z,DumpAs push hl ; Save start DHex: ld a,(hl) ; Get byte call Hex ; Print as hex ld a,l and COL1MSK ; Test column boundaries cp COL1MSK call z,Space ; Print space if so ld a,l and COL2MSK cp COL2MSK call z,Space ; Print space again ld a,e cp l ; Test end reached jr z,DPop ; Yeap inc hl ld a,l and LOMASK ; Test end column jr nz,DHex DPop: call CtlCs ; Test abort jp z,Prmptr ; Yeap ld a,(DumTyp) ; Get dump type cp 'H' ; Test HEX only jr z,DNoAs ; Yeap pop hl DumpAs: call Aster ; Print asterisk DChr: ld a,(hl) ; Get character bit 7,a ; Test printable jr z,DOk ; Yeap call HiOn ; Give hi lite ld a,(hl) ; Get character call DChr1 ; Print it call HiOff ; Set normal attribute jr DEndT DOk: call DChr1 ; Print it DEndT: ld a,e cp l ; Test end reached jr z,DEnd ; Yeap inc hl ld a,l and LOMASK ; Test end column jr nz,DChr DEnd: call Aster ; Print asterisk call crlf ; Give new line push de call CtlCs ; Test abort pop de jp z,Prmptr ; Yeap ld a,e cp l ; Test end reached jr nz,DumpLp pop hl jp Prompt DNoAs: pop bc call crlf ; Give new line ld a,e cp l ; Test end reached jp nz,DumpLp pop hl ; Get back command pointer jp Prompt ; ; ################################### ; ##### JOYCE/PCW specific code ##### ; ################################### ; ; Give video attribute hi lite ; HiOn: call ESCpr ; Give ESCape ld a,'p' jr DOk1 ; Then attribute ; ; Set normal video attribute ; HiOff: call ESCpr ; Give ESCape ld a,'q' jr DOk1 ; Then attribute ; ; Give ESCape prefix ; ESCpr: ld a,esc jr DOk1 ; Print it ; ; ################################### ; ; Print character - use dot if not printable ; DChr1: and NOMSB ; Strip off bit cp DEL ; Test printable jr z,DPer1 cp ' ' jr nc,DOk1 DPer1: ld a,'.' ; Use dot instead DOk1: call Type ; Print character ret ; ; Gnn Position to group 'nn' and read ; G Show current position ; Snn Position to sector 'nn' and read ; Tnn Position to track 'nn' (no read) ; Pos: push af call EndLine ; Test end of line jr nz,PosOk ; Nope pop af jp Inq PosOk: pop af cp 'T' ; Test position to track jr z,PosTkd cp 'S' ; Test position to sector jr z,PosScd cp 'G' ; Test position to group jp z,PosGph jp What ; ; Tnn Position to track 'nn' (no read) ; PosTkd: call Decin ; Get track push hl ld hl,(MaxTrk) ; Get max track call SubDE ; HL:=HL-DE pop hl jp c,OutLim ; Out of limit call SetTrk ; Set track number call Norite ; Disable write ld a,1 ld (NotPos),a ; Set not positioned jr ClcGrp ; ; Snn Position to sector 'nn' and read ; PosScd: call Decin ; Get sector ld a,d or e ; Verify not sector 0 jp z,What push hl ld hl,(Spt) ; Get number of 128-byte records per track call Chk00 ; Check for Boot track call SubDE ; HL:=HL-DE pop hl jp c,What ; Invalid call SetSec ; Set sector number call Read ; Read sector xor a ld (NotPos),a ; Set positioned ClcGrp: call ClcSub ; Calculate group jp Inq ; ; Calculate group from track and sector ; ClcSub: push hl ld de,(SysTrk) ; Get number of reserved tracks ld hl,(CurTrk) ; Get current track call SubDE ; HL:=HL-DE ex de,hl ld hl,(Spt) ; Get number of 128-byte records per track call Mult ; Multiply ex de,hl ld hl,(CurSec) ; Get current sector dec hl add hl,de ; Add it ld a,(BLM) ; Get block mask ld b,a ld a,l and b ld (GrpDis),a ; Set block mask ld a,(BSH) ; Get block shift ld b,a ClcLop: call RotrHL ; Divide by 2 djnz ClcLop ld (Group),hl ; Save group pop hl ret ; ; Position in the directory after a find ; PosDir: push hl ld hl,(BSH) ; Get block shift ; (Reg H holds block mask) xor a ld (FindFl),a ; Set no files found ld a,(DirPos) ; Get directory position rra rra push af and h ld (GrpDis),a ; Set block mask pop af PosDlp: rra dec l jr nz,PosDlp and 1 ld e,a ld d,0 ld (Group),de ; Save group jr PosGp2 ; Position to it ; ; Gnn Position to group 'nn' and read ; PosGph: call Hexin ; Get group push hl ld hl,(DSM) ; Get number of blocks on disc call SubDE ; HL:=HL-DE pop hl jp c,OutLim ; Out of limit ld (Group),de ; Save group xor a ld (GrpDis),a ; Clear block mask push hl PosGp2: call GtkSec ; Get sector call SetTrk ; Set track number ex de,hl call SetSec ; Set sector number call Read ; Read sector xor a ld (NotPos),a ; Set positioned pop hl jp Inq ; ; Build sector from reg DE ; On exit reg HL holds max sector and reg DE holds max track ; GtkSec: ld h,d ; Copy value ld l,e ld a,(BSH) ; Get block shift GLoop: add hl,hl ; Shift it in dec a jr nz,GLoop ld a,(GrpDis) ; Get block mask add a,l ld l,a ; ; Divide by nr of sectors, quotient=track, remainder=sector ; ex de,hl ld hl,(Spt) ; Get number of 128-byte records per track call Neg ; HL:=-HL ex de,hl ld bc,0 DivLp: inc bc add hl,de ; Divide jr c,DivLp dec bc ex de,hl ld hl,(Spt) ; Get number of 128-byte records per track add hl,de push hl ld hl,(SysTrk) ; Get number of reserved tracks add hl,bc ex de,hl pop hl inc hl ret ; ; Fname Print directory for file 'name' and position to its directory sector ; F Find next occurence of name in directory ; PosFil: call Norite ; Disable write ld a,1 ld (FindFl),a ; Set files found call EndLine ; Test end of line jr z,GotaFile ; Yeap, test any file found ld de,FCB ; Point to default FCB ld a,(Drive) ; Get logged drive inc a ld (de),a inc de ld b,.name call MvName ; Parse name ld b,.ext call MvName ; Parse extension push hl ld hl,FCB+.drv ; Point to FCB ld de,TmpName ld b,.name call MovF ; Move name ld de,FCB+.drv ld hl,WildNam ld b,.ext call MovF ; Set wildcard ld a,'?' ld (FCB+_ex),a ; Select all extents ld de,FCB ld c,.srcfrs SrcFile: call BDOS ; Search for file inc a ; Test any found jr nz,FlOk ; Yeap NoFile: ld (DirPos),a ; Reset directory position ld (FoundFlag),a ; Force no file found call IlPrt ; Tell no file found dw NO$FILE pop hl jp Prompt GotaFile: push hl ld a,(FoundFlag) or a ; Test file found jr z,NoFile ; Nope SrcNext: ld c,.srcnxt jr SrcFile FlOk: dec a ld (DirPos),a ; Set directory position ld l,a ld h,0 add hl,hl ; * 2 add hl,hl ; * 4 add hl,hl ; * 6 add hl,hl ; *16 add hl,hl ; *32 ld de,DMA add hl,de ; Position in buffer ld de,_DIR ex de,hl add hl,de ex de,hl call CmpName ; Test found jr nz,SrcNext ; Nope, try next ld a,'D' ld (FoundFlag),a ; Force file found ld (DumTyp),a ; Set dump type to ASCII + HEX jp DumpLp ; ; Find name of file in ^HL - z set says found ; CmpName: push bc push de push hl ld de,TmpName ; Set pointer inc hl ld b,.name ; Set length CmpNamLoop: ld a,(de) cp (hl) ; Compare jr nz,CmpNotFound ; No match inc de inc hl djnz CmpNamLoop ; Loop CmpNotFound: pop hl pop de pop bc ret ; ; Parse file - B characters from ^HL to ^DE ; MvName: call EndLine ; Test end of line jr z,MviPad1 ; Yeap cp '.' ; Test extension next jr z,MviPad cp ':' ; Test drive delimiter follows jr z,MviPad call UpCase ; Convert to upper case ld (de),a ; Unpack it inc hl inc de djnz MvName call EndLine ; Verify end of line ret z ; Yeap inc hl cp '.' ; Verify delimiter ret z cp ':' ret z jp What ; Invalid, so indicate it ; ; Fill remainder with blanks ; MviPad: inc hl MviPad1: ld a,' ' ; Blank it Pad1: ld (de),a inc de djnz Pad1 ret ; ; Kdu:filename.ext Save a file from "yanked" sectors. ; ; As CP/M v 3.0 does not have a SAVE function, one has been added here. ; ; Syntax is: ; Kdu:filename.ext ; ^^^ ^ ^ ; ||| | +-- file extension (0-3 characters) ; ||| +-------- file name (1-8 characters) ; ||+--------------- user # (or none) ; |+---------------- drive designation (A-P or none) ; +----------------- DU command ; ; Drive and user may be omitted. If so, omit the colon as well. Drive ; must be specified if the user is. If the user # is omitted, the cur- ; rent user is used. If the drive is omitted, the current CP/M default ; drive is used. ; ; This function saves the current contents of sequential memory into a ; disk file. The contents of sequential memory are determined by the ; 'yank' function, and the pointer of that function is used here. If ; nothing has been yanked, you get an error. Once the file has been ; saved, the 'yank' pointer is re-initialized to its original value ; (3000H). Control is returned to DU. ; ; ; Turn free-form file name string into file control block ; SvFile: xor a ld (UZer),a ; Default to current user ld de,FCB ld (de),a ; Default to current drive inc de ld b,.name call MvName ; Parse filename dec hl ld a,(hl) ; Get delimiter inc hl cp ':' ; Test drive or user separator jr nz,NoDrv ; Nope push hl ld hl,FCB+.drv ld a,(hl) ; Get drive sub 'A'-1 ; Map 'A'-'P' to 1-16 ld (FCB),a inc hl ld a,(hl) ; Get next cp ' ' ; Test possible user jr z,UZe3 ; Nope ld b,2 ; No more than two digits ld c,0 ; Init user UZe1: ld a,(hl) ; Get a digit cp ' ' ; Test end jr z,UZe2 ld a,c add a,a ; * 2 add a,a ; * 4 add a,c ; * 5 add a,a ; *10 ld c,a ld a,(hl) ; Get new digit sub '0' ; Make binary add a,c ; Insert it ld c,a inc hl djnz UZe1 UZe2: ld a,c ; Get user inc a ; Add displacement (0 means no change) ld (UZer),a ; Set current user UZe3: ld b,.name pop hl ld de,FCB+.drv call MvName ; Parse filename NoDrv: ld b,.ext call MvName ; Parse extension ld (EndVal),hl ; Save line pointer ld b,FCBlen-_ex xor a call Pad1 ; Zero remainder ; ; See if anything is there ; ld hl,(YnkAdr) ; Get yank address ld a,(YnkBase+1) ; Get yank page cp h ; Test any jr nz,SV1 ld a,l and a jr nz,SV1 call IlPrt ; Tell buffer empty dw $EMPTY jp ESvFil ; ; Set the user number ; SV1: ld e,_get ld c,.usrcod call BDOS ; Get current user ld (CUZer),a ; Save ld a,(UZer) ; Get yank user and a ; Test default jr z,UZerd ; Yeap dec a ; Fix for offset ld e,a ld c,.usrcod call BDOS ; Set new user UZerd: ld a,(FCB) ; Get drive or a ; Test default jr nz,SVdrv ; Nope ld a,(Drive) ; Get logged drive inc a SVdrv: dec a call BDOS50 ; Select drive thru BIOS ld a,h ; Test success or l jr z,SVill ; Nope, drive not valid ; ; Create the file ; ld c,.open ld de,FCB call BDOS ; Open file inc a ; Test file does exist jr z,SV2 ; Nope call IlPrt ; Tell file does exist dw $EXIST call YesNo ; Get response jp z,NDelF ; Do not delete ld de,FCB ld c,.delete call BDOS ; Delete file SV2: ld c,.make ld de,FCB call BDOS ; Create new file inc a ; Test success jr nz,SV2a ; Yeap SVill: call IlPrt ; Tell no directory space dw NO$DIR jr NDelF SV2a: ld hl,(YnkBase) ; Get base yank address ld (Ydn),hl ; Init it xor a ld (Scount),a ; Clear record count SV3: ld de,(Ydn) ; Get yank buffer address ld c,.setdma call BDOS ; Set disk buffer ld de,FCB ld c,.dskwr call BDOS ; Write record and a ; Test success jr z,SV4 ; Yeap call IlPrt ; Tell write error dw WR$ERR jr EndSvc SV4: ld hl,Scount inc (hl) ; Advance record count ld hl,(Ydn) ; Get yank buffer ld de,RecLng add hl,de ; Advance it ld (Ydn),hl ex de,hl ld hl,(YnkAdr) ; Get yank address ld a,d cp h ; Test all written jr c,SV3 ; Nope ld a,e cp l jr c,SV3 ld hl,(Scount) ; Get record count ld h,0 call Dec ; Print decimal call IlPrt ; Tell sectors written dw SEC$WRT ld hl,(YnkBase) ; Get base yank address ld (YnkAdr),hl ; Init current yank address EndSvc: ld de,FCB ld c,.close call BDOS ; Close file NDelF: ld a,(Drive) ; Get logged drive call BDOS50 ; Select thru BIOS ld a,(CUZer) ; Get back current user ld e,a ld c,.usrcod call BDOS ; Set user ESvFil: ld hl,(EndVal) ; Get back line pointer jp Prompt ; ; +(nn) Advance one or nn sector(s). ; Plus: ld de,1 ; Default to one sector call EndLine ; Test end of line jr z,PlusGo ; Yeap call Hexin ; Get sector count ld a,d ; Verify correct input or e jp z,What PlusGo: call NxtSec ; Go to next sector dec de ; Count down ld a,d ; Test end or e jr nz,PlusGo ; Nope ; ; Ok, incremented to sector, setup and read ; PlusMi: push hl ld de,(CurSec) ; Get current sector call SetSec ; Set sector number ld de,(CurTrk) ; Get current track call SetTrk ; Set track number pop hl call Read ; Read sector jp ClcGrp ; ; -(nn) Backs up one or nn sector(s). ; Minus: ld de,1 ; Default to one sector call EndLine ; Test end of line jr z,MinGo ; Yeap call Hexin ; Get sector count ld a,d ; Verify correct input or e jp z,What MinGo: push hl ld hl,(CurSec) ; Get current sector dec hl ; Count down ld a,h or l ; Test end jr nz,MinOk ; Nope ld hl,(CurTrk) ; Get current track ld a,h ; Test track 0 or l jr nz,SeaSh ; Nope ld hl,(MaxTrk) ; Get max track ld (CurTrk),hl ; Set current track ld hl,(MaxSec) ; Get max sector jr MinOk ; Wrap to end of disk SeaSh: dec hl ld (CurTrk),hl ; Set current track ld hl,(Spt) ; Get number of 128-byte records per track call Chk00 ; Check for Boot track MinOk: ld (CurSec),hl ; Set current sector pop hl dec de ; Count down ld a,d or e ; Test end jr nz,MinGo ; Nope jr PlusMi ; Setup and read ; ; Go to next sector ; NxtSec: push hl push de ld de,(CurSec) ; Get current sector inc de ld hl,(Spt) ; Get number of 128-byte records per track call Chk00 ; Check for Boot track call SubDE ; HL:=HL-DE ex de,hl jr nc,ChkLst ld de,(CurTrk) ; Get current track inc de ld hl,(MaxTrk) ; Get max track call SubDE ; HL:=HL-DE jr nc,TrAsk ld de,0 ; Wrap to start of disk TrAsk: ld (CurTrk),de ; Set current track ld hl,1 NextOk: ld (CurSec),hl ; Set current sector pop de pop hl ret ChkLst: push hl ; Save new current sector ld de,(MaxTrk) ; Get max track ld hl,(CurTrk) ; Get current track call SubDE ; HL:=HL-DE pop hl ; Get new current sector jr c,NextOk ex de,hl ld hl,(MaxSec) ; Get max sector call SubDE ; HL:=HL-DE ex de,hl jr nc,NextOk ; Ok ld de,0 jr TrAsk ; Wrap to start of disk ; ; B[nn] Boot track number of sectors per track ; Boot: call Decin ; Get track number ld a,d ; Test number given or e jr nz,Boot1 ; Yeap ld de,(Spt) ; Get number of 128-byte records per track jr Boot2 Boot1: push hl ld hl,(Spt) ; Get number of 128-byte records per track call SubDE ; HL:=HL-DE pop hl jp c,What ; Error if nn <= non-track 0 Boot2: ld (Spt00),de ; Set fixed number of 128-byte records per track jp Prompt ; ; Check for Boot track ; Chk00: push hl ld hl,(CurTrk) ; Get current track ld a,h ; Test track 0 or l jr nz,Chk001 ; Nope ld hl,(Spt00) ; Get fixed number of 128-byte records per track ex (sp),hl Chk001: pop hl ret ; ; G Show current position ; Inq: call InqSub ; Show it jp Prompt ; ; Position inquiry subroutine executed via: G S or T (with no operands) ; InqSub: push hl ld de,(SysTrk) ; Get number of reserved tracks ld hl,(CurTrk) ; Get current track call SubDE ; HL:=HL-DE jr c,NoGrp call IlPrt ; Tell 'G=' dw G$IS ld hl,(Group) ; Get group ld b,h ld c,l call Hexb ; Put hex ld a,':' call Type ; Give delimiter ld a,(GrpDis) ; Get block mask call Hex ; Print as hex call IlPrt ; Give comma dw $COMMA NoGrp: call IlPrt ; Tell 'T=' dw T$IS ld hl,(CurTrk) ; Get current track call Dec ; Print decimal call IlPrt ; Tell 'S=' dw S$IS ld hl,(CurSec) ; Get current sector call Dec ; Print decimal call IlPrt ; Tell 'PS=' dw PS$IS ld hl,(PhySec) ; Get physical sector call Dec ; Print decimal call crlf ; Give new line pop hl ret ; ; Move B characters from ^HL to ^DE ; MovF: ld a,(hl) ; Get character ld (de),a ; Unpack it dec b ret z inc de inc hl jr MovF ; ; CHaddr,val,val,.. Change hex values in sector ; CAaddr,char string.. Change ASCII values in sector. may be used here. ; CHaddr-addr,byte Repeats a change with one byte ; CAaddr-addr,byte Repeats an ASCII change ; Chg: ld a,(hl) ; Get type (H, A) call UpCase ; Convert to upper case push af ; Save it inc hl call Disp ; Get first address inc hl ld bc,0 ; Init 'thru' address cp '-' ; Test thru delimiter jr nz,ChgNth ; Nope push de ; Save from address call Disp ; Get thru address inc hl ld b,d ; Unpack address ld c,e pop de jr ChgAH ChgNth: cp ',' ; Verify delimiter jp nz,What ChgAH: pop af ; Get back type cp 'H' ; Test Hex jp z,ChgHex cp 'A' ; Verify ASCII jp nz,What ; ; Change ASCII ; ChgAlp: call EndLine ; Test end of line jp z,Prompt ; Yeap ld a,(de) ; Get character cp ' ' ; Test printable jr c,ChgAHx cp '~'+1 jr c,ChgA2 ChgAHx: call bhex ; Dump as hex jr ChgA3 ChgA2: call Type ; Print character ChgA3: ld (Back),hl ; Save thru pointer call GetVal ; Get ASCII or ld (de),a ; Update character inc hl ; ; See if 'THRU' requested ; ld a,c or a jr z,ChaNth cp e ; Test done jp z,Prompt ld hl,(Back) ; Get thru pointer ChaNth: inc e ; Test end jr nz,ChgAlp call EndLine ; Test end of line jp z,Prompt ; Yeap jp What ; ; Change HEX ; ChgHcm: inc hl ChgHex: call EndLine ; Test end of line jp z,Prompt ; Yeap cp ',' ; Test delimiter jr z,ChgHcm ; Ok, skip it push de ld (HexAd),hl ; Save address call Hexin ; Get hex byte ld a,e pop de push af ld a,(de) ; Get current byte call Hex ; Print as hex pop af ld (de),a ; Save new byte ld a,c ; See if 'THRU' or a jr z,ChhNth ; Nope cp e ; Test done jp z,Prompt ; Yeap ld hl,(HexAd) ; Get address ChhNth: inc e ; Test end jr nz,ChgHex ; Nope call EndLine ; Test end of line jp z,Prompt ; Yeap jp What ; ; R Reads into memory the sector currently positioned at. ; DoRead: ld a,(NotPos) or a ; Test positioned jr nz,CantRD ; Nope call Read ; Read sector jp Prompt CantRD: call IlPrtQ ; Tell not positioned dw CANT$READ jp Prompt ; ; W Writes the current sector to disk. ; DoRite: call Write ; Write sector jp Prompt ; ; Dump byte in Accu as hex ; bhex: push af ld a,'<' call Type ; Give indicator pop af call Hex ; Print as hex ld a,'>' call Type ; Give closure ret ; ; Put hex byte in reg BC ; Hexb: ld a,(DSM+1) ; Get number of blocks on disc or a ; Test byte or word jr z,Hexx ; It's byte ld a,b call Hex ; Print hi as hex jr Hexx ; Then print low ; ; Put hex byte or word in reg BC ; Hexz: ld a,b ; Test hi byte defined or a call nz,Hex ; Print as hex if so Hexx: ld a,c ; Get lo byte ; ; Put byte in Accu as ASCII hex to console ; Hex: push af ; Save byte rra ; Get upper four bits rra rra rra call Nibbl pop af Nibbl: and LOMASK ; Mask lower bits add a,90h daa adc a,40h jp Type ; Print digit ; ; Decimal output routine of number in reg HL ; Dec: push bc push de push hl ld bc,-10 ; Init divisor ld de,-1 ; Init quotient Decou2: add hl,bc ; Divide inc de ; Update quotient jr c,Decou2 ld bc,10 add hl,bc ; Make positive ex de,hl ; Get quotient ld a,h ; Test zero or l call nz,Dec ; Nope, divide recursive ld a,e ; Get remainder add a,'0' ; Make ASCII call Type ; Print digit pop hl pop de pop bc ret ; ; Print space ; Space: ld a,' ' jp Type ; ; Print asterisk ; Aster: ld a,'*' jp Type ; ; Get response Yes (default) or No - Z set reflects No ; YesNo: ld a,'Y' ld ($$Y),a ; Set default call IlPrt ; Ask for Y or N dw Y$N call Coninv ; Get non echoed character call UpCase ; Convert to upper case cp 'N' push af jr nz,Yesit ld ($$Y),a ; Save response Yesit: call IlPrt ; Echo response dw $$Y pop af ; Get result ret ; ; Put no quiet string to console - string pointer followes call ; IlPrtQ: xor a ld (QFlag),a ; Set not quiet ; ; Put string to console - string pointer followes call ; IlPrt: ex (sp),hl ; Get pointer to address push de ld e,(hl) ; Fetch address inc hl ld d,(hl) inc hl push hl ; Bring back pointer ex de,hl IlpLp: call CtlCs ; Test abort jp z,Prmptr ; Yeap ld a,(hl) ; Get character cp _PAUSE ; Test new lines and pausing jr nz,IlNL ; Nope ; ; Control code '1,x' - Print x new lines and pause ; call crlf ; Give new line call NewLines ; Advance lines and pause cp 'C'-'@' ; Test abort jp z,Prmptr call IlPrt ; Delete message dw DEL$MORE jr IlpNx IlNL: cp _NL ; Test new line jr nz,IlpOk ; ; Control code '2' - Print new line ; call crlf ; Give new line jr IlpNx ; ; No control code - Print and test end ; IlpOk: call Type ; Put to console IlpNx: inc hl ld a,(hl) ; Get character back or a ; Test end of string jr nz,IlpLp ; Nope pop hl ; Get back pointer pop de ex (sp),hl ; Set return address ret ; ; Advance lines and pause - Accu returns last character input ; NewLines: inc hl push bc ld b,(hl) ; Get line count NLllop: ld a,lf call Type ; Give new lines djnz NLllop pop bc call IlPrt ; Tell more dw $MORE call Conin ; Get character from console ret ; ; Get hex input and validate sector displacement. ; If correct convert it to an address ; Disp: call Hexin ; Get hex input push af ; save delimiter ld a,d or a ; Verify correct range jr nz,BaDisp ; Nope ld a,e or a jp m,BaDisp add a,LOW DMA ; Build address ld e,a ld d,HIGH DMA pop af ; Get back delimiter ret BaDisp: call IlPrtQ ; Tell invalid displacement dw BAD$DSPL jp Prmptr ; ; Get hex input ; DHin: inc hl ; Skip # Hexin: ld de,0 ; Init result ld a,(hl) ; Get character cp '#' ; Test decimal jr z,HDin ; Yeap, get it HinLp: call EndLine ; Test end of line ret z ; Yeap call IsEnd ; Test closing character ret z ; Yeap call UpCase ; Convert to upper case inc hl cp '0' ; Verify correct range jp c,What cp '9'+1 jr c,HinNum cp 'A' jp c,What cp 'F'+1 jp nc,What sub 'A'-'0'-10 ; Fix for hex HinNum: sub '0' ; Strip off offset ex de,hl add hl,hl ; * 2 add hl,hl ; * 4 add hl,hl ; * 8 add hl,hl ; *16 add a,l ; Insert new digit ld l,a ex de,hl jr HinLp ; ; Get decimal input ; HDin: inc hl ; Skip # Decin: ld de,0 ; Init result ld a,(hl) ; Get character cp '#' ; Test hexadecimal jr z,DHin ; Yeap, get it DinLp: call EndLine ; Test end of line ret z ; Yeap call IsEnd ; Test closing character ret z ; Yeap call UpCase ; Convert to upper case inc hl cp '0' ; Verify correct range jp c,What cp '9'+1 jp nc,What sub '0' ; Strip off offset push hl ld h,d ld l,e add hl,hl ; * 2 add hl,hl ; * 4 add hl,de ; * 5 add hl,hl ; *10 add a,l ; Insert new digit ld l,a ld a,h adc a,0 ld h,a ex de,hl pop hl jr DinLp ; ; Read in a console buffer full ; RdBuf: call IlPrt ; Give prompt dw $COLON ld de,InBuf ld c,.rdkbd call BDOS ; Read line from keyboard ld hl,InBuf+1 ld c,(hl) ; Get length ld b,0 inc hl add hl,bc ; Point to end ld (hl),cr ; Close line ld a,(PFlag) ; Get printer flag or a ; Test enabled call nz,putLST ; Yeap, echo line call crlf ; Give new line ld hl,InBuf+2 ; Return command line ret ; ; Echo line on printer ; putLST: ld hl,InBuf+2 ; Point to command line pLllop: ld a,(hl) ; Get character cp cr ; Test end ret z ; Yeap ld c,a ; Unpack it call Lstout ; Put to printer inc hl jr pLllop ; ; Convert character to upper case ; UpCase: cp 'a'-1 ; Test range ret c cp 'z'+1 ret nc and UPPMASK ; Convert it ret ; ; Put character to printer ; Lstout: push bc ld c,.lstout ; Get function jr ConDdoOSp ; ; Put character to console ; Conout: push bc ld c,.conout ; Get function jr ConDdoOSp ; ; Get state of console ; Const: push bc ld c,.consta ; Get function jr ConDdoOSp ; ; Get character from console without echoing it ; Coninv: push bc push de ld c,.condir ; Get function ld e,_CIN jr ConDdoOS ; ; Get character from console ; Conin: push bc ld c,.conin ; Get function ConDdoOSp: push de ConDdoOS: push hl call BDOS ; Get or put character pop hl pop de pop bc ret ; ; Give new line on console ; crlf: ld a,cr call Type ld a,lf ; ; Console out with tab expansion ; Type: cp tab ; Test tab jr nz,ConLstout ; Nope ld a,_CONPOS call LdSCB ; Get current console position and TABCOL-1 ; Mask lower bits sub TABCOL ; Calculate length push bc ld b,a TypeCol: ld a,' ' call ConLstout ; Type blanks inc b jr nz,TypeCol pop bc ret ; ; Put character to console an printer if requested ; ConLstout: push de ld e,a ; Save character ld a,(QFlag) ; Get quiet flag or a call z,Conout ; Not quiet ld a,(PFlag) ; Get printer flag or a ; Test enabled call nz,Lstout ; Yeap, put to printer pop de ret ; ; Home drive head ; *** NEVER CALLED *** ; HomeHead: push hl ld a,_HOME call BDOS50F ; Home drive head pop hl ret ; ; Set track number in DE ; SetTrk: push hl ld hl,(MaxTrk) ; Get max track call SubDE ; HL:=HL-DE pop hl jp c,OutLim ; Out of range ld (CurTrk),de ; Set current track ld b,d ld c,e push hl ld a,_SETTRK call BDOS50F ; Set track pop hl ret ; ; Set sector number in DE ; SetSec: push hl push de ld hl,(SysTrk) ; Get number of reserved tracks ex de,hl ld (CurSec),hl ; Set current sector ld hl,(CurTrk) ; Get current track call SubDE ; HL:=HL-DE pop bc ld h,b ld l,c push af dec hl ld a,(PHM) ; Get physical sector mask and l ld (LogSec),a ; Save push de ld a,(PSH) ; Get physical sector shift count or a ; Test default jr z,SetSe1 ; Yeap ld d,a SetSe2: call RotrHL ; Divide by 2 dec d jr nz,SetSe2 SetSe1: pop de inc hl ld b,h ld c,l pop af jr nc,NotSys ld a,(First0) ; Get physical 0 flag or a jr nz,GstSec ; Not physical 0 dec hl jr GstSec NotSys: ld hl,(SecTbl) ; Get the sector table pointer ex de,hl dec bc ld a,_SECTRN call BDOS50F ; Translate sector ld a,(Spt+1) ; Get number of 128-byte records per track or a ; Test 16 or 8 bit jr nz,GstSec ld h,a ; Set 8 bit GstSec: ld (PhySec),hl ; Set physical sector ld b,h ld c,l ld a,_SETSEC call BDOS50F ; Set sector pop hl ret ; ; Track overflow error ; OutLim: call IlPrtQ ; Give info dw OUT$TRACK push hl ld hl,(MaxTrk) ; Get max track call Dec pop hl call IlPrt dw $PLUS call Norite ; Disable write jp Prmptr ; Restart ; ; Set disk buffer ^BC ; SetDMA: ld (DMAadr),bc ; Save buffer SetDMA1: ld a,_SETDMA jp BDOS50F ; Set disk buffer ; ; Read disk sector ; Read: ld a,1 ld (WrFlg),a ; Enable write push hl call Read3 ; Read physical sector jr nz,RdErr ; Error ld de,(DMAadr) ; Get buffer address call Move ; Move to buffer pop hl ret RdErr: call IlPrt ; Tell read error dw RD$ERR jr FileErr ; ; Write sector ; Write: ld a,(WrFlg) ; Get write flag or a jr nz,PWrite ; Allowed BadW: call IlPrtQ ; Cannot write dw CANNOT$RD jp Expl PWrite: push hl call Read3 ; Read physical sector jr nz,WrERr ; Error ex de,hl ld hl,(DMAadr) ; Get buffer address call Move ; Move buffer ld a,_WRITE call BDOS50F ; Write selector or a ; Test success jr z,WritOk ; Yeap WrERr: call IlPrt ; Tell write failed dw WRT$ERR FileErr: xor a ld (QFlag),a ; Set not quiet ld hl,NIL ld (DPH.ADR),hl ; Clear DPH WritOk: pop hl ret ; ; Read physical sector - Z set says ok ; Read3: ld bc,(SectBuff) ; Get base buffer push bc call SetDMA1 ; Set disk buffer ld a,_READ call BDOS50F ; Read sector pop hl or a ; Test success ret nz ; Nope, error ld bc,RecLng ; Get record size ld a,(LogSec) ; Get physical sector mask or a ; Test mask ret z Read3L: add hl,bc ; Build sector offset dec a jr nz,Read3L ret ; ; Perform BIOS call ; BDOS50F: ld (BIOS.PB),a ; Save BIOS function ld (BIOS.PB+6),hl ; Save reg pair ld hl,(DPH.ADR) ; Get DPH ld a,h or l ; Test defined jr nz,BDOS50exe ; Yeap call IlPrt ; Tell drive selection error - wanna retry? dw SELD$ERR call YesNo ; Get response jr z,BDOS50noexe ; 'N' ld hl,-1 ld (DPH.ADR),hl ; Reset DPH jr BDOS50exe ; Do BIOS call BDOS50noexe: xor a ld (QFlag),a ; Set not quiet jp Prmptr ; ; Select drive in Accu thru BIOS ; BDOS50: ld c,a ; Save drive ld e,0 ; Force read ld a,_SELDSK ld (BIOS.PB),a ; Set BIOS function ld (BIOS.PB+6),hl ; Save BIOS registers BDOS50exe: ld (BIOS.PB+4),de ld (BIOS.PB+2),bc ld de,BIOS.PB ld c,.bios jp BDOS ; Do BIOS call ; ; ? Displays the help guide ; Help: call IlPrt dw $HELP jp Prompt ; ; Compare BC group # against 1 dm field ; GrpCmp: ld a,c inc d dec d jr z,Cmp8 cp (hl) inc hl ret nz ld a,b Cmp8: cp (hl) ret ; ; HL:=-HL ; Neg: ld a,l cpl ; Build complement ld l,a ld a,h cpl ld h,a inc hl ret ; ; HL:=HL DIV 2 ; RotrHL: srl h ; Simple shift rr l ret ; ; Collect the number of '1' bits in 'A' as a count in 'L' ; Colect: ld h,8 ; Init count CoLop: rla ; Shift bits jr nc,CoSkip inc l ; Count '1' CoSkip: dec h jr nz,CoLop ret ; ; HL:=HL-DE ; SubDE: or a sbc hl,de ; Simple one ret ; ; HL:=HL*DE ; Mult: push bc push de ex de,hl ld b,d ; Copy multiplier ld c,e ld a,b or c ; Test zero jr nz,MlCon ld hl,0 ; Resturn zero if so jr MlDone MlCon: dec bc ld d,h ld e,l MultLp: ld a,b or c jr z,MlDone add hl,de ; Multiply dec bc jr MultLp MlDone: pop de pop bc ret ; ; Routine to fill in disk parameters with every drive change ; On entry ^HL points to DPB ; LogIt: ld de,Spt ; Point to DPB ld b,DPBlen call Move ; Unpack it ld hl,(Spt) ; Get number of 128-byte records per track ld (Spt00),hl ; Init value ld hl,GrpDis ld a,(hl) push af ld a,(BLM) ; Get block mask ld (hl),a ; Unpack it push hl ld hl,(DSM) ; Get number of blocks on disc ex de,hl call GtkSec ; Get sector and track ld (MaxSec),hl ; Save max sector ld (MaxTrk),de ; Save max track pop hl pop af ld (hl),a ; Reset value ld l,0 ld a,(AL0) ; Get directory allocation bitmap, 1st byte call Colect ; Count bits ld a,(AL1) ; Get directory allocation bitmap, 2nd byte call Colect ld h,0 ld (DirGrp),hl ; Save value ret ; ; Get SCB value indexed by Accu ; LdSCB: push de push bc push hl ld de,SCB.PB ld (de),a ; Store into SCB pointer field ld c,.scb call BDOS ; Get SCB field pop hl pop bc pop de ret ; ; ACCU:=ACCU DIV B ; DivAB: ld c,-1 ; Init quotient DivLoop: inc c ; Fix quotient sub b ; Divide jr nc,DivLoop ; Loop until < 0 ld a,c ; Get back result ret ; ; Test for real and logical end of line - Z set if so ; EndLine: ld a,(hl) ; Get character EndLine1: cp cr ; Test real end ret z cp ';' ; Test logical end ret ; ; Test closing character - Z set if found ; IsEnd: cp ',' ; Test separator ret z cp '-' ; Test 'thru' ret z cp '>' ; Test hex closure ret ; ; Command table ; CMD$TAB: db '+' dw Plus db '-' dw Minus db '=' dw Search db '<' dw Save db '>' dw Restor db '#' dw Stats db '?' dw Help db 'A' dw Dump db 'B' dw Boot db 'C' dw Chg db 'D' dw Dump db 'F' dw PosFil db 'G' dw Pos db 'H' dw Dump db 'K' dw SvFile db 'L' dw Login db 'M' dw Map db 'N' dw NewDSk db 'P' dw Prntff db 'Q' dw Quiet db 'R' dw DoRead db 'S' dw Pos db 'T' dw Pos db 'U' dw User db 'V' dw View db 'W' dw DoRite db 'X' dw Xit db 'Y' dw Yank db 'Z' dw Sleep db '/' dw Repeat db '(' dw TogEra db -1 $ID: db _NL db 'DISK UTILITY v8.8',_NL db 'Version for CP/M 3.1',_NL,_NL db 'Type ? for help',_NL db 'Type X to exit',_NL,null NO$PLUS: db '++ Cannot run under this environment ++',_NL,null MEM$FUL: db '++ Out of memory ++',_NL,null DRV$STAT: db 'Statistics for drive ',null $TRACKS: db _NL,'Tracks:',tab,tab,null SYS$TRACKS: db _NL,'Sys tracks:',tab,null SEC$TRACK: db _NL,'Sec/track:',tab,null SEC$TRACK0: db _NL,'Sec/trk 00:',tab,null SEC$NOTRACK: db _NL,'Sec/non-trk 00:',tab,null $GROUPS: db _NL,'Groups:',tab,tab,null DIR$GROUPS: db _NL,'Dir groups:',tab,null SEC$GROUP: db _NL,'Sec/group:',tab,null DIR$ENTRIES: db _NL,'Dir entries:',tab,null $FREE: db ' ++FREE++ ',null LAST$ADR: db 'Last addr=',null YANK$FULL: db '++ Yank memory full ++',_NL,null NO$SAVE: db '++ No ''<'' save command issued ++',_NL,null $AT: db '= at ',null $EOF: db _NL,tab,'++ EOF ++',_NL,null CANT$DUMP: db '++ Can''t dump, no sector read ++',_NL,null USE$G: db 'Use G command following F,',_NL db 'or R or S following T',_NL,null NO$FILE: db '++ File not found ++',_NL,null $EMPTY: db 'Empty!',_NL,null $EXIST: db 'File exists! Delete?',null NO$DIR: db 'No dir space!',_NL,null WR$ERR: db 'Write error',_NL,null SEC$WRT: db ' sectors written.',_NL,null G$IS: db 'G=',null $COMMA: db ', ',null T$IS: db 'T=',null S$IS: db ', S=',null PS$IS: db ', PS=',null CANT$READ: db '++ Can''t read - not positioned ++',_NL db 'Position by:',_NL db tab,'Track then Sector, or',_NL db tab,'Group',_NL,null Y$N: db ' [Y/(N)] ',null $$Y: db 'Y',_NL,null BAD$DSPL: db '++ Bad displacement (not 0-7F) ++',_NL,null $COLON: db _NL,':',null OUT$TRACK: db '++ Not within tracks 0-',null $PLUS: db ' ++',_NL,null RD$ERR: db '++ READ failed, sector may be invalid ++',_NL,null CANNOT$RD: db '++ Cannot write unless read issued ++',_NL,null WRT$ERR: db '++ WRITE failed ++',_NL,null SELD$ERR: db _NL,'Selected drive error',_NL db 'Wanna retry',null $MORE: db '[More]',null DEL$MORE: db cr db ' ',_NL,lf,null $HELP: db _NL,_NL,'Operands in brackets [...] are optional',_NL db 'Numeric values: ''n'' are decimal, ''x'' hex',_NL,_NL db '+[n] step in [n] sectors',_NL db '-[n] step out [n] sectors',_NL db '# print disk parameters for current drive',_NL db '=Abc search for ASCII Abc from current sector',_NL db ' caution: upper/lower case matters',_NL db ' use for hex:',_NL db ' to find "IN 0" use: =<0> or',_NL db ' "(tab)H,0(CR)(LF)" use: =<9>H,0',_NL db '< save current sector into memory buffer',_NL db '> restore saved sector',_NL db '( toggle map display of erased files',_NL db '? help (displays this guide)',_NL db 'A[ff,tt] ASCII dump',_NL db 'B[nn] Boot track number of sectors per track' ; db _PAUSE,6 ; db 'CHANGE:',_NL,_NL db 'CHaddress,byte,byte... (hex)',_NL db 'CAaddress,data... (ASCIIx',_NL db ' Allowed for imbedded hex',_NL db 'CHfrom-through,byte, e.g. CH0-7F,E5',_NL db 'CAfrom-through,byte',_NL db 'D[ff,tt] Dump (hex+ASCII)',_NL db 'Fn.t Find file',_NL db 'F Find next file',_NL db 'Gnn CP/M Allocation Group nn',_NL db 'H[ff,tt] hex dump',_NL db 'K[du:]filename[.ext] Dump sequential memory to disk',_NL db 'L Log in drive',_NL db 'LX Log in drive X',_NL db 'M[nn] Map [from group nn]' ; db _PAUSE,7 ; db 'N New disk',_NL db 'P Printer toggle switch',_NL db 'Q Quiet mode (no msgs)',_NL db 'R Read current sector',_NL db 'Snn Sector nn',_NL db 'Tnn Track nn',_NL db 'Unn User nn for Find command',_NL db 'V[nn] View [nn] ASCII sectors',_NL db 'W Write current sector',_NL db 'X Exit program' db _NL db 'Y Yank current sector into sequential memory',_NL db 'Z[nn] Sleep [nn tenths]',_NL db '/[nn] Repeat [nn (decimal) times]' ; db _PAUSE,10 ; db 'Cancel a function with C or Ctl-C',_NL db 'Suspend output with S or Ctl-S',_NL db 'Separate commands with ";"',_NL db ' Example: G0',_NL db ' +;D;Z#20;/',_NL db ' would step in, dump, sleep 2 seconds',_NL db ' and repeat until CTL-C is typed',_NL db 'All "nn" usage except "/", "T", and "S" are hex',_NL db ' (use #nn for decimal)',_NL db lf,lf,lf,lf db '(See DU.DOC for complete examples)',_NL db lf,lf,lf,null TmpName: ds .name WildNam: db '????????' EndAdr: ; 1st free memory address dw EndCode SectBuff: ; Base buffer dw 0 DMABase: ; DMA address dw 0 SCB.PB: db 0 ; SCB offset goes here db 0 ; Zero for reading value ; BufAd: ; Buffer address dw DMA+RecLng HexAd: ; Address for change HEX dw 0 ToGo: dw -1 TwoUp: ; File count db 0 Toge: ; Map display for erased files db 0 PFlag: ; Printer flag - 0 is disabled db 0 Group: ; Group dw 0 GrpDis: ; Temporary block mask db 0 SaveFl: ; Save buffer indicator - 0 is empty db 0 CurTrk: ; Current track dw 0 CurSec: ; Current sector dw 1 PhySec: ; Physical sector dw 1 LogSec: ; Copy of physical sector mask db 0 FileCt: ; Directory entry count dw 0 DirPos: ; Directory position db 0 FindFl: ; Files foun flag - 0 is none db 0 Ftsw: ; First read flag - 0 is not db -1 NotPos: ; Position flag - 1 is not positioned db 1 WrFlg: ; Write flag - 0 is not allowed db 0 QFlag: ; Quiet flag - 0 is not quiet db 0 First0: ; Physical 0 flag db 0 Drive: ; Logged drive db 0 MaxTrk: ; Max track dw 0 MaxSec: ; Max sector dw 0 Spt00: ; Number of fixed 128-byte records per track dw 0 DlmReq: ; Delimiter required flag - 0 is none db 0 DirGrp: ; Directory allocation count dw 0 SavTrk: ; Current saved track dw 0 SavSec: ; Saved sector dw 0 SecTbl: ; Sector table pointer dw 0 MfPtr: ; Allocation address dw 0 DupFl1: ; Directory delimiter db 0 YnkBase: ; Yank base address dw 0 YnkAdr: ; Yank current address dw 0 DMAadr: ; Disk buffer address dw DMA DPH.ADR: ; Address of DPH dw -1 ; ; Start of BIOS PB ; BIOS.PB: db 0 ; BIOS function db 0 ; BIOS value for Accu dw 0 ; BIOS value for reg pair BC dw 0 ; BIOS value for reg pair DE dw 0 ; BIOS value for reg pair HL ; ; End of BIOS PB ; SetStk: ; Stack pointer dw 0 Back: ; Thru pointer dw 0 DumTyp: ; Dump type - A,D,H db 0 Ydn: ; Yank buffer address dw 0 UZer: ; Current user for K command db 0 CUZer: ; Current user db 0 Scount: ; Yank record count db 0 EndVal: ; Line pointer for K command dw 0 FoundFlag: ; File found flag (0 is no file found) db 0 CurColumn: ; Current console columns db 0 DispCol1: ; Console columns I db 0 DispCol2: ; Console columns II db 0 ConMode: ; Console mode dw 0 LogDsk: ; Logged disk db 0 ; ; Copy of DPB ; Spt: dw 0 ; Number of 128-byte records per track BSH: db 0 ; Block shift. 3 => 1k, 4 => 2k, 5 => 4k.... BLM: db 0 ; Block mask. 7 => 1k, 0Fh => 2k, 1Fh => 4k... db 0 ; Extent mask DSM: dw 0 ; (no. of blocks on the disc)-1 DRM: dw 0 ; (no. of directory entries)-1 AL0: db 0 ; Directory allocation bitmap, first byte AL1: db 0 ; Directory allocation bitmap, second byte dw 0 ; Checksum vector size SysTrk: dw 0 ; Offset, number of reserved tracks PSH: db 0 ; Physical sector shift, 0 => 128-byte sectors ; 1 => 256-byte sectors 2 => 512-byte sectors... PHM: db 0 ; Physical sector mask, 0 => 128-byte sectors ; 1 => 256-byte sectors, 3 => 512-byte sectors... DPBlen equ $-Spt ; ; End of DPB ; SavBuf: ; Current sector ds RecLng ; ; Command line ; InBuf: db COMLEN ; Max length db 0 ; Current length ds COMLEN ; Line db null ; End of line EndCode: end PastCk