*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*
*                                                                *
*                     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 <rtn>).             *
*      - Correct offending TRKMAP bytes (see description         *
*        of the VTOC given in the linear disassembly).           *
*      - Update the VTOC on the disk (type:  AFFBG <rtn>).       *
*      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).)
