; Program to investigate CP/M directory structures on the disk ; ; Warning! assumes first sector number is 0!!!! ; Will only work for drives with capacity of 64K sectors or less ; (=8 megabytes) bdossecs equ 44 ;sectors to read on warm boot .z80 buffer equ 2000h monit equ 0f033h ;return to monitor for error memory equ 16h ;to enable monitor PROM ; loader bios access bios equ 500h conout equ bios+0ch seldsk equ bios+1bh settrk equ bios+1eh setsec equ bios+21h setdma equ bios+24h read equ bios+27h sectran equ bios+30h page start: ld SP,100h ;use default buffer ld C,0 ld E,0 ;make seldsk know this is the first call call seldsk ld A,H or L ld DE,badsel jp z,error ;bad select-HL=0000 ;HL=DPH address ld C,(HL) ;XLT table, low order byte inc HL ld B,(HL) ld A,B or C ;A=0 iff no sector translation ld (trans),A ld (XLT),BC ld DE,7 add HL,DE ;(HL)=DIRBUF address - might as well use it ;for our workspace ld C,(HL) inc HL ld B,(HL) ld (dirbuf),BC push HL call setdma ;use the dirbuf for operations pop HL inc HL ;(HL)=DPB address ld E,(HL) inc HL ld D,(HL) page ex DE,HL ;HL=DPB address ld E,(HL) inc HL ld D,(HL) ;(HL)=sectors per track ld (SPT),DE ;highest sector number before next track inc HL ld A,(HL) ;Block shift factor ld (BSH),A inc HL ld A,(HL) ld (BLM),A ;block mask inc A ld (blksec),A ;sectors per block inc HL inc HL inc HL ld A,(HL) ;A=msb of DSM or total block count of drive ld (blkmsb),A inc HL ld E,(HL) inc HL ld D,(HL) ld (maxdir),DE ld DE,5 add HL,DE ;(HL)=track offset ld C,(HL) inc HL ld B,(HL) ld (trkoff),BC ld (curtrk),BC call settrk ;directory starts at first non-system track page ; Current status of program: ; The disk on which the system file resides has been selected ; and the head has been stepped to the first directory track ; Note that the directory will always start at sector 1 of ; the first non-system track, and will occupy consecutive ; sectors through the end of the directory. The total number ; of sectors occupied is (maxdir)/4, but they may be on more ; than one track. ; nextsec: ;read next directory sector into the DIRBUF ld HL,(SPT) ld BC,(cursec) sbc HL,BC jr nc,trksame ;haven't gone to next track yet ld BC,(curtrk) inc BC ld (curtrk),BC call settrk ;step disk ld BC,0 ;first sector, new track trksame: push BC ;logical sector ld A,(trans) or A jr z,notran ;do we need to do sector translation? ld DE,(XLT) ;translate table call sectran push HL pop BC notran: call setsec call read or A ld DE,permerr jp nz,error ;physical error pop HL ;sector just read inc HL ld (cursec),HL ; ; The next sector of the directory (4 entries) is now in the DIRBUF ; Now see if we can find the file ; ld HL,(dirbuf) ld DE,32d ld B,4 nextdir: push HL push DE push BC call match ;match directory entry with FCB pop BC pop DE pop HL jr z,found ;file found add HL,DE djnz nextdir ; ; we've search this sector-no match. Was it the last? ; ld HL,(absec) inc HL ld (absec),HL dec HL ;HL=sectors read so far add HL,HL add HL,HL ;sectors x 4 = directory entries ld DE,(maxdir) sbc HL,DE ld DE,nofile jp z,error jr nextsec page ; ; Match: match takes as a parameter HL pointing to a directory ; entry. Match returns the zero flag set if the file matches. ; match: ex DE,HL ;use DE to point to directory ld HL,Filefcb+1 ;first character in filename to search ld BC,0b0ch ;11 characters to match nextch: inc DE ld A,(DE) ;next character in directory entry and 7fh ;mask file attribute bits cpi ret nz ;no match, return djnz nextch ret page ; ; Now we have the correct directory entry, with HL pointing to ; the the start of the FCB ; Next we expand the block allocation data into a table of items: ; ; dw track, db first sector, db last sector found: ld DE,15d add HL,DE ;(HL)=records in this extent ld DE,filefcb+15d ;DE=block allocation area in filefcb ld BC,17d ldir ;move to file fcb ld IY,filefcb+16 ;pointer to next block ld IX,boottbl ;pointer to current table entry call getblk ;returns block # in DE newtbl: push DE call mktrk ;returns track in BC, 1st sector in HL ld (IX),C ld (IX+1),B ;store track ld (IX+2),L ;first sector ld DE,(blksec) add HL,DE dec HL ;HL=last sector of block ld A,(spt) dec A ;sectors numbered 0-(spt-1) cp L jr c,newtrk ;block overflows to next track ld (IX+3),L nxtblk: call getblk ;get next block # pop HL ;last block scf ccf sbc HL,DE inc HL ;HL=0 if new block adjacent to last ld A,H or L jr z,moreblks ld BC,4 add IX,BC ;set pointer to next track item jr newtbl newtrk: ld (IX+3),A ;spt-1 last sector on track ld BC,4 add IX,BC ;pointer to next track ld B,A inc B ld A,L ;sector number relative to start of last track sub B ld (IX+3),A ;last sector of this block xor A ld (IX+2),A ;start sector zero ld C,(IX-4) ld B,(IX-3) ;last track inc BC ld (IX),C ld (IX+1),B jr nxtblk page moreblks: push DE ;save new block # ld A,(blksec) ld B,(IX+3) ;last sector so far add A,B ld L,A ;new high sector ld A,(spt) dec A cp L jr c,newtrk ;this block overflows track ld (IX+3),L ;set new last sector jr nxtblk ; getblk returns the block # (IY) in DE and moves IY to the next ; also, exits loop if block # = 0 getblk: ld E,(IY) ld A,(blkmsb) or A jr z,onebyte inc IY ld A,(IY) onebyte: ld D,A inc IY or E ;block # = 0? ret nz pop DE ;lose return in loop jr tbldone ; mktrk gets block # in DE, returns track in BC, 1st sector in HL mktrk: ex DE,HL ld A,(bsh) ld B,A shftblk: add HL,HL djnz shftblk ;HL=absolute sector number ld DE,(spt) scf ccf ld BC,(trkoff) dec BC mortrk: sbc HL,DE inc BC jr nc,mortrk add HL,DE ;restore sector relative in track ret page ; ; table now contains the track/sector map for all the blocks in ; the directory. Now read the table into memory. ; tbldone: ld HL,buffer ;start of CCP-starting DMA nxttrk: push HL ;unitialized data-sector count push HL ;current DMA ;read sectors of next track ld HL,(bootptr) ;point to boottbl entry ld C,(HL) inc HL ld B,(HL) ;BC=track push HL ld A,B or C jr z,bootdone ;last track done call settrk pop HL ;boottbl pointer inc HL ld C,(HL) ;first sector dec C ;will be incremented before setsec inc HL ld B,(HL) ;last sector inc HL ld (bootptr),HL ;save pointer pop HL ;DMA pop DE ;old sectors push BC ;new sectors push HL ;DMA nxtsec: pop HL ;DMA push HL ld B,H ld C,L ;move to BC call setdma pop HL ;last DMA pop BC ;sector data ld A,B ;last sector to read inc C ;read next sector cp C jr c,nxttrk ;done with last sector ld DE,128 add HL,DE ;update DMA push BC push HL ld B,0 ;C=sector to read (<256) ld DE,(xlt) call sectran ld C,L ld B,H call setsec call read or A ld DE,permerr jr nz,error ;physical read error jr nxtsec bootptr: dw boottbl ;initial track value page ; ; the .SYS file is now in memory starting at (buffer) ; ; we must go through it and remove all the BIOS sectors ; to make it a table of CCP and BDOS sectors only for wboot bootdone: ld IX,boottbl-4 ld HL,0 ;count of sectors in table so far nxtitem: ld DE,4 add IX,DE ld B,(IX+2) ld A,(IX+3) ld C,A sub B inc A ld E,A ;sectors in this track add HL,DE ld A,bdossecs ;number of sectors for wboot cp L jr z,movtbl ;this track ends exactly at the BIOS jr nc,nxtitem ;keep reading table ;this is the item we must cut down ;B=first sector, C=last sector, E=C-B+1 ;A=bdossecs, L=sectors through end of track ld D,A ld A,L sub D ;A=sectors to remove ld D,A ld A,C sub D ld (IX+3),A ;must contain at least one sector in track page ; ; table has now been modified for CP/M wboot use ; IX+3 is end of last table entry movtbl: push IX pop HL ld DE,boottbl-4 sbc HL,DE ;HL=length to move ld B,H ld C,L ld HL,boottbl ld DE,buffer+1637h ;boottbl in buffer-after jump table ldir ;move table ;boottbl filled in, now move image to execution ;area ld HL,buffer ld DE,(buffer+1633h) ;ccp start ld C,0 ld A,(filefcb+15) ;128 byte records allocated rrca rr C ;carry=> odd number of records and 7fh ld B,A ldir ;transfer jp buffer+1600h ;coldboot jump ;-------------------------------------------- ; ; TEmp! error: ex DE,HL ld B,(hl) inc HL call pstrng ld HL,errmsg call pstrng ld B,11d ld HL,filefcb+1 call pstr2 ld A,4fh out (memory),A jp monit pstrng: ld B,(HL) inc HL ;HL=first character, B=count pstr2: push HL ld C,(HL) push BC call conout pop BC pop HL djnz pstr2 ret ;storage filefcb: db 0 ;drive 'A:' db 'CPM ' db 'SYS' ds 20 BSH: ds 1 ;block shift factor = log2(records/block) BLM: ds 1 ;block mask = records/block-1 blkmsb: ds 1 ;non-zero is >255 blocks on drive ;=msb of DSM in DPB blksec: ds 2 ;sectors per block (BLM+1) dirbuf: ds 2 ;address of DIRBUF trans: ds 1 ;0 if no sector translation XLT: ds 2 ;translate table address SPT: ds 2 ;sector per track count maxdir: ds 2 ;number of directory entries on disk trkoff: ds 2 ;track offset curtrk: ds 2 ;current track cursec: ds 2 absec: ds 2 ;absolute sector ;messages badsel: db 6,'Select' permerr: db 4,'Read' nofile: db 14,'File not found' errmsg: db 14,' error: file ' boottbl: ds 20 end