*================================================================* * * * Write Function handler * * * *================================================================* * * * Use subfunction code as an index into the write sub- * * function entry point table. Do a "stack jump" to execute the * * desired WRITE subfunction. (Note: The write-position sub- * * functions are never called by normal DOS commands. However, * * they are available to assembly language programmers.) * * * *----------------------------------------------------------------* (AC70) * WRITE subfunction entry point table. FNWRITE LDA FILTYPWA ;Check if file is locked. * (P.S. Subfunctions with the position option BMI TOFILOCK ;Error - can't write to a locked file. * are available to the user, but never LDA SUBCODFM ;Check if subcode is legal. * called by DOS.) CMP #5 ;(Must be < = 5.) (AAF1) ;Index to subfunction. BCS TOERRSUB ;Error - illegal subcode. WRSUBTBL DA GOODFMXT-1 ;(0), Exit. ASL ;Subcode * 2, cause 2 bytes/address. DA WRITEONE-1 ;(1), Write one byte. TAX ;Index table of subfunction addresses. DA WRITERNG-1 ;(2), Write a range of bytes. LDA WRSUBTBL+1,X ;Get address (minus 1) of subfunction DA PSNWRONE-1 ;(3), Position and write one byte. PHA ;entry point and stick it on the stack DA PSNWRRNG-1 ;(4), Pos'n & write range of bytes. LDA WRSUBTBL,X ;(hi byte first). Then do a "stack jump" (AAFB) DA GOODFMXT-1 ;(5), Exit. PHA ;to execute the given WRITE subfunction. (AC69) RTS (AC6A) ------------ TOERRSUB JMP RNGERRSB ;Go handle range error. ------------ ;(See dis'mbly of errors.) (AC6D) ------------ TOFILOCK JMP FILELOKD ;Go print file-locked message. ------------ ;(See dis'mbly of errors.) *----------------------------------------------------------------* * * * Position and write-one-byte * * subfunction handler * * * *----------------------------------------------------------------* * * * Adjust file pointer and then write one byte. * * (NOTE: This subfunction is never accessed by normal DOS cmds * * but it is available to assembly language programmers.) * * * *----------------------------------------------------------------* (ACBB) PSNWRONE JSR CALCFPTR ;Using R-, L-, & B-parameters, calculate ;the position of the file pointer. * Calculate the exact position of the three-byte file pointer: * FILPTSEC = sector offset (low/hi format) into entire file (2 bytes). * FILPTBYT = byte offset into current sector (1 byte). * All three bytes define the exact position of the file pointer * via the following formula: * (record number * record length) + byte offset into record * where: RECNMBFM = record number from R-parameter (set * by user when using random access files * or simply incremented when using other * file types). * RECLENWA = record length parsed from L-parameter * and assigned with open command (else * defaulted to a size of 1). * BYTOFFFM = offset into the current record (set by * user when using open command or * occassionally used with sequential files * as a B-parameter). * Note that you can actually directly access any byte in any * file by bypassing the command interpreter and setting the * L-, B- & R-parameters however you want. (B300) CALCFPTR LDA RECNMBFM ;Put record # in multiplier and STA FILPTBYT ;also save it in the work area. STA RECNMBWA LDA RECNMBFM+1 STA FILPTSEC STA RECNMBWA+1 LDA #0 ;Zero out the hi order byte of the sector (B314) STA FILPTSEC+1 ;offset into the file. * Calculate: Record number * record length. * This routine simply multilplies two 16-bit * numbers together. It may at first seem * a bit confusing because FILPTSEC & FILPTBYT * are used both for holding the multiplier * (record #) and part of the product result. * However, the bits of the product don't get mixed * up with the bits of the multiplier because rolling * in a product bit also rolls out the last-used * multiplier bit (ie., there is no bit overlap). (B317) LDY #16 ;16 bits / one 2-byte number. NMBXLEN TAX ;Save part of running product. ;(On first entry, set (x) = 0). LDA FILPTBYT ;Set (a) = multiplier. LSR ;Put multiplier bit in carry. BCS NMBXLEN1 ;If (c)=1, go add multiplicand to running product. TXA ;(a) = part of running product. BCC NMBXLEN2 ;Always branch. No use adding multiplicand cause ;bit in multiplier is a 0. Therefore, just go shift ;running product. NMBXLEN1 CLC ;Add multiplicand to running version of shifted product. LDA FILPTSEC+1 ADC RECLENWA STA FILPTSEC+1 TXA ;Set (a) = low byte of running product. ADC RECLENWA+1 NMBXLEN2 ROR ;Shift the running product (as a unit) 1 bit ROR FILPTSEC+1 ;right for next time around. ROR FILPTSEC ;Shift lower 2 bytes of running product and ROR FILPTBYT ;at the same time throw out the last-used ;multiplier bit. DEY ;Reduce bit counter. (B33C) BNE NMBXLEN ;Branch if haven't done all 16 bits yet. * Copy byte offset into record from * the FM parameter list to the work area. (B33E) CLC LDA BYTOFFFM (B342) STA BYTOFFWA * Calculate lowest order byte of file pointer * BYTOFFWA = offset into current record. * = byte offset into record * + (record length * record number). (B345) ADC FILPTBYT STA FILPTBYT LDA BYTOFFFM+1 STA BYTPFFWA+1 ADC FILPTSEC STA FILPTSEC BCC CALCRTS INC FILPTSEC+1 CALCRTS RTS (B35C) *----------------------------------------------------------------* * * * Write-one byte * * subfunction handler * * * *----------------------------------------------------------------* * * * The write-one-byte subfunction handler ($ACBE) is called * * by most DOS commands that write data to the disk. Almost all * * of the subroutines that are employed by WRITEONE ($ACBE) are * * also used during the reading process. Because these sub- * * routines must be versatile enough to support an array of entry * * conditions, it can be difficult to trace different patterns * * of program execution. In order to make this task easier, the * * structure of a T/S list and the major flags used to govern * * execution are described below: * * * * Structure of a T/S list * * Byte offset Function of bytes * * $00 Unused * * $01, $02 Link to next T/S list. (If no more T/S lists * * are present, the link bytes are zeroed out.) * * $03, $04 Unused * * $05, $06 Relative sector number (with respect to the * * entire file) of the first data sector pair * * that can be described in the present T/S list. * * Possible values are: $0000, $007A, 2*$007A, * * 3*$007A, $4*007A. The image of these bytes are * * stored in the work area at RELFIRST ($B5DC, * * $B5DD). * * $07 - $0B Unused. * * $0C, $0D First data pair. (Track and sector values of * * the first data sector.) * * $0E, $0F Second data pair. * * $10, $11 Third data pair. * * . . * * . . * * . . * * $FE, $FF One hundred and twenty-second data pair. * * If there are less than 122 data sectors in the * * file, the unused data-pair bytes are zeroed out. * * * * Major decisions and flags used: * * Use current data sector? * * - yes = FILPTSEC/+1 = RELPREV/+1 * * - no = FILPTSEC/+1 < > RELPREV/+1 * * - when a file is first opened, the open function * * (FNOPEN, $AB22) sets FILPTSEC: $0000 and * * RELPREV: $007A. This forces the computer to * * try to use the file's first data sector. * * - when a data sector is filled by successively * * adding data bytes, FILPTSEC/+1 > RELPREV/+1. * * - if the position function was used to adjust * * the filpointer out of the range of RELPREV, * * FILPTSEC/+1 < > RELPREV/+1. * * Use current T/S list? * * - yes = FILPTSEC/+1 > = RELFIRST/+1 * * and FILPTSEC/+1 < RELASTP1/+1. * * - no = FILPTSEC/+1 < RELFIRST/+1 * * or FILPTSEC/+1 >= RELASTP1/+1. * * Update current data or T/S list sectors? * * - yes = bit 6 (data) or bit 7 (T/S list) of UPDATFLG * * ($B5D5) are set. * * - no = bits 6 and 7 of UPDATFLG are clear. * * Read first T/S list? * * - yes = (c) = 0 - carry cleared if file was just * * opened. File pointer was previously * * backed up by CALCFPTR ($B300). * * - no = (c) = 1 - file is long enough that another T/S * * list must be used. * * Writing data? * * - yes = OPCODEFM ($B105) = 4. * * - no = OPCODEFM < > 4. * * Any more data pairs listed in current T/S list? * * - no = trk byte portion of data pair = $00. * * - yes = trk byte portion of data pair < > $00. * * Any more T/S lists in file? * * - yes = trk byte portion of link < > $00. * * - no = trk byte portion of link = $00. * * (Link to first T/S list is contained in the first two * * bytes of the file description entry in the directory sec. * * Link to subsequent T/S lists are contained in 2nd and 3rd * * bytes (offsets $01 and $02) of previous T/S lists.) * * * * Note: RELPREV ($B5E0, $B5E1) * * = the relative sector number (in relation to * * the entire file) of the last data sector that * * was read or written. (Values can range from * * $0000, $007A, 2*$007A, 3*$007A, 4*$007A.) * * RELFIRST ($B5DC, $B5DD) * * = the relative sector number (in relation to * * the entire file) of the first data sector * * that is (or can be) listed in the current * * T/S list. (Values can range from $0000, * * $007A, 2*$007A, 3*$007A, 4*$007A.) * * RELASTPL1 ($B5DE, $B5DF) * * = one greater than the maximum relative sector * * number (in relation to the entire file) of * * the last data sector that can possibly be * * listed in the current T/S list. (Values can * * vary from $007A, 2*$007A, 3*$007A, 4*$007A, * * to 5*$007A.) * * * * Execution pattern: * * WRITEONE ($ACBE) loads the accumulator (from ONEIOBUF, * * $B5C3) with the data byte to be written and then JSR's to * * WRTDATA ($ACDA). WRTDATA saves the output byte on the stack * * and calls NXTDATRD ($B0B6). * * NXTDATRD and its associated subroutines can use several * * different execution patterns when called by a write sub- * * function. (For instance, you could be writing to a newly * * created file, shortening or lengthening a pre-existing file or * * rewriting an old file with the same length as the original * * version.) Consequently, various versions and combinations of * * the following execution patterns are possible. (See formatted * * disassembly for further details.) * * * * Pattern 1: = FILPTSEC/+1 = RELPREV/+1 * * - select the current data sector. * * - exit the NXTDATRD routine via XITNXDAT. * * Pattern 2: = FILPTSEC/+1 < > RELPREV/+1 * * = FILPTSEC/+1 > = NDXFIRST/+1 * * and FILPTSEC/+1 < RELASTP1/+1 * * = trk portion of data pr < > 0 * * - update the current data sector if necessary. * * 2a: - get valid link byte to the next data sector from * * the current T/S list. * * - read in the next data sector. * * - update RELPREV. * * - exit the NXTDATRD routine via XITNXDAT. * * Pattern 3: = FILPTSEC/+1 < > RELPREV/+1 * * = FILPTSEC/+1 > = RELFIRST/+1 * * and FILPTSEC/+1 < RELASTP1/+1 * * = trk portion of data pr = 0 * * = OPCODEFM < > 4 * * - update the current data sector if necessary. * * 3a: - detect zeroed out data pair in the current T/S * * list. * * - test OPCODEFM to determine that we are not * * writing and set the carry flag to signal an out- * * of-data error has occurred. * * - abort the read subfunction via NDATERR. * * Pattern 4: = FILPTSEC/+1 < > RELPREV/+1 * * = FILPTSEC/+1 < = RELFIRST/+1 * * = carry clear at $AF69 * * = trk portion of link byte to next T/S list < > 0 * * - update current data and/or T/S list secs if * * necessary. * * - get link to file's first T/S list from the file * * description entry in the current directory sec. * * - read in the first T/S list. * * - loop back to do 2a, 3a or 5. * * Pattern 5: = FILPTSEC/+1 < > RELPREV/+1 * * = FILPTSEC/+1 > = RELASTP1/+1 * * = carry set at $AF69 * * = trk portion of link to next T/S list < > 0 * * - update current data and/or current T/S list * * sectors if necessary. * * - get valid link bytes to the next T/S list from * * the current T/S list. * * - read in the next T/S list. * * - loop back to do 2a, 3a or repeat 5. * * Pattern 6: = FILPTSEC/+1 < > RELPREV/+1 * * = FILPTSEC/+1 > = RELASTP1/+1 * * = carry set at $AF69 * * = trk portion of link to next T/S list < > 0 * * = OPCODEFM < > 4 * * - update current data and/or current T/S list * * sectors if necessary. * * - get zeroed out link bytes to next T/S list from * * the current T/S list. * * - allocate a new T/S list sector in the VTOC. * * - put link to new T/S list in link bytes of old * * (current) T/S list. * * - write modified old (current) T/S list to disk. * * - initialize the new T/S list by zeroing out the * * T/S list buffer. * * - write new zeroed-out T/S list sector to disk. * * - loop back to do 3a. * * * * If no errors are encountered, the NXTDATRD routine * * ($B0B6) is exited via XITNXDAT ($B12C). XITNXDAT points the * * A4L pointer ($42, $43) at the DOS data sector buffer and sets * * (y) to index that buffer. When an "RTS" is encountered, * * execution returns to the WRTDATA routine at $BCDE. * * The output byte, which has been patiently waiting on * * the stack, is finally stored in the data sector buffer by * * WRTDATA. Bit 6 of the update flag (UPDATFLG, $B5D5) is then * * set to signal that the data sector buffer has changed and * * therefore requires updating on the disk. Next INCREC ($B15B) * * is used to increment the byte offset into the record. If the * * resulting offset corresponds to the record length, the byte * * offset is reset to zero and the record number is incremented * * instead. Finally, the file pointer is increased by one byte * * and the write-one-byte subfunction is exited via a jump to * * GOODFMXT ($B37F). * * * * Note that the write subfunctions do not necessarily send * * data to the disk. Partially filled data sectors and T/S lists * * scheduled for updating are actually written to the disk by the * * CLOSE function when set bits are discovered at bit positions 6 * * and 7 (respectively) in the update flag (UPDATFLG, $B5D5). * * * * Because the read-one-byte (READONE, $AC8A) and write-one- * * byte (WRITEONE, $ACBE) subfunctions must be called repeatedly * * to read or write successive byte, most DOS commands only use * * these routines sparingly. However, the one-byte subfunctions * * are the only vehicles employed by the READ and WRITE commands. * * No wonder text files operate at a snail's pace!!! * * * *----------------------------------------------------------------* (ACBE) WRITEONE LDA ONEIOBUF ;Get byte to write from the one-data-byte ;buffer located in the FM parameterr list. (ACC1) JSR WRTDATA ;Put data byte in data sector buffer. ;(Write data sector buffer to disk if necessary.) * Subroutine which writes data. (ACDA) WRTDATA PHA ;Save byte to write on stack. (ACDB) JSR NXTDATRD ;Read next data sector into the data ;sector buffer if necessary. * Note: The write data SUBFUNCTIONS may seem strange because * they don't necessarily write data bytes to the disk. All they * really do is store data in the data sector buffer (in the DOS * buffer chain). If single data bytes are added repeatedly, the * data sector buffer fills up (at which point FILPTSEC < > * RELPREV). If this happens, the data bit in the update flag * (UPDATFLG) will also be set. When these two criteria are met, * the WRITDATA routine in the NXTDATRD subroutine is used to * write the data sector buffer to the disk. If the subfunction is * exited when the data sector buffer is not full but the UPDATFLG * is on, the data sector buffer is written to the disk by the * CLOSE function. * Read data sector if necessary. * * Check if need to read next data sector * buffer into the data sector buffer. * * - Is data sector we want already in memory? * - If so, does it require updating? * If sector offset into file (FILPTSEC) equals * the relative sector number of the last sector * read or written (RELPREV), then sector we want * is presently in memory. If it is not in memory, * read in the data sector wanted. However, first * check if the data sector has changed since the * last read or write. If it has, the disk must * be updated before we read in the new data * sector so we don't overwrite the data sector * buffer and loose information. * NOTE: - if this subroutine is called from a * write subfunction and FILPTSEC is not * equal to RELPREV, then the data sector * buffer must be full & there4 should be * written to the disk before any more * information is read in. * - if the file was just opened, the open * subfunction set FILPTSEC = #$0000 and * RELPREV = #$FFFF so always forces reading * of new data sector even if the correct * sector is already in memory. (B0B6) NXTDATRD LDA FILPTSEC ;Last sector used versus sector wanted? CMP RELPREV (B0BC) BNE CKWCURDA ;Not same - will eventualy have to read in ;a new data sector. (B0BE) LDA FILPTSEC+1 ;Maybe same - check hi bytes. CMP RELPREV+1 (B0C4) BEQ XITNXDAT ;Same so go exit. * Write data sector if necessary. * Data sector we want is not presently * in memory. Check if need to write * current data sector buffer before we * read in the wanted data sector. (B0C6) CKWCURDA JSR CKDATUP ;Check update flag to see if the data sector ;buffer has changed since the last read or write. ;If it has, write the data sector buffer to the disk. * Check if data sector buffer has changed * since the last read or write was done. (AF1D) CKDATUP BIT UPDATFLG ;Check bit 6 to see if data sector ;buffer requires updating. (AF20) BVS WRITDATA ;Take branch if data requires updating. (AF22) RTS ============ * Write present data sector buffer to the disk. * Updates disk so can read in next data sector * without overwriting and therefore losing * previous data that hasn't yet been updated * on the disk. (AF23) WRITDATA JSR PRPDAIOB * Prepare RWTS's IOB to read/write * the data sector. (AFE4) PRPDAIOB LDY DATBUFFM ;Get addr of data LDA DATBUFFM+1 ;sec buf from the STY IBBUFP ;FM parameter list (AFED) STA IBBUFP+1 ;& designate it as ;the I/O buffer for ;RWTS's IOB. (AFF0) LDX CURDATRK ;Enter driver with LDY CURDATSC ;(x)/(y) = trk/sec (AFF6) RTS ;vals corresponding ;to the data sector. (AF26) LDA #2 ;Opcode for RWTS's write command. (AF28) JSR RWTSDRVR ;Call driver to write data sector buf. * Read/write track/sector driver. (B052) RWTSDRVR . . (See dis'mbly of RWTSDRVR 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 ============= * Should current T/S list be used? * (That is, should the data sector be listed in * the present T/S list sector? If not, * then will need to read in the correct * T/S list.) * Is the sector offset into the file of the present * data sector less than the relative sector number * of the FIRST DATA SECTOR that can be described in * in the T/S list presently in memory? (If less, * then need to read in a different T/S list sector.) (B0C9) CKCURTS LDA FILPTSEC+1 ;Sector offset into file associated with (B0CC) CMP RELFIRST+1 ;the present data sector versus the ;relative sector number of the 1rst data ;sector that can be described in the present ;T/S list. (B0CF) BCC NEEDNXTS ;Data sector wanted represents a SMALLER ;offset into file so need a different T/S list. ;(Start by reading file's first T/S list.) (B0D1) BNE CKCURTS1 ;Sector offset of wanted data sector is ;LARGER than that of the first data sector ;that can be described in the present T/S list ;so it may still belong to this T/S list. (B0D3) LDA FILPTSEC ;Hi bytes same - so compare low bytes. CMP RELFIRST (B0D9) BCC NEEDNXTS ;Sector offset of wanted file is LESS, ;so read in a different list. ;(Start by reading file's first T/S list.) * Sector offset associated with the data * sector wanted is either GREATER THAN OR * EQUAL TO the relative sector offset * associated with the first data sector that can * be described in this T/S list. Therefore, * compare the sector offset of the sector * wanted with the relative sector number * (plus 1) of the LAST sector that can be * described in the present T/S list. (B0DB) CKCURTS1 LDA FILPTSEC+1 ;Sector offset associated with data sector (B0DE) CMP RELASTP1+1 ;we want versus the relative sector number ;(plus 1) of the LAST data sector that can possibly ;be described in the present T/S list. (B0E1) BCC GETDATPR ;Sector offset assoc with data sector we ;want IS described in the present T/S list. (B0E3) BNE NEEDNXTS ;Sector offset of present data sector is ;LARGER than that of the LAST data sector ;(plus 1) that can possibly be described in the ;present T/S list, so we need a new T/S list sector. (B0E5) LDA FILPTSEC ;Hi bytes same - so compare low bytes. CMP RELASTP1 (B0EB) BCC GETDATPR ;Sector offset associated with data sector ;we want is LESS than the relative sector ;number (plus 1) of the last data sector that ;can possibly be listed in the present T/S list. ;There4, data sector wanted should be described ;in the present T/S list. * The data sector we want is NOT listed * in the present T/S list, so we must read * a different T/S list sector. * (NOTE: Routine is entered with (c) = 1 if need a * higher numbered T/S list. If (c) = 0, then the file * pointer was backed up and we need a smaller numbered * T/S list.) (B0ED) NEEDNXTS JSR READTS ;Read in the next (or first) T/S list. However, ;first check the update flag to see if the T/S list ;presently in memory requires updating before ;we read in the next (or first) T/S list sector. ;If updating is required, write the present T/S ;list. * Writes old T/S list to disk if necessary * & reads in next T/S list. If there is no next * T/S list then a new trk/sec pair is allocated * & the old T/S list is linked to the new T/S list. * Also updates ASIGNTRK, UPDATFLG, CURTSTRK, * CURTSSEC, RELFIRST, RELASTP1, FM parameter list, etc. * Read T/S list sector. (AF5E) READTS PHP ;Save (c) denoting if 1rst T/S list or not. ;(c) = 0 = read 1rst T/S list sec. ;(c) = 1 = read next T/S list sec. (AF5F) 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 loose information when read the new ;T/S list sector.) * Check if T/S list requires updating. * (i.e. Has T/S list buf changed since * the last read or write?) (AF34) CKTSUPDT LDA UPDATFLG ;If bit 7 set, BMI WRITETS ;updating required. (AF39) RTS ============ * Write the T/S list buffer. (AF3A) WRITETS JSR SETTSIOB ;Prep 4 WRITING the ;T/S list buffer. * Prep RWTS's IOB 4 * reading or writing * the T/S list buffer. * (Get adr of T/S list * buf from FM parm * list & designate T/S * list buf as the I/O * buf in RWTS's IOB. * Exit with (x)/(y) * = trk/sec of current * T/S list sector.) (AF4B) SETTSIOB LDA TSBUFFM STA IBBUFP LDA TSBUFFM+1 STA IBBUFP+1 LDX CURTSTRK LDY CURTSSEC (AF5D) RTS (AF3D) LDA #2 ;Write opcode 4 RWTS. (AF3F) JSR RWTSDRVR ;Call 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 ;Clr bit7 of update AND UPDATFLG ;flag to signal that STA UPDATFLG ;T/S list sec is up (AF4A) RTS ;to date. ============ (AF62) JSR SETTSIOB ;Prepare RWTS's IOB for READing a 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 STA IBBUFP ;list buf from the LDA TSBUFFM+1 ;FM parameter list (AF54) STA IBBUFP+1 ;& designate T/S list ;buf as the I/O buf in ;RWTS's IOB. (AF57) LDX CURTSTRK ;Set (x)/(y) = trk/sec LDY CURTSSEC ;of current T/S list. (AF5D) RTS (AF65) JSR SELTSBUF ;Select the T/S list buffer. * Point A4L/H at the T/S list sector buffer. (AF0C) SELTSBUF LDX #2 ;Ndx for T/S list buf. (AF0E) BNE PT2FMBUF ;ALWAYS. (AF12) PT2FMBUF LDA WRKBUFFM,X ;Get adr of the STA A4L ;desired buf from LDA WRKBUFFM+1,X ;the FM parameter STA A4L+1 ;list & put it in the (AF1C) RTS ;A4L/H pointer. (AF68) PLP ;Get saved (c) back from the stack. (AF69) BCS RDNXTTS ;If (c) = 1, already read the first T/S ;list sector, so go read next one. * Read the FIRST T/S list sector. * (Carry was clear.) (AF6B) RDFIRSTS LDX FIRSTSTK ;Set (x)/(y)=trk/sec of first T/S list sec. LDY FIRTSSEC (AF71) JMP RDTSLST ;Go read T/S list sector into buffer. ------------ * Read NEXT T/S list sector. * (Carry was set.) (AF74) RDNXTTS LDY #1 ;Index into T/S list buffer. LDA (A4L),Y ;Trk for link to next T/S list sector. (AF78) BEQ TSLNKZRO ;Link zeroed out, so there are no more ;T/S list sectors for the file. (AF7A) TAX ;(x) = track # of next T/S list sector. INY LDA (A4L),Y ;Sector for link to next T/S list sector. TAY ;(y) = sec # of next T/S list sector. (AF7F) JMP RDTSLST ;Go read in the next T/S list sector. ------------ * T/S link zeroed out, so now must * decide if got an error or not. (AF82) TSLNKZRO LDA OPCODEFM ;Check read/write status to see if want CMP #4 ;to add another T/S list or not. BEQ UPDATETS ;WRITING, so go update link. SEC ;We were READING & the link zeroed out, so (AF8A) RTS ;return with (c) = 1 to signal that an ============ ;error occurred. (Remember, we previously ;set the return code to a default value ;corresponding to a file-not-found error.) * Writing and link zeroed out, so * must assign a new T/S list sector. (AF8B) UPDATETS JSR ASGNTKSC ;Find and reserve trk/sec values ;for a new T/S list sector. * Asign trk/sec vals for a new T/S list. (B244) ASGNTKSC . . (See formatted disassembly given in the open function handler (FNOPEN).) . . (RTS) * Link the new T/S list sector to * the last T/S list sector and then * write the updated version of the * last T/S list sector to the disk. (AF8E) LNKOLDNW LDY #2 ;Offset to sector portion of link. STA (A4L),Y ;Put new sector value in link. PHA ;Also save it on the stack. DEY ;Offset to trk portion of link. LDA ASIGNTRK ;Put new trk value in link. STA (A4L),Y PHA ;Also save trk value on the stack. (AF9A) JSR WRITETS * Write the T/S list buffer. (AF3A) WRITETS JSR SETTSIOB ;Prep 4 WRITING the ;T/S list buffer. * Prep RWTS's IOB 4 * reading or writing * the T/S list buffer. * (Get adr of T/S list * buf from FM parm * list & designate T/S * list buf as the I/O * buf in RWTS's IOB. * Exit with (x)/(y) * = trk/sec of current * T/S list sector.) (AF4B) SETTSIOB LDA TSBUFFM STA IBBUFP LDA TSBUFFM+1 STA IBBUFP+1 LDX CURTSTRK LDY CURTSSEC (AF5D) RTS (AF3D) LDA #2 ;Write opcode 4 RWTS. (AF3F) JSR RWTSDRVR ;Call 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 ;Clr bit7 of update AND UPDATFLG ;flag to signal that STA UPDATFLG ;T/S list sec is up (AF4A) RTS ;to date. * Set up a brand new T/S list sector * and write it to the disk. (AF9D) ZOUTTS JSR ZCURBUF ;Zero out the T/S list buffer. * Zero out the current 256-byte buffer. (B7D6) ZCURBUF LDA #0 TAY ZCURBUF1 STA (A4L),Y INY BNE ZCURBUF1 (B7DE) RTS (AFA0) LDY #5 ;At offsets 5 & 6 into the new T/S list LDA RELASTP1 ;put the relative sector number (in STA (A4L),Y ;relation to the entire file) of the FIRST INY ;data sector pair that will described in LDA RELASTP1+1 ;this new T/S list. (Possible values: STA (A4L),Y ;$007A, 2*$007A, 3*$007A and 4*$007A.) PLA ;Get trk/sec values (x/y) of this new TAX ;T/S list sector off of the stack. PLA TAY LDA #2 ;Write opcode for RWTS. (AFB3) BNE RDWRTS ;ALWAYS - go write the T/S list sector. * Subroutine to read the T/S list sector. (AFB5) RDTSLST LDA #1 ;Read opcode for RWTS. * Code common to read/write T/S list sector. (AFB7) RDWRTS STX CURTSTRK ;New T/S list sector trk/sec values (AFBA) STY CURTSSEC ;(x/y) become current T/S list trk/sec ;values. (AFBD) JSR RWTSDRVR ;Call RWTS driver to read/write the ;current T/S list sector. * Read or write the current T/S list. (B052) RWTSDRVR . . (See formatted dis'mbly of RWTS driver using READ or WRITE.) . . (RTS) * Update the FM work area * (not in DOS buffer chain). (AFC0) LDY #5 ;Offset into current T/S list buffer. LDA (A4L),Y ;Get & save the relative sector number (AFC4) STA RELFIRST ;of the first data sector that can be ;described in this T/S list. (Value ;equals $0000, $007A, 2*$007A, 3*$007A, ;or 4*$007A.) (AFC7) CLC ;Add the maximum number of secs that ADC MXSCURTS ;can be described in this T/S list. (AFCB) STA RELASTP1 ;Store the maximum relative sector number ;(plus 1) that can possibly be described ;in this particular T/S list. (That is, ;reset RELASTP1.) (AFCE) INY LDA (A4L),Y STA RELFIRST+1 ADC MXSCURTS+1 STA RELASTP1+1 ;Value equals $0000, $007A, CLC ;2*$007A, 3*$007A, 4*$007A or 5*$007A. (AFDB) RTS ============ (B0F0) BCC CKCURTS ;Go back and check if this is ;the correct T/S list. ;ALWAYS TAKE THIS BRANCH WHEN WRITING. (B0F2) RTS ;Return with (c)=1 to signal potential error ============ ;cause ran out of t/s lists while READING. * We know that the data sector wanted * should be described in the present T/S * list so now calculate the offset into * the T/S list sector where the data * sector pair should be described. (B0F3) GETDATPR SEC ;Calculate offset to the data pair: LDA FILPTSEC ; Sector offset of data sector in file (B0F7) SBC RELFIRST ; minus the relative index of first data ; sector pair described in present T/S list. (B0FA) ASL ;Times 2 cause 2 bytes used to describe a data pair. ADC #12 ;Add 12 cause 1rst data pair is always listed TAY ;12 bytes from the start of the T/S list buf. (B0FE) JSR SELTSBUF ;Point A4L/H at the T/S list buffer. * Point the A4L/H pointer at the * T/S list sector buffer. (AF0C) SELTSBUF LDX #2 ;Index to select T/S list sector buffer. (AF0E) BNE PT2FMBUF ;ALWAYS. (AF12) PT2FMBUF LDA WRKBUFFM,X ;Get address of selected buffer from STA A4L ;the FM parm list & put it in the pointer. LDA WRKBUFFM+1,X STA A4L+1 (AF1C) RTS (B101) LDA (A4L),Y ;Get trk number part of data sector pair. (B103) BNE RDDATSEC ;Go read in the data sector. * The track number part of the data sector * pair was zero. Therefore there are no * more data sector pairs described in this * T/S list. (B105) LDA OPCODEFM ;Check to see if writing or not. CMP #4 BEQ NEWPAIR ;ALWAYS BRANCH WHEN WRITING. SEC ;Not writing and ran out of data sector pairs (B10D) RTS ;in the present t/s list, so go exit with ============ ;carry set to signal potential error. * Since we ran out of data sector pairs * while writing, we must add a new data * pair to the T/S list. (B10E) NEWPAIR JSR NWDATKSC ;Add a new data sector pair to the T/S list. ;Zero out the data sector buffer and set the ;update flag to signal that both the T/S ;list sector and data sector require updating. * Designate trk/sec values for new data * sector and add new data sector pair * to the T/S list. (B134) NWDATKSC STY SCRNSRCH ;Save offset to data pair in T/S list. (B137) JSR ASGNTKSC ;Find and deisgnate an available sector. (B244) ASGNTKSC . . (See complete formatted dis'mbly given in the OPEN function.) . . (RTS) (B13A) LDY SCRNSRCH ;Retrieve offset to data pair. INY STA (A4L),Y ;Put sector value in the T/S list STA CURDATSC ;and in the work area. DEY LDA ASIGNTRK ;Put track value in the T/S list STA (A4L),Y ;and in the work area. STA CURDATRK (B14C) JSR SELDABUF ;Go select the data sector buffer. * Point A4L/H at the data sector buffer. (AF10) SELDABUF LDX #4 ;Index to select (AF12) ;data sector buffer. PT2FMBUF LDA WRKBUFFM,X ;Get addr of STA A4L ;selected buffer LDA WRDBUFFM+1,X ;from FM parm STA A4L+1 ;list & put it in (AF1C) RTS ;A4L/H pointer. (B14F) ZOUTDAT JSR ZCURBUF ;Zero out the data sector buffer. * Zero out all 256 bytes of the current * buffer pointer to by the A4L/H pointer. (B7D6) ZCURBUF LDA #0 TAY ZCURBUF1 STA (A4L),Y INY BNE ZCURBUF1 (B7DE) RTS (B152) LDA #%11000000 ;Set both bits 6 & 7 in flag to ORA UPDATFLG ;signal that both the data & T/S list STA UPDATFLG ;sectors require updating. (B15A) RTS * Note: If your follow this jump through, * you may realize that sometimes we eventually * exit the present function without writing * the T/S list and data sector buffers back * to the disk. However, after the subfunction * is exited, the CLOSE function eventually tests the * status of the update flag (UPDATFLG, $B5D5) and then * writes the T/S list and data sector buffers back to * the disk. (B111) JMP SETPREV ----------- * The data sector pair associated with the * data sector wanted was contained in the * current T/S list, so now read in the data * sector wanted. (B114) RDDATSEC STA CURDATRK ;Save trk/sec values of current data sector INY ;in the work area. LDA (A4L),Y ;Sector number of current data sector. STA CURDATSC (B11D) JSR READDATA ;Go read in the data sector. * Read data sector from disk * to the data sector buffer. (AFDC) READDATA JSR PRPDAIOB ;Set up RWTS's IOB to read a data sector. * Prepare RWTS's IOB to read/write * the data sector. (AFE4) PRPDAIOB LDY DATBUFFM ;Get addr of data LDA DATBUFFM+1 ;sec buf from STY IBBUFP ;the FM parm list (AFED) STA IBBUFP+1 ;& designate it ;as the I/O buffer ;for RWTS's IOB. (AFF0) LDX CURDATRK ;Enter driver with LDY CURDATSC ;(x)/(y) = trk/sec (AFF6) RTS ;vals corresponding ;to the data sector. (AFDF) LDA #1 ;Read opcode for RWTS. (AFE1) JMP RWTSDRVR ;Call RWTS driver to read the data sector. ------------ * Read/write track/sector driver. (B052) RWTSDRVR . . (See dis'mbly of RWTSDRVR using READ.) . . (RTS) * Save sector offset into file value * associated with the data sector just read. (B120) SETPREV LDA FILPTSEC ;Current sector offset into file. STA RELPREV ;Offset into file of last data sector read. LDA FILPTSEC+1 (B129) STA RELPREV+1 * Exit the read-next-data-sector routine. (B12C) XITNXDAT JSR SELDABUF ;Select the data sector buffer. * Point A4L/H at the data sector buffer. (AF10) SELDABUF LDX #4 ;Index to select data sector buffer. PT2FMBUF LDA WRKBUFFM,X ;Get address of selected buffer from STA A4L ;the FM parameter list & put it in the LDA WRKBUFFM+1,X ;A4L/H pointer. STA A4L+1 (AF1C) RTS (B12F) LDY FILPTBYT ;(y) = offset into current data sector. CLC ;Exit cleanly. (B133) RTS ============ (ACDE) PLA ;Get data byte to write off the stack. STA (A4L),Y ;Put data byte in the data sector buffer. LDA #%01000000 ;Set bit 6 to signal that the data sector ORA UPDATFLG ;buffer has changed and therefore, the disk STA UPDATFLG ;requires updating. (ACE9) JSR INCREC ;Either increment the record number or ;increment the byte offset into the record. * Adjust record number or byte offset * into a given record. * * This routine is used both when reading * and writing. The pattern of execution * varies with the structure of the file. * However, some files are treated as if * they have one type of structure when * they are read and another type of * structure when they are being written: * - random access text files have a fixed * record length assigned by the user. * - sequential text & Applesoft files * have a record length of one. * - during a LOAD or BLOAD, Applesoft or * Binary files are considered to be * composed of a collection of one-byte * long records. * - when SAVEing or BSAVEing however, these * files are treated as if they consist of * one long record. * Copy the record number from the work * area to the FM parameter list. (B15B) INCREC LDX RECNMBWA ;Get the current record number. STX RECNMBFM ;Store it in the FM parameter list. LDX RECNMBWA+1 (B164) STX RECNMBFM+1 * Copy the current byte offset into the record * from the work area to the FM parameter list. (B167) LDX BYTOFFWA ;Get offset into record from work area. LDY BYTOFFWA+1 STX BYTOFFFM ;Store it in the FM parameter list. (B170) STY BYTOFFFM+1 * Increment the byte offset into the record. * If it equals the record length, then reset * the offset to zero and kick up the record * number. (B173) INX BNE BYTVSREC INY BYTVSREC CPY RECLENWA+1 ;Fixed value via OPEN command, else (B177) ;L-parameter via SAVE or BSAVE command (from work area). (B17A) KIKOFF1 BNE SETBYTOF CPX RECLENWA KIKOFF2 BNE SETBYTOF LDX #0 ;Offset into record was the same as the (B183) LDY #0 ;record length so prepare to reset the ;offset into the record to zero. (B185) INC RECNMBWA NOKIKOFF BNE SETBYTOF (B18A) INC RECNMBWA+1 * On fall through or entry from NOKIKOFF, * reset the offset into the record to zero. * On branched entry from KIKOFF1 or KIKOFF2, * increment the offset into the record. (B18D) SETBYTOF STX BYTOFFWA STY BYTOFFWA+1 (B193) RTS (ACEC) JMP INCFILPT ------------ * Increment byte offset into current data * sector. If at the end of the sector, * then increment the offset into the entire file. (B194) INCFILPT INC FILPTBYT ;Kick up offset into sector. BNE INCPTRTN ;Branch if not at end of current sector. INC FILPTSEC ;Offset into sector wrapped around cause (B19C) BNE INCPTRTN ;at end of sector, so kick up offset into ;entire file. (B19E) INC FILPTSEC+1 ;Increment hi byte if necessary. INCPTRTN RTS (B1A1) (ACC4) JMP GOODFMXT ;Done writing one byte so go exit. ------------ * Exit File Manager with or without errors. (B367) RNGERRSB LDA #3 (B369) BNE BADFMXIT ;ALWAYS. (B37B) FILELOKD LDA #10 (B37D) BNE BADFMXIT ;ALWAYS. (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 ;Avoid that infamous $48 bug. STA STATUS (B38E) JSR CPYFMWA ;Copy work area to the work buffer. * Copy the FM work area buffer (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 FM work buffer. (AF0A) BEQ PT2FMBUF ;ALWAYS. (AF12) PT2FMBUF LDA WRKBUFFM,X ;Get address of selected buffer from the STA A4L ;FM parameter list and 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 ;back from the stack. (B392) LDX STKSAV ;Adjust the stack pointer to force exit TXS ;to the caller even if several subroutines (B396) RTS ;deeper than original entry point. (That is, ============ ;normally returns to AFTRFUNC ($A6AB) ;located in the FMDRIVER routine ($A6A8).)