1019 lines
76 KiB
Plaintext
1019 lines
76 KiB
Plaintext
******************************************************************
|
|
* *
|
|
* BSAVE Command Handler *
|
|
* *
|
|
*----------------------------------------------------------------*
|
|
* *
|
|
* The BSAVE Command saves an image of memory as a binary *
|
|
* file on the disk. The portion of memory saved is defined by *
|
|
* the starting address (A-parameter) and the number of bytes to *
|
|
* be stored (L-parameter). Both parameters must be issued and *
|
|
* can be specified in either decimal or hexadecimal notation. *
|
|
* The maximum file length that can be saved is 32767 ($7FFF) *
|
|
* bytes. A file name must also accompany the command. Drive, *
|
|
* volume and slot parameters are optional. *
|
|
* *
|
|
* Execution pattern: *
|
|
* The BSAVE command closes (if necessary) and then opens *
|
|
* the file. Next the address and length values are placed in *
|
|
* the data sector buffer. The rest of the file is then *
|
|
* sequentially stored in the data sector buffer. Each time the *
|
|
* buffer is filled, it is written to the disk. If the last *
|
|
* segment of the program does not completely fill the data sector*
|
|
* buffer, the update flag (UPDATFLG, $B5D5) is set to indicate *
|
|
* that the disk requires updating. When the file is closed, the *
|
|
* status of the flag is tested & (if necessary) the last data *
|
|
* sector is written to the disk. The file is then verified and *
|
|
* reclosed via the verify command. *
|
|
* In order to avoid a possible source of confusion, note *
|
|
* that the file manager makes different assumptions about the *
|
|
* structure of the binary file depending on whether the file is *
|
|
* being BLOADED or BSAVED. During the BSAVE process, the binary *
|
|
* file is considered to be a single record with a record length *
|
|
* equal to the length of the file (val of L-parameter = LENPRSD *
|
|
* --> RECLENFM). However, during the BLOAD process a binary *
|
|
* file is treated as a collection of single-byte-long records. *
|
|
* *
|
|
******************************************************************
|
|
|
|
|
|
* On entry - CUMLOPTN ($AA65) has been updated
|
|
* to reflect parsed option words.
|
|
* - the validity of the options issued
|
|
* with the command (and their numeric
|
|
* values) have been checked. (Only
|
|
* volume, drive, slot, address and
|
|
* length parameters are allowed with the
|
|
* BSAVE command. Their parsed values are
|
|
* stored at VOLPRSD ($AA66-67), DRVPRSD
|
|
* ($AA68-69), SLOTPRSD ($AA6A-6B), ADRPRSD
|
|
* ($AA72) & LENPRSD ($AA6C) respectively.
|
|
* - a legal file name has been parsed and
|
|
* stored in the primary file name buffer
|
|
* (PRIMFNBF, $AA75).
|
|
|
|
|
|
(A331)
|
|
CMDBSAVE LDA #%00001001 ;Test bits 0 and 3 of CUMLOPTN to see if the
|
|
AND CUMLOPTN ;A(ddress) and L(ength) parms were issued with BSAVE cmd.
|
|
CMP #%00001001
|
|
BEQ DOBSAV ;Both A- & L-parameters present.
|
|
(A33A) JMP CKIFCTRL ;Got a syntax error.
|
|
------------ ;(See error routine at end of this dis'mbly.)
|
|
|
|
(A33D)
|
|
DOBSAV LDA #4 ;Code for BINARY file.
|
|
(A33F) JSR OPNCKTYP ;Close (if necessary) & then reopen file.
|
|
|
|
(A3D5)
|
|
OPNCKTYP STA FILTYPFM ;Put code for file type in the FM parameter
|
|
(A3D8) PHA ;list & save it on the stack.
|
|
;($00=Text, $01=Integer, $02=Applesoft,
|
|
;$04=Binary, $08=S-type, $10=Relocatable,
|
|
;$20=A-type and $40=B-type.)
|
|
(A3D9) JSR HNDLCMD ;Use the file manager command handler to open the file.
|
|
|
|
* Common file manager command handler code.
|
|
(A2A8)
|
|
HNDLCMD LDA #1 ;Open opcode.
|
|
HNDLCMD1 STA TEMPBYT ;Store opcode in temporary location.
|
|
LDA LENPRSD ;Get L-parameter from parsed table.
|
|
BNE SAVLENFM ;Was a non-zero L-parm issued with cmd?
|
|
LDA LENPRSD+1
|
|
BNE SAVLENFM
|
|
LDA #1 ;Length was 0 so make it 1 instead.
|
|
STA LENPRSD
|
|
SAVLENFM LDA LENPRSD ;Put length in FM parm list.
|
|
STA RECLENFM
|
|
LDA LENPRSD+1
|
|
STA RECLENFM+1
|
|
CLSLOCBF JSR CMDCLOSE ;Close file if it's already open.
|
|
(A2C8)
|
|
|
|
(A2EA)
|
|
CMDCLOSE .
|
|
.
|
|
(See dis'mbly of CMDCLOSE given below.)
|
|
.
|
|
.
|
|
- Note that execution flows thru CMDCLOSE twice if the
|
|
file is already open.
|
|
- The first time thru, the matching DOS filename buffer is
|
|
located & then CLOSEONE is used to close the file.
|
|
- Execution then jumps back to the start of CMDCLOSE.
|
|
- On this second pass, a matching filename is not found
|
|
because the DOS filename buffer was released on the
|
|
first pass. Therefore, A5L/H is left pointing at the
|
|
highest numbered (lowest in memory) FREE DOS buffer
|
|
when CMCLOSE is exited via EVENTXIT and CLOSERTS.
|
|
- If the file is not already open on the first entry to
|
|
CMDCLOSE, only one pass is made. This single pass
|
|
resembles the second pass mentioned above.
|
|
.
|
|
.
|
|
- If necessary, the CLOSE FUNCTION updates the data
|
|
sector, T/S list sector & the VTOC. It also fixes
|
|
up links in the directory sectors and updates the
|
|
file size if needed.
|
|
.
|
|
.
|
|
(RTS)
|
|
|
|
(A2CB) LDA A5L+1 ;Hi byte of A5L/H pointer which points at the highest
|
|
;numbered (lowest in memory) free DOS name buffer (in chain).
|
|
(A2CD) BNE SAVFNPTR ;Branch if found a free buffer.
|
|
(A2CF) JMP NOBUFERR ;Go issue an out-of-buffers message.
|
|
------------ ;(See dis'mbly of errors.)
|
|
|
|
(A2D2)
|
|
SAVFNPTR STA A3L+1 ;Reset A3L/H to point at DOS buffer that we
|
|
LDA A5L ;will use for file name field buffer (chain).
|
|
STA A3L
|
|
(A2D8) JSR CPYPFN
|
|
|
|
* NOTE: This (re)assigns a DOS buffer to the
|
|
* file we want to OPEN. The buffer may or may
|
|
* not be the same one that was just released by
|
|
* the CLOSE cmd above. The highest numbered
|
|
* (lowest in memory) free DOS buffer is used.
|
|
(A743)
|
|
CPYPFN LDY #29 ;30 bytes to copy (0 to 29).
|
|
CPYPRIM LDA PRIMFNBF,Y ;Copy the name of the file wanted from
|
|
STA (A3L),Y ;the primary filename buffer into the
|
|
DEY ;filename field buffer (in DOS chain).
|
|
BPL CPYRIM ;More chars to get.
|
|
(A74D) RTS
|
|
|
|
(A2DB) JSR BUFS2PRM
|
|
|
|
* Get addresses of the various DOS buffers from the
|
|
* chain buffer & put them in the FM parameter list.
|
|
(A74E)
|
|
BUFS2PRM LDY #30 ;Get addr of FM work buf, T/S list
|
|
ADRINPRM LDA (A3L),Y ;buf, data sector buf & next DOS
|
|
STA WRKBUFFM-30,Y ;filename buf from chain
|
|
INY ;pointer buffer & put them in FM parm list.
|
|
CPY #38 ;(P.S. Adr of next DOS file name buf is
|
|
BNE ADRINPRM ;not used by DOS.)
|
|
(A75A) RTS
|
|
|
|
(A2DE) JSR CPY2PARM
|
|
|
|
* Put volume, drive, & slot values plus the
|
|
* address of the primary filename buffer
|
|
* in the FM parameter list.
|
|
(A71A)
|
|
CPY2PARM LDA VOLPRSD ;From parsed table.
|
|
STA VOLFM
|
|
LDA DRVPRSD ;From parsed table.
|
|
STA DRVFM
|
|
LDA SLOTPRSD ;From parsed table.
|
|
STA SLOTFM
|
|
LDA ADRPFNBF ;Get the adr of the primary file
|
|
STA FNAMBUFM ;name buf from the constants tbl
|
|
LDA ADRPFNBF+1 ;and put it in the FM parm list.
|
|
STA FNAMBUFM+1
|
|
LDA A3L ;Save adr of current DOS file name
|
|
STA CURFNADR ;buf in table of DOS variables.
|
|
LDA A3L+1
|
|
STA CURFNADR+1
|
|
(A742) RTS
|
|
|
|
(A2E1) LDA TEMPBYT ;Get open opcode back from temporary
|
|
STA OPCODEFM ;buffer & put it in the FM parameter list.
|
|
(A2E7) JMP FMDRIVER
|
|
------------
|
|
|
|
* Use the file manager driver to do the OPEN FUNCTION.
|
|
(A6A8)
|
|
FMDRIVER JSR FILEMGR ;Call the file manager to do the function.
|
|
|
|
* File manager proper.
|
|
(AB06)
|
|
FILEMGR TSX ;Save stk ptr so can later rtn 2 caller.
|
|
STX STKSAV
|
|
(AB0A) JSR RSTRFMWA
|
|
|
|
* Copy FM work buf (in DOS chain) to
|
|
* FM work area (not in DOS chain).
|
|
(AE6A)
|
|
RSTRFMWA JSR SELWKBUF ;Point A4L/H at FM
|
|
;work buffer.
|
|
|
|
* Get addr of FM
|
|
* work buff from
|
|
* the FM parm list
|
|
* & put it in the
|
|
* A4L/H pointer.
|
|
(AF08)
|
|
SELWKBUF LDX #0
|
|
(AF0A) BEQ PT2FMBUF
|
|
|
|
(AF12)
|
|
PT2FMBUF LDA WRKBUFFM,X
|
|
STA A4L
|
|
LDA WRKBUFFM+1,X
|
|
STA A4L+1
|
|
(AF1C) RTS
|
|
|
|
(AE6D) LDY #0 ;Zero out return
|
|
(AE6F) STY RTNCODFM ;code in FM parm
|
|
;list to signal
|
|
;no errors as
|
|
;default.
|
|
(AE72)
|
|
STORFMWK LDA (A4L),Y ;Copy FM wrk buf
|
|
STA FMWKAREA,Y ;to FM wrk area.
|
|
INY
|
|
CPY #45 ;45 bytes to copy
|
|
BNE STORFMWK ;(0 to 44).
|
|
CLC ;WHY?????
|
|
(AE7D) RTS
|
|
|
|
(AB0D) LDA OPCODEFM ;Check if opcode is legal.
|
|
CMP #13 ;(Must be less than 13.)
|
|
BCS TOERROP ;Opcode too large so got range error.
|
|
ASL ;Double val of opcode & put it in (x)
|
|
TAX ;so it indexes tables of adrs.
|
|
LDA FMFUNCTB+1,X ;Stick adr of appropriate function
|
|
PHA ;handler on stack (hi byte first).
|
|
LDA FMFUNCTB,X
|
|
PHA
|
|
(AB1E) RTS ;DO STACK JUMP TO FUNCTION ENTRY POINT.
|
|
|
|
.
|
|
.
|
|
(AB22)
|
|
FNOPEN .
|
|
.
|
|
- uses part of COMNOPEN routine.
|
|
- reads in VTOC to get link to 1rst directory.
|
|
- reads directory secs in & looks for file
|
|
description entry with matching filename.
|
|
- if matching name found, reads in the 1rst
|
|
T/S list sector belonging to the file.
|
|
- if no match found, starts a new file:
|
|
(1) creates new file description entry
|
|
- copies name to 1rst available space
|
|
in the directory sector. (If can't
|
|
find an available space, then issues
|
|
a disk-full error message.)
|
|
- assigns secs for file.
|
|
- writes updated VTOC to disk.
|
|
- puts link to first T/S list, file size,
|
|
etc. in directory entry space.
|
|
- writes directory sector buffer to disk.
|
|
(2) creates new T/S list & writes it to disk.
|
|
- reads T/S list back into T/S list buffer.
|
|
.
|
|
.
|
|
(RTS)
|
|
============
|
|
|
|
TOERROP JMP RNGERROP ;Go handle range error.
|
|
(AB1F) ------------ ;(See dis'mbly of errors.)
|
|
|
|
* Return here after doing the OPEN function.
|
|
* (Cause after @ function is done, use the
|
|
* stack to get back to the original caller.)
|
|
(A6AB)
|
|
AFTRFUNC BCC FMDRVRTN ;(c) = 0 = no errors.
|
|
LDA RTNCODFM ;Get error code from FM parameter list.
|
|
CMP #5 ;End-of-data error?
|
|
(A6B2) BEQ TOAPPTCH ;Yes - Got a zeroed-out T/S link or a
|
|
;zeroed-out data pair listed in a T/S list.
|
|
;(Not applicable to the open function.)
|
|
(A6B4) JMP OTHRERR ;No. See dis'mbly of errors.
|
|
------------
|
|
|
|
(A6C3)
|
|
FMDRVRTN RTS
|
|
|
|
(A3DC) PLA ;Pull file type code wanted from stack.
|
|
(A3DD) JMP CHKFTYPE ;Go check if type wanted = type found.
|
|
------------
|
|
|
|
* Check if file type wanted = file type found.
|
|
* (If saving a file which has same name as one already
|
|
* on disk, may get a type-mismatch error. However,
|
|
* a mismatch error is not possible if saving a new file.)
|
|
(A7C4)
|
|
CHKFTYPE EOR FILTYPFM ;Type found (via OPEN function).
|
|
(A7C7) BEQ CKTYPRTN ;Branch if type wanted = type found.
|
|
|
|
* File types didn't match.
|
|
* Check if correct type but locked.
|
|
(A7C9) AND #%01111111 ;Maybe matched - disregard lock bit.
|
|
(A7CB) BEQ CKTYPRTN ;Branch if matched.
|
|
|
|
* Type wanted < > type found!!!!!
|
|
* So go close file & then issue a
|
|
* type-mismatch-error message.
|
|
(A7CD) JSR CMDCLOSE ;Wrong kind of file so go close it.
|
|
|
|
(A2EA)
|
|
CMDCLOSE .
|
|
.
|
|
(See dis'mbly of CLOSE command.)
|
|
.
|
|
.
|
|
(RTS)
|
|
|
|
(A7D0) JMP TYPMISM ;Exit with type-mismatch error.
|
|
------------ ;(See dis'mbly of errors.)
|
|
|
|
CKTYPRTN RTS
|
|
(A7D3) ============
|
|
|
|
(A342) LDA ADRPRSD+1 ;Prepare to write address to disk.
|
|
LDY ADRPRSD ;(a) & (y) = hi / low bytes of parsed address values.
|
|
(A348) JSR WRADRLEN ;WRITE THE ADDRESS OF THE FILE by using the
|
|
;write-one-byte subfunction (WRITEONE, $ACBE) twice.
|
|
|
|
* Write two bytes.
|
|
* Code which writes address and length values
|
|
* to the data sector buffer. (Later, the data
|
|
* sector buffer is written to the disk.) Calls
|
|
* write-one-byte subfunction twice. Note that
|
|
* LEN2RDWR is used as a temporary buffer for data
|
|
* transfer as shown below:
|
|
* low byte of address (ADRPRSD) --> LEN2RDWR --> ONEIOBUF --> data sector buffer.
|
|
* hi byte of address (ADRPRSD+1) --> LEN2RDWR+1 --> ONEIOBUF --> data sector buffer.
|
|
(A3E0)
|
|
WRADRLEN STY LEN2RDWR ;Put low byte in FM parm list in case
|
|
;this is a L-parameter and we need it
|
|
;as a counter when later write data
|
|
;to the disk.
|
|
(A3E3) STY ONEIOBUF ;Put byte to write in parm list.
|
|
(A3E6) STA LEN2RDWR+1 ;Put hi byte in FM parm list in case
|
|
;this is a L-parameter and we need it
|
|
;as a counter when later write data
|
|
;to the disk.
|
|
(A3E9) LDA #4 ;Put write opcode in the FM parm list.
|
|
STA OPCODEFM
|
|
LDA #1 ;Put one-byte subcode in FM parm list.
|
|
STA SUBCODFM
|
|
(A3F3) JSR FMDRIVER ;Call FM driver to do the write.
|
|
|
|
* USE THE FILE MANAGER DRIVER TO
|
|
* WRITE THE LOW BYTE OF THE ADDRESS.
|
|
(A6A8)
|
|
FMDRIVER JSR FILEMGR ;Call the file manager to do the function.
|
|
|
|
* File manager proper.
|
|
(AB06)
|
|
FILEMGR TSX ;Save stk ptr so can later rtn to caller.
|
|
STX STKSAV
|
|
(AB0A) JSR RSTRFMWA
|
|
|
|
* Copy FM work buf (in DOS chain) to
|
|
* FM work area (not in DOS chain).
|
|
(AE6A)
|
|
RSTRFMWA JSR SELWKBUF ;Point A4L/H at
|
|
;FM work buf.
|
|
|
|
* Get addr of FM
|
|
* work buff from
|
|
* the FM parm list
|
|
* & put it in the
|
|
* A4L/H pointer.
|
|
(AF08)
|
|
SELWKBUF LDX #0
|
|
(AF0A) BEQ PT2FMBUF
|
|
|
|
(AF12)
|
|
PT2FMBUF LDA WRKBUFFM,X
|
|
STA A4L
|
|
LDA WRKBUFFM+1,X
|
|
STA A4L+1
|
|
(AF1C) RTS
|
|
|
|
(AE6D) LDY #0 ;Zero out return
|
|
(AE6F) STY RTNCODFM ;code in FM parm
|
|
;list 2 signal no
|
|
(AE72) ;errors as default.
|
|
STORFMWK LDA (A4L),Y ;Copy FM work buf
|
|
STA FMWKAREA,Y ;to FM work area.
|
|
INY
|
|
CPY #45 ;45 bytes to copy
|
|
BNE STORFMWK ;(0 to 44).
|
|
CLC ;WHY?????
|
|
(AE7D) RTS
|
|
|
|
(AB0D) LDA OPCODEFM ;Check if opcode is legal.
|
|
CMP #13 ;(Must be less than 13.)
|
|
BCS TOERROP ;Opcode too large so got range error.
|
|
ASL ;Double val of opcode & put it in (x)
|
|
TAX ;so it indexes tables of adrs.
|
|
LDA FMFUNCTB+1,X ;Stick adr of appropriate function
|
|
PHA ;handler on stack (hi byte first).
|
|
LDA FMFUNCTB,X
|
|
PHA
|
|
(AB1E) RTS ;DO STACK JUMP TO FUNCTION ENTRY POINT.
|
|
|
|
.
|
|
.
|
|
(See dis'mbly of write function (FNWRITE, $AC70) and
|
|
write-one-byte subfunction (WRITEONE, $ACBE).
|
|
.
|
|
.
|
|
Conditions on entry:
|
|
- File was just opened so appropriate T/S list is in memory.
|
|
- Since this is the very first time this function is called by
|
|
the BSAVE command, then:
|
|
FILPTSEC: $0000, FILPTBYT: $00,
|
|
RECNMBFM: $0000, RECNMBWA: $0000,
|
|
BYTOFFWA: $0000, BYTOFFFM: $0000,
|
|
RELFIRST: $0000, RELASTP1: $007A,
|
|
RELPREV: $FFFF,
|
|
LEN2RDWR: contents of ADRPRSD,
|
|
ONEIOBUF: low byte of parsed addr value.
|
|
(ONEIOBUF is low byte of the 2-byte CURIOBUF buffer.)
|
|
RECLENWA = RECLENFM = LENPRSD = val issued as L-parameter.
|
|
- Note the condition of the RELPREV and RELASTP1 flags.
|
|
These settings cause execution to eventually branch to the
|
|
GETDATPR ($B0F3) routine.
|
|
- When a brand new file is being created, GETDATPR detects the
|
|
zero bytes in the zeroed out T/S list. As a result, ASGNTKSC
|
|
($B244) is eventually called via NEWPAIR ($B10E) to allocate a
|
|
new data sector. The new trk/sec values (data pair) are then
|
|
placed in the T/S list buffer. Next, the data sector is
|
|
initialized (zeroed out) and bits 6 and 7 of the update flag
|
|
(UPDATFLG, $B5D5) are set to signal that both the data and T/S
|
|
list sectors require updating. Finally, the data to be written
|
|
is transferred from the ONEIOBUF ($B5C3) to the data sector.
|
|
- If the named file already exists, the trk/sec values of the
|
|
first data sector are obtained from the original T/S list
|
|
sector. Next, the data sector is read and one byte of data is
|
|
transferred from the ONEIOBUF ($B5C3) to the data sector
|
|
buffer. (This new byte overwrites an old (orignal) data byte.)
|
|
The write-one-byte subfunction is exited after bit 6 of the
|
|
update flag (UPDATFLG, $B5D5) is set to signal that the data
|
|
sector requires updating.
|
|
.
|
|
.
|
|
(RTS)
|
|
============
|
|
|
|
TOERROP JMP RNGERROP ;Go handle range error.
|
|
(AB1F) ------------ ;(See dis'mbly of errors.)
|
|
|
|
* Return here after doing the WRITE function.
|
|
* (Cause after @ function is done, use stack
|
|
* to get back to the original caller.)
|
|
(A6AB)
|
|
AFTRFUNC BCC FMDRVRTN ;(c) = 0 = no errors.
|
|
LDA RTNCODFM ;Get error code from FM parameter list.
|
|
CMP #5 ;End-of-data error?
|
|
(A6B2) BEQ TOAPPTCH ;Yes. Got a zeroed-out T/S link or a
|
|
;zeroed-out data pair listed in a T/S list.
|
|
;(Not applicable to the write function.)
|
|
(A6B4) JMP OTHRERR ;No. See dis'mbly of errors.
|
|
------------
|
|
|
|
FMDRVRTN RTS
|
|
(A6C3)
|
|
|
|
* Now send the high byte of the addr
|
|
* to the data sector buffer.
|
|
(A3F6) LDA LEN2RDWR+1 ;Put hi byte of address to write in parm list.
|
|
STA ONEIOBUF
|
|
(A3FC) JMP FMDRIVER ;Go write hi byte.
|
|
------------
|
|
|
|
* USE THE FILE MANAGER DRIVER TO WRITE
|
|
* THE HI BYTE OF THE ADDRESS.
|
|
(A6A8)
|
|
FMDRIVER JSR FILEMGR ;Call the file manager to do the function.
|
|
|
|
* File manager proper.
|
|
(AB06)
|
|
FILEMGR TSX ;Save stk pointer so can later rtn to caller of FM.
|
|
STX STKSAV
|
|
(AB0A) JSR RSTRFMWA
|
|
|
|
* Copy FM work buf (in DOS chain) to
|
|
* FM work area (not in DOS chain).
|
|
(AE6A)
|
|
RSTRFMWA JSR SELWKBUF ;Point A4L/H at FM work buf.
|
|
|
|
* Get addr of FM work buff from
|
|
* the FM parm list & put it in
|
|
* the A4L/H pointer.
|
|
(AF08)
|
|
SELWKBUF LDX #0 ;Offset to select
|
|
;work buffer.
|
|
(AF0A) BEQ PT2FMBUF ;ALWAYS.
|
|
|
|
(AF12)
|
|
PT2FMBUF LDA WRKBUFFM,X
|
|
STA A4L
|
|
LDA WRKBUFFM+1,X
|
|
STA A4L+1
|
|
(AF1C) RTS
|
|
|
|
(AE6D) LDY #0 ;Zero out return code in FM parm list to
|
|
STY RTNCODFM ;signal no errors as default condition.
|
|
STORFMWK LDA (A4L),Y ;Copy FM work buf to FM work area.
|
|
STA FMWKAREA,Y
|
|
INY
|
|
CPY #45 ;45 bytes to copy (0 to 44).
|
|
BNE STORFMWK
|
|
CLC ;WHY?????
|
|
(AE7D) RTS
|
|
|
|
(AB0D) LDA OPCODEFM ;Check if opcode is legal.
|
|
CMP #13 ;(Must be less than 13.)
|
|
BCS TOERROP ;Opcode too large so got range error.
|
|
ASL ;Double val of opcode & put it in (x)
|
|
TAX ;so it indexes tables of adrs.
|
|
LDA FMFUNCTB+1,X ;Stick adr of appropriate function
|
|
PHA ;handler on stack (hi byte first).
|
|
LDA FMFUNCTB,X
|
|
PHA
|
|
(AB1E) RTS ;DO STACK JUMP TO FUNCTION ENTRY POINT.
|
|
|
|
.
|
|
.
|
|
(See dis'mbly of write function (FNWRITE, $AC70)
|
|
and write-one-byte (WRITEONE, $ACBE) subfunction.
|
|
On entry - file was recently opened so appropriate
|
|
T/S list sec is in memory.
|
|
- FILPTSEC: $0000, FILPTBYT: $01,
|
|
RECNMBFM: $0000, RECNMBWA: $0000,
|
|
BYTOFFWA: $0001, BYTOFFFM: $0000,
|
|
RELFIRST: $0000, RELASTP1: $007A,
|
|
RELPREV: $0000,
|
|
LEN2RDWR: contents of ADRPRSD,
|
|
ONEIOBUF: hi byte of parsed addr value.
|
|
(ONEIOBUF is low byte of the 2-byte CURIOBUF buffer.)
|
|
RECLENWA = RECLENFM = LENPRSD = val issued as L-parameter.
|
|
- the 2nd byte of the address is transferred
|
|
from ONEIOBUF ($B5C3) to the data sector buffer.
|
|
.
|
|
.
|
|
(RTS)
|
|
============
|
|
|
|
TOERROP JMP RNGERROP ;Go handle range error.
|
|
(AB1F) ------------ ;(See dis'mbly of errors.)
|
|
|
|
* Return here after doing the write function.
|
|
* (Cause after @ function is done, use stack to get
|
|
* back to the original caller.)
|
|
(A6AB)
|
|
AFTRFUNC BCC FMDRVRTN ;(c) = 0 = no errors.
|
|
LDA RTNCODFM ;Get error code from FM parameter list.
|
|
CMP #5 ;End-of-data error?
|
|
(A6B2) BEQ TOAPPTCH ;Yes. Got a zeroed-out T/S link or a
|
|
;zeroed-out data pair listed in a T/S list.
|
|
;(Not applicable to the write function.)
|
|
(A6B4) JMP OTHRERR ;No. See dis'mbly of errors.
|
|
------------
|
|
|
|
(A6C3)
|
|
FMDRVRTN RTS
|
|
|
|
* Prepare to write file length fo file.
|
|
(A34B) LDA LENPRSD+1 ;(a) = hi byte of parsed length value.
|
|
LDY LENPRSD ;(y) = low byte of parsed length value.
|
|
(A351) JSR WRADRLEN ;Write file length (in low/hi format) as the
|
|
;3rd & 4th bytes in the data sector buffer.
|
|
|
|
* WRITE LENGTH OF FILE TO FILE.
|
|
(A3E0)
|
|
WRADRLEN .
|
|
.
|
|
(As per dis'mbly given above.)
|
|
.
|
|
.
|
|
Calls write-one-byte subfunction twice to put L(ength) parameter
|
|
as the 3rd & 4th bytes in the data sector buffer. (Later, the
|
|
buffer is written as the 1rst sector in the file.) Note that
|
|
LEN2RDWR ($B5C1) is used as a temporary buffer for data transfer:
|
|
low byte of length (LENPRSD) --> LEN2RDWR --> ONEIOBUF --> data sector buffer.
|
|
hi byte of length (LENPRSD+1) --> LEN2RDWR+1 --> ONEIOBUF --> data sector buffer.
|
|
Conditions on entry to 1rst call to write-one-byte subfunction:
|
|
FILPTSEC: $0000, FILPTBYT: $02,
|
|
RECNMBFM: $0000, RECNMBWA: $0000,
|
|
BYTOFFWA: $0002, BYTOFFFM: $0001,
|
|
RELFIRST: $0000, RELASTP1: $007A,
|
|
RELPREV: $0000,
|
|
LEN2RDWR: parsed length value,
|
|
ONEIOBUF: low byte of parsed length value.
|
|
(ONEIOBUF is low byte of the 2-byte CURIOBUF buffer.)
|
|
RECLENWA = RECLENFM = LENPRSD = val issued as L-parameter.
|
|
|
|
Conditions on entry for 2nd call to write-one-byte subfunction:
|
|
FILPTSEC: $0000, FILPTBYT: $03,
|
|
RECNMBFM: $0000, RECNMBWA: $0000,
|
|
BYTOFFWA: $0003, BYTOFFFM: $0002,
|
|
RELFIRST: $0000, RELASTP1: $007A,
|
|
RELPREV: $0000,
|
|
LEN2RDWR: parsed length value,
|
|
ONEIOBUF: hi byte of parsed length value.
|
|
(ONEIOBUF is low byte of the 2-byte CURIOBUF buffer.)
|
|
RECLENWA = RECLENFM = LENPRSD = val issued as L-parameter.
|
|
.
|
|
.
|
|
(RTS)
|
|
|
|
* Prepare to write rest of BINARY file.
|
|
(A354) LDA ADRPRSD+1 ;Put addr of source buffer in the Fm parm list.
|
|
LDY ADRPRSD
|
|
(A35A) JMP RWRANGE ;Go to write-range routine to write rest
|
|
------------ ;of file to disk. (File is also verified.)
|
|
|
|
* Prepare to write a range of bytes.
|
|
(A3FF)
|
|
RDWRANGE STY CURIOBUF ;Put adr of output buf in FM parm list.
|
|
(A402) STA CURIOBUF+1 ;(P.S. NOTE: THE LOW BYTE OF (CURIOBUF,
|
|
;$B5C3, $B5C4) IS THE ONE-BYTE BUFFER
|
|
;(ONEIOBUF, $B5C3) THAT WAS USED FOR
|
|
; WRITING VIA THE ONE-BYTE FUNCTION ABOVE.)
|
|
(A405) LDA #2 ;Set subcode for range of bytes.
|
|
;(Opcode is still set to write.)
|
|
(A407) JMP VRFYRWNG ;Go call file manager to write data to
|
|
------------ ;the disk. Next verify the info & close file.
|
|
|
|
(B686)
|
|
VRFYRWNG STA SUBCODFM ;Put range-of-bytes subcode in RWTS's IOB.
|
|
(B689) JSR FMDRIVER ;Call FM driver to write a range of bytes.
|
|
|
|
* USE THE FILE MANAGER DRIVER TO
|
|
* WRITE THE REST OF THE FILE.
|
|
(A6A8)
|
|
FMDRIVER JSR FILEMGR ;Call the file manager to do the function.
|
|
|
|
* File manager proper.
|
|
(AB06)
|
|
FILEMGR TSX ;Save stk pointer so can later rtn to caller of FM.
|
|
STX STKSAV
|
|
(AB0A) JSR RSTRFMWA
|
|
|
|
* Copy FM work buf (in DOS chain) to
|
|
* FM work area (not in DOS chain).
|
|
(AE6A)
|
|
RSTRFMWA JSR SELWKBUF ;Point A4L/H at FM work buf.
|
|
|
|
* Get addr of FM work buff from
|
|
* the FM parm list & put it in
|
|
* the A4L/H pointer.
|
|
(AF08)
|
|
SELWKBUF LDX #0 ;Offset to select
|
|
;work buffer.
|
|
(AF0A) BEQ PT2FMBUF ;ALWAYS.
|
|
|
|
(AF12)
|
|
PT2FMBUF LDA WRKBUFFM,X
|
|
STA A4L
|
|
LDA WRKBUFFM+1,X
|
|
STA A4L+1
|
|
(AF1C) RTS
|
|
|
|
(AE6D) LDY #0 ;Zero out return code in FM parm list to
|
|
STY RTNCODFM ;signal no errors as default condition.
|
|
STORFMWK LDA (A4L),Y ;Copy FM work buf to FM work area.
|
|
STA FMWKAREA,Y
|
|
INY
|
|
CPY #45 ;45 bytes to copy (0 to 44).
|
|
BNE STORFMWK
|
|
CLC ;WHY?????
|
|
(AE7D) RTS
|
|
|
|
(AB0D) LDA OPCODEFM ;Check if opcode is legal.
|
|
CMP #13 ;(Must be less than 13.)
|
|
BCS TOERROP ;Opcode too large so got range error.
|
|
ASL ;Double val of opcode & put it in (x)
|
|
TAX ;so it indexes tables of adrs.
|
|
LDA FMFUNCTB+1,X ;Stick adr of appropriate function
|
|
PHA ;handler on stack (hi byte first).
|
|
LDA FMFUNCTB,X
|
|
PHA
|
|
(AB1E) RTS ;DO STACK JUMP TO FUNCTION ENTRY POINT.
|
|
|
|
.
|
|
.
|
|
(See dis'mbly of the write function and (FNWRITE, $AC70) and
|
|
write-a-range-of-bytes subfunction (WRITERNG, $ACCA).)
|
|
On entry - file was recently opened so appropriate
|
|
T/S list sector is in memory.
|
|
- FILPTSEC: $0000, FILPTBYT: $04,
|
|
RECNMBFM: $0000, RECNMBWA: $0000,
|
|
BYTOFFWA: $0004, BYTOFFFM: $0003,
|
|
RELFIRST: $0000, RELASTP1: $007A,
|
|
RELPREV: $0000, CURIOBUF: starting addr from ADRPRSD,
|
|
LEN2RDWR: parsed length value.
|
|
(ONEIOBUF = low byte of CURIOBUF),
|
|
RECLENWA = RECLENFM = LENPRSD = val issued as L-parameter.
|
|
On exit - FILPTSEC: variable, FILPTBYT: variable
|
|
RECNMBFM: $0000, RECNMBWA: $0000,
|
|
BYTOFFWA: variable, BYTOFFFM: variable
|
|
RELFIRST: $0000, or $007A or multiple of $007A,
|
|
RELASTP1: $007A, or multiple of $007A,
|
|
RELPREV: variable, CURIOBUF: (val in ADRPRSD) * (parsed length),
|
|
LEN2RDWR: $0000
|
|
(ONEIOBUF = low byte of CURIOBUF),
|
|
RECLENWA = RECLENFM = LENPRSD = val issued as L-parameter.
|
|
Data are transferred from the source buffer to
|
|
the data sector buffer. As the data are
|
|
transferred, the value in CURIOBUF ($B5C3) is incremented
|
|
to point at the next byte in the source buffer.
|
|
Similarly, the value in LEN2RDWR ($B5C1) is decremented.
|
|
When LEN2RDWR=0, all the data have been transferred.
|
|
Each time a byte is transferred to the data sec buf,
|
|
bit 6 of UPFDATFLG ($B5D5) is set to indicate that the data
|
|
sec requires updating on the disk. When the data
|
|
sec buffer is full, it is written to the disk. If
|
|
a new file is being created, or if a pre-existing
|
|
file is being enlarged, the ASNGTKSC ($B244) routine
|
|
is called to find a free data sec. The trk & sec values
|
|
of the data sec are then added to the T/S list. The
|
|
write subfunction can be exited when the data sec buffer
|
|
is not completely, full. However, the close function
|
|
eventually tests the status of the UPDATFLG ($B5D5) flag
|
|
and if necessary, writes the updated data sec & T/S list
|
|
sec buffers to the disk. (P.S. Note that when an
|
|
Applesoft or binary file is being written, it is considered
|
|
to consist of a collection of one-byte long records.)
|
|
.
|
|
.
|
|
(RTS)
|
|
============
|
|
|
|
TOERROP JMP RNGERROP ;Go handle range error.
|
|
(AB1F) ------------ ;(See dis'mbly of errors.)
|
|
|
|
* Return here after doing the write function.
|
|
*(Cause after @ function is done, use stack to get
|
|
* back to the original caller.)
|
|
(A6AB)
|
|
AFTRFUNC BCC FMDRVRTN ;(c) = 0 = no errors.
|
|
LDA RTNCODFM ;Get error code from FM parameter list.
|
|
CMP #5 ;End-of-data error?
|
|
(A6B2) BEQ TOAPPTCH ;Yes. Got a zeroed-out T/S link or a
|
|
;zeroed-out data pair listed in a T/S list.
|
|
;(Not applicable to the write function.)
|
|
(A6B4) JMP OTHRERR ;No. See dis'mbly of errors.
|
|
------------
|
|
|
|
(A6C3)
|
|
FMDRVRTN RTS
|
|
|
|
(B68C) JSR CMDCLOSE ;Close file & then verify the data.
|
|
|
|
(A2EA)
|
|
CMDCLOSE .
|
|
.
|
|
(See dis'mbly of CLOSE command.)
|
|
- USE CLOSE COMMAND TO WRITE LAST DATA AND
|
|
T/S LIST SECTORS TO THE DISK IF NECESSARY
|
|
AND THEN FREE THE DOS BUFFER.
|
|
- Because the file is open, execution flows
|
|
through the CLOSE command twice. The first
|
|
time thru, the matching DOS filename buffer
|
|
is located and then CLOSEONE ($A2FC) is used
|
|
to close the file via the close FUNCTION. The second
|
|
time thru, a matching filename buffer is not found
|
|
because the DOS buffer was released on the first pass.
|
|
Therefore, A5L/H is left pointing at the highest
|
|
numbered (lowest in memory) FREE DOS buffer when
|
|
the CLOSE command is exited via EVENTXIT and CLOSERTS.
|
|
.
|
|
.
|
|
(RTS)
|
|
|
|
(B68F) JMP CMDVERFY ;Go verify the data.
|
|
------------
|
|
|
|
(A27D)
|
|
CMDVERFY LDA #12 ;Verify opcode.
|
|
(A27F) BNE LOKUNLOK ;ALWAYS.
|
|
|
|
(A277)
|
|
LOKUNLOK JSR HNDLCMD1 ;Call part of the main command handler
|
|
;routine to VERIFY the file.
|
|
|
|
* Part of common file manager command handler code.
|
|
(A2AA)
|
|
HNDLCMD1 STA TEMPBYT ;Store opcode in temporary location.
|
|
LDA LENPRSD ;Get L-parameter from parsed table.
|
|
BNE SAVLENFM ;Was a non-zero L-parm issued with cmd?
|
|
LDA LENPRSD+1
|
|
BNE SAVLENFM
|
|
LDA #1 ;Length was 0 so make it 1 instead.
|
|
STA LENPRSD
|
|
SAVLENFM LDA LENPRSD ;Put length in FM parm list.
|
|
STA RECLENFM
|
|
LDA LENPRSD+1
|
|
STA RECLENFM+1
|
|
CLSLOCBF JSR CMDCLOSE ;Close file if it's already open.
|
|
(A2C8)
|
|
|
|
(A2EA)
|
|
CMDCLOSE .
|
|
.
|
|
(See dis'mbly of CMDCLOSE command.)
|
|
.
|
|
.
|
|
- File was just closed, so now the close command
|
|
is simply used to locate a free DOS buffer for
|
|
verification by reading. (A5L/H is left pointing
|
|
at the highest numbered (lowest in memory)
|
|
FREE DOS buffer when CMCLOSE is exited via
|
|
EVENTXIT and CLOSERTS.)
|
|
.
|
|
.
|
|
(RTS)
|
|
|
|
(A2CB) LDA A5L+1 ;Hi byte of A5L/H pointer which points at the highest
|
|
;numbered (lowest in memory) free DOS name buffer (in chain).
|
|
(A2CD) BNE SAVFNPTR ;Branch if found a free buffer.
|
|
(A2CF) JMP NOBUFERR ;Go issue an out-of-buffers message.
|
|
------------ ;(See dis'mbly of errors.)
|
|
|
|
(A2D2)
|
|
SAVFNPTR STA A3L+1 ;Reset A3L/H to point at DOS buffer that we
|
|
LDA A5L ;will use for file name field buffer (chain).
|
|
STA A3L
|
|
(A2D8) JSR CPYPFN
|
|
|
|
* NOTE: This (re)assigns a DOS buffer to the
|
|
* file we want to VERIFY. The buffer may or may not
|
|
* be the same one that was just released by the close cmd
|
|
* above. The highest numbered (lowest in memory) free
|
|
* DOS buffer is used.
|
|
(A743)
|
|
CPYPFN LDY #29 ;30 bytes to copy (0 to 29).
|
|
CPYPRIM LDA PRIMFNBF,Y ;Copy the name of the file wanted from
|
|
STA (A3L),Y ;the primary filename buffer into the
|
|
DEY ;filename field buffer (in DOS chain).
|
|
BPL CPYRIM ;More chars to get.
|
|
(A74D) RTS
|
|
|
|
(A2DB) JSR BUFS2PRM
|
|
|
|
* Get addresses of the various DOS buffers from the
|
|
* chain buffer & put them in the FM parameter list.
|
|
(A74E)
|
|
BUFS2PRM LDY #30 ;Get addr of FM work buf, T/S list
|
|
ADRINPRM LDA (A3L),Y ;buf, data sector buf & next DOS
|
|
STA WRKBUFFM-30,Y ;filename buf from chain
|
|
INY ;pointer buffer & put them in FM parm list.
|
|
CPY #38 ;(P.S. Adr of next DOS file name buf is
|
|
BNE ADRINPRM ;not used by DOS.)
|
|
(A75A) RTS
|
|
|
|
(A2DE) JSR CPY2PARM
|
|
|
|
* Put volume, drive, & slot values plus the
|
|
* address of the primary filename buffer
|
|
* in the FM parameter list.
|
|
(A71A)
|
|
CPY2PARM LDA VOLPRSD ;From parsed table.
|
|
STA VOLFM
|
|
LDA DRVPRSD ;From parsed table.
|
|
STA DRVFM
|
|
LDA SLOTPRSD ;From parsed table.
|
|
STA SLOTFM
|
|
LDA ADRPFNBF ;Get the adr of the primary file
|
|
STA FNAMBUFM ;name buf from the constants tbl
|
|
LDA ADRPFNBF+1 ;and put it in the FM parm list.
|
|
STA FNAMBUFM+1
|
|
LDA A3L ;Save adr of current DOS file name
|
|
STA CURFNADR ;buf in table of DOS variables.
|
|
LDA A3L+1
|
|
STA CURFNADR+1
|
|
(A742) RTS
|
|
|
|
(A2E1) LDA TEMPBYT ;Get verify opcode back from the temporary
|
|
STA OPCODEFM ;buffer & put it in the FM parameter list.
|
|
(A2E7) JMP FMDRIVER
|
|
------------
|
|
|
|
* Use the file manager driver to do the VERIFY function.
|
|
(A6A8)
|
|
FMDRIVER JSR FILEMGR ;Call the file manager to do the function.
|
|
|
|
* File manager proper.
|
|
(AB06)
|
|
FILEMGR TSX ;Save stk pointer so can later rtn to caller of FM.
|
|
STX STKSAV
|
|
(AB0A) JSR RSTRFMWA
|
|
|
|
* Copy FM work buf (in DOS chain) to
|
|
* FM work area (not in DOS chain).
|
|
(AE6A)
|
|
RSTRFMWA JSR SELWKBUF ;Point A4L/H at FM work buf.
|
|
|
|
* Get addr of FM work buff from
|
|
* the FM parm list & put it in
|
|
* the A4L/H pointer.
|
|
(AF08)
|
|
SELWKBUF LDX #0 ;Offset to select
|
|
;work buffer.
|
|
(AF0A) BEQ PT2FMBUF ;ALWAYS.
|
|
|
|
(AF12)
|
|
PT2FMBUF LDA WRKBUFFM,X
|
|
STA A4L
|
|
LDA WRKBUFFM+1,X
|
|
STA A4L+1
|
|
(AF1C) RTS
|
|
|
|
(AE6D) LDY #0 ;Zero out return code in FM parm list to
|
|
STY RTNCODFM ;signal no errors as default condition.
|
|
STORFMWK LDA (A4L),Y ;Copy FM work buf to FM work area.
|
|
STA FMWKAREA,Y
|
|
INY
|
|
CPY #45 ;45 bytes to copy (0 to 44).
|
|
BNE STORFMWK
|
|
CLC ;WHY?????
|
|
(AE7D) RTS
|
|
|
|
(AB0D) LDA OPCODEFM ;Check if opcode is legal.
|
|
CMP #13 ;(Must be less than 13.)
|
|
BCS TOERROP ;Opcode too large so got range error.
|
|
ASL ;Double val of opcode & put it in (x)
|
|
TAX ;so it indexes tables of adrs.
|
|
LDA FMFUNCTB+1,X ;Stick adr of appropriate function
|
|
PHA ;handler on stack (hi byte first).
|
|
LDA FMFUNCTB,X
|
|
PHA
|
|
(AB1E) RTS ;DO STACK JUMP TO FUNCTION ENTRY POINT.
|
|
|
|
.
|
|
.
|
|
(AD18)
|
|
FNVERIFY .
|
|
.
|
|
(See dis'mbly of the VERIFY function.)
|
|
- reads in VTOC & appropriate directory, T/S list
|
|
& data sectors.
|
|
- The verify function simply opens & then reads
|
|
a file. Actual verification occurs deep within
|
|
RWTS. As the data are read in, the disk bytes are
|
|
translated to nibbles & cummulatively EORed
|
|
together to calculate a running checksum.
|
|
If the final calculated checksum agrees
|
|
with the checksum read off the disk, the integrity
|
|
of the data is verified. Failure to verify yeilds
|
|
an I/O error.
|
|
.
|
|
.
|
|
(RTS)
|
|
============
|
|
|
|
TOERROP JMP RNGERROP ;Go handle range error.
|
|
(AB1F) ------------ ;(See dis'mbly of errors.)
|
|
|
|
* Return here after doing the VERIFY function.
|
|
* (Cause after @ function is done, use the
|
|
* stack to get back to the original caller.)
|
|
(A6AB)
|
|
AFTRFUNC BCC FMDRVRTN ;(c) = 0 = no errors.
|
|
LDA RTNCODFM ;Get error code from FM parameter list.
|
|
CMP #5 ;End-of-data error?
|
|
(A6B2) BEQ TOAPPTCH ;Yes. Got a zeroed-out T/S link or a
|
|
;zeroed-out data pair listed in a T/S list.
|
|
;(Not applicable to the verify function.)
|
|
(A6B4) JMP OTHRERR ;No. See dis'mbly of errors.
|
|
------------
|
|
|
|
(A6C3)
|
|
FMDRVRTN RTS
|
|
|
|
(A27A) JMP CMDCLOSE ;Go back into CMDCLOSE to close the
|
|
------------ ;file that was just opened via the
|
|
;verify function.
|
|
|
|
(A2EA)
|
|
CMDCLOSE .
|
|
.
|
|
(See dis'mbly of CLOSE command.)
|
|
.
|
|
.
|
|
(RTS) ;Return to the caller of the BSAVE command.
|
|
============ ;(Normally returns to AFTRCMD ($A17D)
|
|
;located in the cmd parsing and processing
|
|
;routines.)
|
|
|
|
* No Address or Length parameters were
|
|
* issued with the BSAVE comand.
|
|
(A000)
|
|
CKIFCTRL LDA BUF200 ;Is the FIRST character in the input buf
|
|
CMP DCTRLCHR ;equal to DOS'S control character?
|
|
(A006) BEQ CHKIFCR ;Yes.
|
|
|
|
* Was DOS's control character the only
|
|
* character on the line?
|
|
(A00B)
|
|
CHKIFCR LDA BUF200+1 ;Get second char in input buffer.
|
|
CMP #$8D ;Is it a carriage return?
|
|
(A010) BNE PRSYNERR ;Wasn't just DOS's ctrl char (and a <cr>) so
|
|
;go issue a syntax-error message.
|
|
|
|
(A018)
|
|
PRSYNERR JMP SYNTXERR ;Go issue a syntax-error message.
|
|
------------ ;(See dis'mbly of errors.)
|