💾 Archived View for mirrors.apple2.org.za › archive › www.textfiles.com › apple › ANATOMY › psnwrrng… captured on 2024-08-19 at 03:02:26.
View Raw
More Information
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
- ================================================================*
- *
- 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 & write-a-range *
- of bytes subfunction handler *
- *
- ----------------------------------------------------------------*
- *
- Adjust file pointer and then write a range of bytes. *
- (NOTE: This subfunction is never accessed by normal DOS cmds *
- but it is available to assembly language programmers.) *
- *
- ----------------------------------------------------------------*
(ACC7)
PSNWRRNG 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-range of bytes *
- subfunction handler *
- *
- ----------------------------------------------------------------*
- *
- Although the write-range-of-bytes subfunction handler *
- ($ACCA) is not used by the write command, it is pressed into *
- service by most DOS commands that send data to the disk. *
- Almost all of the subroutines that are employed by WRITERNG *
- 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: *
- WRITERNG ($ACCA) first calls INCIOBUF ($B1A2) to point *
- the A4L pointer ($42, $43) at the current data source memory *
- location (CURIOBUF, $B5C3-$B5C4). After loading the *
- accummulator with the data byte to be written, WRITERNG 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 execution returns to the WRITERNG routine at $ACD4. *
- DECRWLEN ($B1B5) is then called to check if there are any *
- more bytes left to write. If the byte counter (LEN2RDWR, *
- $B1C1-$B1C2) equals zero, the subfunction is exited via a jump *
- to GOODFMXT ($B3F7). Otherwise, LEN2RDWR is decremented and *
- execution jumps back to WRITERNG ($ACCA) to write another *
- byte. *
- *
- 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). *
- *
- ----------------------------------------------------------------*
(ACCA)
WRITERNG JSR INCIOBUF
* Point A4L/H at the current data source
* memory location (CURIOBUF) for writing.
* Also increment the source location for
* next time around.
(B1A2)
INCIOBUF LDY CURIOBUF ;Get adr of source location.
LDX CURIOBUF+1 ;from the FM parameter list.
STY A4L
STX A4L+1
INC CURIOBUF ;Kick up address of source location.
BNE INCIORTN
INC CURIOBUF+1
INCIORTN RTS
(B1B4)
(ACCD) LDY #0 ;Set (y) for indirect addressing of source buf.
LDA (A4L),Y ;Get byte to write.
(ACD1) 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)
(ACD4) JSR DECRWLEN ;Check if done writing.
;If not, reduce byte counter.
* Reduce count of number of bytes left to write.
* (When counter equals zero, exit the file manager.)
(B1B5)
DECRWLEN LDY LEN2RDWR ;Length to read or length-1 left to write
;(from FM parameter list).
(B1B8) BNE DECLENRW ;More bytes to read or write.
LDX LEN2RDWR+1 ;Low byte was zero, check hi byte.
BEQ RWLEN0 ;Counter = 0, so done read/write.
DEC LEN2RDWR+1
DECLENRW DEC LEN2RDWR ;Reduce counter.
(B1C5) RTS
=============
(B1C6)
RWLEN0 JMP GOODFMXT ;Done read/write so go exit the file manager.
------------
(ACD7) JMP WRITERNG ;Go back to write next data byte.
------------
- 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).)