(*************************************************************************) (* *) (* FORMAT v1.00 (c) Copyright S.J.Kay 2nd May 1995 *) (* *) (* Support utility for IBM Z80 Emulator CP/M 3 to allow formating *) (* floppy disks to a CP/M format *) (* *) (*************************************************************************) {$C-} { turn off ^C and ^S checking } type registers = record case boolean of true : (AL, AH, BL, BH, CL, CH, DL, DH : byte); false : (AX, BX, CX, DX, BP, SI, DI, DS, ES, FLAGS : integer) end; const DrvDta : array [0..6, 0..3] of integer = ( { Media EndTrk SecTrk TrkCap } ( 1, 39, 9, 6250 ), { 40T DD diskette in 40T DD 5.25" } ( 2, 39, 9, 6250 ), { 40T DD diskette in 80T HD 5.25" } ( 1, 39, 9, 6250 ), { 80T DD diskette in 80T HD 5.25" } ( 3, 79, 15, 10416 ), { 80T HD diskette in 80T HD 5.25" } ( 4, 79, 9, 6250 ), { 80T DD diskette in 80T DD 3.5" } ( 4, 79, 9, 6250 ), { 80T DD diskette in 80T HD 3.5" } ( 4, 79, 18, 12500 ) { 80T HD diskette in 80T HD 3.5" } ); var R : registers; ComLne : string[127] absolute $0080; ComPrm : string[127]; DPT : array[0..10] of byte; DrvCde : integer; DPH : integer; DrvInf : char; procedure ProcZ80 (Fn, Ax : byte; BCx, DEx, HLx : integer); begin inline ( $3A/Fn/ { ld a,(Fn) } $32/* + 17/ { ld (FNCNMB),a } $3A/Ax/ { ld a,(Ax) } $ED/$4B/BCx/ { ld bc,(BCx) } $ED/$5B/DEx/ { ld de,(DEx) } $2A/HLx/ { ld hl,(HLx) } $D3/$FF { out (FNCNMB),a } ) end; function FuncZ80 (Fn, Ax : byte; BCx, DEx, HLx : integer) : byte; const BytVal : byte = 0; begin inline ( $3A/Fn/ { ld a,(Fn) } $32/* + 17/ { ld (FNCNMB),a } $3A/Ax/ { ld a,(Ax) } $ED/$4B/BCx/ { ld bc,(BCx) } $ED/$5B/DEx/ { ld de,(DEx) } $2A/HLx/ { ld hl,(HLx) } $D3/$FF/ { out (FNCNMB),a } $32/BytVal { ld (BYTVAL),a } ); FuncZ80 := BytVal end; function GetByt (Seg, Off : integer) : byte; begin GetByt := FuncZ80($B0, 0, 0, Seg, Off) end; procedure SetByt (Seg, Off : integer ; BytPut : byte); begin ProcZ80($B1, BytPut, 0, Seg, Off) end; function GetWrd (Seg, Off : integer) : integer; begin GetWrd := FuncZ80($B0, 0, 0, Seg, Off) + FuncZ80($B0, 0, 0, Seg, Off + 1) shl 8 end; procedure SetWrd (Seg, Off, WrdPut : integer); begin ProcZ80($B1, lo(WrdPut), 0, Seg, Off); ProcZ80($B1, hi(WrdPut), 0, Seg, Off + 1) end; procedure Intr (Int : byte; var R : registers); begin ProcZ80($A1, Int, $AA55, $55AA, addr(R)) end; procedure Msdos (var R : registers); begin Intr($21, R) end; function Seg (var Dummy) : integer; const SegAdr : integer = 0; begin ProcZ80($A0, 0, 0, 0, addr(SegAdr)); Seg := SegAdr end; function Ofs (var VarTyp) : integer; begin Ofs := addr(VarTyp) end; procedure BiosX (Fn, Ax : byte; BCx, DEx, HLx : integer); begin inline ( $3A/Fn/ { ld a,(Fn) } $4F/ { ld c,a } $87/ { add a,a } $81/ { add a,c } $06/$00/ { ld b,0 } $4F/ { ld c,a } $2A/$01/$00/ { ld hl,(0001h) } $09/ { add hl,bc } $22/* + 17/ { ld (zzzz),hl } $3A/Ax/ { ld a,(Ax) } $ED/$4B/BCx/ { ld bc,(BCx) } $ED/$5B/DEx/ { ld de,(DEx) } $2A/HLx/ { ld hl,(HLx) } $CD/$00/$00 { call zzzz } ) end; procedure GetBIOSdata (AdrSor, AdrDst, Amount : integer); begin BiosX(28, 0, $0100, 0, 0); { set xmove banks, bank #0 to bank #1 } BiosX(24, 0, Amount, AdrSor, AdrDst) { move memory } end; function GetBIOSword (AdrSor : integer) : integer; const WrdDst : integer = 0; begin GetBIOSdata(AdrSor, addr(WrdDst), 2); GetBIOSword := WrdDst end; function GetBIOSbyte (AdrSor : integer) : byte; const BytDst : byte = 0; begin GetBIOSdata(AdrSor, addr(BytDst), 1); GetBIOSbyte := BytDst end; procedure DiskReset; begin R.AH := $00; R.DL := DrvCde; intr($13, R) end; procedure GetInterrupt (Int : byte; var S, O : integer); begin R.AH := $35; R.AL := Int; Msdos(R); S := R.ES; O := R.BX end; procedure SetInterrupt (Int : byte; S, O : integer); begin R.AH := $25; R.AL := Int; R.DS := S; R.DX := O; Msdos(R) end; procedure ReportError (ErrCde : byte); begin writeln; writeln; case ErrCde of $01 : writeln('Bad command'); $02 : writeln('Address mark not found'); $03 : writeln('Disk is write protected'); $04 : writeln('Sector not found'); $06 : writeln('Diskette removed'); $08 : writeln('DMA overrun'); $09 : writeln('DMA across 64 KB boundary'); $0C : writeln('Bad media type'); $10 : writeln('Bad CRC or ECC'); $20 : writeln('Controller failed'); $40 : writeln('Seek failed'); $80 : writeln('Drive not ready') else writeln('Unknown disk error') end end; procedure DataFromCPM (var TotSde : integer; var CylTot : integer; var EndTrk : integer; var TotSec : integer; var SecSze : integer); var DPB, SPT, DSM, OFF : integer; UNT, BLM, PSH, PHM : byte; begin UNT := GetBIOSbyte(DPH-2); DPB := GetBIOSword(DPH+12); SPT := GetBIOSword(DPB); BLM := GetBIOSbyte(DPB+3); DSM := GetBIOSword(DPB+5); OFF := GetBIOSword(DPB+13); PSH := GetBIOSbyte(DPB+15); PHM := GetBIOSbyte(DPB+16); TotSde := 2 - (UNT shr 7); CylTot := ((((DSM + 1) * (BLM + 1) + BLM) div SPT) + OFF); EndTrk := CylTot div TotSde - 1; TotSec := SPT div (PHM + 1); SecSze := PSH end; procedure CalcDiskValues ( TotSec, SecSze, EndTrk : integer; var Gap0, Gap1 : integer); var TrkCap, TrkLen : integer; x : integer; begin if DrvInf = #0 then begin if EndTrk > 60 then begin if (TotSec * (128 shl SecSze)) > 5120 then DrvInf := '7' { 80T HD diskette in 80T HD 3.5" drive } else DrvInf := '6' { 80T DD diskette in 80T HD 3.5" drive } end else DrvInf := '2' { 40T DD diskette in 80T HD 5.25" drive } end; x := ord(DrvInf) - ord('1'); TrkCap := DrvDta[x, 3]; TrkLen := TrkCap - trunc(TrkCap * (3.5 / 100.0) + 96.0); Gap1 := (TrkLen - (TotSec * (62 + (128 shl SecSze)))) div TotSec; if Gap1 > 255 then Gap1 := 255; Gap0 := Gap1 div 3 end; function SetupFloppy : boolean; var x, Retry : integer; Error : boolean; begin Retry := 3; x := ord(DrvInf) - ord('1'); repeat R.AH := $17; R.AL := DrvDta[x, 0]; R.DL := DrvCde; intr($13, R); Retry := Retry - 1; Error := odd(R.FLAGS); if Error and (Retry <> 0) then DiskReset until (not Error) or (Retry = 0); Retry := 3; if not Error then repeat R.AH := $18; R.CH := DrvDta[x, 1]; R.CL := DrvDta[x, 2]; R.DL := DrvCde; intr($13, R); Retry := Retry - 1; Error := odd(R.FLAGS); if Error and (Retry <> 0) then DiskReset until (not Error) or (Retry = 0); if Error then ReportError(R.AH); SetupFloppy := not Error end; function VerifyFormat (TotSec, SdeNmb, CylNmb : integer) : boolean; var Retry : integer; Error : boolean; begin R.AH := $04; R.AL := TotSec; R.CH := CylNmb; R.CL := 1; R.DH := SdeNmb; R.DL := DrvCde; intr($13, R); Error := odd(R.FLAGS); if Error then ReportError(R.AH); VerifyFormat := Error end; function TrackFormat (SdeNmb, CylNmb, TotSec, SecSze : integer) : boolean; var FmtTbl : array [0..255, 0..3] of byte; i : integer; SecNmb : integer; begin SecNmb := 1; for i := 0 to TotSec - 1 do begin FmtTbl[i, 0] := CylNmb; FmtTbl[i, 1] := SdeNmb; FmtTbl[i, 2] := SecNmb; FmtTbl[i, 3] := SecSze; SecNmb := SecNmb + 1 end; R.AH := $05; R.AL := $01; R.CH := CylNmb; R.CL := $00; R.DH := SdeNmb; R.DL := DrvCde; R.ES := seg(FmtTbl); R.BX := ofs(FmtTbl); intr($13, R); if odd(R.FLAGS) then ReportError(R.AH); TrackFormat := odd(R.FLAGS) end; procedure FormatFloppy (TotSde, CylTot, EndTrk, TotSec, SecSze : integer); var SdeNmb, CylNmb, CylCnt : integer; Finish, Error : boolean; Key : char; begin SdeNmb := 0; CylNmb := 0; CylCnt := 0; Finish := false; repeat if keypressed then begin read(kbd, Key); Finish := Key = ^[ end; if not Finish then begin Error := TrackFormat(SdeNmb, CylNmb, TotSec, SecSze); if not Error then Error := VerifyFormat(TotSec, SdeNmb, CylNmb); SdeNmb := (SdeNmb + ord(TotSde = 2)) and $01; CylNmb := CylNmb + ord(SdeNmb = 0); CylCnt := CylCnt + 1; Finish := CylNmb > EndTrk; if not Error then write((CylCnt / CylTot) * 100.0:3:0, ^M) end until Error or Finish; bdos(13) end; procedure StartFormat; var TotSde, CylTot, EndTrk, TotSec, SecSze : integer; Gap0, Gap1 : integer; DskPrmS, DskPrmO, i : integer; begin DataFromCPM(TotSde, CylTot, EndTrk, TotSec, SecSze); CalcDiskValues(TotSec, SecSze, EndTrk, Gap0, Gap1); writeln('Formatting: Tracks=', EndTrk+1, ' Sides=', TotSde, ' Sec/Trk=', TotSec, ' Byt/Sec=', 128 shl SecSze, ' Gap=', Gap1); write(' 0 percent completed', ^M); if SetupFloppy then begin GetInterrupt($1E, DskPrmS, DskPrmO); for i := 0 to 10 do DPT[i] := GetByt(DskPrmS, DskPrmO + i); DPT[3] := SecSze; { set sector size code } DPT[4] := TotSec; { set last sector on track } DPT[5] := Gap0; { set intersector gap for read/write } DPT[7] := Gap1; { set intersector gap for format } DPT[8] := $E5; { data format value } SetInterrupt($1E, seg(DPT), ofs(DPT)); DiskReset; FormatFloppy(TotSde, CylTot, EndTrk, TotSec, SecSze); SetInterrupt($1E, DskPrmS, DskPrmO); DiskReset end end; procedure PromptUser; var FmtNxt : STRING[1]; Key : char; begin repeat writeln; writeln('Insert a disk to be formatted in drive ', chr(ord('A') + DrvCde)); writeln; writeln('Press ENTER when ready'); repeat read(kbd, Key) until Key in [^M, ^[, ^C]; if Key = ^M then begin writeln; StartFormat; writeln; writeln; write('Format another disk (y/n) ?: '); buflen := 1; readln(FmtNxt) end; writeln until ((FmtNxt <> 'Y') and (FmtNxt <> 'y')) or (Key = ^[) end; procedure CheckAndFormat; begin DrvCde := ord(ComPrm[1]) - ord('A'); DPH := GetBIOSword(bioshl(21) + DrvCde * 2); if (DPH <> 0) and (DrvCde in [0..1]) then PromptUser else writeln('Drive specified not supported') end; procedure ShowUsage; begin writeln; writeln('FORMAT v1.00 (c) Copyright S.J.Kay 2nd May 1995'); writeln; writeln('Formats disk according to the data held in the CP/M 3 DPB'); writeln; writeln('If more than 59 tracks:-'); writeln(' Format will be 80T DD/HD diskette in 80T HD 3.5" drive'); writeln('If less than 60 tracks:-'); writeln(' Format will be 40T DD diskette in 80T HD 5.25" drive'); writeln; writeln('Use an appropriate switch if other hardware is required.'); writeln; writeln('Use:- FORMAT D: [/1/2/3/4/5/6/7]'); writeln; writeln('D: = format diskette in drive A or B'); writeln('/1 = 40T DD diskette in 40T DD 5.25" drive'); writeln('/2 = 40T DD diskette in 80T HD 5.25" drive'); writeln('/3 = 80T DD diskette in 80T HD 5.25" drive'); writeln('/4 = 80T HD diskette in 80T HD 5.25" drive'); writeln('/5 = 80T DD diskette in 80T DD 3.5" drive'); writeln('/6 = 80T DD diskette in 80T HD 3.5" drive'); writeln('/7 = 80T HD diskette in 80T HD 3.5" drive') end; var Error : boolean; begin DrvInf := #0; ComPrm := ComLne; while pos(' ', ComPrm) <> 0 do delete(ComPrm, pos(' ', ComPrm), 1); Error := pos(':', ComPrm) <> 2; if not Error then begin if (pos('/', ComPrm) = 3) and (length(ComPrm) = 4) then begin Error := not (ComPrm[4] in ['1'..'7']); if not Error then DrvInf := ComPrm[4] end else Error := (length(ComPrm) <> 2) and (DrvInf = #0); if not Error then CheckAndFormat else ShowUsage end else ShowUsage end.