*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::* * * * CLOSE function handler * * * *----------------------------------------------------------------* * * * In order to understand the execution pattern of the close * * function handler (FNCLOSE, $AC06) you must first be familiar * * with the writing process. As a file is being written, data * * are transferred from their source memory locations to the DOS * * data sector buffer. Each time the data sector buffer is * * filled, it is automatically written to the disk. However, if * * the last segment of data does not completely fill the buffer, * * the write process is exited with bit six of the update flag * * (UPDATFLG, $B5D5) set. When the file is subsequently closed, * * FNCLOSE tests UPDATFLG and writes the data sector buffer if * * necessary. A similar situation occurs with the T/S list * * buffer. FNCLOSE also tests bit seven of UPDATFLG to see if * * the T/S list requires updating. If the T/S list buffer has * * changed since the last read or write, it is written to the * * disk. * * After updating the data and T/S list buffers on the disk * * (if necessary), FNCLOSE calls the FIXMAP routine ($B2C3) to * * free up any sectors that were assigned in the VTOC but not * * used. (This step is necessary because the write process * * allocates an entire track to the file even if only a few * * sectors are required.) * * Once the VTOC is updated on the disk, the new file-size * * bytes are stored in the appropriate file description entry of * * the directory buffer. Finally the directory buffer is written * * to the disk and the close function handler is exited. * * * * As described above, the FIXMAP routine ($B2C3) frees up * * any sectors that were unnecessarily assigned to a file. * * Therefore, if you happen to hit the reset key or turn the * * computer off before a file is closed, the disk may be left * * with several allocated but unused sectors. This wasted disk * * space can only be recovered with a disk repair program or by * * manually updating the VTOC as follows: * * - Catalog the disk. * * - Enter the monitor (type: CALL -151 ). * * - Correct offending TRKMAP bytes (see description * * of the VTOC given in the linear disassembly). * * - Update the VTOC on the disk (type: AFFBG ). * * A file must be at least two sectors long (one T/S list * * sector and one data sector) to be useful. Therefore, if you * * encounter a catalog which describes a one-sector long file, * * you can be sure that (a) the file-size bytes in the file's * * description entry were zapped, (b) the file is 257 sectors * * long (001 in 256 modulus format) or (c) a new file was opened * * but never closed. * * * *::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::* (AC06) FNCLOSE JSR CKDATUP ;Write data sector buffer if necessary. * Check if the data sector buffer has * changed since the last read or write. (AF1D) CKDATUP BIT UPDATFLG ;Check bit 6 to see if changed. BVS WRITDATA ;Take branch if changed. (AF22) RTS ============ * Write the present data sector buffer to the disk. * (Updates the disk so that we can read in the next * data sector without overwriting, and therefore losing, * previous data.) (AF23) WRITDATA JSR PRPDAIOB ;Prepare to write the data sector buffer. * Prepare RWTS's IOB to read/write * the data sector buffer. (AFE4) PRPDAIOB LDY DATBUFFM ;Get address of the data sector buffer LDA DATBUFFM+1 ;from the FM parameter list and designate STY IBBUFP ;it as the I/O buffer for RWTS's IOB. STA IBBUFP+1 LDX CURDATRK ;Enter RWTS driver with (x)/(y) = trk/sec LDY CURDATSC ;values for the data sector. (AFF6) RTS (AF26) LDA #2 ;Write opcode for RWTS. (AF28) JSR RWTSDRVR ;Call RWTS driver to write the data sector buffer. * Read/Write Track/Sector driver. (B052) RWTSDRVR . . (See dis'mbly of RWTS driver using WRITE.) . . (RTS) (AF2B) LDA #%10111111 ;Shut bit 6 off in the update flag to AND UPDATFLG ;signal that the data sector buffer is STA UPDATFLG ;now up to date. (AF33) RTS ============ (AC09) JSR CKTSUPDT ;Write T/S list sec buf if updating is ;required. (If T/S list buf has changed ;since last read or write, then write it ;back to the disk so don't overwrite buf ;and lose information when read the new ;T/S list sector.) * Check if T/S list requires updating. * (ie. Has T/S list buf changed since * the last read or write?) (AF34) CKTSUPDT LDA UPDATFLG BMI WRITETS ;If bit 7 set, updating is required. (AF39) RTS ============ * Write the updated T/S list sector buffer. (AF3A) WRITETS JSR SETTSIOB ;Prepare to write the T/S list. * Prepare RWTS's IOB for reading * or writing the T/S list sector. (AF4B) SETTSIOB LDA TSBUFFM ;Get adr of the T/S list buf from the FM STA IBBUFP ;parameter list & designate T/S list buf LDA TSBUFFM+1 ;as the I/O buffer in RWTS's IOB. STA IBBUFP+1 LDX CURTSTRK ;Set (x)/(y) = trk/sec of current T/S list. LDY CURTSSEC (AF5D) RTS (AF3D) LDA #2 ;Write opcode for RWTS. (AF3F) JSR RWTSDRVR ;Call RWTS driver to write the T/S list sector. * Read/Write Track/Sector driver. (B052) RWTSDRVR . . (See dis'mbly of RWTS driver using WRITE.) . . (RTS) (AF42) LDA #$7F ;Clear bit 7 of the update flag to AND UPDATFLG ;signal that the T/S list sec is up to date. STA UPDATFLG (AF4A) RTS ============ (AC0C) JSR FIXMAP * Free up sectors that were assigned but * not used. Whenever something is written * to the disk, the whole trk is allocated in * the VTOC whether it is needed or not. * There4, once we are done writing, go back * and free up the unneeded sectors. (B2C3) FIXMAP LDA SIGNTRK ;Has a track already been assigned? BNE FREEXTRA ;Yes - go free up the extra sectors. (B2C8) RTS ;No - go exit. ============ (B2C9) FREEXTRA PHA ;Save trk number on the stack. (B2CA) JSR READVTOC ;Read in the VTOC. * Read the Volume Table of Contents (VTOC). (AFF7) READVTOC LDA #1 ;Read opcode for RWTS. (AFF9) BNE RDWRVTOC ;ALWAYS. * Code common to read/write VTOC. (AFFD) RDWRVTOC LDY ADRVTOC ;Get address of VTOC from the STY IBBUFP ;FM constants table & designate it LDY ADRVTOC+1 ;as the I/O buffer in RWTS's IOB. STY IBBUFP+1 LDX TRKWA ;Enter RWTS driver with (x)/(y) equal LDY #0 ;to the trk/sec values of the VTOC. (B00E) JMP RWTSDRVR ;Call RWTS driver to read/write the VTOC. ------------ * Read/Write Track/Sector driver. (B052) RWTSDRVR . . (See dism'bly of RWTS driver using READ.) . . (RTS) (B2CD) LDY ASIGNSEC ;Number of next sec which could ;have been written. (B2D0) PLA ;Get assignment track # back from the stack. (B2D1) CLC ;We don't want to free the last sector used, ;so clear the carry here so we don't free ;that sector up when we begin rotating the ;usage maps in the SUB2FREE routine below. (B2D2) JSR SUB2FREE ;Adjust assignment map to free up sectors ;by setting bit corresponding to the sector ;number. Next, merge ASIGNMAP with the ;appropriate trk map in the VTOC. * Subroutine to free up sectors that were * deleted or prematurely assigned but not used. * * This tricky little routine is easy to * understand once you realize that: * 1) Unlike the VTOC, ASIGNMAP does not * contain any unnecessarily assigned * sectors. * 2) If the disk was just written to, * ASIGNMAP does not house any newly * assigned sectors (even if those sectors * are necessary or not). * 3) Sectors are normally assigned in * descending order. * 4) The number of ROR's done = number * of sectors that need to be assigned. * For example: If the last track had never * been assigned and only sectors $0F and * $0E were needed, then on entry to SUB2FREE, * the first two bytes would appear as follows: * Sector numbers: CBA98765 43210FED * Bit values: 11111111 11111... * After the first ROR, the status of sector * $0D is determined by the entry status of * the carry (c): * Sector numbers: DCBA9876 543210FE * Bit values: c1111111 111111.. * On the next ROR, the "CPY SECPERTK" * instruction conditions (c) to clear for * sector $0E: * Sector numbers: EDCBA987 6543210F * Bit values: 0c111111 1111111. * On the next ROR, the "CPY SECPERTK" * instruction conditions (c) to clear for * sector $0F: * Sector numbers: FEDCBA98 76543210 * Bit values: 00c11111 11111111 * When ASIGNMAP is merged with the corresponding * TRKMAP in the VTOC, the sectors that were * unnecessarily assigned in the VTOC are * freed. (Note: When used in the DELETE * function, SUB2FREE is repeatedly called * up by FREESEC to free one sector at a time.) (B2DD) SUB2FREE LDX #$FC ;Set index to shift 4 bytes as a unit (B2DF) ;(#$FC --> #$FF). STNDARD ROR ASIGNMAP-$FC,X ;4 bytes = map for 1 track. INX BNE STNDARD (B2E5) INY ;When (y) = 16, ASIGNMAP is back in ;standard position. (B2E6) CPY SECPERTK ;Condition (c) for next shift. BNE SUB2FREE ASL ;Trk*4 to index TRKMAP. ASL TAY (B2EE) BEQ SUB2RTN ;Track value of 0 not allowed. * ASIGNMAP now reflects the true sector assignment * and is in standard position. Therefore, merge it * with the appropriate TRKMAP in the VTOC to free up * extra sectors. (B2F0) LDX #4 MERGMAPS LDA ASIGNMAP-1,X ORA TRKMAP0+3,Y ;Do the merge. STA TRKMAP0+3,Y DEY ;Reduce indices. DEX BNE MERGMAPS SUB2RTN RTS (B2FF) (B2D5) LDA #0 ;No more deallocations needed. STA ASIGNTRK (B2DA) JMP WRITVTOC ;Write the corrected VTOC back to the disk. ------------ * Write the Volume Table of Contents (VTOC). (AFFB) WRITVTOC LDA #2 ;Write opcode for RWTS. * Code common to read/write VTOC. (AFFD) RDWRVTOC LDY ADRVTOC ;Get address of VTOC from the STY IBBUFP ;FM constants table & designate it LDY ADRVTOC+1 ;as the I/O buffer in RWTS's IOB. STY IBBUFP+1 LDX TRKWA ;Enter RWTS driver with (x)/(y) equal LDY #0 ;to the trk/sec values of the VTOC. (B00E) JMP RWTSDRVR ;Call driver to read/write the VTOC. ------------ * Read/Write Track/Sector driver. (B052) RWTSDRVR . . (See dism'bly of RWTS driver using WRITE.) . . (RTS) * Was the last last operation a WRITE? (AC0F) LDA #%00000010 ;If bit 1 is set, then just did a write. AND UPDATFLG (AC14) BEQ TOGDFMXT ;Last operation wasn't a write, so we can ;just exit because there is no need to ;update the directory sector. * The last operation was a write, so * we better prepare to fix up the * file-size and T/S link bytes in * the directory sector. (AC16) JSR READVTOC ;Read the VTOC. * Read the Volume Table of Contents (VTOC). (AFF7) READVTOC LDA #1 ;Read opcode for RWTS. (AFF9) BNE RDWRVTOC ;ALWAYS. * Code common to read/write VTOC. (AFFD) RDWRVTOC LDY ADRVTOC ;Get address of VTOC from the STY IBBUFP ;FM constants table & designate it LDY ADRVTOC+1 ;as the I/O buffer in RWTS's IOB. STY IBBUFP+1 LDX TRKWA ;Enter RWTS driver with (x)/(y) equal LDY #0 ;to the trk/sec values of the VTOC. (B00E) JMP RWTSDRVR ;Call driver to read/write the VTOC. ------------ * Read/Write Track/Sector driver. (B052) RWTSDRVR . . (See dism'bly of RWTS driver using READ.) . . (RTS) * Read in all the directory sectors up * to the directory sector containing the * file description entry corresponding to * the file we are closing. (AC19) LDA #0 (AC1B) CLC ;(c) = 0 = signal to read the first (AC1C) ;directory sector. PURGEDIR JSR RDDIRECT ;Go read directory sector into buffer. * Read a directory sector. (B011) RDDIRECT PHP ;Save (c) on stack: ; (c) = 0 = read 1rst directory sector. ; (c) = 1 = read next directory sector. (B012) JSR PT2DIRBF * Designate the directory sector buffer * as I/O buffer in RWTS's IOB. (B045) PT2DIRBF LDA ADRDIRBF ;Get addr of direc STA IBBUFP ;sec buf from the LDA ADRDIRBF+1 ;FM constants tbl STA IBBUFP+1 ;& designate it as (B051) RTS ;as the I/O buffer. (B015) PLP ;Check if 1rst directory sec or not. (B016) BCS RDNXTDIR ;Go read next directory sector. * Read the first directory sector. * (Carry = 0.) (B018) RDFIRDIR LDY FIRDIRSC ;(y)/(x) = trk/sec vals of first directory LDX FIRDIRTK ;sector (from the VTOC buffer). (B01E) BNE DODIRRD ;ALWAYS - go read in directory sector. * Read the next directory sector. * (Carry = 1.) (B020) RDNXTDIR LDX DIRLNKTK ;Get track of next directory sec from the ;link in the current directory sector. (B023) BNE GETDIRLK ;Link not zeroed out. SEC ;Link zeroed out - exit with (c) = 1 to (B026) RTS ;signal there are no more directory secs. ============ (B027) GETDIRLK LDY DIRLNKSC ;Get sector of next directory sec from the ;link in the current directory sector. * Call to read in the directory sector. (B02A) DODIRRD STX CURDIRTK ;Save trk/sec vals of directory sec that (B02D) STY CURDIRSC ;we are about to read so they will be ;the current directory sec values for the ;next time around. (B030) LDA #1 ;Read opcode for RWTS. (B032) JSR RWTSDRVR ;Call RWTS driver to do the read. * Read/Write Track/Sector driver. (B052) RWTSDRVR . . (See dis'mbly of RWTS driver using READ.) . . (RTS) (B035) CLC ;Link didn't zero out so signal that there (B036) RTS ;are more directory secs to read & exit. ============ (AC1F) SEC ;(c) = 1 = already read first directory sec. DEC SECNXD1R ;Index for # of directory sectors. (AC23) BNE PURGEDIR ;If 0, then just read directory sector ;corresponding to the file wanted. * Update the file size and write * the directory sector to the disk. (AC25) LDX BYTNXD1R ;(x) = offset of the file description in the ;directory sector. (AC28) LDA FILENSEC ;Number of secs in file (from FM work area). STA FIL1SIZE,X LDA FILENSEC+1 STA FIL1SIZE+1,X (AC34) JSR WRDIRECT ;Write the updated directory sector. * Write the directory buffer. (B037) WRDIRECT JSR PT2DIRBF * Designate the directory sector buffer * as the I/O buffer in RWTS's IOB. (B045) PT2DIRBF LDA ADRDIRBF ;Get the addr of the directory sec buf STA IBBUFP ;in the FM constants table. LDA ADRDIRBF+1 ;Store it in RWTS's IOB. STA IBBUFP+1 (B051) RTS (B03A) LDX CURDIRTK ;Enter RWTS driver with (x)/(y) = trk/sec vals. LDY CURDIRSC LDA #2 ;Write opcode for RWTS. (B042) JMP RWTSDRVR ;Call RWTS driver to write the directory sector buffer. ------------ (B052) RWTSDRVR . . (See dis'mbly of RWTS driver using WRITE.) . . (RTS) (AC37) TOGDFMXT JMP GOODFMXT ;Exit file manager cleanly. ------------ * Exit the file manager. (B37F) GOODFMXT LDA RTNCODFM CLC ;(c) = 0 to signal good operation. BCC FMEXIT BADFMXIT SEC ;(c) = 1 to signal unsuccessful. FMEXIT PHP ;Save status on stack. STA RTNCODFM ;Store return code in FM parameter list. LDA #0 STA STATUS (B38E) JSR CPYFMWA ;Copy the work area to the work buffer. * Copy the FM work area (non-chain) to * the FM work buffer (in DOS chain). (AE7E) CPYFMWA JSR SELWKBUF ;Select the FM work buffer (in DOS chain). * Point the A4L/H pointer at the FM work buffer. (AF08) SELWKBUF LDX #0 ;Set index to select work buffer. (AF0A) BEQ PT2FMBUF ;ALWAYS. (AF12) PT2FMBUF LDA WRKBUFFM,X ;Get address of selected buffer from the STA A4L ;FM parameter list & put it in the pointer. LDA WRKBUFFM+1,X STA A4L+1 (AF1C) RTS (AE81) LDY #0 ;Initialize index. STORWRK LDA FMWKAREA,Y ;Get byte from the FM work area. STA (A4L),Y ;Put it in the work buffer. INY CPY #45 ;45 bytes to copy (0 to 44). BNE STORWRK (AE8D) RTS (B391) PLP ;Retrieve status of success of operation ;from the stack. (B392) LDX STKSAV ;Adjust stack pointer to force exit to the TXS ;caller of the function even if we are (B396) RTS ;several subroutines deeper than the ============ ;original entry point. (Returns to AFTRFUNC ;($A6AB) in the FMDRIVER routine ($A6A8).)