TITL BUILDFSM"9/7/77 * * This program reads every sector on the specified or default * disk and builds a free space map containing all sectors * with ID 0000 or with ID's which do not belong to any file * in DIRECTRY on that disk. * COPY NPTDEFS ORG 100H XEQ 100H BLDFSM LXI SP,STACK CALL GETCI Put CI/CO file #'s into CIFILE,COFILE resp. CALL STUNIT Put the relevent unit # in TUNIT. CALL GETIDS Put all valid file ID's into IDTABL. CALL INIT Clear the in-core FSM and its buffer FSMBUF. CALL DDRI Initialize driver. * CALL OUST DW 0A0DH ASC /Tracks done: / DB 0 * XRA A STA TTRK Start with track zero. * * While track <= 76 : * NEXTRK LDA TTRK CPI 77 Last track is number 76. JP ALLDUN * XRA A STA TSEC Start with sector zero on this track. * * While sector <= 15 : * NEXSEC CALL ESCTST Does user want out? LDA TSEC CPI 16 Last sector is number 15. JP TRKDUN CALL RDSKH Read header to IHEAD. JMP NEXS0 ERROR--free this block since it is screwed up. LHLD IHFID Set HL= file ID. SHLD BLKID Remember this block's ID. MOV A,H Is ID=0? ORA L JNZ NEXS1 Nope. LDA IHPRO Should only be one sector in a free block. ANI 0FH Just in case. CPI 1 JNZ NEXS0 This free block is sick. CALL ADDFSM Add this block to the FSM. LXI H,TSEC Go to next sector. INR M JMP NEXSEC NEXS0 MVI A,1 If ID=0 make sure we only write one header. STA IHPRO IHPRO is number of sectors in this block. JMP NEXS2 We don't need to look it up -- it's unused. NEXS1 CALL SEARCH Look this ID up. JC VALID Carry set => valid block. NEXS2 CALL FREEBL Make OHEAD an unowned-block header (=STDHD). * * Now we want to put the next IHPRO sectors into the FSM and * write out their headers. * LDA IHPRO Check for bad # of sectors. NEXS3 MOV B,A LDA TSEC ADD B CPI 16+1 TSEC+IHPRO can't be more than 16. JM NEXS4 MVI A,1 This block had delusions of grandeur. Give it STA IHPRO only one sector, and free that. LDA TSEC INR A (We want to set LAST=TSEC+1 so we do only one sector.) NEXS4 STA LAST We stop when TSEC=LAST. NEXS5 CALL ADDFSM Set current sector's bit in FSMBUF. CALL WDSKH Write out the header. CALL REPORT ERROR--mention it but keep going. LXI H,TSEC Sets M=current sector. INR M LDA LAST Was that the last one? CMP M JNZ NEXS5 Nope. JMP NEXSEC Continue with this track. * VALID LXI H,TSEC Sets M=current sector. LDA IHPRO Move to next header by adding # of sectors in ADD M this block to the current sector. MOV M,A LXI H,BUFPTR Do the same for the FSMBUF pointer. LDA IHPRO ADD M MOV M,A JMP NEXSEC Continue with this track. * * End of inner loop => current track finished. * TRKDUN CALL PACK Pack info for track from FSMBUF to FSM. LDA TTRK PUSH PSW ANI 0FH Do a CRLF before every 16th track. CZ CRLF POP PSW CALL HEX CALL OUST DW ' ' Two spaces. DB 0 INR A CPI DIRDT We don't want to play with the DIRECTRY. JNZ TRKD1 PUSH PSW ANI 0FH CZ CRLF (If directory is on track 0,16,32, etc.) POP PSW CALL OUST Mention that we're at the DIRECTRY. ASC /DI / DB 0 INR A Skip the directory track. LHLD FSMPTR Don't change FSM entries for directory. INX H INX H Skip 16 bits. SHLD FSMPTR TRKD1 STA TTRK CALL INITB Clear the FSMBUF. JMP NEXTRK Continue with disk. * * End of outer loop => all done. * ALLDUN CALL WRTFSM Write out the new FSM. CALL OUST DW 0A0DH ASC /Recovered / DB 0 LHLD RECOVS CALL DOUT CALL OUST ASC / blocks. Number of free blocks = / DB 0 LHLD FREE CALL DOUT CALL OUST DB '.' DW 0A0DH DB 0 EXIT CALL SYS DB RETOP LAST DB 0 * * Global variables. * DS 50 STACK DS 1 CR EQU 0DH MODE EQU 0 The escape character. FREE DW 0 Count of number of free blocks on disk. RECOVS DW 0 Number of free blocks which were recovered. BLKID DW 0 Stores file ID for current block. IDTABL EQU CXBUF Leaves room for lots of files. BUFPTR DW 0 Pointer to next location in FSMBUF. FSMPTR DW 0 Pointer to next location in FSM. FSMBUF DS 16 Holds availability info for one track. FSM DS 77+77 Holds packed availability info for whole disk. * TDAD EQU $ Transfer descriptor for disk operations. * TSEC DB 0 Sector. TTRK DB 0 Track. TBCNT DW 0 How many bytes. TFID DW 0 The file ID. TBUF DW 0 Where data should come from or go to. TUNIT DB 0 The unit # of the disk in question. * IHEAD EQU $ Headers are read to here. * IHSEC DB 0 Sector from which this header was read. IHTRK DB 0 Track from which this header was read. IHFOR DW 0 Forward pointer. IHBAK DW 0 Back pointer. IHFID DW 0 File ID. IHPRO DB 0 Number of sectors in this block. IHSIZ DW 0 Size of block (=IHPRO*256). IHSPR DW 'PT' Spare. * OHEAD EQU $ Headers are written from here. * OHSEC DB 0 OHTRK DB 0 OHFOR DW 0 OHBAK DW 0 OHFID DW 0 OHPRO DB 0 OHSIZ DW 0 OHSPR DW 'PT' * * Standard header -- for a free block. * STDHD DW 0 Sector and track. STFOR DW 32768 First sector. STBAK DW 65535 Last sector. STFID DW 0 ID 0000 => no file. STPRO DB 1 Protect status and number of sectors. STSIZ DW 256 Block size in bytes. TSSPR DW 'TP' P.T. of course. * * FSM parameters -- must match system! //// * DAFSS EQU 0 FSM's sector. DAFST EQU 26 FSM's track. IDFSM EQU 1 FSM's file ID #. * * FSM descriptor. * FSMDS DB DAFSS Sector. DB DAFST Track. DW 77+77 Size. DW IDFSM ID number. DW FSM Where to get data from. * **************************************************************** * * * Subroutines and the like: * * * **************************************************************** * * This subroutine sets TUNIT to the unit specified by the * user or to the default unit if nothing (or just /) was * specified. * STUNIT CALL NONBL Get next non-blank from CI. STA LCHAR Remember it. JZ DEFALT No unit spec. CPI '/' JNZ BADSYN No slash => error. CALL NONBL STA LCHAR JZ DEFALT Just a slash -- use default. SUI '0' Translate unit to binary. MOV B,A Save in B. JM BADUNI Unit shouldn't be less than 0 ... LHLD SYSGLO ... or >= MAXUNI in global area. LXI D,GLMXU DAD D MOV A,M DCR A MAXUNI = (Highest unit number)+1 CMP B MOV A,B Unit spec to A. JP SETIT Everything's okay. BADUNI MVI A,ERICU Illegal char in unit. JMP PTERR * DEFALT LHLD SYSGLO Use the default unit. LXI D,GLUNI DAD D M=default unit #. MOV A,M Unit # to A. SETIT STA TUNIT LDA LCHAR Have we read a delimiter yet? CPI CR RZ . CPI ';' RZ . CALL NONBL Nope -- next char should be it. RZ . BADSYN MVI A,ERSYN Bad syntax. JMP PTERR * LCHAR DB 0 * * This routine zeroes the in-core FSM and its buffer. * INIT MVI A,77+77 FSM size. MVI B,0 What to put in the FSM. LXI H,FSM SHLD FSMPTR Initialize pointer. INIT1 MOV M,B INX H DCR A JNZ INIT1 * * Now the buffer: (This is also an entry point.) * INITB MVI A,16 Buffer size (16 bytes, 1 byte per bit.) MVI B,0 LXI H,FSMBUF SHLD BUFPTR Initialize pointer. INIT2 MOV M,B INX H DCR A JNZ INIT2 RET * * Pack FSMBUF into BC, then store it in FSM. * PACK LXI H,FSMBUF CALL PAC Pack 8 bytes from HL on into A. MOV B,A LXI H,FSMBUF+8 CALL PAC Pack the rest. MOV C,A LHLD FSMPTR HL-> where next track map should go in FSM. MOV M,B Put it there. INX H MOV M,C INX H SHLD FSMPTR Update pointer. LXI H,FSMBUF Reset buffer's pointer back to start of buffer. SHLD BUFPTR RET * * Pack 8 bytes from HL to HL+7 into A. * PAC XRA A MVI D,8 Do 8 bytes. PAC1 MOV E,A Save partial. MOV A,M Get 'bit'. RAR . Put in carry. MOV A,E Get partial. RAL . Shift in bit. INX H DCR D JNZ PAC1 Do it again. RET * * Move a block of memory from DE to HL. The count follows * the call to MOVEF and is one byte long. * MOVEF XTHL Get count. MOV C,M INX H Move up return. XTHL . Put return address back. XRA A MOV B,A BC has count. * * Move a block of BC bytes from DE to HL. * (This is an entry point.) * MOVEV MOV A,B ORA C RZ . No chars to transfer. * MOV1 LDAX D MOV M,A INX H INX D DCR C JNZ MOV1 * * Check high order byte of count -- if it's zero we're done. * MOV A,B ORA A RZ . That's all. DCR B JMP MOV1 * * Set current sector's bit in the FSMBUF. * ADDFSM PUSH H LHLD BUFPTR HL-> next "bit" in FSMBUF. MVI M,1 Set the "bit". INX H SHLD BUFPTR Point to next "bit". LHLD FREE Count the number of free blocks. INX H SHLD FREE LHLD BLKID See if we are actually recovering garbage. MOV A,H (If the file ID was non-zero, we are.) ORA L JZ ADDF1 LHLD RECOVS Keep track of the number of recovered blocks. INX H SHLD RECOVS ADDF1 POP H RET * * Some kind of terrible error occured -- say something. * REPORT CALL OUST DW 0A0DH CRLF ASC /Error at sector / DB 0 LDA TSEC CALL HEX CALL OUST ASC / track / DB 0 LDA TTRK CALL HEX CALL OUST ASC /. Continuing./ DW 0A0DH DB 0 RET * * Copy the standard header to OHEAD for output of a free block. * FREEBL LXI D,STDHD Source. LXI H,OHEAD Destination. LXI B,HELEN Count (header length). CALL MOVEV FIXBLK LDA TSEC Make sure sector and track are right. STA OHSEC LDA TTRK STA OHTRK RET * * Re-write FSM. * WRTFSM LXI D,FSMDS Set up descriptor. LXI H,TDAD LXI B,8 There are 8 bytes in the FSM descriptor. CALL MOVEV CALL WDSK Write. JMP WRTF1 Error. RET WRTF1 CALL OUST DW 0A0DH ASC /Couldn't write out the FSMAP. Sorry./ DB 0 RET * * * Read all valid ID's from the directory into IDTABL, using * ID 0000 to terminate the list. * GETIDS LDA TUNIT Get the appropriate unit. ADI '0' Make it ascii. STA UNIT Add unit # to DIRECTRY/ to generate correct name. MVI A,16 STA CNTR Counter for 16 256-byte sectors in DIRECTRY. LXI H,IDTABL SHLD IDPTR IDPTR is where next ID should be put. LXI D,DIRNAM LXI H,0 Static system buffer. CALL SYS DB OPEOP Open the DIRECTRY. JMP PTERR STA DIRNUM Save file #. IDLOOP LDA DIRNUM LXI B,256 Transfer count. LXI D,DIRBUF Where to put data. CALL SYS DB RBLOP JMP PTERR LDA DIRBUF First byte in block is count of # of entries. ORA A Any files in this block? JZ NXTBLK Nope. MOV B,A Use B as counter to go through all files. LXI D,DIRBUF+2+12 DE-> First ID # in this block. //// * ONEBLK LHLD IDPTR Where to put the next ID. LDAX D MOV M,A Put ID in IDTABL. INX H INX D LDAX D It's two bytes long. MOV M,A INX H SHLD IDPTR LXI H,20 Offset to next ID. (Entries are 21 bytes long.) //// DAD D XCHG . Set DE=DE+20 //// DCR B Are we done with this block? JNZ ONEBLK * NXTBLK LDA CNTR Was that the last block? DCR A STA CNTR JNZ IDLOOP Do next sector. * * Done--close the file and end the list with ID 0000. * LDA DIRNUM CALL SYS DB CLOOP JMP PTERR * LHLD IDPTR MVI M,0 INX H MVI M,0 * RET * * Local storage (for GETIDS). * DIRNAM ASC "DIRECTRY/" UNIT DB 0 Relevent unit number goes here as part of name. DB 0 Terminator for name. DIRNUM DB 0 File number when opened. DIRBUF DS 256 Room for one DIRECTRY sector. CNTR DB 0 IDPTR DW 0 Pointer to next entry in IDTABL. * * Look for ID IHFID in IDTABL. Set carry iff found. * SEARCH LXI H,IDTABL HL-> next ID to look at. SRCH1 MOV D,M Set DE= next ID. INX H MOV E,M INX H MOV A,D Is this the end-of-table marker (ID 0000)? ORA E (Clears carry) RZ . Yes. ID not found. LDA IHFID CMP D JNZ SRCH1 Doesn't match. LDA IHFID+1 CMP E JNZ SRCH1 Doesn't match. STC . Found!!! RET * * See if user wants to quit. * ESCTST CALL CONTST RZ . Nope. CALL CONIN ANI 7FH JZ EXIT Yep. LHLD SYSGLO User typed something, not MODE, save the char. LXI D,GLFLG 'CHAR READY' flag. DAD D MVI M,1 Set the flag. RET *