.Z80 ;*************************************************************** ;File: GEOM.Z80 ;Purp: Experimentally determine geometry of hard disk. ; ; CP/M System EQUates: bdos equ 5 ;CP/M Entry point prints equ 9 ;print string function ; Definition of used I/O addresses: GIDE equ 20h ; base address of GIDE board IdeDOR equ GIDE+6 ; Digital Output Register IdeDat equ GIDE+8 ; Data Register IdeErr equ GIDE+9 ; Error Register IdePCmp equ IdeErr IdeSCnt equ GIDE+0Ah ; Sector Count IdeSNum equ GIDE+0Bh ; Sector Number IdeCLo equ GIDE+0Ch ; Cylinder Low IdeCHi equ GIDE+0Dh ; Cylinder High IdeSDH equ GIDE+0Eh ; Drive and Head IdeCmd equ GIDE+0Fh ; Command / Status IdeStat equ IdeCmd ; Commands for IDE hard disk drives: CmdRd equ 20h ; Read Sector CmdWr equ 30h ; Write Sector CmdInit equ 91h ; Initialize Drive Params ;Register usage: ; ; HL = cylinder ; B = head ; C = sector ASEG ;absolute code (M80) ORG 0100h jp start defb 'Z3ENV' ;flag string defb 1 ;type 1 external z3env: defw 0000 ;env address goes here altld: defw 0000 ;alternate load address (if type 3) start: ld de,Signon ;point at intro message ld c,prints ;print_string call bdos ;do it. ; ld de,Intro ;intro message ld c,prints call bdos ; xor a ;0 in A ld l,a ;and in L ld h,a ;and in H (start at cylinder 0) ld b,a ;start at head 0 ld c,a inc c ;start at sector 1 ; ;start by finding number of heads ; headcnt: call tfsetup ;set up parms ld a,CmdRd ;read command call sndcmd ;send it call readsec ;read data call chknf ;see if NOT FOUND error occurred jr nz,maxhead ;jrif not found inc b ; else bump up head number ld a,b ;get head count cp 11h ;check max+1 jr nc,maxhead ;jump out and continue if head # too big jr headcnt ;and try again. maxhead: ld a,b ;get head count ld (Head),a ;and save it for later ; ld a,'.' ;print progress indicator... push bc push de call pchar pop de pop bc ; seccnt: ld b,0 ;operate on head 0 call tfsetup ;set up parms ld a,CmdRd ;read command call sndcmd ;send it call readsec ;read data call chknf ;see if NOT FOUND error occurred jr nz,maxsec ;jrif not found inc c ; else bump up sector number jr z,sccnt1 ;jrif wrap to 0. Check once more and exit jr seccnt ;and try again. sccnt1: ld b,0 ;operate on head 0 call tfsetup ;set up parms ld a,CmdRd ;read command call sndcmd ;send it call readsec ;read data call chknf ;see if NOT FOUND error occurred jr z,mxsec1 ;jrif sector 0 (=256) valid maxsec: dec c ;normalize sector count mxsec1: ld a,c ;get in A ld (Sector),a ;save for later ; ld a,'.' ;print progress indicator... push bc push de call pchar pop de pop bc ; cylcnt: ld c,1 ;operate on sector 1 call tfsetup ;set up parms ld a,CmdRd ;read command call sndcmd ;send it call readsec ;read data call chknf ;see if NOT FOUND error occurred jr nz,maxcyl ;jrif not found inc hl ; else bump up cylinder number ld a,l ;get l or h ;check for max jr z,maxcyl ;jrif cylinder number wrapped to 0 jr cylcnt ;and try again. maxcyl: ld (Cylinder),hl ;save it for later ; ld a,'.' ;print progress indicator... push bc push de call pchar pop de pop bc ; ld de,Exitmsg ;point at exit message ld c,prints ;printstring in C call bdos ;print it ; ld hl,(Cylinder) ;cylinder count in HL call wrd2bcd ;make BCD or a ;check msb jr z,blnk0 ;jrif A=0 push hl ;save hl call prnyb ;only LSN of A is significant pop hl ;get it back jr prcyl ;and print LSW of cylinder blnk0: ld a,' ' ;print a space instead of 0 call pchar ;do it prcyl: ld a,h ;check for leading 0 cp 10h ;check BCD jr c,blnk1 ;jrif a leading 0 call prword ;call if not a leading 0 jr prhead ;and go print number of heads blnk1: push af ;save it ld a,' ' ;get a space call pchar ;and print it pop af ;get it back push hl ;save hl call prnyb ;print nybble pop hl ;get hl ld a,l ;get least call prbyte ;and print it prhead: ld de,Headmsg ;point at head message ld c,prints ;printstring in C call bdos ;print it ; ld a,(Head) ;get head count call byt2bcd ;convert to bcd ld a,l ;get bcd value cp 10h ;check for leading 0 jr c,blnk2 ;jrif leading 0 call prbyte ;call in no leading 0 jr prspt ;and go do sec/trk blnk2: push af ;save ld a,' ' ;get a space call pchar ;print it pop af ;get it back call prnyb ;and print it ; prspt: ld de,Sptmsg ;point at sec/trk message ld c,prints ;printstring in C call bdos ;print it ; ld a,(Sector) ;get sector count or a ;check for 0 jr nz,prspt1 ;if not zero, to the usual ld a,2 ;0 means 256 call prnyb ;print first nybble ld a,56h ;load BCD of the rest call prbyte ;and print it jr done ; prspt1: call byt2bcd ;convert to BCD in HL ld a,h ;see if leading 0 or a ;set flagz jr z,blnk3 ;jrif leading 0 call prnyb ;print msn jr prspt2 ;and continue blnk3: ld a,' ' ;get a space call pchar ;and print it prspt2: ld a,l ;get LSBCDB call prbyte ;and print it ; Done: ld de,crlf ;point to closing message ld c,prints call bdos ; ld c,0 ;system reset jp bdos ;and exit ; Subroutines: wait for hard disk ready (non-busy) resp. DRQ active. WaitRdy: in a,(IdeCmd) rla jr c,WaitRdy ret WaitDrq: in a,(IdeCmd) bit 3,a jr z,WaitDrq ret ;*************************************************************** ;Routine: tfsetup ;purpose: initialize IDE task file registers ;Entry: HL = cylinder, B = head, C = sector ;Exit: Task file set up, ;Used: AF tfsetup: call WaitRdy ;Wait for Task file ld a,1 ;1 sector out (IdeSCnt),a ;set count of 1 ld a,c ;get sector out (IdeSNum),a ;set up sector number ld a,l ;get low byte of cylinder out (IdeCLo),a ;set up low byte of cylinder ld a,h ;get high byte of cylinder out (ideCHi),a ;set up high byte of cylinder ld a,b ;get head number or 0A0h ;make into SDH byte (drive 0) out (IdeSDH),a ;set up drive and head ret ;and done ;*************************************************************** ;Routine: sndcmd ;Purpose: write command byte to IDE command register ;Entry: A=command byte, Task file set up ;Exit: Command sent to IDE ;Used: A sndcmd: out (IdeCmd),a ;send it ret ;and done ;*************************************************************** ;Routine: readsec ;purpose: read 512-byte sector from IDE drive ;Entry: read sector command issued ;Exit: buffer filled ;Uses: AF, HL, BC (HL, BC saved/restored, though) readsec: push hl push bc ;these are all we need to save ld bc,IdeDat ;set up count, port in BC ld hl,(BufAdr) ;get buffer in HL call WaitDrq ;wait until data ready inir inir ;read the sector pop bc pop hl ;get regs back ret ;*************************************************************** ;Routine: chknf ;Purpose: check for ID NOT FOUND error from IDE drive ;Entry: None ;Exit: Z=1 no error Z=0 if not found. Error exit if not NFerr ;Used: AF chknf: in a,(IdeStat) ;get status and 1 ;mask error bit ret z ;return if no error in a,(IdeErr) ; error, get error byte bit 4,a ;check IDNF bit ret nz ;return if ID Not Found error ; ld (ErrReg),a ;save contents of IdeErr ; ld de,unxpct ;point to error message ld c,9 ;print string call bdos ;do it ; ld a,(ErrReg) ;get error register contents back call prbyte ;print it in hex ; ld de,crlf ;point to error message ld c,9 ;print string call bdos ;do it ; ld c,0 ;do warm boot jp bdos ;and quit (function 0 doesn't return) ; ;*************************************************************** ;Routine: BYT2BCD ;Purpose: convert 8-bit binary value to BCD ;Entry: A = 8-bit binary value ;Exit: HL = BCD value, A=garbage, BC points to BCD number ;Uses: ALL ; byt2bcd: ld l,a ;dividend ld h,0 ;in HL ld bc,bcdnum ;point to result space ld de,100 ;divisor in DE call divide ;do division ld (bcdnum+1),a ;save result jr bt2bcd1 ;and continue ;*************************************************************** ;Routine: WRD2BCD ;Purpose: convert 16-bit binary value to BCD ;Entry: HL = 16-bit binary value ;Exit: A = MSB of BCD, HL = LSW of BCD, BC points to BCD number ;Uses: All ; wrd2bcd: ld bc,bcdnum+1 ;point to result space ld de,10000 ;get divisor call divide ;divide ld (bcdnum+2),a ;save result ld de,1000 ;get next divisor call divide ; do divide call savetens ;save tens digit ld de,100 ;now by 100 call divide call saveones ;save one's digit bt2bcd1: ld de,10 ;now by 10 call divide call savetens ld de,1 call divide call saveones ;save last one's digit ld hl,(bcdnum) ;get LSW ld a,(bcdnum+2) ;get MSN inc bc ;point at BCD number ret ;and return divide: ld a,0ffh ;-1 in A divlp: inc a ;bump the quotient or a ;clear carry sbc hl,de ;subtract jr nc,divlp ;until carry add hl,de ;restore HL to positive ret ;return w/A=quotient, HL=remainder savetens: rlca rlca ;move low nybble rlca ;to high rlca and 0f0h ;mask high nybble ld (bc),a ;save tens ret ;and go back saveones: ld e,a ;save ones ld a,(bc) ;get tens back or e ;or in ones ld (bc),a ;save 2 digits back dec bc ;bump to next ret ;and go back bcdnum: defs 3,0 ;space for 3 bytes of BCD ;*************************************************************** ;Routine: PRWORD ;Purpose: Print 16-bit value to console ;Entry: HL = word ;Exit: 16-bit value displayed as 4 hex digits on console ;Uses: ALL prword: ld a,h ;get high byte call prbyte ;print it ld a,l ;get low byte and fall through to ;prbyte to print it ;*************************************************************** ;Routine: PRBYTE ;Purpose: Print 8-bit value to console ;Entry: A = byte ;Exit: 8-bit value displayed as 2 hex digits on console ;Uses: ALL prbyte: push af ;save byte for later rrca rrca ;move upper nybble rrca ;to lower rrca call prnyb ;print it pop af ;get byte back and fall through ;to prnyb to print lower nybble ;*************************************************************** ;Routine: PRNYB ;Purpose: Print 4-bit value to console ;Entry: A(0:3) = nybble of interest ;Exit: 4-bit value displayed as 1 hex digit on console ;Uses: ALL prnyb: and 0fh ;mask lower nybble cp 0ah ;bigger than 9? jr c,prnyb1 ;no, jump around add a,7 ;else bump to letters prnyb1: add a,30h ;make into hex digit ;and fall through to pchar ;*************************************************************** ;Routine: pchar ;Purpose: print character in A on the console ;Entry: A has ASCII character ;Exit: character in A printed to console ;Uses: BC, DE, AF ; pchar: push hl ld e,a ld c,2 call bdos pop hl ret ;*************************************************************** ; Messages and fixed data: ; Signon: defb 0dh, 0ah defb 'GIDE Geometry Seeker -- John D. Baker, ' defb '6 January 1996' defb 0dh, 0ah, 0ah, '$' Intro: defb 'Scanning $' Exitmsg: defb ' Finished!', 0dh, 0ah, 0ah defb ' Cylinders: $' Headmsg: defb 0dh,0ah defb ' Heads: $' Sptmsg: defb 0dh,0ah defb 'Sectors/track: $' Unxpct: defb 0dh, 0ah, 7 defb '%% Unexpected error from IDE drive!' defb 0dh, 0ah defb 'Error register: 0x$' crlf: defb 0dh, 0ah, '$' cr2lf: defb 0dh, 0ah, 0ah, '$' ;*************************************************************** ; RAM variables: ; ErrReg: defs 1 ; error register byte Cylinder: defs 2 ; cylinder number (0..65534) Head: defs 1 ; head number (0..15) Sector: defs 1 ; sector number (1..256[0]) BufAdr: defw Buffer ; address of 512 byte sector buffer ; Buffer: end