********************************************************************************
* DOS 3.3 FST from GS/OS System 6.0.1 *
* By Monte Benaresh, Cary Farrier, Jack Valois, and Linda Pan. *
* *
* Copyright 1992 Apple Computer, Inc. *
********************************************************************************
* Disassembly by Andy McFadden, using 6502bench SourceGen v1.7. *
* Last updated 2020/07/30 *
* *
* The OMF binary was converted to a loaded form with SourceGen. *
* *
* Thanks: *
* GS/OS Reference Manual *
* GS/OS Device Driver Reference *
* Programmer's Reference for System 6.0 *
* Brutal Deluxe "GS/OS Internals" *
* Kelvin Sherlock's "So You Want to Write an FST" presentation *
* Beneath Apple DOS *
********************************************************************************
* For path-based calls, DP locations $30-$47 are populated, and pathnames are *
* passed in. For refNum-based calls, DP locations $30-$41 are populated, and *
* VCR/FCR are passed in. *
* *
* The VCR structure is extended to $14 bytes, adding: *
* +$0e (4B) vptr to work buffer *
* +$12 (2B) number of sectors in catalog track *
* *
* The VCR's work buffer holds the catalog track. (Technically, the VTOC and *
* up to 15 catalog sectors, which may or may not live on one track.) *
* *
* The FCR structure is extended to $32 bytes, adding: *
* +$16 (4B) dir: VCR work buf; file: vptr to 512-byte read buffer *
* +$1a (4B) file EOF *
* +$1e (2B) ProDOS auxType *
* +$20 (4B) file mark *
* +$24 (2B) ProDOS file type ($0f/DIR for catalog) *
* +$26 (2B) file type (raw byte, with lock flag cleared) *
* +$28 (4B) number of sectors allocated to file *
* +$2c (2B) dir: current displacement; file: catalog sector with FDE (0-15) *
* +$2e (2B) FDE index within catalog sector (0-7) *
* +$30 (2B) dir: number of entries; file: track/sector of data in buffer *
* *
* The FCR work buffer holds the most-recently-read T/S list sector in the *
* first half. The second half is used as a buffer for the Read routine. The *
* T/S value at FCR +$30 identifies which sector is in the second half. (Not *
* needed for the T/S sector, which is self-identifying.) *
* *
* "FDE" is File Descriptive Entry. This is the structure in the disk catalog *
* that has the file name, type, and the track/sector of the first T/S list. *
* Each catalog sector holds up to 7 FDEs. *
********************************************************************************
O_DOSFDE_TRK .eq $00 {const} ;track of first track/sector list
O_FCR_REF_NUM .eq $00 {const} ;(2B) FCR ref num; set by ALLOC_FCR call
O_VCR_ID .eq $00 {const} ;(2B) VCR ID; set by ALLOC_VCR call
DRIVER_OPEN .eq $01 {const}
E_BAD_SYSTEM_CALL .eq $01 {const} ;bad GS/OS call number
O_DOSCAT_NXTTRK .eq $01 {const} ;track number of next catalog sector
O_DOSTS_NXTTRK .eq $01 {const} ;track number of next T/S list sector
DRIVER_READ .eq $02 {const}
FSID_DOS33 .eq $02 {const}
O_DOSFDE_TYPE .eq $02 {const} ;file type and lock flag
O_FCR_PATH_NAME .eq $02 {const} ;(4B) virtual pointer to file's pathname
O_VCR_NAME .eq $02 {const} ;(4B) virtual pointer to the volume name
O_DOSFDE_NAME .eq $03 {const} ;(30B) file name
E_INVALID_PCOUNT .eq $04 {const} ;parameter count out of range
O_DOSTS_SCTOFF .eq $05 {const} ;(2B) sector offset of first T/S entry
O_FCR_FST_ID .eq $06 {const} ;(2B) FST ID of owning FST
O_VCR_STATUS .eq $06 {const} ;(2B) volume status
O_FCR_VOL_ID .eq $08 {const} ;(2B) VCR ID of volume file resides on
O_VCR_OPEN_CNT .eq $08 {const} ;(2B) number of open files on volume
O_VCR_FST_ID .eq $0a {const} ;(2B) owner ID number
O_DOSCAT_ENTRY .eq $0b {const} ;offset of first File Descriptive Entry
O_DOSTS_LIST .eq $0c {const} ;(244B) T/S pairs; entries may be zero
O_FCR_NEWLINE .eq $0c {const} ;(4B) virtual pointer to list of newline characters
O_VCR_DEV .eq $0c {const} ;(2B) device where volume was last seen
O_VCRX_BUF_VPTR .eq $0e {const} ;(4B) vpointer to 4KB work buffer
O_FCR_NEWLINE_LEN .eq $10 {const} ;(2B) length of newline list
E_INVALID_DEV_NUM .eq $11 {const} ;invalid device number (request)
O_FCR_MASK .eq $12 {const} ;(2B) newline mask ($0000 if mode disabled)
O_VCRX_NUM_CAT_SCT .eq $12 {const} ;(2B) number of sectors in catalog track
O_FCR_ACCESS .eq $14 {const} ;(2B) access mode flags (1=read, 2=write)
GEN_VOL_MAX_LEN .eq 21 {const} ;max length of generated volume name
O_FCRX_BUF_VPTR .eq $16 {const} ;(4B) vpointer to 512-byte read buffer
O_FCRX_EOF .eq $1a {const} ;(4B) file EOF
DOS_MAX_NAME_LEN .eq 30 {const} ;filenames are limited to 30 chars
O_FCRX_AUXTYPE .eq $1e {const} ;(2B) ProDOS file auxType
O_FCRX_MARK .eq $20 {const} ;(4B) file mark
O_DOSFDE_LEN .eq $21 {const} ;length of file, in sectors
E_DRVR_BAD_PARM .eq $22 {const} ;bad call parameter
DOSFDE_SIZE .eq $23 {const} ;size of an FDE entry
O_FCRX_FILE_TYPE .eq $24 {const} ;(2B) ProDOS file type
O_FCRX_DOS_TYPE .eq $26 {const} ;(2B) DOS file type (0=TXT, 1=INT, etc)
O_FCRX_NUM_SCT .eq $28 {const} ;(2B) number of sectors allocated to file
E_DRVR_WR_PROT .eq $2b {const} ;device is write-protected
E_DRVR_BAD_COUNT .eq $2c {const} ;invalid byte count
O_FCRX_CUR_DISP .eq $2c {const} ;dir: current displacement, for GetDirEnt
O_FCRX_FDE_SCT .eq $2c {const} ;(2B) file: catalog sector of FDE entry
E_DRVR_DISK_SW .eq $2e {const} ;disk has been switched
O_FCRX_FDE_INDEX .eq $2e {const} ;(2B) index of FDE within catalog sector
O_FCRX_BUF_TS .eq $30 {const} ;(2B) file: T/S of sector in FCR work buf
O_FCRX_NUM_ENTRIES .eq $30 {const} ;(2B) dir: total number of entries in catalog
O_DIB_DEVICE_ID .eq $34 {const} ;(2B) general type of device
O_VTOC_NUMTRK .eq $34 {const} ;number of tracks per disk
O_VTOC_NUMSCT .eq $35 {const} ;number of sectors per track
O_VTOC_INUSE .eq $38 {const} ;start of in-use bitmap
E_BAD_PATH_SYNTAX .eq $40 {const} ;invalid pathname syntax
E_INVALID_REF_NUM .eq $43 {const} ;invalid reference number
E_VOL_NOT_FOUND .eq $45 {const} ;volume not found
E_FILE_NOT_FOUND .eq $46 {const} ;file not found
E_BAD_FILE_FORMAT .eq $4a {const} ;incompatible file format (a/k/a version error)
E_END_OF_FILE .eq $4c {const} ;end-of-file encountered
E_OUT_OF_RANGE .eq $4d {const} ;position out of range
E_INVALID_ACCESS .eq $4e {const} ;access not allowed
E_BUFF_TOO_SMALL .eq $4f {const} ;buffer too small
E_DIR_ERROR .eq $51 {const} ;directory error
E_UNKNOWN_VOL .eq $52 {const} ;unknown volume type
E_PARM_RANGE_ERR .eq $53 {const} ;parameter out of range
E_OUT_OF_MEM .eq $54 {const} ;out of memory
E_DUP_VOLUME .eq $57 {const} ;duplicate volume name
E_END_OF_DIR .eq $61 {const} ;end of directory has been reached
E_RES_NOT_FOUND .eq $63 {const} ;file does not contain required resource
E_INVALID_FST_OP .eq $65 {const}
drvr_dev_num .eq $00 {addr/2} ;number of device to which call is made
drvr_call_num .eq $02 {addr/2} ;number of call being made
drvr_buf_ptr .eq $04 {addr/4} ;pointer to buffer for reading or writing data
drvr_req_cnt .eq $08 {addr/4} ;number of bytes to transfer to or from driver
drvr_blk_num .eq $10 {addr/4} ;number of block at which to start read or write
drvr_blk_size .eq $14 {addr/2} ;bytes per block for device
drvr_code .eq $16 {addr/2} ;device's FST num / status code / control code
drvr_cache_prio .eq $1a {addr/2} ;sort of caching to implement
drvr_dib_ptr .eq $20 {addr/4} ;pointer to DIB for device
param_blk_ptr .eq $32 {addr/4} ;pointer to user's parameter block
dev1_num .eq $36 {addr/2} ;device number from parameter block
fcr_path1_vptr .eq $3a {addr/4} ;virtual pointer to FCR or path #1
vcr_path2_vptr .eq $3e {addr/4} ;virtual pointer to VCR or path #2
path_flag .eq $42 {addr/2} ;flags for path information
span2 .eq $46 {addr/2} ;max distance between separators for path2
work_buf_ptr1 .eq $80 {addr/4} ;pointer to 4K work buffer
vol_name_ptr .eq $84 {addr/4} ;ptr to class 1 input string with vol name
vcr_ptr .eq $88 {addr/4} ;pointer to VCR
work_buf_ptr2 .eq $8c {addr/4} ;pointer to 4K work buffer
fcr_ptr .eq $90 {addr/4} ;points at an FCR
fcr_ptr2 .eq $94 {addr/4} ;points at an FCR
gbuf_ptr .eq $98 {addr/4} ;points to SYS_GBUF ($00/9a00)
strcmp_arg0 .eq $a0 {addr/4} ;arg to string compare function
strcmp_arg1 .eq $a4 {addr/4} ;arg to string compare function
fde_ptr .eq $a8 {addr/4} ;catalog File Descriptive Entry ptr
gen_vol_src_ptr .eq $ac {addr/2} ;used when generating volume name
file_name_ptr .eq $b2 {addr/2} ;pointer to the filename part of a path
gen_vol_name_ptr .eq $b8 {addr/2} ;pointer to generated volume name
newline_ptr .eq $ba {addr/4} ;pointer to list of newline chars
newline_mask .eq $be {addr/2} ;NewLine char mask; zero if disabled
newline_char .eq $c0 {addr/2} ;NewLine char (when list len == 1)
rslt_cmd_ptr .eq $c2 {addr/2} ;point to result output cmds
strip_counter .eq $c4 {addr/2} ;text_mode strip counter, for Read
DEV_DISPATCHER .eq $01fc00 ;- dispatches call to device driver
ALLOC_SEG .eq $01fc1c ;+ allocates a memory segment; returns v-ptr
RELEASE_SEG .eq $01fc20 ;+ frees block of memory obtained with ALLOC_SEG
ALLOC_VCR .eq $01fc24 ;- allocates a VCR
RELEASE_VCR .eq $01fc28 ;- releases a VCR
ALLOC_FCR .eq $01fc2c ;- allocates an FCR
RELEASE_FCR .eq $01fc30 ;- releases an FCR
DEREF .eq $01fc38 ;+ converts virtual pointer to direct pointer
GET_SYS_GBUF .eq $01fc3c ;- returns a reference to a 1K global buffer
SYS_EXIT .eq $01fc40 ;- return from FST to calling program
FIND_VCR .eq $01fc48 ;- find a VCR by ID or volume name
GET_FCR .eq $01fc64 ;- returns the Nth FCR
LOCK_MEM .eq $01fc68 ;+ locks all segments acquired with ALLOC_SEG
UNLOCK_MEM .eq $01fc6c ;+ unlocks all segments acquired with ALLOC_SEG
MOVE_INFO .eq $01fc70 ;+ moves data between memory buffers
DBG_FST_REMOVE_VCR .eq $fe0358
DBG_FST_APP_ENTRY .eq $ff0500
DBG_FST_SYS_ENTRY .eq $ff0510
.org $020100
;
; Segment 02: Kind=Data; Attrs=Private; Name='main'
;
; -----
;
; FST header.
;
02/0100: 46 53 54 20 Header .str ‘FST ’ ;signature
02/0104: 8c 0a 02 00 .dd4 AppEntry ;app_entry
02/0108: bc 0b 02 00 .dd4 SysEntry ;sys_entry
02/010c: 02 00 .dd2 $0002 ;fst_id ($0002 == DOS 3.3)
02/010e: 03 18 .dd2 $1803 ;attributes (strip hi, read-only, Apple II-specific)
02/0110: 03 01 .dd2 $0103 ;version
02/0112: 00 01 .dd2 256 ;block size (256 bytes / sector)
02/0114: 30 02 00 00 .dd4 560 ;max volume size (35*16=560)
02/0118: 20 00 00 00 .dd4 32 ;min volume size (2*16=32)?
02/011c: ff ff ff ff .dd4 $ffffffff ;max file size
02/0120: 00 00 00 00 .dd4 $00000000 ;reserved
02/0124: 10 41 70 70+ .l1str ‘Apple II DOS 3.3’ ;name
02/0135: 1c 44 4f 53+ .l1str ‘DOS 3.3 FST v01.03’ ;comment
02/0152: 00 00 .dd2 $0000 ;reserved?
; Free-form text blob, not part of the header.
02/0154: 44 4f 53 20+ .str ‘DOS 3.3 FST. Written by Monte Benaresh, maintained by Cary Far’
+ ‘rier and Jack Valois. Tested by Linda Pan. Copyright 1992 Ap’
+ ‘ple Computer’
;
; Global variables.
;
02/01dc: 00 00 00 00 work_buf_vptr .dd4 $00000000 ;vptr to 4KB work buffer
02/01e0: 00 00 dev_num_arg .dd2 $0000
02/01e2: 00 00 wronly_01e2 .dd2 $0000 ;written, never read
02/01e4: 00 00 .fill 2,$00
02/01e6: 00 00 wronly_01e6 .dd2 $0000 ;written, never read (holds an FCR index)
02/01e8: 00 00 fcr_index .dd2 $0000
02/01ea: 00 00 num_cat_sectors .dd2 $0000
02/01ec: 00 00 strcmp_mask .dd2 $0000
02/01ee: 00 00 current_cat_sct .dd2 $0000
02/01f0: 00 00 current_cat_ent .dd2 $0000
02/01f2: 00 00 call_class .dd2 $0000 ;x2: $00=ProDOS 16, $02=GS/OS
02/01f4: 00 00 pcount .dd2 $0000 ;parameter count
02/01f6: 00 00 00 00+ .fill 6,$00
02/01fc: 00 00 file_mark_adj .dd2 $0000
02/01fe: 00 00 newline_len .dd2 $0000 ;length of newline list
02/0200: 00 00 00 00 .fill 4,$00
02/0204: 00 00 00 00 read_end .dd4 $00000000 ;for Read: mark + request count
02/0208: 00 00 00 00 read_pos .dd4 $00000000 ;for Read: adjusted mark
02/020c: 00 00 .fill 2,$00
02/020e: 00 00 track_sect .dd2 $0000 ;a track/sector value
02/0210: 00 00 00 00 read_req_cnt .dd4 $00000000 ;for Read: request count limited by EOF
02/0214: 00 00 sector_offset .dd2 $0000
data_remain_count
02/0216: 00 00 .dd2 $0000
02/0218: 00 00 data_copy_count .dd2 $0000
02/021a: 00 00 00 00 file_mark .dd4 $00000000
02/021e: 00 00 00 00 request_count .dd4 $00000000 ;request count, for Read
02/0222: 00 00 00 00 ts_list_index .dd4 $00000000 ;index of first T/S list entry, for Read
02/0226: 00 00 wronly_0226 .dd2 $0000 ;written, never read
02/0228: 00 00 00 00 txfr_count .dd4 $00000000 ;tranfser count, for Read
02/022c: 00 00 .fill 2,$00
02/022e: 00 00 trk_sct_val .dd2 $0000 ;first data sector in file
02/0230: 00 00 00 00 wronly_0230 .dd4 $00000000 ;written, never read
02/0234: 00 00 00 00 sys_gbuf_ptr .dd4 $00000000 ;vptr to global buffer ($00/9a00, $400 bytes)
02/0238: 00 00 text_mode .dd2 $0000 ;text conversion mode; $0001 means clear high bits
alloc_rsrc_flags
02/023a: 00 00 .dd2 $0000 ;set after alloc of VCR ($8000) and/or FCR ($4000)
02/023c: 00 00 00 00 .fill 4,$00
02/0240: 00 00 error_mod .dd2 $0000 ;$0000 or $8000, ORed with error result
02/0242: 00 00 temp_pcount_max .dd2 $0000
02/0244: 00 00 temp_pcount_min .dd2 $0000
req_vol_name_buf
02/0246: 00 00 00 00+ .fill 50,$00 ;buffer for vol name from path in GS/OS call
req_file_name_buf
02/0278: 00 00 00 00+ .fill 50,$00 ;buffer for file name from path in GS/OS call
gen_vol_name_buf
02/02aa: 00 00 00 00+ .fill 50,$00 ;buffer for vol name generated by FST
gen_vol_colon_buf
02/02dc: 00 00 00 00+ .fill 50,$00 ;buffer for gen vol name, with leading colon
02/030e: 00 00 num_cat_entries .dd2 $0000 ;number of non-deleted entries in catalog
;
; File attributes. Data is copied out of here by the CopyFileParamsOut function
; (+000cb2).
02/0310: 00 00 file_dos_type .dd2 $0000
02/0312: 00 00 file_pro_type .dd2 $0000 ;ProDOS-style file type ($00-ff)
02/0314: 00 00 file_access .dd2 $0000 ;ProDOS-style access flags
02/0316: 00 00 file_auxtype .dd2 $0000 ;aux type (load addr or zero)
02/0318: 00 00 00 00 file_eof .dd4 $00000000 ;file length, in bytes
02/031c: 00 00 file_len_in_sct .dd2 $0000 ;file length, in sectors
02/031e: 00 00 file_entry_num .dd2 $0000 ;index in directory
file_storage_type
02/0320: 01 00 .dd2 $0001 ;$0001 (ordinary file) or $000f (volume dir)
02/0322: 02 00 .dd2 FSID_DOS33 ;hard-coded FST ID
02/0324: 00 00 zero .dd2 $0000 ;zeroes, for fields that are always zero
;
; Handles the GS/OS Open call. The bulk of the implementation is in DoOpen;
; this provides a wrapper that ensures proper clean-up.
;
.rwid longm,longx
02/0326: 9c 56 03 Open stz :saved_err
02/0329: 22 68 fc 01 jsl LOCK_MEM ;lock GS/OS memory
02/032d: 20 cc 1e jsr DoOpen ;do the work
02/0330: 8d 56 03 sta :saved_err ;save the error code
02/0333: ad dc 01 lda work_buf_vptr ;do we still own the work buffer?
02/0336: aa tax ;(global is nulled out when VCR takes ownership)
02/0337: 0d de 01 ora work_buf_vptr+2
02/033a: f0 07 beq :NoAlloc ;no, v-ptr is null, branch
02/033c: ac de 01 ldy work_buf_vptr+2
02/033f: 22 20 fc 01 jsl RELEASE_SEG ;release the work buffer
02/0343: 22 6c fc 01 :NoAlloc jsl UNLOCK_MEM ;unlock GS/OS memory
02/0347: ad 56 03 lda :saved_err ;check error code
02/034a: c9 01 00 cmp #$0001 ;set carry if error code is nonzero
02/034d: 90 03 bcc :Done
02/034f: 0d 40 02 ora error_mod
02/0352: 5c 40 fc 01 :Done jml SYS_EXIT ;return to caller
02/0356: 00 00 :saved_err .fill 2,$00
;
; Handles the GS/OS Volume call.
; (GS/OS ref p.195 and p.409)
;
; Given the name of a block device, returns the volume name, along with some
; other information the volume (total blocks, free blocks, file sys ID).
;
; GS/OS is kind enough to resolve the device name to a number before calling us,
; so we just need to get the volume name and other info.
;
; The ProDOS-16 VOLUME call requires a pointer to a buffer that doesn't have an
; explicit size. None of the references I've seen (GS/OS ref, ProDOS-16 ref)
; document how big the buffer should be. It needs to hold the largest possible
; volume name, prefixed with a ':', but I can't find a documented length limit
; for a GS/OS volume name. ProDOS-8 requires 16 bytes to hold the 15-char
; volume name plus a leading '/', and our volume name is only 13, so we should
; be fine.
;
; On entry:
; $00-01: device number of named block device.
;
• Clear variables
]name_out_ptr .var $b4 {addr/4}
02/0358: 9c 3a 02 Volume stz alloc_rsrc_flags ;clear allocation flags
02/035b: 22 68 fc 01 jsl LOCK_MEM ;lock GS/OS memory
02/035f: 20 95 11 jsr GetDeviceId ;check the device class
02/0362: b0 71 bcs :Fail
02/0364: f0 05 beq :Found525 ;zero means 5.25" drive; branch if so
02/0366: a9 52 00 lda #E_UNKNOWN_VOL ;not DOS 3.3 on a 5.25" disk,
02/0369: 80 6a bra :Fail ; so we don't handle this device
02/036b: ad f2 01 :Found525 lda call_class ;ProDOS-16?
02/036e: f0 24 beq :IsP16 ;yes, branch
02/0370: a0 06 00 ldy #$0006 ;volName pointer field
02/0373: b7 32 lda [param_blk_ptr],y
02/0375: 85 b4 sta ]name_out_ptr ;copy to DP
02/0377: c8 iny
02/0378: c8 iny
02/0379: b7 32 lda [param_blk_ptr],y
02/037b: 85 b6 sta ]name_out_ptr+2
02/037d: a9 18 00 lda #GEN_VOL_MAX_LEN+3 ;want room for at least 25 bytes
02/0380: c7 b4 cmp []name_out_ptr] ;compare to output buffer size
02/0382: 90 0e bcc :SizeOk ;size is okay, branch
02/0384: a9 15 00 lda #GEN_VOL_MAX_LEN ;require 21 bytes for name string
02/0387: a0 02 00 ldy #$0002 ;offset to string length
02/038a: 97 b4 sta []name_out_ptr],y ;tell them how big the buffer must be
02/038c: a9 4f 00 lda #E_BUFF_TOO_SMALL
02/038f: 82 ad 01 brl :Cleanup
02/0392: 80 0d :SizeOk bra :GotVolPtr
02/0394: a0 04 00 :IsP16 ldy #$0004 ;volName field
02/0397: b7 32 lda [param_blk_ptr],y ;get volName parameter
02/0399: 85 b4 sta ]name_out_ptr ;copy to DP
02/039b: c8 iny
02/039c: c8 iny
02/039d: b7 32 lda [param_blk_ptr],y
02/039f: 85 b6 sta ]name_out_ptr+2
02/03a1: a9 aa 02 :GotVolPtr lda #gen_vol_name_buf ;set buffer for the generated volume name
02/03a4: 85 b8 sta gen_vol_name_ptr
02/03a6: a9 00 10 lda #4096 ;want a 4KB chunk
02/03a9: 22 1c fc 01 jsl ALLOC_SEG ;allocate work buffer
02/03ad: 90 06 bcc :AllocOk
02/03af: a9 54 00 lda #E_OUT_OF_MEM
02/03b2: 82 8a 01 brl :Cleanup
02/03b5: 8e dc 01 :AllocOk stx work_buf_vptr ;store the vptr
02/03b8: 8c de 01 sty work_buf_vptr+2
02/03bb: 22 38 fc 01 jsl DEREF ;get a real pointer
02/03bf: 86 80 stx work_buf_ptr1 ;save in DP
02/03c1: 84 82 sty work_buf_ptr1+2
; Buffers and pointers are set up. Generate a volume name from the VTOC.
02/03c3: 20 12 10 jsr ReadVtocGenName ;read VTOC and first cat sect into work buf
02/03c6: 90 11 bcc :VtocOk
02/03c8: c9 22 00 cmp #E_DRVR_BAD_PARM ;bad parameter result?
02/03cb: f0 05 beq :RetUnkVol ;yes, convert to Unknown Volume
02/03cd: c9 2c 00 cmp #E_DRVR_BAD_COUNT ;bad count result?
02/03d0: d0 03 bne :Fail ;no, keep it
02/03d2: a9 52 00 :RetUnkVol lda #E_UNKNOWN_VOL
02/03d5: 38 :Fail sec
02/03d6: 82 66 01 brl :Cleanup
02/03d9: a9 aa 02 :VtocOk lda #gen_vol_name_buf ;set pointer to generated volume name
02/03dc: 85 84 sta vol_name_ptr
02/03de: a9 02 00 lda #^gen_vol_name_buf
02/03e1: 85 86 sta vol_name_ptr+2
02/03e3: a5 00 lda drvr_dev_num ;device number for device with volume
02/03e5: 8d e0 01 sta dev_num_arg ;make sure we scan it first
02/03e8: 20 30 0f jsr GetOrAllocVcr ;get a VCR (by volume name)
02/03eb: 90 67 bcc :GotVcr ;success, branch
02/03ed: c9 57 00 cmp #E_DUP_VOLUME ;failed; was it a "duplicate volume" error?
02/03f0: d0 e3 bne :Fail ;no, give up
02/03f2: a5 42 lda path_flag
02/03f4: 29 00 40 and #$4000 ;is pathname1 ($3a) non-null?
02/03f7: f0 05 beq :NullPath1 ;it's null, branch
02/03f9: a9 57 00 :FailDupVol lda #E_DUP_VOLUME
02/03fc: 80 d7 bra :Fail
; We failed to get a VCR because of an E_DUP_VOLUME failure, and pathname1 is
; null. Copy out the volume name of the thing we found, then report the
; failure. (This appears to be an undocumented mode where you can get a volume
; name without specifying a block device name, probably an internal feature.)
;
; The generated volume name is only valid if we failed because we found a DOS
; 3.3 disk with the same hash but a mismatched VTOC/catalog. Non-DOS volumes
; should have caused a failure earlier.
02/03fe: ad f2 01 :NullPath1 lda call_class ;ProDOS-16?
02/0401: f0 19 beq :IsP16 ;yes, branch
02/0403: b2 b8 lda (gen_vol_name_ptr) ;get string length
02/0405: 1a inc A ;increment it for leading ':'
02/0406: a0 02 00 ldy #$0002
02/0409: 97 b4 sta []name_out_ptr],y ;set string length
02/040b: a5 b4 lda ]name_out_ptr ;get the output pointer
02/040d: 18 clc
02/040e: 69 03 00 adc #$0003 ;advance it by 3 so we line up with P16
02/0411: 85 b4 sta ]name_out_ptr
02/0413: 90 02 bcc :NoInc
02/0415: e6 b6 inc ]name_out_ptr+2
02/0417: a0 02 00 :NoInc ldy #$0002 ;(redundant)
02/041a: 80 05 bra :Cont
02/041c: b2 b8 :IsP16 lda (gen_vol_name_ptr) ;get string length
02/041e: 1a inc A ;increment it for leading '/'
02/041f: 87 b4 sta []name_out_ptr] ;store it; note only the first byte matters
02/0421: e2 20 :Cont sep #$20
.rwid shortm
02/0423: a0 02 00 ldy #$0002 ;src is past length byte, dst is past hole for ':'
02/0426: b1 b8 :CopyLoop lda (gen_vol_name_ptr),y ;get a character
02/0428: f0 05 beq :LoopEnd ;the generated name is null-terminated
02/042a: 97 b4 sta []name_out_ptr],y ;store it
02/042c: c8 iny
02/042d: 10 f7 bpl :CopyLoop ;(always)
02/042f: c2 20 :LoopEnd rep #$20
.rwid longm
02/0431: ad f2 01 lda call_class ;ProDOS-16?
02/0434: d0 0f bne :NotP16 ;no, branch
02/0436: a0 01 00 ldy #$0001 ;fill hole left after length byte
02/0439: b7 b4 lda []name_out_ptr],y
02/043b: 29 00 ff and #$ff00 ;volume names start with a '/', which only really
02/043e: 09 2f 00 ora #‘/’ ; makes sense for ProDOS volumes
02/0441: 97 b4 sta []name_out_ptr],y
02/0443: 80 b4 bra :FailDupVol ;after all that... screw it
02/0445: a0 01 00 :NotP16 ldy #$0001 ;fill hole left after length word
02/0448: b7 b4 lda []name_out_ptr],y ;volume names start with a ':'
02/044a: 29 00 ff and #$ff00
02/044d: 09 3a 00 ora #‘:’
02/0450: 97 b4 sta []name_out_ptr],y
02/0452: 80 a5 bra :FailDupVol
; Back to the "successful call" path.
02/0454: ad f2 01 :GotVcr lda call_class ;ProDOS-16?
02/0457: f0 19 beq :IsP16 ;yes, branch
02/0459: b2 b8 lda (gen_vol_name_ptr) ;get string length
02/045b: 1a inc A ;increment it for leading ':'
02/045c: a0 02 00 ldy #$0002 ;index of length word
02/045f: 97 b4 sta []name_out_ptr],y ;set string length
02/0461: a5 b4 lda ]name_out_ptr ;get the output pointer
02/0463: 18 clc
02/0464: 69 03 00 adc #$0003 ;advance it by 3 so we line up with P16
02/0467: 85 b4 sta ]name_out_ptr
02/0469: 90 02 bcc :NoInc
02/046b: e6 b6 inc ]name_out_ptr+2
02/046d: a0 02 00 :NoInc ldy #$0002 ;(redundant)
02/0470: 80 05 bra :Cont
; Copy the volume name and other parameters out. Hopefully users of the class 0
; made the buffer "big enough".
02/0472: b2 b8 :IsP16 lda (gen_vol_name_ptr) ;get string length
02/0474: 1a inc A ;increment it for leading '/'
02/0475: 87 b4 sta []name_out_ptr]
02/0477: e2 20 :Cont sep #$20
.rwid shortm
02/0479: a0 02 00 ldy #$0002 ;src is past length byte, dst is past hole for ':'
02/047c: b1 b8 :CopyLoop2 lda (gen_vol_name_ptr),y ;copy bytes
02/047e: f0 05 beq :CopyDone2 ;stop when null termination reached
02/0480: 97 b4 sta []name_out_ptr],y
02/0482: c8 iny
02/0483: 10 f7 bpl :CopyLoop2 ;(always)
02/0485: c2 20 :CopyDone2 rep #$20
.rwid longm
02/0487: ad f2 01 lda call_class ;ProDOS-16?
02/048a: d0 45 bne :NotP16 ;no, branch
; Fill out remaining class 0 (ProDOS-16) return values.
02/048c: a0 01 00 ldy #$0001
02/048f: b7 b4 lda []name_out_ptr],y
02/0491: 29 00 ff and #$ff00
02/0494: 09 2f 00 ora #‘/’ ;start volume name with a '/'
02/0497: 97 b4 sta []name_out_ptr],y
02/0499: a0 35 00 ldy #O_VTOC_NUMSCT ;get VTOC entry for number of sectors
02/049c: b7 8c lda [work_buf_ptr2],y ; per track
02/049e: 29 20 00 and #$0020 ;is it 32?
02/04a1: d0 05 bne :Is32Sect ;yes, branch
02/04a3: a9 30 02 lda #560 ;560 sectors per disk
02/04a6: 80 03 bra :Cont
02/04a8: a9 40 06 :Is32Sect lda #1600 ;1600 sectors per disk (assuming 50 tracks)
02/04ab: a0 08 00 :Cont ldy #$0008 ;totalBlocks field
02/04ae: 97 32 sta [param_blk_ptr],y ;store value
02/04b0: c8 iny
02/04b1: c8 iny
02/04b2: a9 00 00 lda #$0000
02/04b5: 97 32 sta [param_blk_ptr],y
02/04b7: 20 76 05 jsr CalcFreeSpace ;compute number of free sectors
02/04ba: a0 0c 00 ldy #$000c ;freeBlocks field
02/04bd: 97 32 sta [param_blk_ptr],y ;store value
02/04bf: c8 iny
02/04c0: c8 iny
02/04c1: a9 00 00 lda #$0000
02/04c4: 97 32 sta [param_blk_ptr],y
02/04c6: a9 02 00 lda #FSID_DOS33
02/04c9: a0 10 00 ldy #$0010 ;fileSysId field
02/04cc: 97 32 sta [param_blk_ptr],y
02/04ce: 18 clc
02/04cf: 80 6e bra :Cleanup ;all done
; Fill out remaining class 1 (GS/OS) return values.
;
; BUG? There are branches to the cleanup code that don't clear the carry or set
; A-reg to zero. It looks like the last instruction that affected the carry was
; at +000367, when adding 3 to the name pointer. If that happened to cross a
; bank boundary, the carry flag will be set, and calls will appear to fail.
02/04d1: a0 01 00 :NotP16 ldy #$0001
02/04d4: b7 b4 lda []name_out_ptr],y
02/04d6: 29 00 ff and #$ff00
02/04d9: 09 3a 00 ora #‘:’ ;volume names start with ':', so put one in the
02/04dc: 97 b4 sta []name_out_ptr],y ; gap we left
02/04de: ad f4 01 lda pcount ;get parameter count (2-6)
02/04e1: 8d 74 05 sta :pcount_ctr
02/04e4: ce 74 05 dec :pcount_ctr ;decrement 3x
02/04e7: ce 74 05 dec :pcount_ctr
02/04ea: ce 74 05 dec :pcount_ctr
02/04ed: 30 50 bmi :Cleanup ;if pCount was 2, we're done (BUG? A-reg nonzero)
02/04ef: a0 35 00 ldy #O_VTOC_NUMSCT
02/04f2: b7 8c lda [work_buf_ptr2],y ;get number of sectors per track from VTOC
02/04f4: 29 20 00 and #$0020 ;is it 16 or 32?
02/04f7: d0 05 bne :Is32Sect
02/04f9: a9 30 02 lda #560 ;16 sectors * 35 tracks = 560
02/04fc: 80 03 bra :Cont
02/04fe: a9 40 06 :Is32Sect lda #1600 ;32 sectors * 50 tracks = 1600
02/0501: a0 0a 00 :Cont ldy #$000a ;totalBlocks field
02/0504: 97 32 sta [param_blk_ptr],y
02/0506: c8 iny
02/0507: c8 iny
02/0508: a9 00 00 lda #$0000
02/050b: 97 32 sta [param_blk_ptr],y
02/050d: ce 74 05 dec :pcount_ctr ;done yet?
02/0510: 30 2d bmi :Cleanup
02/0512: 20 76 05 jsr CalcFreeSpace ;calculate free sectors
02/0515: a0 0e 00 ldy #$000e ;freeBlocks field
02/0518: 97 32 sta [param_blk_ptr],y
02/051a: c8 iny
02/051b: c8 iny
02/051c: a9 00 00 lda #$0000
02/051f: 97 32 sta [param_blk_ptr],y
02/0521: ce 74 05 dec :pcount_ctr ;done yet?
02/0524: 30 19 bmi :Cleanup
02/0526: a9 02 00 lda #FSID_DOS33
02/0529: a0 12 00 ldy #$0012 ;fileSysId field
02/052c: 97 32 sta [param_blk_ptr],y
02/052e: ce 74 05 dec :pcount_ctr ;done yet?
02/0531: 30 0c bmi :Cleanup ;(BUG? A-reg nonzero)
02/0533: a9 00 01 lda #256 ;256 bytes/sector
02/0536: a0 14 00 ldy #$0014 ;blockSize field
02/0539: 97 32 sta [param_blk_ptr],y
02/053b: 18 clc ;(the code above should branch here instead)
02/053c: a9 00 00 lda #$0000
; Clean up after success or failure. Carry flag and A-reg returned to caller.
02/053f: 48 :Cleanup pha ;preserve A-reg
02/0540: 08 php ;preserve carry flag
02/0541: 90 16 bcc :CleanSuccess
02/0543: 2c 3a 02 bit alloc_rsrc_flags ;failed; did we allocate a VCR?
02/0546: 10 11 bpl :CleanSuccess ;no, nothing to free
02/0548: a0 08 00 ldy #O_VCR_OPEN_CNT
02/054b: a9 00 00 lda #$0000
02/054e: 97 88 sta [vcr_ptr],y ;set the open count to zero
02/0550: a0 00 00 ldy #O_VCR_ID
02/0553: b7 88 lda [vcr_ptr],y ;get the VCR ID
02/0555: 22 28 fc 01 jsl RELEASE_VCR ;release it
02/0559: ad dc 01 :CleanSuccess lda work_buf_vptr ;do we need to free the work buffer?
02/055c: aa tax
02/055d: 0d de 01 ora work_buf_vptr+2
02/0560: f0 07 beq :NotAlloc ;vptr is null, nothing to do
02/0562: ac de 01 ldy work_buf_vptr+2
02/0565: 22 20 fc 01 jsl RELEASE_SEG ;release work buffer
02/0569: 28 :NotAlloc plp ;restore carry
02/056a: 68 pla ;restore A-reg
02/056b: 90 03 bcc :NoErr
02/056d: 0d 40 02 ora error_mod
02/0570: 5c 40 fc 01 :NoErr jml SYS_EXIT
02/0574: 00 00 :pcount_ctr .fill 2,$00
;
; Calculates the number of free sectors. Used by the Volume call.
;
; On entry:
; VTOC in work buffer
;
; On exit:
; A-reg: number of free blocks (0-560 or 0-1600)
;
02/0576: a0 35 00 CalcFreeSpace ldy #O_VTOC_NUMSCT
02/0579: b7 8c lda [work_buf_ptr2],y ;get number of sectors per track
02/057b: 29 ff 00 and #$00ff ;16 or 32
02/057e: 4a lsr A
02/057f: 4a lsr A
02/0580: 4a lsr A
02/0581: 4a lsr A ;divide by 16 (now 1 or 2)
02/0582: 8d cc 05 sta :words_per_track
02/0585: 9c ce 05 stz :free_blocks
02/0588: a0 34 00 ldy #O_VTOC_NUMTRK
02/058b: b7 8c lda [work_buf_ptr2],y ;get number of tracks per disk
02/058d: 29 ff 00 and #$00ff ;35 or 50
02/0590: 8d d0 05 sta :num_tracks
02/0593: ad cc 05 :NextTrack lda :words_per_track
02/0596: 8d d2 05 sta :word_count ;init counter to 1 or 2
02/0599: ad d0 05 lda :num_tracks
02/059c: 3a dec A ;count down tracks
02/059d: 30 29 bmi :Done
02/059f: 8d d0 05 sta :num_tracks
02/05a2: 0a asl A
02/05a3: 0a asl A ;multiply track number x4
02/05a4: 69 38 00 adc #O_VTOC_INUSE ;add offset of sector in-use bitmap
02/05a7: a8 tay
02/05a8: b7 8c :Loop32 lda [work_buf_ptr2],y ;get 16 bits of in-use data
02/05aa: 8d d4 05 sta :tmp_bits
02/05ad: ad ce 05 lda :free_blocks ;get current count
02/05b0: a2 10 00 ldx #16
02/05b3: 0e d4 05 :BitLoop asl :tmp_bits ;shift a bit into carry flag
02/05b6: 69 00 00 adc #$0000 ;increment block count if set
02/05b9: ca dex
02/05ba: d0 f7 bne :BitLoop ;continue through all bits
02/05bc: 8d ce 05 sta :free_blocks ;save updated value
02/05bf: ce d2 05 dec :word_count ;do we need to do the other 16 sectors?
02/05c2: f0 cf beq :NextTrack ;no, branch
02/05c4: c8 iny ;yes, advance to next set of 16 bits
02/05c5: c8 iny
02/05c6: 80 e0 bra :Loop32
02/05c8: ad ce 05 :Done lda :free_blocks
02/05cb: 60 rts
:words_per_track
02/05cc: 00 00 .fill 2,$00
02/05ce: 00 00 :free_blocks .fill 2,$00
02/05d0: 00 00 :num_tracks .fill 2,$00
02/05d2: 00 00 :word_count .fill 2,$00
02/05d4: 00 00 :tmp_bits .fill 2,$00
;
; Handles the GS/OS GetFileInfo call.
; (p.154 and p.389)
;
• Clear variables
02/05d6: 9c 30 02 GetFileInfo stz wronly_0230
02/05d9: 9c 32 02 stz wronly_0230+2
02/05dc: 22 68 fc 01 jsl LOCK_MEM ;lock GS/OS memory
02/05e0: 20 7e 0c jsr PathCallSetup ;split path, get DOS catalog, etc.
02/05e3: 90 03 bcc :PathOk
02/05e5: 82 fe 00 brl :Cleanup
02/05e8: 9c 08 07 :PathOk stz :wronly_0708
02/05eb: a0 0e 00 ldy #O_VCRX_BUF_VPTR
02/05ee: b7 88 lda [vcr_ptr],y ;get vptr to work buffer
02/05f0: aa tax
02/05f1: c8 iny
02/05f2: c8 iny
02/05f3: b7 88 lda [vcr_ptr],y
02/05f5: a8 tay
02/05f6: 22 38 fc 01 jsl DEREF ;convert to real pointer
02/05fa: 86 8c stx work_buf_ptr2 ;copy to DP
02/05fc: 84 8e sty work_buf_ptr2+2
02/05fe: a0 12 00 ldy #O_VCRX_NUM_CAT_SCT
02/0601: b7 88 lda [vcr_ptr],y ;get number of sectors in catalog track
02/0603: 8d ea 01 sta num_cat_sectors ;store
02/0606: b2 b2 lda (file_name_ptr) ;requesting info on volume dir?
02/0608: d0 2a bne :NotVolDir ;no, branch
; Set up some results for volume dir info.
02/060a: 9c 16 03 stz file_auxtype ;auxType is zero
02/060d: 9c 1a 03 stz file_eof+2 ;high byte of EOF is zero
02/0610: 9c 1c 03 stz file_len_in_sct
02/0613: ad ea 01 lda num_cat_sectors ;get number of sectors in catalog
02/0616: 1a inc A ;add one for VTOC
02/0617: ae f2 01 ldx call_class ;ProDOS-16?
02/061a: d0 03 bne :NotP16 ;no, branch
02/061c: 8d 1c 03 sta file_len_in_sct ;save value as length in sectors
02/061f: eb :NotP16 xba ;multiply by 256 (number of bytes in a sector)
02/0620: 8d 18 03 sta file_eof ;store that as EOF
02/0623: a9 0f 00 lda #$000f
02/0626: 8d 12 03 sta file_pro_type ;set file type to DIR
02/0629: 8d 20 03 sta file_storage_type ;set storage type to volume dir
02/062c: a9 c3 00 lda #$00c3 ;access flags: destroy/rename/write/read (unlocked)
02/062f: 8d 14 03 sta file_access
02/0632: 80 38 bra :PrepCopy ;copy params out
02/0634: a0 08 00 :NotVolDir ldy #O_VCR_OPEN_CNT
02/0637: b7 88 lda [vcr_ptr],y ;get the open count
02/0639: f0 05 beq :NoneOpen ;zero, file can't be open
02/063b: 20 3f 12 jsr FindOpenFile
02/063e: 90 74 bcc :FoundOpenFile
02/0640: 20 a7 15 :NoneOpen jsr FindFileByName
02/0643: 90 06 bcc :FoundFile
02/0645: a9 51 00 lda #E_DIR_ERROR ;unexpectedly found blank entry
02/0648: 82 9b 00 brl :Cleanup
02/064b: ad f0 01 :FoundFile lda current_cat_ent ;did we find the FDE?
02/064e: 10 06 bpl :GotFde ;yes, branch
02/0650: a9 46 00 lda #E_FILE_NOT_FOUND
02/0653: 82 90 00 brl :Cleanup
02/0656: ad 34 02 :GotFde lda sys_gbuf_ptr ;use global buffer
02/0659: 85 98 sta gbuf_ptr
02/065b: ad 36 02 lda sys_gbuf_ptr+2
02/065e: 85 9a sta gbuf_ptr+2
02/0660: a9 02 00 lda #DRIVER_READ
02/0663: 85 02 sta drvr_call_num
02/0665: 20 80 16 jsr GetFileAttrs ;get file attributes
02/0668: 90 02 bcc :PrepCopy
02/066a: 80 79 bra :Cleanup1
02/066c: ad f2 01 :PrepCopy lda call_class ;ProDOS-16?
02/066f: d0 21 bne L20692 ;no, branch
02/0671: a9 01 00 lda #$0001
02/0674: 8d f4 01 sta pcount ;set pCount to 1 for result copier
02/0677: ad 12 03 lda file_pro_type
02/067a: c9 0f 00 cmp #$000f ;DIR?
02/067d: d0 06 bne :NotDir
02/067f: a9 30 02 lda #560 ;number of sectors on 140K floppy
02/0682: 8d 16 03 sta file_auxtype ;save as auxtype
02/0685: a9 d0 0d :NotDir lda #parm_c0_gfi
02/0688: 85 c2 sta rslt_cmd_ptr
02/068a: 20 b2 0d jsr CopyFileParamsOut ;copy the parameters out
02/068d: a9 00 00 lda #$0000 ;success
02/0690: 80 54 bra :Cleanup
02/0692: a9 ea 0d L20692 lda #parm_c1_gfi
02/0695: 85 c2 sta rslt_cmd_ptr
02/0697: 20 b2 0d jsr CopyFileParamsOut ;copy the parameters out
02/069a: ad f4 01 lda pcount
02/069d: c9 08 00 cmp #$0008 ;pCount < 8?
02/06a0: 90 0d bcc :Success ;yes, bail
02/06a2: a0 20 00 ldy #$0020 ;optionList field
02/06a5: 20 1f 16 jsr OutputOptionList ;generate an option list
02/06a8: 90 05 bcc :Success
02/06aa: a9 4f 00 lda #E_BUFF_TOO_SMALL
02/06ad: 80 37 bra :Cleanup
02/06af: a9 00 00 :Success lda #$0000 ;return with no error
02/06b2: 80 32 bra :Cleanup
02/06b4: a0 14 00 :FoundOpenFile ldy #O_FCR_ACCESS
02/06b7: b7 94 lda [fcr_ptr2],y ;get access mode
02/06b9: 8d 14 03 sta file_access
02/06bc: a0 1a 00 ldy #O_FCRX_EOF
02/06bf: b7 94 lda [fcr_ptr2],y ;get file length
02/06c1: 8d 18 03 sta file_eof
02/06c4: c8 iny
02/06c5: c8 iny
02/06c6: b7 94 lda [fcr_ptr2],y
02/06c8: 8d 1a 03 sta file_eof+2
02/06cb: a0 1e 00 ldy #O_FCRX_AUXTYPE
02/06ce: b7 94 lda [fcr_ptr2],y ;get file's auxType
02/06d0: 8d 16 03 sta file_auxtype
02/06d3: a0 24 00 ldy #O_FCRX_FILE_TYPE
02/06d6: b7 94 lda [fcr_ptr2],y ;get file's ProDOS-equivalent type
02/06d8: 8d 12 03 sta file_pro_type
02/06db: a0 28 00 ldy #O_FCRX_NUM_SCT
02/06de: b7 94 lda [fcr_ptr2],y ;get file length, in sectors
02/06e0: 8d 1c 03 sta file_len_in_sct
02/06e3: 80 87 bra :PrepCopy ;do the copy
02/06e5: 38 :Cleanup1 sec ;(?)
02/06e6: 48 :Cleanup pha ;preserve exit status
02/06e7: ad dc 01 lda work_buf_vptr ;is the work buffer allocated?
02/06ea: aa tax
02/06eb: 0d de 01 ora work_buf_vptr+2
02/06ee: f0 07 beq :NoAlloc ;no, branch
02/06f0: ac de 01 ldy work_buf_vptr+2
02/06f3: 22 20 fc 01 jsl RELEASE_SEG ;release the work buffer
02/06f7: 22 6c fc 01 :NoAlloc jsl UNLOCK_MEM ;release GS/OS memory
02/06fb: 68 pla ;restore exit status
02/06fc: c9 01 00 cmp #$0001 ;set carry if A-reg nonzero
02/06ff: 90 03 bcc :Exit
02/0701: 0d 40 02 ora error_mod
02/0704: 5c 40 fc 01 :Exit jml SYS_EXIT
02/0708: 00 00 :wronly_0708 .fill 2,$00 ;written, never read
;
; Handles the GS/OS GetDirEntry call. The bulk of the implementation is in
; DoGetDirEntry; this allocates some memory, sets some pointers, and ensures
; proper cleanup.
;
02/070a: 22 68 fc 01 GetDirEntry jsl LOCK_MEM ;lock GS/OS memory
02/070e: a6 3a ldx fcr_path1_vptr ;get FCR vptr
02/0710: a4 3c ldy fcr_path1_vptr+2
02/0712: 22 38 fc 01 jsl DEREF ;convert to real ptr
02/0716: 86 90 stx fcr_ptr ;copy to DP
02/0718: 84 92 sty fcr_ptr+2
02/071a: a6 3e ldx vcr_path2_vptr ;get VCR vptr
02/071c: a4 40 ldy vcr_path2_vptr+2
02/071e: 22 38 fc 01 jsl DEREF ;convert to real ptr
02/0722: 86 88 stx vcr_ptr ;copy to DP
02/0724: 84 8a sty vcr_ptr+2
02/0726: a9 00 10 lda #4096
02/0729: 22 1c fc 01 jsl ALLOC_SEG ;allocate 4K work buffer to hold catalog track
02/072d: 90 04 bcc :MemOk
02/072f: a9 54 00 lda #E_OUT_OF_MEM
02/0732: 60 rts ;(BUG? should be JML SYS_EXIT)
02/0733: 8e dc 01 :MemOk stx work_buf_vptr ;save vpointer
02/0736: 8c de 01 sty work_buf_vptr+2
02/0739: 22 38 fc 01 jsl DEREF ;get real pointer
02/073d: 86 80 stx work_buf_ptr1 ;copy to DP
02/073f: 84 82 sty work_buf_ptr1+2
02/0741: ad 34 02 lda sys_gbuf_ptr ;copy GBUF pointer to DP
02/0744: 85 98 sta gbuf_ptr
02/0746: ad 36 02 lda sys_gbuf_ptr+2
02/0749: 85 9a sta gbuf_ptr+2
02/074b: ad f2 01 lda call_class ;(unused... vestigial?)
02/074e: 20 07 17 jsr DoGetDirEntry ;do the actual work
02/0751: 48 pha ;preserve error code
02/0752: ad dc 01 lda work_buf_vptr ;do we need to free the work buffer?
02/0755: aa tax
02/0756: 0d de 01 ora work_buf_vptr+2
02/0759: f0 07 beq :NoFree ;vptr is null, nothing to free
02/075b: ac de 01 ldy work_buf_vptr+2
02/075e: 22 20 fc 01 jsl RELEASE_SEG ;free the work buffer
02/0762: 22 6c fc 01 :NoFree jsl UNLOCK_MEM ;unlock GS/OS memory
02/0766: 68 pla ;restore error code
02/0767: c9 01 00 cmp #$0001 ;set carry if error code is nonzero
02/076a: 90 03 bcc :Done
02/076c: 0d 40 02 ora error_mod
02/076f: 5c 40 fc 01 :Done jml SYS_EXIT
;
; Handles the GS/OS Read call. The bulk of the implementation is in DoRead;
; this provides a wrapper that ensures proper clean-up.
;
02/0773: 22 68 fc 01 Read jsl LOCK_MEM ;lock GS/OS memory
02/0777: a6 3a ldx fcr_path1_vptr ;get vptr to FCR
02/0779: a4 3c ldy fcr_path1_vptr+2
02/077b: 22 38 fc 01 jsl DEREF ;convert to real ptr
02/077f: 86 90 stx fcr_ptr ;copy to DP
02/0781: 84 92 sty fcr_ptr+2
02/0783: a6 3e ldx vcr_path2_vptr ;get vptr to VCR
02/0785: a4 40 ldy vcr_path2_vptr+2
02/0787: 22 38 fc 01 jsl DEREF ;convert to real ptr
02/078b: 86 88 stx vcr_ptr ;copy to DP
02/078d: 84 8a sty vcr_ptr+2
02/078f: a0 0e 00 ldy #O_VCRX_BUF_VPTR
02/0792: b7 88 lda [vcr_ptr],y ;get 4K work buffer
02/0794: aa tax
02/0795: c8 iny
02/0796: c8 iny
02/0797: b7 88 lda [vcr_ptr],y
02/0799: a8 tay
02/079a: 22 38 fc 01 jsl DEREF ;convert to real ptr
02/079e: 86 8c stx work_buf_ptr2 ;copy to DP
02/07a0: 84 8e sty work_buf_ptr2+2
02/07a2: a0 2e 00 ldy #O_FCRX_FDE_INDEX
02/07a5: b7 90 lda [fcr_ptr],y ;get FDE index
02/07a7: aa tax
02/07a8: a0 2c 00 ldy #O_FCRX_FDE_SCT
02/07ab: b7 90 lda [fcr_ptr],y ;get FDE sector
02/07ad: 20 bb 0e jsr SetFdePtr ;set up pointer to FDE entry
02/07b0: 20 e6 19 jsr DoRead ;do the work
02/07b3: 48 pha ;preserve error code
02/07b4: 08 php ; and error status
02/07b5: 22 6c fc 01 jsl UNLOCK_MEM ;unlock GS/OS memory
02/07b9: 28 plp ;restore error status
02/07ba: 68 pla
02/07bb: 90 03 bcc :Okay
02/07bd: 0d 40 02 ora error_mod
02/07c0: 5c 40 fc 01 :Okay jml SYS_EXIT
;
; Handles the GS/OS Close call.
; (p.101 and p.268)
;
02/07c4: 22 68 fc 01 Close jsl LOCK_MEM ;lock GS/OS memory
02/07c8: a6 3a ldx fcr_path1_vptr ;get FCR vpointer
02/07ca: a4 3c ldy fcr_path1_vptr+2
02/07cc: 22 38 fc 01 jsl DEREF ;get real pointer
02/07d0: 86 90 stx fcr_ptr ;copy to DP
02/07d2: 84 92 sty fcr_ptr+2
02/07d4: a6 3e ldx vcr_path2_vptr ;get VCR vpointer
02/07d6: a4 40 ldy vcr_path2_vptr+2
02/07d8: 22 38 fc 01 jsl DEREF ;get real pointer
02/07dc: 86 88 stx vcr_ptr
02/07de: 84 8a sty vcr_ptr+2
02/07e0: a0 24 00 ldy #O_FCRX_FILE_TYPE
02/07e3: b7 90 lda [fcr_ptr],y ;get file type
02/07e5: c9 0f 00 cmp #$000f ;DIR (volume directory)?
02/07e8: f0 0f beq :IsCat ;yes, don't free the read buffer (shared with VCR)
02/07ea: a0 16 00 ldy #O_FCRX_BUF_VPTR
02/07ed: b7 90 lda [fcr_ptr],y ;get vptr to sector read buffer
02/07ef: aa tax
02/07f0: c8 iny
02/07f1: c8 iny
02/07f2: b7 90 lda [fcr_ptr],y
02/07f4: a8 tay
02/07f5: 22 20 fc 01 jsl RELEASE_SEG ;free it
02/07f9: a0 00 00 :IsCat ldy #O_FCR_REF_NUM
02/07fc: b7 90 lda [fcr_ptr],y ;get FCR reference number
02/07fe: 22 30 fc 01 jsl RELEASE_FCR ;free it
02/0802: 90 05 bcc :FcrFreeOk
02/0804: a9 43 80 lda #E_INVALID_REF_NUM-$8000
02/0807: 80 13 bra :Cleanup
02/0809: a0 08 00 :FcrFreeOk ldy #O_VCR_OPEN_CNT
02/080c: b7 88 lda [vcr_ptr],y ;get open count
02/080e: d0 05 bne :StuffOpen ;nonzero as expected, branch
02/0810: a9 40 1f lda #$1f40 ;return an internal WTF code
02/0813: 80 07 bra :Cleanup
02/0815: 3a :StuffOpen dec A ;reduce open count by 1
02/0816: 97 88 sta [vcr_ptr],y
02/0818: 18 clc ;success
02/0819: a9 00 00 lda #$0000
02/081c: 48 :Cleanup pha
02/081d: 08 php
02/081e: 22 6c fc 01 jsl UNLOCK_MEM ;release GS/OS memory
02/0822: 28 plp
02/0823: 68 pla
02/0824: 90 03 bcc :Done
02/0826: 0d 40 02 ora error_mod
02/0829: 5c 40 fc 01 :Done jml SYS_EXIT
;
; Handles the GS/OS GetDevNumber call.
;
; Given a device name or volume name, returns the device number.
;
02/082d: 22 68 fc 01 GetDevNumber jsl LOCK_MEM ;lock GS/OS buffers
02/0831: a5 36 lda dev1_num ;get device number
02/0833: f0 0f beq :FindDev ;not known, go fish
02/0835: a0 06 00 :SetRet ldy #$0006 ;devNum field (class 1)
02/0838: ae f2 01 ldx call_class ;ProDOS-16?
02/083b: d0 03 bne :NotP16 ;no, branch
02/083d: a0 04 00 ldy #$0004 ;devNum field (class 0)
02/0840: 97 32 :NotP16 sta [param_blk_ptr],y ;store it in output field
02/0842: 80 17 bra :Success ;finish up
02/0844: 20 7e 0c :FindDev jsr PathCallSetup ;resolve the volume name
02/0847: 90 0b bcc :PathOk
02/0849: c9 57 00 cmp #E_DUP_VOLUME ;was it a "duplicate volume" error?
02/084c: d0 03 bne :NotDupVol
02/084e: a9 52 00 lda #E_UNKNOWN_VOL ;yes, rewrite as "unknown volume"
02/0851: 38 :NotDupVol sec
02/0852: 80 0b bra :Cleanup
02/0854: a0 0c 00 :PathOk ldy #O_VCR_DEV
02/0857: b7 88 lda [vcr_ptr],y ;get device num from VCR
02/0859: 80 da bra :SetRet ;write to parm block
02/085b: 18 :Success clc ;report success
02/085c: a9 00 00 lda #$0000
02/085f: 48 :Cleanup pha ;preserve return status
02/0860: 08 php
02/0861: ad dc 01 lda work_buf_vptr ;release work buffer if allocated
02/0864: aa tax
02/0865: 0d de 01 ora work_buf_vptr+2
02/0868: f0 07 beq :NoBuf
02/086a: ac de 01 ldy work_buf_vptr+2
02/086d: 22 20 fc 01 jsl RELEASE_SEG ;release it
02/0871: 22 6c fc 01 :NoBuf jsl UNLOCK_MEM ;unlock GS/OS buffers
02/0875: 28 plp
02/0876: 68 pla
02/0877: 90 03 bcc :NoErr
02/0879: 0d 40 02 ora error_mod
02/087c: 5c 40 fc 01 :NoErr jml SYS_EXIT
;
; Handles the GS/OS GetEOF call.
; (p.153 and p.388)
;
02/0880: 22 68 fc 01 GetEof jsl LOCK_MEM ;lock GS/OS memory
02/0884: a6 3a ldx fcr_path1_vptr ;get vpointer to FCR
02/0886: a4 3c ldy fcr_path1_vptr+2
02/0888: 22 38 fc 01 jsl DEREF ;get real pointer
02/088c: 86 90 stx fcr_ptr ;copy to DP
02/088e: 84 92 sty fcr_ptr+2
02/0890: a0 1a 00 ldy #O_FCRX_EOF
02/0893: b7 90 lda [fcr_ptr],y ;get file EOF
02/0895: aa tax
02/0896: c8 iny
02/0897: c8 iny
02/0898: b7 90 lda [fcr_ptr],y
02/089a: 48 pha
02/089b: ad f2 01 lda call_class ;ProDOS-16?
02/089e: f0 0d beq :IsP16 ;yes, branch
02/08a0: 68 pla
02/08a1: a0 06 00 ldy #$0006 ;eof field (+2)
02/08a4: 97 32 sta [param_blk_ptr],y ;set output value
02/08a6: 8a txa
02/08a7: 88 dey
02/08a8: 88 dey
02/08a9: 97 32 sta [param_blk_ptr],y
02/08ab: 80 0b bra :Success
02/08ad: 68 :IsP16 pla
02/08ae: a0 04 00 ldy #$0004 ;eof field (+2)
02/08b1: 97 32 sta [param_blk_ptr],y ;set output value
02/08b3: 8a txa
02/08b4: 88 dey
02/08b5: 88 dey
02/08b6: 97 32 sta [param_blk_ptr],y
02/08b8: 18 :Success clc ;clear the carry
02/08b9: a9 00 00 lda #$0000 ;set the acc
02/08bc: 48 pha ;preserve both
02/08bd: 08 php
02/08be: 22 6c fc 01 jsl UNLOCK_MEM ;unlock GS/OS memory
02/08c2: 28 plp ;restore the cleared carry
02/08c3: 68 pla ;restore $0000
02/08c4: 90 03 bcc :Done ;(always)
02/08c6: 0d 40 02 ora error_mod
02/08c9: 5c 40 fc 01 :Done jml SYS_EXIT
;
; Handles the GS/OS GetMark call.
; (p.161 and p.392)
;
02/08cd: 22 68 fc 01 GetMark jsl LOCK_MEM ;lock GS/OS memory
02/08d1: a6 3a ldx fcr_path1_vptr ;get FCR vptr
02/08d3: a4 3c ldy fcr_path1_vptr+2
02/08d5: 22 38 fc 01 jsl DEREF ;get real pointer
02/08d9: 86 90 stx fcr_ptr ;save in DP
02/08db: 84 92 sty fcr_ptr+2
02/08dd: a0 20 00 ldy #O_FCRX_MARK
02/08e0: b7 90 lda [fcr_ptr],y ;get mark
02/08e2: aa tax
02/08e3: c8 iny
02/08e4: c8 iny
02/08e5: b7 90 lda [fcr_ptr],y
02/08e7: 48 pha
02/08e8: ad f2 01 lda call_class ;ProDOS-16?
02/08eb: f0 0d beq :IsP16 ;yes, branch
02/08ed: 68 pla
02/08ee: a0 06 00 ldy #$0006
02/08f1: 97 32 sta [param_blk_ptr],y
02/08f3: 8a txa
02/08f4: 88 dey
02/08f5: 88 dey
02/08f6: 97 32 sta [param_blk_ptr],y
02/08f8: 80 0b bra :Success
02/08fa: 68 :IsP16 pla
02/08fb: a0 04 00 ldy #$0004 ;position field (high)
02/08fe: 97 32 sta [param_blk_ptr],y
02/0900: 8a txa
02/0901: 88 dey ;position field (low)
02/0902: 88 dey
02/0903: 97 32 sta [param_blk_ptr],y
02/0905: 18 :Success clc ;clear the carry
02/0906: a9 00 00 lda #$0000 ;set the acc
02/0909: 48 pha ;preserve both
02/090a: 08 php
02/090b: 22 6c fc 01 jsl UNLOCK_MEM ;unlock GS/OS memory
02/090f: 28 plp ;pull the cleared carry
02/0910: 68 pla ;pull the $0000
02/0911: 90 03 bcc :Done ;(always)
02/0913: 0d 40 02 ora error_mod
02/0916: 5c 40 fc 01 :Done jml SYS_EXIT
;
; Handles the GS/OS SetMark call.
; (p.190 and p.407)
;
; The ProDOS-16 call just takes a 32-bit file position. The GS/OS call allows
; relative movement.
;
02/091a: 22 68 fc 01 SetMark jsl LOCK_MEM ;lock GS/OS memory
02/091e: a6 3a ldx fcr_path1_vptr ;get FCR vpointer
02/0920: a4 3c ldy fcr_path1_vptr+2
02/0922: 22 38 fc 01 jsl DEREF ;convert to real pointer
02/0926: 86 90 stx fcr_ptr ;copy to DP
02/0928: 84 92 sty fcr_ptr+2
02/092a: ad f2 01 lda call_class ;ProDOS-16?
02/092d: f0 11 beq :IsP16 ;yes, branch
02/092f: a0 06 00 ldy #$0006 ;displacement field
02/0932: b7 32 lda [param_blk_ptr],y
02/0934: 8d 0f 0a sta :displacement ;copy to local storage
02/0937: c8 iny
02/0938: c8 iny
02/0939: b7 32 lda [param_blk_ptr],y
02/093b: 8d 11 0a sta :displacement+2
02/093e: 80 11 bra :NotP16
02/0940: a0 02 00 :IsP16 ldy #$0002 ;position field
02/0943: b7 32 lda [param_blk_ptr],y
02/0945: 8d 0f 0a sta :displacement ;copy to local storage
02/0948: c8 iny
02/0949: c8 iny
02/094a: b7 32 lda [param_blk_ptr],y
02/094c: 8d 11 0a sta :displacement+2
02/094f: 80 16 bra :SeekToDisp
02/0951: a0 04 00 :NotP16 ldy #$0004 ;base field
02/0954: b7 32 lda [param_blk_ptr],y ;get base
02/0956: f0 0f beq :SeekToDisp ;branch to appropriate handler
02/0958: 3a dec A
02/0959: f0 1a beq :SeekEofMinus
02/095b: 3a dec A
02/095c: f0 55 beq :SeekMarkPlus
02/095e: 3a dec A
02/095f: f0 33 beq :SeekMarkMinus
02/0961: a9 53 00 lda #E_PARM_RANGE_ERR
02/0964: 82 96 00 brl :Cleanup
02/0967: ad 0f 0a :SeekToDisp lda :displacement ;use displacement as new mark
02/096a: 8d 13 0a sta :new_mark
02/096d: ad 11 0a lda :displacement+2
02/0970: 8d 15 0a sta :new_mark+2
02/0973: 80 5b bra :CheckVsEof
02/0975: a0 1a 00 :SeekEofMinus ldy #O_FCRX_EOF
02/0978: b7 90 lda [fcr_ptr],y ;get EOF position
02/097a: 38 sec
02/097b: ed 0f 0a sbc :displacement ;subtract displacement
02/097e: 8d 13 0a sta :new_mark
02/0981: c8 iny
02/0982: c8 iny
02/0983: b7 90 lda [fcr_ptr],y
02/0985: ed 11 0a sbc :displacement+2
02/0988: b0 05 bcs :ESubOk
02/098a: a9 4d 00 lda #E_OUT_OF_RANGE ;can't move past start of file
02/098d: 80 6e bra :Cleanup
02/098f: 8d 15 0a :ESubOk sta :new_mark+2
02/0992: 80 56 bra :GotNewMark
02/0994: a0 20 00 :SeekMarkMinus ldy #O_FCRX_MARK
02/0997: b7 90 lda [fcr_ptr],y ;get current file mark
02/0999: 38 sec
02/099a: ed 0f 0a sbc :displacement ;subtract displacement
02/099d: 8d 13 0a sta :new_mark
02/09a0: c8 iny
02/09a1: c8 iny
02/09a2: b7 90 lda [fcr_ptr],y
02/09a4: ed 11 0a sbc :displacement+2
02/09a7: b0 05 bcs :SubOk
02/09a9: a9 4d 00 lda #E_OUT_OF_RANGE ;can't move past start of file
02/09ac: 80 4f bra :Cleanup
02/09ae: 8d 15 0a :SubOk sta :new_mark+2
02/09b1: 80 37 bra :GotNewMark
02/09b3: a0 20 00 :SeekMarkPlus ldy #O_FCRX_MARK
02/09b6: b7 90 lda [fcr_ptr],y ;get current file mark
02/09b8: 18 clc
02/09b9: 6d 0f 0a adc :displacement ;add displacement
02/09bc: 8d 13 0a sta :new_mark
02/09bf: c8 iny
02/09c0: c8 iny
02/09c1: b7 90 lda [fcr_ptr],y
02/09c3: 6d 11 0a adc :displacement+2
02/09c6: 90 05 bcc :SeekPlusOk
02/09c8: a9 4d 00 lda #E_OUT_OF_RANGE ;overflowed 32-bit value
02/09cb: 80 30 bra :Cleanup
02/09cd: 8d 15 0a :SeekPlusOk sta :new_mark+2
02/09d0: ad 15 0a :CheckVsEof lda :new_mark+2 ;test new position vs. EOF
02/09d3: a0 1c 00 ldy #O_FCRX_EOF+2
02/09d6: d7 90 cmp [fcr_ptr],y ;compare high word
02/09d8: 90 10 bcc :GotNewMark ;less than, we're good
02/09da: d0 09 bne :NewPosBad ;not equal, fail
02/09dc: 88 dey
02/09dd: 88 dey
02/09de: b7 90 lda [fcr_ptr],y ;high equal, check low word
02/09e0: cd 13 0a cmp :new_mark
02/09e3: b0 05 bcs :GotNewMark ;EOF >= mark, we're good; branch
02/09e5: a9 4d 00 :NewPosBad lda #E_OUT_OF_RANGE
02/09e8: 80 13 bra :Cleanup
02/09ea: ad 13 0a :GotNewMark lda :new_mark
02/09ed: a0 20 00 ldy #O_FCRX_MARK
02/09f0: 97 90 sta [fcr_ptr],y ;update the mark
02/09f2: ad 15 0a lda :new_mark+2
02/09f5: c8 iny
02/09f6: c8 iny
02/09f7: 97 90 sta [fcr_ptr],y
02/09f9: 18 clc ;report success
02/09fa: a9 00 00 lda #$0000
02/09fd: 48 :Cleanup pha
02/09fe: 22 6c fc 01 jsl UNLOCK_MEM ;unlock GS/OS memory
02/0a02: 68 pla
02/0a03: c9 01 00 cmp #$0001 ;set carry if value nonzero
02/0a06: 90 03 bcc :Done
02/0a08: 0d 40 02 ora error_mod
02/0a0b: 5c 40 fc 01 :Done jml SYS_EXIT
02/0a0f: 00 00 00 00 :displacement .fill 4,$00 ;displacement value from parameter list
02/0a13: 00 00 00 00 :new_mark .fill 4,$00 ;computed file position
;
; Handles the GS/OS Flush call.
; (p.142 and p.379)
;
; We don't support writing, so there's nothing to do except verify pCount.
; Which is unnecessary, because the application entry function does that for us.
;
; According to GS/OS tech note #13, there is a second parameter to Flush
; (flushType) that isn't documented in the GS/OS reference.
;
02/0a17: ad f2 01 Flush lda call_class ;ProDOS-16?
02/0a1a: f0 0e beq :Success ;yes, branch
02/0a1c: ad f4 01 lda pcount
02/0a1f: c9 03 00 cmp #$0003 ;pCount < 3?
02/0a22: 90 06 bcc :Success ;yes, victory
02/0a24: a9 04 00 lda #E_INVALID_PCOUNT
02/0a27: 38 sec
02/0a28: 80 04 bra :Cleanup
02/0a2a: 18 :Success clc
02/0a2b: a9 00 00 lda #$0000
02/0a2e: 90 03 :Cleanup bcc :Done
02/0a30: 0d 40 02 ora error_mod
02/0a33: 5c 40 fc 01 :Done jml SYS_EXIT
;
; Handles the GS/OS FSTSpecific call.
; (p.145)
;
02/0a37: a0 04 00 FstSpecific ldy #$0004 ;commandNum field
02/0a3a: b7 32 lda [param_blk_ptr],y ;get command
02/0a3c: f0 10 beq :InvalidOp ;zero is invalid
02/0a3e: 3a dec A ;test for command == 1
02/0a3f: d0 05 bne L20A46 ;nonzero, branch
02/0a41: 20 42 16 jsr FstSetTextMode ;invoke func #1: set_text_mode
02/0a44: 80 0c bra :Finish
02/0a46: 3a L20A46 dec A ;test for command == 2
02/0a47: d0 05 bne :InvalidOp ;nonzero, branch
02/0a49: 20 66 16 jsr FstGetTextMode ;invoke func #2: get_text_mode
02/0a4c: 80 04 bra :Finish
02/0a4e: a9 65 00 :InvalidOp lda #E_INVALID_FST_OP
02/0a51: 38 sec
02/0a52: c9 01 00 :Finish cmp #$0001
02/0a55: 90 03 bcc :Done
02/0a57: 0d 40 02 ora error_mod
02/0a5a: 5c 40 fc 01 :Done jml SYS_EXIT
;
; Handles calls that take a path as an argument and try to modify something,
; e.g. Delete. We want to offer the most useful error code possible, so we only
; return "write protected" if the path looks okay otherwise.
;
02/0a5e: 22 68 fc 01 RejectPathCall jsl LOCK_MEM ;lock GS/OS memory
02/0a62: 20 7e 0c jsr PathCallSetup ;validate the arguments
02/0a65: b0 03 bcs :PathFail
02/0a67: a9 2b 00 lda #E_DRVR_WR_PROT ;all good, so just return "write protected"
02/0a6a: 48 :PathFail pha
02/0a6b: ad dc 01 lda work_buf_vptr ;did we allocate the work buffer?
02/0a6e: aa tax
02/0a6f: 0d de 01 ora work_buf_vptr+2
02/0a72: f0 07 beq :NoAlloc
02/0a74: ac de 01 ldy work_buf_vptr+2 ;release the work buffer
02/0a77: 22 20 fc 01 jsl RELEASE_SEG
02/0a7b: 22 6c fc 01 :NoAlloc jsl UNLOCK_MEM ;unlock GS/OS memory
02/0a7f: 68 pla
02/0a80: c9 01 00 cmp #$0001 ;clear carry if A-reg == 0
02/0a83: 90 03 bcc :Done
02/0a85: 0d 40 02 ora error_mod
02/0a88: 5c 40 fc 01 :Done jml SYS_EXIT
********************************************************************************
* Application Entry *
* *
* On entry (JSL, reg width uncertain): *
* X-reg: GS/OS call num * 2 *
* Y-reg: call class * 2 (P16=0, GS/OS=2) *
* *
* On exit: *
* Carry flag set on error *
* A-reg: error number *
* (note: returns via sys_exit) *
********************************************************************************
02/0a8c: 8f 00 05 ff AppEntry stal DBG_FST_APP_ENTRY
02/0a90: 4b phk
.dbank K (auto)
02/0a91: ab plb
02/0a92: c2 30 rep #$30
02/0a94: 9c f4 01 stz pcount
02/0a97: 9c 40 02 stz error_mod
02/0a9a: 8c f2 01 sty call_class
02/0a9d: c0 00 00 cpy #$0000 ;ProDOS-16 (class 0) call?
02/0aa0: f0 20 beq :SkipChk ;yes, no pCount to check; branch
02/0aa2: a7 32 lda [param_blk_ptr]
02/0aa4: 8d f4 01 sta pcount
02/0aa7: bd 54 0b lda min_max_pcounts-2,x ;get min/max parameter count as 16-bit int
02/0aaa: e2 20 sep #$20 ; (note no range check on X-reg)
.rwid shortm
02/0aac: 8d 42 02 sta temp_pcount_max ;separate values
02/0aaf: eb xba
02/0ab0: 8d 44 02 sta temp_pcount_min
02/0ab3: ad f4 01 lda pcount ;range-check pCount
02/0ab6: cd 42 02 cmp temp_pcount_max
02/0ab9: b0 15 bcs :InvalidCount
02/0abb: cd 44 02 cmp temp_pcount_min
02/0abe: 90 10 bcc :InvalidCount
02/0ac0: c2 20 rep #$20
.rwid longm
02/0ac2: 9c dc 01 :SkipChk stz work_buf_vptr ;init v-ptr
02/0ac5: 9c de 01 stz work_buf_vptr+2
02/0ac8: e0 69 00 cpx #105 ;is it in our list?
02/0acb: b0 0a bcs BadSysCall ;no, bail
02/0acd: 7c ec 0a jmp (app_handlers,x)
.rwid shortm
02/0ad0: c2 20 :InvalidCount rep #$20
.rwid longm
02/0ad2: a9 04 00 lda #E_INVALID_PCOUNT
02/0ad5: 80 03 bra ErrExit
02/0ad7: a9 01 00 BadSysCall lda #E_BAD_SYSTEM_CALL
02/0ada: 38 ErrExit sec
02/0adb: 0d 40 02 ora error_mod
02/0ade: 5c 40 fc 01 jml SYS_EXIT
02/0ae2: a9 2b 00 RejectWriteCall lda #E_DRVR_WR_PROT
02/0ae5: 80 f3 bra ErrExit
02/0ae7: a9 65 00 JudgeName lda #E_INVALID_FST_OP
02/0aea: 80 ee bra ErrExit
02/0aec: d7 0a app_handlers .dd2 BadSysCall
02/0aee: 5e 0a .dd2 RejectPathCall ;$xx01 (Create)
02/0af0: 5e 0a .dd2 RejectPathCall ;$xx02 (Destroy)
02/0af2: d7 0a .dd2 BadSysCall
02/0af4: 5e 0a .dd2 RejectPathCall ;$xx04 (ChangePath)
02/0af6: 5e 0a .dd2 RejectPathCall ;$xx05 (SetFileInfo)
02/0af8: d6 05 .dd2 GetFileInfo ;$xx06 GetFileInfo
02/0afa: e7 0a .dd2 JudgeName ;$xx07 JudgeName
02/0afc: 58 03 .dd2 Volume ;$xx08 Volume
02/0afe: d7 0a .dd2 BadSysCall
02/0b00: d7 0a .dd2 BadSysCall
02/0b02: 5e 0a .dd2 RejectPathCall ;$xx0b (ClearBackupBit)
02/0b04: d7 0a .dd2 BadSysCall
02/0b06: d7 0a .dd2 BadSysCall
02/0b08: d7 0a .dd2 BadSysCall
02/0b0a: d7 0a .dd2 BadSysCall
02/0b0c: 26 03 .dd2 Open ;$xx10 Open
02/0b0e: d7 0a .dd2 BadSysCall
02/0b10: 73 07 .dd2 Read ;$xx12 Read
02/0b12: e2 0a .dd2 RejectWriteCall ;$xx13 (Write)
02/0b14: c4 07 .dd2 Close ;$xx14 Close
02/0b16: 17 0a .dd2 Flush ;$xx15 Flush
02/0b18: 1a 09 .dd2 SetMark ;$xx16 SetMark
02/0b1a: cd 08 .dd2 GetMark ;$xx17 GetMark
02/0b1c: e2 0a .dd2 RejectWriteCall ;$xx18 (SetEOF)
02/0b1e: 80 08 .dd2 GetEof ;$xx19 GetEOF
02/0b20: d7 0a .dd2 BadSysCall
02/0b22: d7 0a .dd2 BadSysCall
02/0b24: 0a 07 .dd2 GetDirEntry ;$xx1c GetDirEntry
02/0b26: d7 0a .dd2 BadSysCall
02/0b28: d7 0a .dd2 BadSysCall
02/0b2a: d7 0a .dd2 BadSysCall
02/0b2c: 2d 08 .dd2 GetDevNumber ;$xx20 GetDevNumber
02/0b2e: d7 0a .dd2 BadSysCall
02/0b30: d7 0a .dd2 BadSysCall
02/0b32: d7 0a .dd2 BadSysCall
02/0b34: e2 0a .dd2 RejectWriteCall ;$xx24 (Format)
02/0b36: e2 0a .dd2 RejectWriteCall ;$xx25 (EraseDisk)
02/0b38: d7 0a .dd2 BadSysCall
02/0b3a: d7 0a .dd2 BadSysCall
02/0b3c: d7 0a .dd2 BadSysCall
02/0b3e: d7 0a .dd2 BadSysCall
02/0b40: d7 0a .dd2 BadSysCall
02/0b42: d7 0a .dd2 BadSysCall
02/0b44: d7 0a .dd2 BadSysCall
02/0b46: d7 0a .dd2 BadSysCall
02/0b48: d7 0a .dd2 BadSysCall
02/0b4a: d7 0a .dd2 BadSysCall
02/0b4c: d7 0a .dd2 BadSysCall
02/0b4e: d7 0a .dd2 BadSysCall
02/0b50: d7 0a .dd2 BadSysCall
02/0b52: 37 0a .dd2 FstSpecific ;$xx33 FSTSpecific
02/0b54: d7 0a .dd2 BadSysCall
;
; Minimum/maximum values for the pCount parameter. First (low) byte is max+1,
; second (high) byte is min.
;
02/0b56: 08 01 min_max_pcounts .dd2 $0108 ;$2001 Create
02/0b58: 02 01 .dd2 $0102 ;$2002 Destroy
02/0b5a: 02 01 .dd2 $0102 ; ...
02/0b5c: 04 02 .dd2 $0204
02/0b5e: 0d 02 .dd2 $020d
02/0b60: 0d 01 .dd2 $010d
02/0b62: 07 01 .dd2 $0107
02/0b64: 07 02 .dd2 $0207
02/0b66: 03 02 .dd2 $0203
02/0b68: 03 02 .dd2 $0203
02/0b6a: 02 01 .dd2 $0102
02/0b6c: 02 01 .dd2 $0102
02/0b6e: 01 00 .dd2 $0001
02/0b70: 04 02 .dd2 $0204
02/0b72: 02 01 .dd2 $0102
02/0b74: 10 02 .dd2 $0210
02/0b76: 05 04 .dd2 $0405
02/0b78: 06 04 .dd2 $0406
02/0b7a: 06 04 .dd2 $0406
02/0b7c: 02 01 .dd2 $0102
02/0b7e: 02 01 .dd2 $0102 ;$2015 Flush (takes 2 params; bug?)
02/0b80: 04 03 .dd2 $0304
02/0b82: 03 02 .dd2 $0203
02/0b84: 04 03 .dd2 $0304
02/0b86: 03 02 .dd2 $0203
02/0b88: 02 01 .dd2 $0102
02/0b8a: 02 01 .dd2 $0102
02/0b8c: 12 05 .dd2 $0512
02/0b8e: 01 00 .dd2 $0001
02/0b90: 01 00 .dd2 $0001
02/0b92: 02 01 .dd2 $0102
02/0b94: 03 02 .dd2 $0203
02/0b96: 01 00 .dd2 $0001
02/0b98: 01 00 .dd2 $0001
02/0b9a: 01 00 .dd2 $0001
02/0b9c: 05 03 .dd2 $0305
02/0b9e: 05 03 .dd2 $0305
02/0ba0: 01 00 .dd2 $0001
02/0ba2: 02 01 .dd2 $0102
02/0ba4: 02 01 .dd2 $0102
02/0ba6: 03 00 .dd2 $0003
02/0ba8: 02 01 .dd2 $0102
02/0baa: 09 02 .dd2 $0209
02/0bac: 0c 02 .dd2 $020c
02/0bae: 06 05 .dd2 $0506
02/0bb0: 06 05 .dd2 $0506
02/0bb2: 07 06 .dd2 $0607
02/0bb4: 07 06 .dd2 $0607
02/0bb6: 01 00 .dd2 $0001
02/0bb8: 01 00 .dd2 $0001
02/0bba: 12 01 .dd2 $0112 ;$2033 FSTSpecific
********************************************************************************
* System Entry *
* *
* Provides Startup, Shutdown, Remove VCR, and Deferred Flush. *
* *
* On entry (JSL with wide regs): *
* X-reg: call number * 2 (1-4 -> 2-8) *
* *
* On exit: *
* Carry set on error *
* A-reg: error number (0 on success) *
********************************************************************************
02/0bbc: 8f 10 05 ff SysEntry stal DBG_FST_SYS_ENTRY
02/0bc0: 4b phk
.dbank K (auto)
02/0bc1: ab plb
02/0bc2: e0 0b 00 cpx #11 ;range check
02/0bc5: b0 03 bcs SysError
02/0bc7: 7c 05 0c jmp (sys_handlers,x)
02/0bca: a9 01 00 SysError lda #$0001 ;set error code, but not carry (bug?)
02/0bcd: 6b rtl
;
; System startup handler, called on cold and warm starts.
;
; Grabs a pointer to the global buffer and returns.
;
02/0bce: 22 3c fc 01 SysStartup jsl GET_SYS_GBUF ;get the GS/OS global buffer for FST use
02/0bd2: 8e 34 02 stx sys_gbuf_ptr ;save the pointer
02/0bd5: 8c 36 02 sty sys_gbuf_ptr+2
02/0bd8: a9 01 00 lda #$0001
02/0bdb: 8d 38 02 sta text_mode ;set text mode to "clear high bits"
02/0bde: 80 00 bra RtlSuccess
; Reports success.
02/0be0: a9 00 00 RtlSuccess lda #$0000
02/0be3: 18 clc
02/0be4: 6b rtl
;
; Handles system notification of removal of a VCR.
;
; We need to release the 4KB work buffer. Removal of the VCR itself is handled
; by the caller.
;
02/0be5: 8f 58 03 fe RemoveVCR stal DBG_FST_REMOVE_VCR
02/0be9: a6 3e ldx vcr_path2_vptr ;get VCR ptr
02/0beb: a4 40 ldy vcr_path2_vptr+2
02/0bed: 86 88 stx vcr_ptr ;save a copy (why?)
02/0bef: 84 8a sty vcr_ptr+2
02/0bf1: a0 0e 00 ldy #O_VCRX_BUF_VPTR
02/0bf4: b7 3e lda [vcr_path2_vptr],y ;get vptr to 4KB work buffer
02/0bf6: aa tax
02/0bf7: c8 iny
02/0bf8: c8 iny
02/0bf9: 17 3e ora [vcr_path2_vptr],y ;check vpointer for null
02/0bfb: f0 07 beq :Done ;nothing to do
02/0bfd: b7 3e lda [vcr_path2_vptr],y
02/0bff: a8 tay
02/0c00: 22 20 fc 01 jsl RELEASE_SEG ;release memory
02/0c04: 6b :Done rtl
;
; System entry handler address table.
;
02/0c05: ca 0b sys_handlers .dd2 SysError ;unused (returns error w/o setting carry)
02/0c07: ce 0b .dd2 SysStartup ;sys_startup
02/0c09: e0 0b .dd2 RtlSuccess ;sys_shutdown
02/0c0b: e5 0b .dd2 RemoveVCR ;remove_vcr (a/k/a sys_remove_vol)
02/0c0d: e0 0b .dd2 RtlSuccess ;deferred_flush
;
; Given a volume name, return a VCR. If an existing entry is found, this
; confirms that the VTOC data matches what we've seen previously. If we can't
; find a match, return with carry clear and A-reg=0.
;
; Returns an error if we find a matching volume that isn't DOS 3.3 and has a
; nonzero open count, or if it is DOS but the VTOC doesn't match what we expect.
;
; On entry:
; $84-87: pointer to volume name string
; $b8-bb: pointer to buffer to receive generated volume name
; $01e0: device number to examine first
;
; On exit:
; Carry set on failure
; A-reg: volume ID, or zero if not found
; On success, Z-flag set according to A-reg
;
02/0c0f: a6 84 GetVcrByName ldx vol_name_ptr ;get pointer to volume name
02/0c11: a4 86 ldy vol_name_ptr+2
02/0c13: a9 00 00 lda #$0000 ;volume ref num not known, use zero
02/0c16: 22 48 fc 01 jsl FIND_VCR ;find it
02/0c1a: 90 05 bcc :FoundIt
02/0c1c: a9 00 00 :ReturnSuccess lda #$0000 ;didn't find it, return immediately with success
02/0c1f: 18 clc ; but A-reg set to zero
02/0c20: 60 rts
02/0c21: 22 38 fc 01 :FoundIt jsl DEREF ;get a pointer to the VCR
02/0c25: 86 88 stx vcr_ptr
02/0c27: 84 8a sty vcr_ptr+2
02/0c29: a0 0a 00 ldy #O_VCR_FST_ID
02/0c2c: b7 88 lda [vcr_ptr],y ;check the FST ID
02/0c2e: c9 02 00 cmp #FSID_DOS33 ;DOS 3.3?
02/0c31: f0 0a beq :IsDos33 ;yes, branch
02/0c33: 20 5d 11 jsr FreeVcrIfIdle ;no, try to discard it
02/0c36: 90 e4 bcc :ReturnSuccess
02/0c38: a9 57 00 :FailDupVolume lda #E_DUP_VOLUME ;a non-DOS33 VCR with nonzero open count has the
02/0c3b: 38 sec ; same volume name, so return error
02/0c3c: 60 rts
02/0c3d: a0 08 00 :IsDos33 ldy #O_VCR_OPEN_CNT
02/0c40: b7 88 lda [vcr_ptr],y ;increment the open count
02/0c42: 1a inc A
02/0c43: 97 88 sta [vcr_ptr],y
02/0c45: a5 36 lda dev1_num ;device number passed in with call
02/0c47: d0 1a bne :HaveDev
02/0c49: a0 0c 00 ldy #O_VCR_DEV
02/0c4c: b7 88 lda [vcr_ptr],y ;get device ID from VCR
02/0c4e: 8d e0 01 sta dev_num_arg ;examine this device first
02/0c51: 20 b0 11 jsr FindDosVolByName ;find DOS volume with matching name
02/0c54: 90 0d bcc :HaveDev
02/0c56: a0 08 00 ldy #O_VCR_OPEN_CNT
02/0c59: b7 88 lda [vcr_ptr],y ;decrement the open count
02/0c5b: 3a dec A
02/0c5c: 97 88 sta [vcr_ptr],y
02/0c5e: a9 45 00 lda #E_VOL_NOT_FOUND ;fail
02/0c61: 38 sec
02/0c62: 60 rts
02/0c63: a0 08 00 :HaveDev ldy #O_VCR_OPEN_CNT
02/0c66: b7 88 lda [vcr_ptr],y ;decrement the open count
02/0c68: 3a dec A
02/0c69: 97 88 sta [vcr_ptr],y
02/0c6b: 20 71 11 jsr CmpStoredVtoc ;check for exact match
02/0c6e: b0 07 bcs :Mismatch
; Success.
02/0c70: a0 00 00 ldy #O_VCR_ID
02/0c73: b7 88 lda [vcr_ptr],y ;get VCR ID
02/0c75: 18 clc ;return it with success
02/0c76: 60 rts
; The VTOC/catalog sector stored in the VCR don't match what we previously
; recorded for this volume. (1 in 10K chance of changing the contents without
; changing the volume name.)
02/0c77: 20 5d 11 :Mismatch jsr FreeVcrIfIdle ;free the existing VCR if possible
02/0c7a: b0 bc bcs :FailDupVolume ;not possible, report the duplicate volume
02/0c7c: 80 9e bra :ReturnSuccess ;carry clear, but A-reg unknown (bug?)
;
; Does a bunch of setup for pathname-based GS/OS calls:
; GetDevNumber
; GetFileInfo
; Open
; (and all of the rejected calls, like Delete)
;
; Splits the volume name and pathname, verifying their correctness.
;
; Gets or updates the VCR, ensuring that the catalog track is loaded into the
; work buffer.
;
; On exit:
; $00: updated (device driver num)
; $80-83: pointer to work buffer
; $88-8b: pointer to VCR
; $b2-b3: pointer to pathname portion
; $dc-df: v-pointer to work buffer
; Carry set on error
; A-reg: error code
;
02/0c7e: 9c e0 01 PathCallSetup stz dev_num_arg
02/0c81: 9c e2 01 stz wronly_01e2
02/0c84: 9c dc 01 stz work_buf_vptr ;make sure this is zeroed
02/0c87: 9c de 01 stz work_buf_vptr+2
02/0c8a: a9 00 10 lda #4096 ;allocate 4KB chunk
02/0c8d: 22 1c fc 01 jsl ALLOC_SEG
02/0c91: 90 04 bcc :MemOk
02/0c93: a9 54 00 lda #E_OUT_OF_MEM
02/0c96: 60 rts
02/0c97: 8e dc 01 :MemOk stx work_buf_vptr ;save work buffer vptr
02/0c9a: 8c de 01 sty work_buf_vptr+2
02/0c9d: 22 38 fc 01 jsl DEREF ;convert to real pointer
02/0ca1: 86 80 stx work_buf_ptr1 ;copy to DP
02/0ca3: 84 82 sty work_buf_ptr1+2
02/0ca5: a9 46 02 lda #req_vol_name_buf ;set up pointer to vol name buffer
02/0ca8: 85 84 sta vol_name_ptr
02/0caa: a9 02 00 lda #^req_vol_name_buf
02/0cad: 85 86 sta vol_name_ptr+2
02/0caf: a9 78 02 lda #req_file_name_buf ;set up pointer to file name buffer
02/0cb2: 85 b2 sta file_name_ptr
02/0cb4: a9 02 00 lda #^req_file_name_buf ;(this appears to be setting $b2 up as a 4-byte
02/0cb7: 85 b4 sta file_name_ptr+2 ; pointer, which it is not... bug?)
02/0cb9: a9 aa 02 lda #gen_vol_name_buf ;set up pointer to generated vol name buffer
02/0cbc: 85 b8 sta gen_vol_name_ptr
02/0cbe: a9 02 00 lda #^gen_vol_name_buf ;(again, setting up $b8 as a 4-byte pointer, even
02/0cc1: 85 ba sta gen_vol_name_ptr+2 ; though it's 2-byte... bug?)
02/0cc3: 20 cc 12 jsr SplitPath ;split volume name and file name
02/0cc6: 90 04 bcc :PathOk
02/0cc8: a9 40 00 lda #E_BAD_PATH_SYNTAX
02/0ccb: 60 rts
02/0ccc: a5 36 :PathOk lda dev1_num ;do we have a device number?
02/0cce: f0 1d beq :NoDev ;no, move along
02/0cd0: 85 00 sta drvr_dev_num
02/0cd2: 20 95 11 jsr GetDeviceId ;get the ID for the specified device
02/0cd5: b0 07 bcs :UnknownVol ;device check failed, bail
02/0cd7: d0 05 bne :UnknownVol ;found a non-5.25" drive ($0000), bail
02/0cd9: 20 12 10 jsr ReadVtocGenName ;read the VTOC and verify this is DOS
02/0cdc: 90 05 bcc :GotDev ;all good, branch
02/0cde: 38 :UnknownVol sec
02/0cdf: a9 52 00 lda #E_UNKNOWN_VOL
02/0ce2: 60 rts
02/0ce3: a9 aa 02 :GotDev lda #gen_vol_name_buf ;change pointer to generated name buf
02/0ce6: 85 84 sta vol_name_ptr
02/0ce8: a9 02 00 lda #^gen_vol_name_buf
02/0ceb: 85 86 sta vol_name_ptr+2
02/0ced: a6 84 :NoDev ldx vol_name_ptr ;get ptr to requested or generated vol name
02/0cef: a4 86 ldy vol_name_ptr+2
02/0cf1: a9 00 00 lda #$0000 ;VCR ID zero
02/0cf4: 22 48 fc 01 jsl FIND_VCR ;find VCR by volume name
02/0cf8: b0 5b bcs :VcrNotFound
02/0cfa: 22 38 fc 01 jsl DEREF ;convert to real pointer
02/0cfe: 86 88 stx vcr_ptr ;copy to DP
02/0d00: 84 8a sty vcr_ptr+2
02/0d02: a0 0a 00 ldy #O_VCR_FST_ID
02/0d05: b7 88 lda [vcr_ptr],y ;get the file system ID
02/0d07: c9 02 00 cmp #FSID_DOS33 ;is this a DOS 3.3 volume?
02/0d0a: f0 1b beq :GotDosVcr ;yes, branch
; We found a VCR for a non-DOS volume with a matching volume name. If it
; doesn't have any open files we can discard it; if it does we have to fail.
02/0d0c: a0 08 00 :FoundWrongVcr ldy #O_VCR_OPEN_CNT
02/0d0f: b7 88 lda [vcr_ptr],y ;get open count
02/0d11: f0 42 beq :VcrNotFound ;none open, branch to replace it with ours
02/0d13: 9c e0 01 stz dev_num_arg
02/0d16: 20 b0 11 jsr FindDosVolByName ;scan all devices for a matching volume
02/0d19: a9 45 00 lda #E_VOL_NOT_FOUND ;if we didn't find it, vol not found
02/0d1c: 90 03 bcc :DupVol ;if we did find it, dup vol
02/0d1e: 82 8e 00 brl :Return
02/0d21: a9 57 00 :DupVol lda #E_DUP_VOLUME
02/0d24: 82 88 00 brl :Return
02/0d27: a0 0e 00 :GotDosVcr ldy #O_VCRX_BUF_VPTR
02/0d2a: b7 88 lda [vcr_ptr],y ;get vpointer to work buffer
02/0d2c: aa tax ;(VTOC and catalog track stored here)
02/0d2d: c8 iny
02/0d2e: c8 iny
02/0d2f: b7 88 lda [vcr_ptr],y
02/0d31: a8 tay
02/0d32: 22 38 fc 01 jsl DEREF ;convert to real pointer
02/0d36: 86 8c stx work_buf_ptr2 ;copy to DP
02/0d38: 84 8e sty work_buf_ptr2+2
02/0d3a: 20 71 11 jsr CmpStoredVtoc ;see if it's an exact match
02/0d3d: b0 14 bcs :Mismatch ;(are we guaranteed to have filled work_buf_ptr1?)
02/0d3f: a0 06 00 ldy #O_VCR_STATUS
02/0d42: b7 88 lda [vcr_ptr],y
02/0d44: 29 00 40 and #$4000 ;check swapped-out flag
02/0d47: f0 5e beq :NotSwapped ;not swapped, branch
02/0d49: a0 0c 00 ldy #O_VCR_DEV
02/0d4c: b7 88 lda [vcr_ptr],y ;get device num of last associated device
02/0d4e: 8d e0 01 sta dev_num_arg ;save it
02/0d51: 80 09 bra :VcrFound
02/0d53: 80 b7 :Mismatch bra :FoundWrongVcr
02/0d55: 64 88 :VcrNotFound stz vcr_ptr ;zero out the VCR info
02/0d57: 64 8a stz vcr_ptr+2
02/0d59: 9c e0 01 stz dev_num_arg
02/0d5c: 9c b0 0d :VcrFound stz :have_vcr_flag ;clear flag
02/0d5f: a5 88 lda vcr_ptr ;do we have a VCR?
02/0d61: 05 8a ora vcr_ptr+2
02/0d63: f0 0b beq :VcrNull ;no, skip forward
02/0d65: ee b0 0d inc :have_vcr_flag ;set flag
02/0d68: a0 08 00 ldy #O_VCR_OPEN_CNT
02/0d6b: b7 88 lda [vcr_ptr],y ;increment the VCR's open count
02/0d6d: 1a inc A
02/0d6e: 97 88 sta [vcr_ptr],y
02/0d70: 20 b0 11 :VcrNull jsr FindDosVolByName ;scan devices for a matching volume
02/0d73: 08 php ;save result status
02/0d74: 48 pha
02/0d75: ad b0 0d lda :have_vcr_flag ;check flag
02/0d78: f0 0b beq :Cleanup ;no VCR, skip decr of open count
02/0d7a: a0 08 00 ldy #O_VCR_OPEN_CNT
02/0d7d: b7 88 lda [vcr_ptr],y ;decrement the VCR's open count
02/0d7f: 3a dec A
02/0d80: 97 88 sta [vcr_ptr],y
02/0d82: 9c b0 0d stz :have_vcr_flag ;clear flag
02/0d85: 68 :Cleanup pla ;get status from volume search
02/0d86: 28 plp
02/0d87: b0 26 bcs :Return ;failed, bail
02/0d89: a5 88 lda vcr_ptr ;check the VCR pointer again
02/0d8b: 05 8a ora vcr_ptr+2
02/0d8d: f0 13 beq :VcrNull2 ;null, skip this
02/0d8f: a0 0c 00 ldy #O_VCR_DEV
02/0d92: a5 00 lda drvr_dev_num
02/0d94: 97 88 sta [vcr_ptr],y ;update the "device where last seen" field
02/0d96: a0 06 00 ldy #O_VCR_STATUS
02/0d99: b7 88 lda [vcr_ptr],y
02/0d9b: 29 ff bf and #$bfff ;clear the "swapped" flag
02/0d9e: 97 88 sta [vcr_ptr],y
02/0da0: 80 05 bra :NotSwapped
02/0da2: 20 30 0f :VcrNull2 jsr GetOrAllocVcr ;get VCR, read catalog track into work buffer
02/0da5: b0 08 bcs :Return
02/0da7: a0 0c 00 :NotSwapped ldy #O_VCR_DEV
02/0daa: b7 88 lda [vcr_ptr],y
02/0dac: 85 00 sta drvr_dev_num ;update driver device num
02/0dae: 18 clc
02/0daf: 60 :Return rts
02/0db0: 00 00 :have_vcr_flag .fill 2,$00
;
; Copies file info parameters out. Used for GetDirEntry and GetFileInfo.
;
; On entry:
; $c2-c3: pointer to one of the parameter offset tables, below
; $01f4: pCount; set to 1 for class 0 calls
;
CopyFileParamsOut
02/0db2: a0 fe ff ldy #$fffe ;set to -2 so first increment is to zero
02/0db5: ae f4 01 ldx pcount ;copy this many args
02/0db8: c8 :Loop iny
02/0db9: c8 iny ;advance 2 bytes at a time
02/0dba: b1 c2 lda (rslt_cmd_ptr),y ;get offset and flags
02/0dbc: 0a asl A ;shift $8000 to carry, $4000 to N flag
02/0dbd: 08 php ;preserve flags
02/0dbe: 30 09 bmi :NoCopy ;if $4000 set, don't copy data
02/0dc0: 4a lsr A ;shift back without carry (strips $8000 flag)
02/0dc1: 5a phy ;preserve param index
02/0dc2: a8 tay ;put data offset in Y-reg
02/0dc3: b9 0e 03 lda file_dos_type-2,y ;get data value
02/0dc6: 7a ply ;restore param index
02/0dc7: 97 32 sta [param_blk_ptr],y ;write data value
02/0dc9: 28 :NoCopy plp ;restore flags
02/0dca: 90 ec bcc :Loop ;if $8000 was not set, branch
02/0dcc: ca dex ;decrement param count
02/0dcd: d0 e9 bne :Loop ;branch if not done
02/0dcf: 60 rts
;
; Parameter copy commands, for GetDirEntry and GetFileInfo.
;
; Each entry is the offset of the source data, indexed from $030e. The entry is
; ORed with $4000 to skip forward in the output without copying, and $8000 to
; decrement pCount. Copying stops when pCount reaches zero.
;
; For class 0 calls, pCount is set to 1, and only the last entry has $8000 set.
; Source offset $0016 holds a zero value.
02/0dd0: 00 40 parm_c0_gfi .dd2 $4000
02/0dd2: 00 40 .dd2 $4000
02/0dd4: 06 00 .dd2 $0006
02/0dd6: 04 00 .dd2 $0004
02/0dd8: 08 00 .dd2 $0008
02/0dda: 16 00 .dd2 $0016
02/0ddc: 12 00 .dd2 $0012
02/0dde: 16 00 .dd2 $0016
02/0de0: 16 00 .dd2 $0016
02/0de2: 16 00 .dd2 $0016
02/0de4: 16 00 .dd2 $0016
02/0de6: 0e 00 .dd2 $000e
02/0de8: 16 80 .dd2 $8016
02/0dea: 00 40 parm_c1_gfi .dd2 $4000 ;+$00: pCount (skip)
02/0dec: 00 40 .dd2 $4000 ;+$02: pathname lo (skip)
02/0dee: 00 c0 .dd2 $c000 ;+$04: pathname hi (skip, decr)
02/0df0: 06 80 .dd2 $8006 ;+$06: access (decr)
02/0df2: 04 80 .dd2 $8004 ;+$08: fileType (decr)
02/0df4: 08 00 .dd2 $0008 ;+$0a: auxType lo
02/0df6: 16 80 .dd2 $8016 ;+$0c: auxType hi, always zero (decr)
02/0df8: 12 80 .dd2 $8012 ;+$0e: storageType (decr)
02/0dfa: 16 00 .dd2 $0016 ;+$10: createDateTime, 4 words
02/0dfc: 16 00 .dd2 $0016 ; ...
02/0dfe: 16 00 .dd2 $0016
02/0e00: 16 80 .dd2 $8016
02/0e02: 16 00 .dd2 $0016 ;+$18: modDateTime, 4 words
02/0e04: 16 00 .dd2 $0016 ; ...
02/0e06: 16 00 .dd2 $0016
02/0e08: 16 80 .dd2 $8016
02/0e0a: 00 40 .dd2 $4000 ;+$20: optionList lo (skip)
02/0e0c: 00 c0 .dd2 $c000 ;+$22: optionList hi (skip, decr)
02/0e0e: 0a 00 .dd2 $000a ;+$24: eof lo
02/0e10: 0c 80 .dd2 $800c ;+$26: eof hi (decr)
02/0e12: 0e 00 .dd2 $000e ;+$28: blocksUsed lo
02/0e14: 16 80 .dd2 $8016 ;+$2a: blocksUsed hi, always zero (decr)
02/0e16: 16 00 .dd2 $0016 ;+$2c: resourceEOF lo, always zero
02/0e18: 16 80 .dd2 $8016 ;+$2e: resourceEOF hi, always zero (decr)
02/0e1a: 16 00 .dd2 $0016 ;+$30: resourceBlocks lo, always zero
02/0e1c: 16 80 .dd2 $8016 ;+$32: resourceBlocks hi, always zero (decr)
02/0e1e: 00 40 parm_c0_gde .dd2 $4000
02/0e20: 00 40 .dd2 $4000
02/0e22: 00 40 .dd2 $4000
02/0e24: 00 40 .dd2 $4000
02/0e26: 00 40 .dd2 $4000
02/0e28: 00 40 .dd2 $4000
02/0e2a: 10 00 .dd2 $0010 ;+$0c: entryNum
02/0e2c: 04 00 .dd2 $0004
02/0e2e: 0a 00 .dd2 $000a
02/0e30: 0c 00 .dd2 $000c
02/0e32: 0e 00 .dd2 $000e
02/0e34: 16 00 .dd2 $0016
02/0e36: 16 00 .dd2 $0016
02/0e38: 16 00 .dd2 $0016
02/0e3a: 16 00 .dd2 $0016
02/0e3c: 16 00 .dd2 $0016
02/0e3e: 16 00 .dd2 $0016
02/0e40: 16 00 .dd2 $0016
02/0e42: 16 00 .dd2 $0016
02/0e44: 16 00 .dd2 $0016
02/0e46: 06 00 .dd2 $0006
02/0e48: 08 00 .dd2 $0008
02/0e4a: 16 00 .dd2 $0016
02/0e4c: 14 80 .dd2 $8014
02/0e4e: 00 40 parm_c1_gde .dd2 $4000
02/0e50: 00 c0 .dd2 $c000
02/0e52: 00 c0 .dd2 $c000
02/0e54: 00 c0 .dd2 $c000
02/0e56: 00 c0 .dd2 $c000
02/0e58: 00 40 .dd2 $4000
02/0e5a: 00 c0 .dd2 $c000
02/0e5c: 10 80 .dd2 $8010
02/0e5e: 04 80 .dd2 $8004
02/0e60: 0a 00 .dd2 $000a
02/0e62: 0c 80 .dd2 $800c
02/0e64: 0e 00 .dd2 $000e
02/0e66: 16 80 .dd2 $8016
02/0e68: 16 00 .dd2 $0016
02/0e6a: 16 00 .dd2 $0016
02/0e6c: 16 00 .dd2 $0016
02/0e6e: 16 80 .dd2 $8016
02/0e70: 16 00 .dd2 $0016
02/0e72: 16 00 .dd2 $0016
02/0e74: 16 00 .dd2 $0016
02/0e76: 16 80 .dd2 $8016
02/0e78: 06 80 .dd2 $8006
02/0e7a: 08 00 .dd2 $0008
02/0e7c: 16 80 .dd2 $8016
02/0e7e: 14 80 .dd2 $8014
02/0e80: 00 40 .dd2 $4000
02/0e82: 00 c0 .dd2 $c000
02/0e84: 16 00 .dd2 $0016
02/0e86: 16 80 .dd2 $8016
02/0e88: 16 00 .dd2 $0016
02/0e8a: 16 80 .dd2 $8016
;
; Reads a sector.
;
; The desired track and sector are passed in the A-reg. The low byte holds the
; track number (0-35), the high byte holds the sector (0-15). (This is what you
; get when you load a T/S entry as a 16-bit value.)
;
; The caller sets drvr_call_num, so technically this could be used to write as
; well as read.
;
; On entry:
; A-reg: track/sector
; $04-07 (drvr_buf_ptr): buffer that will receive data
;
; On exit:
; Carry set on error
; A-reg: error code
;
02/0e8c: eb ReadSector xba ;xSTT-> TTxS
02/0e8d: e2 20 sep #$20 ; -> (TT)xS
.rwid shortm
02/0e8f: 0a asl A
02/0e90: 0a asl A
02/0e91: 0a asl A
02/0e92: 0a asl A ;-> (TT)S0
02/0e93: c2 20 rep #$20 ;-> TTS0
.rwid longm
02/0e95: 4a lsr A
02/0e96: 4a lsr A
02/0e97: 4a lsr A
02/0e98: 4a lsr A ; -> 0TTS
02/0e99: 85 10 sta drvr_blk_num ;set the desired block number (0-559)
02/0e9b: 64 12 stz drvr_blk_num+2
02/0e9d: a9 00 01 lda #256
02/0ea0: 85 14 sta drvr_blk_size ;request 256-byte sectors
02/0ea2: 85 08 sta drvr_req_cnt ;read one sector
02/0ea4: 64 0a stz drvr_req_cnt+2
02/0ea6: a9 02 00 lda #FSID_DOS33
02/0ea9: 85 16 sta drvr_code ;fstNum field
02/0eab: 64 1a stz drvr_cache_prio ;no caching
02/0ead: 22 00 fc 01 :Retry jsl DEV_DISPATCHER ;make call
02/0eb1: b0 01 bcs :CheckSw
02/0eb3: 60 rts
; The 5.25" disk driver reports a "disk switched" error if the previous access
; was more than a second ago. This is because the drive itself has no way to
; report that the disk was ejected.
02/0eb4: c9 2e 00 :CheckSw cmp #E_DRVR_DISK_SW ;was it a disk-switched error?
02/0eb7: f0 f4 beq :Retry ;yes, retry
02/0eb9: 38 sec ;no, return with error
02/0eba: 60 rts
;
; Sets the File Descriptive Entry pointer to the entry for the requested file.
; Offset +$00/01 in the FDE holds the track/sector for the file's first T/S
; list.
;
; On entry:
; A-reg: catalog sector ($00-0f)
; X-reg: index within sector ($00-07)
;
; On exit:
; Carry set on error
; $a8-ab: points to appropriate FDE entry in VCR work buffer
;
02/0ebb: c9 10 00 SetFdePtr cmp #16 ;check sector number
02/0ebe: b0 20 bcs :Return ;we support at most 15 sectors per catalog
02/0ec0: eb xba ;multiply by 256
02/0ec1: 69 00 01 adc #$0100 ;skip VTOC
02/0ec4: 69 0b 00 adc #O_DOSCAT_ENTRY ;offset within sector to FDE list
02/0ec7: e0 08 00 cpx #$0008 ;check index; a sector can only hold 7 FDEs
02/0eca: b0 14 bcs :Return
02/0ecc: ca :MultLoop dex
02/0ecd: 30 05 bmi :GotOffset
02/0ecf: 69 23 00 adc #DOSFDE_SIZE ;offset += index * FDE_size
02/0ed2: 80 f8 bra :MultLoop
02/0ed4: 65 8c :GotOffset adc work_buf_ptr2 ;add the offset to the work buffer pointer
02/0ed6: 85 a8 sta fde_ptr
02/0ed8: a5 8e lda work_buf_ptr2+2
02/0eda: 69 00 00 adc #$0000
02/0edd: 85 aa sta fde_ptr+2
02/0edf: 60 rts
02/0ee0: 60 :Return rts
;
; Gets the first byte of the File Descriptive Entry, which should be one of:
;
; $00: entry has never been used
; $ff: entry is a deleted file; last byte of name is track num
; $01-31: track number of first T/S list
;
; In GS/OS System 6.0.1, the FST was updated to ignore entries whose filename
; field is nothing but 30 spaces. These are treated as deleted entries.
;
; On entry:
; $a8-ab: pointer to FDE
;
; On exit:
; A-reg: first byte of FDE ($00, $FF, or track number)
; Z-flag set according to A-reg contents
; X-reg/Y-reg preserved
;
02/0ee1: a7 a8 GetFdeStatus lda [fde_ptr]
02/0ee3: 29 ff 00 and #$00ff ;test for $00
02/0ee6: f0 22 beq :Done ;yes, branch (entry never used)
02/0ee8: c9 ff 00 cmp #$00ff ;test for $ff
02/0eeb: f0 1d beq :Done ;yes, branch (entry deleted)
02/0eed: 5a phy ;preserve X/Y regs
02/0eee: da phx
; Check for blank entry (30 spaces).
02/0eef: a2 0f 00 ldx #15
02/0ef2: a0 03 00 ldy #O_DOSFDE_NAME
02/0ef5: b7 a8 :Loop lda [fde_ptr],y
02/0ef7: c9 a0 a0 cmp #$a0a0 ;two high-ASCII spaces
02/0efa: d0 0a bne :NotBlank
02/0efc: c8 iny
02/0efd: c8 iny
02/0efe: ca dex
02/0eff: d0 f4 bne :Loop
02/0f01: a9 ff 00 lda #$00ff ;treat it like a deleted entry
02/0f04: 80 02 bra :Finish
02/0f06: a7 a8 :NotBlank lda [fde_ptr] ;get first byte again
02/0f08: fa :Finish plx ;restore X/Y regs
02/0f09: 7a ply
02/0f0a: 29 ff 00 :Done and #$00ff
02/0f0d: 60 rts
;
; Compares two GS/OS strings that hold volume or file names. The characters in
; the second string will have a bit mask applied.
;
; Comparison is limited to the first 32767 characters.
;
; On entry:
; $a0-a3: pointer to first string
; $a4-a7: pointer to second string
; $01ec: mask to apply to bytes in second string
;
; On exit:
; Carry clear on match, set on mismatch
;
02/0f0e: a7 a0 StringCompare lda [strcmp_arg0] ;get string length
02/0f10: c7 a4 cmp [strcmp_arg1] ;compare
02/0f12: d0 18 bne :Mismatch
02/0f14: a8 tay
02/0f15: 10 03 bpl :IsPos ;check if negative
02/0f17: a0 ff 7f ldy #$7fff ;limit string len so we can BPL
02/0f1a: c8 :IsPos iny ;(unnecessary: we checked len, so BNE would work)
02/0f1b: e2 20 sep #$20 ;increment str len to get index to last char
.rwid shortm
02/0f1d: b7 a4 :Loop lda [strcmp_arg1],y ;compare strings, with mask
02/0f1f: 2d ec 01 and strcmp_mask
02/0f22: d7 a0 cmp [strcmp_arg0],y
02/0f24: d0 06 bne :Mismatch
02/0f26: 88 dey
02/0f27: 10 f4 bpl :Loop
02/0f29: c2 21 rep #$21 ;long A-reg, clear carry
.rwid longm
02/0f2b: 60 rts
.rwid shortm
02/0f2c: 38 :Mismatch sec
02/0f2d: c2 20 rep #$20
.rwid longm
02/0f2f: 60 rts
;
; Looks for an existing VCR, by volume name. If found, and it appears to be the
; same disk, use it. If not found, allocate a new one.
;
; This reads the VTOC and first 15 catalog sectors into the VCR's work buffer.
;
; On exit:
; Carry set on error
; A-reg: error code (will be E_DUP_VOLUME if we found a volume with a matching
; name, but either it's not DOS or the VTOC contents don't match)
; $84-87: pointer to volume name string
; $8c-8f: pointer to work buffer
; $b8-bb: pointer to buffer to receive generated volume name
; $01e0: device number to examine first
;
02/0f30: 20 0f 0c GetOrAllocVcr jsr GetVcrByName ;find the VCR by name
02/0f33: 90 04 bcc :NoError
02/0f35: a9 57 00 lda #E_DUP_VOLUME ;got an error during search, return only this
02/0f38: 60 rts
02/0f39: f0 03 :NoError beq :VolIdZero ;volume ID is zero, branch
02/0f3b: 82 b8 00 brl :GotGoodVcr
; We didn't find a VCR, so create a new one.
02/0f3e: 20 b0 11 :VolIdZero jsr FindDosVolByName
02/0f41: 90 04 bcc :AllocVcr
02/0f43: a9 45 00 lda #E_VOL_NOT_FOUND
02/0f46: 60 rts
02/0f47: a9 14 00 :AllocVcr lda #$0014 ;over-allocate
02/0f4a: a6 84 ldx vol_name_ptr ;get volume name
02/0f4c: a4 86 ldy vol_name_ptr+2
02/0f4e: 22 24 fc 01 jsl ALLOC_VCR ;create new VCR
02/0f52: 90 04 bcc :GotVcr
02/0f54: a9 54 00 lda #E_OUT_OF_MEM
02/0f57: 60 rts
02/0f58: 22 38 fc 01 :GotVcr jsl DEREF ;get real pointer to new VCR
02/0f5c: 86 88 stx vcr_ptr
02/0f5e: 84 8a sty vcr_ptr+2
02/0f60: a9 00 80 lda #$8000 ;indicate that VCR was allocated on this call
02/0f63: 0c 3a 02 tsb alloc_rsrc_flags
02/0f66: ad dc 01 lda work_buf_vptr ;do we have a work buffer?
02/0f69: 0d de 01 ora work_buf_vptr+2
02/0f6c: d0 14 bne :HaveWrkBuf ;yes, branch
02/0f6e: a9 00 10 lda #4096 ;allocate a 4KB work buffer
02/0f71: 22 1c fc 01 jsl ALLOC_SEG
02/0f75: 90 05 bcc :MemOk
02/0f77: a9 54 00 lda #E_OUT_OF_MEM
02/0f7a: 80 55 bra :FailClean
02/0f7c: 8e dc 01 :MemOk stx work_buf_vptr
02/0f7f: 8c de 01 sty work_buf_vptr+2
02/0f82: a0 0a 00 :HaveWrkBuf ldy #O_VCR_FST_ID
02/0f85: a9 02 00 lda #FSID_DOS33
02/0f88: 97 88 sta [vcr_ptr],y ;set the filesystem ID
02/0f8a: a0 08 00 ldy #O_VCR_OPEN_CNT
02/0f8d: a9 00 00 lda #$0000
02/0f90: 97 88 sta [vcr_ptr],y ;set number of open files to zero
02/0f92: a0 0c 00 ldy #O_VCR_DEV
02/0f95: a5 00 lda drvr_dev_num
02/0f97: 97 88 sta [vcr_ptr],y ;set device number
02/0f99: a0 0e 00 ldy #O_VCRX_BUF_VPTR
02/0f9c: ad dc 01 lda work_buf_vptr ;get vptr to work buffer allocated elsewhere
02/0f9f: aa tax
02/0fa0: 97 88 sta [vcr_ptr],y ;copy into VCR
02/0fa2: c8 iny
02/0fa3: c8 iny
02/0fa4: ad de 01 lda work_buf_vptr+2
02/0fa7: 97 88 sta [vcr_ptr],y
02/0fa9: a8 tay
02/0faa: 22 38 fc 01 jsl DEREF ;get a real pointer
02/0fae: 86 8c stx work_buf_ptr2 ;copy to DP
02/0fb0: 84 8e sty work_buf_ptr2+2
02/0fb2: 9c dc 01 stz work_buf_vptr ;zero out our copy of the vptr; it's now owned
02/0fb5: 9c de 01 stz work_buf_vptr+2 ; by the VCR
02/0fb8: a0 08 00 ldy #O_VCR_OPEN_CNT
02/0fbb: b7 88 lda [vcr_ptr],y
02/0fbd: 1a inc A ;increment the open count
02/0fbe: 97 88 sta [vcr_ptr],y
02/0fc0: 20 5d 15 jsr ReadCatTrack
02/0fc3: 08 php ;preserve result
02/0fc4: 48 pha
02/0fc5: a0 08 00 ldy #O_VCR_OPEN_CNT
02/0fc8: b7 88 lda [vcr_ptr],y ;decrement the open count
02/0fca: 3a dec A
02/0fcb: 97 88 sta [vcr_ptr],y
02/0fcd: 68 pla ;restore result
02/0fce: 28 plp
02/0fcf: 90 1b bcc :ReadOk
02/0fd1: 48 :FailClean pha ;preserve error code
02/0fd2: a0 08 00 ldy #O_VCR_OPEN_CNT
02/0fd5: a9 00 00 lda #$0000
02/0fd8: 97 88 sta [vcr_ptr],y ;zero out the open count
02/0fda: a0 00 00 ldy #O_VCR_ID
02/0fdd: b7 88 lda [vcr_ptr],y ;get the VCR ID
02/0fdf: 22 28 fc 01 jsl RELEASE_VCR ;release the VCR
02/0fe3: a9 00 80 lda #$8000
02/0fe6: 1c 3a 02 trb alloc_rsrc_flags ;clear "allocated VCR on this call" flag
02/0fe9: 68 pla ;restore error code
02/0fea: 38 sec
02/0feb: 60 rts
02/0fec: ad ea 01 :ReadOk lda num_cat_sectors
02/0fef: a0 12 00 ldy #O_VCRX_NUM_CAT_SCT
02/0ff2: 97 88 sta [vcr_ptr],y
02/0ff4: 80 1a bra :RetSuccess
02/0ff6: a0 0e 00 :GotGoodVcr ldy #O_VCRX_BUF_VPTR
02/0ff9: b7 88 lda [vcr_ptr],y ;get v-pointer to work buffer
02/0ffb: aa tax
02/0ffc: c8 iny
02/0ffd: c8 iny
02/0ffe: b7 88 lda [vcr_ptr],y
02/1000: a8 tay
02/1001: 22 38 fc 01 jsl DEREF ;convert to real pointer
02/1005: 86 8c stx work_buf_ptr2 ;copy to DP
02/1007: 84 8e sty work_buf_ptr2+2
02/1009: a0 0c 00 ldy #O_VCR_DEV
02/100c: a5 00 lda drvr_dev_num
02/100e: 97 88 sta [vcr_ptr],y ;update VCR device number
02/1010: 18 :RetSuccess clc
02/1011: 60 rts
;
; Reads the DOS Volume Table of Contents from T17 S0, and generates the
; synthetic volume name. Tests the contents of the VTOC to determine whether
; this is actually a DOS disk.
;
; If everything succeeds, the error code modifier value is set to $8000.
;
; On entry:
; $80-83: pointer to work buffer (will receive VTOC and 1st cat sect)
; $b8-bb: pointer to buffer to receive generated volume name
;
; On exit:
; Carry set on error
; A-reg: error code
;
02/1012: a5 80 ReadVtocGenName lda work_buf_ptr1 ;read sector into work buffer
02/1014: 85 04 sta drvr_buf_ptr
02/1016: a5 82 lda work_buf_ptr1+2
02/1018: 85 06 sta drvr_buf_ptr+2
02/101a: a9 02 00 lda #DRIVER_READ
02/101d: 85 02 sta drvr_call_num
02/101f: a9 11 00 lda #$0011 ;T17 S0
02/1022: 20 8c 0e jsr ReadSector ;read the sector
02/1025: 90 01 bcc :ReadOk
02/1027: 60 rts ;failed
; Test to see if this looks like a DOS 3.3. disk.
02/1028: e2 30 :ReadOk sep #$30
.rwid shortm,shortx
02/102a: a2 09 ldx #$09 ;test 10 values
02/102c: bd 41 11 :ChkLoop lda vtoc_chk_indices,x ;get the byte number to test
02/102f: a8 tay ;save a copy in Y-reg
02/1030: b7 04 :ChkIt lda [drvr_buf_ptr],y ;get value from VTOC
02/1032: dd 4b 11 cmp vtoc_chk_values,x ;compare to expected value
02/1035: d0 0b bne :Mismatch ;failed, branch
02/1037: ca :ChkNext dex ;good, move to next entry
02/1038: 30 18 bmi :CheckOk ;done, branch
02/103a: 98 tya ;get the index again
02/103b: dd 41 11 cmp vtoc_chk_indices,x ;is next entry an alternate value (which will fail)?
02/103e: f0 f7 beq :ChkNext ;yes, skip it
02/1040: 80 ea bra :ChkLoop ;no, branch to check it
02/1042: ca :Mismatch dex ;move to next entry
02/1043: 30 06 bmi :ChkFail
02/1045: 98 tya ;get the index again
02/1046: dd 41 11 cmp vtoc_chk_indices,x ;is this entry an alternate value?
02/1049: f0 e5 beq :ChkIt ;yes, try this instead
02/104b: 38 :ChkFail sec
02/104c: c2 30 rep #$30
.rwid longm,longx
02/104e: a9 52 00 lda #E_UNKNOWN_VOL ;doesn't look like a DOS 3.3 disk
02/1051: 60 rts
; Looks like DOS, read the first catalog sector.
.rwid shortm,shortx
02/1052: c2 31 :CheckOk rep #$31 ;long regs, clear carry
.rwid longm,longx
02/1054: a0 01 00 ldy #$0001 ;offset to T/S of first catalog sector
02/1057: b7 04 lda [drvr_buf_ptr],y
02/1059: d0 04 bne :CatTSOk ;must be nonzero
02/105b: a9 51 00 lda #E_DIR_ERROR
02/105e: 60 rts ;note carry not set (bug?)
02/105f: 48 :CatTSOk pha ;preserve T/S
02/1060: a5 04 lda drvr_buf_ptr
02/1062: 85 80 sta work_buf_ptr1 ;save VTOC ptr
02/1064: 18 clc
02/1065: 69 00 01 adc #$0100 ;add 256 to ptr for catalog sector
02/1068: 85 04 sta drvr_buf_ptr
02/106a: a5 06 lda drvr_buf_ptr+2
02/106c: 85 82 sta work_buf_ptr1+2
02/106e: 69 00 00 adc #$0000
02/1071: 85 06 sta drvr_buf_ptr+2
02/1073: a9 02 00 lda #DRIVER_READ
02/1076: 85 02 sta drvr_call_num
02/1078: 68 pla ;restore T/S
02/1079: 20 8c 0e jsr ReadSector ;read catalog sector
02/107c: 90 01 bcc :GenVolName
02/107e: 60 rts
; Generate a hash value on the VTOC and first catalog sector. As noted in the
; Programmer's Reference for System 6.0, "Since DOS 3.3 disks do not have real
; names, and volume numbers are not unique, the DOS 3.3 FST invents a name for
; each disk by hashing the directory contents, ...". Note this only works
; because the FST is read-only.
02/107f: a0 fe 01 :GenVolName ldy #$01fe ;512 bytes (VTOC and first catalog sector)
02/1082: a9 00 00 lda #$0000 ;initial value
02/1085: 18 clc
02/1086: 57 80 :HashLoop eor [work_buf_ptr1],y ;rolling XOR
02/1088: 6a ror A ;with a shift
02/1089: 10 03 bpl :PosHash
02/108b: 49 ff 7f eor #$7fff ;invert everything but the high bit
02/108e: 88 :PosHash dey
02/108f: 88 dey
02/1090: 10 f4 bpl :HashLoop
02/1092: 8d 55 11 sta volume_hash ;save result
; Form the synthetic volume name.
02/1095: a2 00 00 ldx #$0000
02/1098: bd 35 11 lda vol_name_pfx,x ;first word is buffer address (why?)
02/109b: 85 ac sta gen_vol_src_ptr
02/109d: e2 20 sep #$20
.rwid shortm
02/109f: a0 02 00 ldy #$0002 ;leave room for 2-byte length
02/10a2: b1 ac :CopyLoop lda (gen_vol_src_ptr),y ;copy "DOS 3.3 v" to output buffer
02/10a4: f0 08 beq :CopyDone ;stop if we hit null terminator
02/10a6: 91 b8 sta (gen_vol_name_ptr),y
02/10a8: c8 iny
02/10a9: c0 0f 00 cpy #15 ;stop no matter what if we hit 15 chars
02/10ac: d0 f4 bne :CopyLoop
; Convert the 16-bit hash value into a decimal number from 0000 to 9999.
02/10ae: c2 20 :CopyDone rep #$20
.rwid longm
02/10b0: 9c 59 11 stz hash1
02/10b3: 9c 5b 11 stz hash2
02/10b6: ad 55 11 lda volume_hash
02/10b9: 29 ff 07 and #$07ff
02/10bc: 8d 57 11 sta hash0
02/10bf: 38 L210BF sec
02/10c0: e9 e8 03 sbc #$03e8
02/10c3: 90 11 bcc L210D6
02/10c5: 8d 57 11 sta hash0
02/10c8: ad 59 11 lda hash1
02/10cb: 69 00 00 adc #$0000
02/10ce: 8d 59 11 sta hash1
02/10d1: ad 57 11 lda hash0
02/10d4: 80 e9 bra L210BF
02/10d6: ad 57 11 L210D6 lda hash0
02/10d9: 38 sec
02/10da: e9 64 00 sbc #$0064
02/10dd: 90 0e bcc L210ED
02/10df: 8d 57 11 sta hash0
02/10e2: ad 59 11 lda hash1
02/10e5: 69 ff 00 adc #$00ff
02/10e8: 8d 59 11 sta hash1
02/10eb: 80 e9 bra L210D6
02/10ed: ad 57 11 L210ED lda hash0
02/10f0: 38 sec
02/10f1: e9 0a 00 sbc #$000a
02/10f4: 90 0e bcc L21104
02/10f6: 8d 57 11 sta hash0
02/10f9: ad 5b 11 lda hash2
02/10fc: 69 00 00 adc #$0000
02/10ff: 8d 5b 11 sta hash2
02/1102: 80 e9 bra L210ED
02/1104: ad 57 11 L21104 lda hash0
02/1107: eb xba
02/1108: 6d 5b 11 adc hash2
02/110b: 8d 5b 11 sta hash2
; We now have two values of the form $0m0n, where 'm' and 'n' are 0-9. Add
; $3030 to convert to ASCII digits, and append them.
;
; The fixed string was 15 chars max, we add a 4-digit number, and we add two
; zeroes to the end, for a total of 21 bytes.
02/110e: ad 59 11 lda hash1
02/1111: 69 30 30 adc #$3030
02/1114: 91 b8 sta (gen_vol_name_ptr),y ;write first two digits
02/1116: c8 iny
02/1117: c8 iny
02/1118: ad 5b 11 lda hash2
02/111b: 69 30 30 adc #$3030
02/111e: 91 b8 sta (gen_vol_name_ptr),y ;write second two digits
02/1120: c8 iny
02/1121: c8 iny
02/1122: a9 00 00 lda #$0000
02/1125: 91 b8 sta (gen_vol_name_ptr),y ;stick a zero on the end just to be safe
02/1127: 88 dey ;don't count the zeroes
02/1128: 88 dey
02/1129: 98 tya
02/112a: 92 b8 sta (gen_vol_name_ptr) ;write string length at start of buffer
02/112c: c2 31 rep #$31 ;clear carry
02/112e: a9 00 80 lda #$8000 ;failure codes from now on have the high bit set
02/1131: 8d 40 02 sta error_mod
02/1134: 60 rts
;
; Synthetic volume name prefix.
02/1135: 35 11 vol_name_pfx .dd2 vol_name_pfx
02/1137: 44 4f 53 20+ .zstr ‘DOS 3.3 v’
;
; Expected values for a DOS 3.3 VTOC sector. This is used to determine if the
; disk is DOS or something else.
;
; $03: release number of DOS (3)
; $27: maximum T/S pairs per T/S list (122)
; $36-37: number of bytes per sector ($0100)
; $31: direction of track allocation ($01 or $ff)
; $34: number of tracks per disk (35 or 50)
; $35: number of sectors per track (16 or 32)
;
; Note the test accepts 400KB images.
vtoc_chk_indices
02/1141: 03 27 36 37+ .bulk $03,$27,$36,$37,$31,$31,$34,$34,$35,$35
02/114b: 03 7a 00 01+ vtoc_chk_values .bulk $03,$7a,$00,$01,$01,$ff,$23,$32,$10,$20
02/1155: 00 00 volume_hash .fill 2,$00
;
; Temporary storage used when generating a decimal hash value.
02/1157: 00 00 hash0 .fill 2,$00
02/1159: 00 00 hash1 .fill 2,$00
02/115b: 00 00 hash2 .fill 2,$00
;
; Releases a VCR if it's open count is zero.
;
; On entry:
; $88-8b: pointer to VCR
;
; On exit:
; Carry set on failure (files are open)
; (note: A-reg indeterminate)
;
02/115d: a0 08 00 FreeVcrIfIdle ldy #O_VCR_OPEN_CNT
02/1160: b7 88 lda [vcr_ptr],y ;get open count
02/1162: f0 02 beq :NoOpen ;none, continue
02/1164: 38 sec ;some open, fail
02/1165: 60 rts
02/1166: a0 00 00 :NoOpen ldy #O_VCR_ID
02/1169: b7 88 lda [vcr_ptr],y ;get the VCR's ID
02/116b: 22 28 fc 01 jsl RELEASE_VCR ;release it
02/116f: 18 clc
02/1170: 60 rts
;
; Compares 512 bytes from the VCR work buffer with saved data (the VTOC and
; first catalog sector). Useful for determining whether the disk in the drive
; has changed.
;
; On entry:
; $8c-8f: pointer to previously-read data
;
; On exit:
; Carry set on error
;
• Clear variables
]tmp_ptr .var $ae {addr/4}
02/1171: a0 0e 00 CmpStoredVtoc ldy #O_VCRX_BUF_VPTR
02/1174: b7 88 lda [vcr_ptr],y ;get the work buffer vptr stored in the VCR
02/1176: aa tax
02/1177: c8 iny
02/1178: c8 iny
02/1179: b7 88 lda [vcr_ptr],y
02/117b: a8 tay
02/117c: 22 38 fc 01 jsl DEREF ;get a real pointer
02/1180: 86 ae stx ]tmp_ptr
02/1182: 84 b0 sty ]tmp_ptr+2
02/1184: a0 fe 01 ldy #$01fe
02/1187: b7 8c :Loop lda [work_buf_ptr2],y
02/1189: d7 ae cmp []tmp_ptr],y
02/118b: d0 06 bne :Different
02/118d: 88 dey
02/118e: 88 dey
02/118f: 10 f6 bpl :Loop
02/1191: 18 clc ;no differences, return success
02/1192: 60 rts
02/1193: 38 :Different sec
02/1194: 60 rts
;
; Obtains the device ID for the specified device.
;
; The Apple 5.25" drive (includes Unidisk, Duodisk, DiskIIc and Disk ][ drives)
; has device ID $0000. Since the FST only works with 140K floppies, any nonzero
; return value indicates an incompatible device.
;
; See table 8-1 in the GS/OS Driver Reference for a list of IDs.
;
; NOTE: I'm not entirely sure what's going on here, but it looks like they're
; making a Driver_Open call with an invalid deviceNum as an undocumented way to
; get the DIB of a device.
;
; On entry:
; $00: device number
;
; On exit:
; Carry set on error
; A-reg: device ID ($0000-$001f) or error code
; On success, Z flag set according to value in A-reg
;
02/1195: a5 00 GetDeviceId lda drvr_dev_num ;preserve device number
02/1197: 48 pha
02/1198: 85 04 sta drvr_buf_ptr ;store device number here (?)
02/119a: 64 00 stz drvr_dev_num ;set device number to zero
02/119c: a9 01 00 lda #DRIVER_OPEN ;open device to get DIB
02/119f: 85 02 sta drvr_call_num
02/11a1: 22 00 fc 01 jsl DEV_DISPATCHER
02/11a5: 7a ply
02/11a6: 84 00 sty drvr_dev_num ;restore device numer
02/11a8: b0 05 bcs :Return
02/11aa: a0 34 00 ldy #O_DIB_DEVICE_ID
02/11ad: b7 20 lda [drvr_dib_ptr],y ;get device ID
02/11af: 60 :Return rts
;
; Finds a DOS 3.3 volume with a matching volume name. Loads the disk VTOC and
; first catalog sector into the work buffer.
;
; Devices are scanned in sequential order. If no device number is specified,
; the scan begins at the start of the list, and checks all devices. If a device
; number is provided, that device is checked first. If it doesn't match,
; devices with matching device IDs are scanned in sequential order, starting
; with the next device.
;
; Increment the VCR's open count before calling here.
;
; On entry:
; $80-83: pointer to work buffer (will receive VTOC and 1st cat sect)
; $b8-bb: pointer to buffer to receive generated volume name
; $01e0: device number to examine first
;
; On exit:
; Carry set on failure
;
FindDosVolByName
02/11b0: ae e0 01 ldx dev_num_arg ;device num set?
02/11b3: d0 32 bne :HaveDev ;yes, branch
02/11b5: e8 :Loop inx
02/11b6: 86 00 stx drvr_dev_num
02/11b8: 20 12 10 jsr ReadVtocGenName
02/11bb: 90 09 bcc :FoundDos
02/11bd: c9 11 00 cmp #E_INVALID_DEV_NUM ;did we reach the end of the device list?
02/11c0: f0 76 beq :VolNotFound ;yes, bail
02/11c2: a6 00 :NextDev ldx drvr_dev_num ;nope, just not a DOS 3.3 disk; try the next one
02/11c4: 80 ef bra :Loop
; We found a DOS 3.3 volume. Check to see if it's the one we're looking for.
02/11c6: a5 84 :FoundDos lda vol_name_ptr ;set up pointers for string comparison
02/11c8: 85 a0 sta strcmp_arg0
02/11ca: a5 86 lda vol_name_ptr+2
02/11cc: 85 a2 sta strcmp_arg0+2
02/11ce: a5 b8 lda gen_vol_name_ptr
02/11d0: 85 a4 sta strcmp_arg1
02/11d2: a9 02 00 lda #^gen_vol_name_buf
02/11d5: 85 a6 sta strcmp_arg1+2
02/11d7: a9 ff 00 lda #$00ff ;mask no bits
02/11da: 8d ec 01 sta strcmp_mask
02/11dd: 20 0e 0f jsr StringCompare ;does the volume name match?
02/11e0: 90 54 bcc :Success ;yes, return success
02/11e2: 9c 40 02 stz error_mod ;clear the error modifier set by the VTOC reader
02/11e5: 80 db bra :NextDev ;try the next device
02/11e7: 86 00 :HaveDev stx drvr_dev_num
02/11e9: 20 95 11 jsr GetDeviceId ;get device ID (e.g. 5.25" drive == 0)
02/11ec: 90 01 bcc :GotId
02/11ee: 60 rts ;couldn't even do that, bail
02/11ef: 8d 3d 12 :GotId sta :device_id
02/11f2: 20 12 10 :CheckVtoc jsr ReadVtocGenName ;read VTOC, make sure it's DOS 3.3
02/11f5: 90 23 bcc :GotVtoc ;looks good, branch
; We didn't find it in the desired device. Check to see if it has moved to
; another device with the same device ID. We start from the device we were
; given and search forward in the list, then loop back around if we don't find
; it.
02/11f7: c9 11 00 cmp #E_INVALID_DEV_NUM ;invalid device number?
02/11fa: d0 02 bne :Loop2 ;no, jump into loop
02/11fc: 64 00 :Inval stz drvr_dev_num ;start at the start of the device list
02/11fe: a6 00 :Loop2 ldx drvr_dev_num
02/1200: e8 inx
02/1201: ec e0 01 cpx dev_num_arg ;did we reach the initial device?
02/1204: f0 32 beq :VolNotFound ;yes, give up
02/1206: 86 00 stx drvr_dev_num
02/1208: 20 95 11 jsr GetDeviceId ;check next device
02/120b: 90 06 bcc :GotId2 ;success, branch
02/120d: c9 11 00 cmp #E_INVALID_DEV_NUM ;did we hit the end of the device list?
02/1210: f0 ea beq :Inval ;yes, go back to start of list
02/1212: 60 rts
02/1213: cd 3d 12 :GotId2 cmp :device_id ;is this device the same type as the initial request?
02/1216: f0 da beq :CheckVtoc ;yes, check it
02/1218: 80 e4 bra :Loop2 ;no, move on to next device
; We found a DOS 3.3 VTOC during our device scan. See if the volume name
; matches.
02/121a: a5 84 :GotVtoc lda vol_name_ptr ;set up pointers for string comparison
02/121c: 85 a0 sta strcmp_arg0
02/121e: a5 86 lda vol_name_ptr+2
02/1220: 85 a2 sta strcmp_arg0+2
02/1222: a5 b8 lda gen_vol_name_ptr
02/1224: 85 a4 sta strcmp_arg1
02/1226: a9 02 00 lda #^gen_vol_name_buf
02/1229: 85 a6 sta strcmp_arg1+2
02/122b: a9 ff 00 lda #$00ff ;mask no bits
02/122e: 8d ec 01 sta strcmp_mask
02/1231: 20 0e 0f jsr StringCompare
02/1234: b0 c8 bcs :Loop2 ;didn't match, try next device
02/1236: 18 :Success clc
02/1237: 60 rts
02/1238: a9 45 00 :VolNotFound lda #E_VOL_NOT_FOUND
02/123b: 38 sec
02/123c: 60 rts
02/123d: 00 00 :device_id .fill 2,$00 ;will be zero for 5.25" drives
;
; Finds an open file. Used by GetFileInfo.
;
; Does not find the volume dir.
;
; On entry:
; $88-8b: pointer to VCR associated with file
;
; On exit:
; Carry set if not found
; $01e6: index into FCR table
;
• Clear variables
]name_ptr .var $ae {addr/4}
02/123f: 9c e6 01 FindOpenFile stz wronly_01e6
02/1242: 9c e8 01 stz fcr_index ;init FCR index
02/1245: ee e8 01 :FcrLoop inc fcr_index ;advance to next FCR
02/1248: ad e8 01 lda fcr_index
02/124b: 22 64 fc 01 jsl GET_FCR ;get the FCR
02/124f: b0 7a bcs :Return ;failed, bail
02/1251: 22 38 fc 01 jsl DEREF ;convert vptr to pointer
02/1255: 86 94 stx fcr_ptr2 ;copy to DP
02/1257: 84 96 sty fcr_ptr2+2
02/1259: a0 06 00 ldy #O_FCR_FST_ID
02/125c: b7 94 lda [fcr_ptr2],y ;get the file system ID
02/125e: c9 02 00 cmp #FSID_DOS33 ;is it DOS 3.3?
02/1261: d0 e2 bne :FcrLoop ;no, move on to the next one
02/1263: a0 08 00 ldy #O_FCR_VOL_ID
02/1266: b7 94 lda [fcr_ptr2],y ;get the FCR's volume ID
02/1268: a0 00 00 ldy #O_VCR_ID
02/126b: d7 88 cmp [vcr_ptr],y ;compare to VCR's volume ID
02/126d: d0 d6 bne :FcrLoop ;doesn't match, move on
02/126f: a0 24 00 ldy #O_FCRX_FILE_TYPE
02/1272: b7 94 lda [fcr_ptr2],y ;get the file's type
02/1274: c9 0f 00 cmp #$000f ;is it DIR?
02/1277: f0 cc beq :FcrLoop ;yes, we don't handle that here
02/1279: a0 02 00 ldy #O_FCR_PATH_NAME
02/127c: b7 94 lda [fcr_ptr2],y ;get vptr to pathname
02/127e: aa tax
02/127f: c8 iny
02/1280: c8 iny
02/1281: b7 94 lda [fcr_ptr2],y
02/1283: a8 tay
02/1284: 22 38 fc 01 jsl DEREF ;convert to real pointer
02/1288: 86 ae stx ]name_ptr ;copy to DP
02/128a: 84 b0 sty ]name_ptr+2
02/128c: e2 20 sep #$20
.rwid shortm
02/128e: a0 03 00 ldy #$0003
02/1291: c8 :PathBrkLoop iny
02/1292: b7 ae lda []name_ptr],y ;scan the pathname
02/1294: c9 3a cmp #‘:’ ;look for a colon
02/1296: d0 f9 bne :PathBrkLoop
02/1298: a2 01 00 ldx #$0001
02/129b: c8 :CopyLoop iny
02/129c: e8 inx
02/129d: b7 ae lda []name_ptr],y ;copy the interesting part of the filename out
02/129f: 9d dc 02 sta gen_vol_colon_buf,x ; into this buffer
02/12a2: d0 f7 bne :CopyLoop
02/12a4: c2 30 rep #$30
.rwid longm
02/12a6: 8a txa
02/12a7: 3a dec A ;dec twice to convert index to len
02/12a8: 3a dec A
02/12a9: 8d dc 02 sta gen_vol_colon_buf ;save len at start of string
02/12ac: a2 dc 02 ldx #gen_vol_colon_buf ;use this as string #1
02/12af: a0 02 00 ldy #^gen_vol_colon_buf
02/12b2: 86 a4 stx strcmp_arg1
02/12b4: 84 a6 sty strcmp_arg1+2
02/12b6: a5 b2 lda file_name_ptr ;use filename arg as string #2
02/12b8: 85 a0 sta strcmp_arg0
02/12ba: a9 02 00 lda #^req_file_name_buf
02/12bd: 85 a2 sta strcmp_arg0+2
02/12bf: 20 0e 0f jsr StringCompare ;does it match?
02/12c2: b0 81 bcs :FcrLoop ;no, keep looking
02/12c4: ad e8 01 lda fcr_index
02/12c7: 8d e6 01 sta wronly_01e6 ;save FCR index
02/12ca: 18 clc
02/12cb: 60 :Return rts
;
; Separates a pathname into volume name and file name components. If the
; pathname is just a volume name (e.g. opening the volume directory), ($b2) will
; be zero.
;
; Colons are valid chars in DOS 3.3 names, but not in GS/OS pathnames. The FST
; converts ':' to and from ',', because it's hard to get a comma in a DOS name.
;
; On entry:
; $84-87: pointer to volume name buffer
; $b2-b3: pointer to file name buffer
;
; On exit:
; Volume name, if any, copied to $84-87 (without leading ':')
; Filename copied to $b2-b3, with ',' replaced by ':'
; Carry set on error
;
02/12cc: a5 42 SplitPath lda path_flag ;check status of pathname1
02/12ce: 29 00 40 and #$4000
02/12d1: d0 12 bne :GotPath1 ;nonzero, pathname1 is non-null
.rwid shortm
02/12d3: c2 30 :NoPath1 rep #$30
.rwid longm
02/12d5: a9 00 00 lda #$0000 ;zero out the first three bytes of these two buffers
02/12d8: a0 01 00 ldy #$0001 ;(zero out the 2-byte length and the first char)
02/12db: 97 84 sta [vol_name_ptr],y
02/12dd: 91 b2 sta (file_name_ptr),y ;(why would GS/OS ask us to handle a path-based call
02/12df: 87 84 sta [vol_name_ptr] ; with a null pathname?)
02/12e1: 92 b2 sta (file_name_ptr)
02/12e3: 18 clc ;success
02/12e4: 60 rts
02/12e5: e2 20 :GotPath1 sep #$20
.rwid shortm
02/12e7: a0 02 00 ldy #$0002 ;skip first two bytes, which hold the string len
02/12ea: b7 3a lda [fcr_path1_vptr],y ;get first char
02/12ec: f0 e5 beq :NoPath1 ;if it's a zero, branch to no-path case
02/12ee: c9 3a cmp #‘:’ ;is it a colon?
02/12f0: d0 44 bne :FileNameOnly ;no, this is just the filename; branch
; pathname1 starts with a volume name.
02/12f2: c8 :VolCopyLoop iny ;advance index
02/12f3: b7 3a lda [fcr_path1_vptr],y ;get char from input path
02/12f5: f0 0d beq :VolEnd ;found null terminator, done
02/12f7: c9 3a cmp #‘:’ ;found a colon?
02/12f9: f0 09 beq :VolEnd ;yes, end of vol name
02/12fb: 88 dey ;decr because we're not copying leading ':'
02/12fc: 97 84 sta [vol_name_ptr],y
02/12fe: c8 iny ;incr it back
02/12ff: c0 17 00 cpy #GEN_VOL_MAX_LEN+2 ;reached max generated len? (+2 for len word)
02/1302: 90 ee bcc :VolCopyLoop ;not yet, loop
02/1304: 88 :VolEnd dey ;again, decr because we're not copying leading ':'
02/1305: a9 00 lda #$00
02/1307: 97 84 sta [vol_name_ptr],y ;add null termination
02/1309: c2 21 rep #$21 ;long acc, clear carry
.rwid longm
02/130b: 98 tya
02/130c: e9 01 00 sbc #$0001 ;add 2
02/130f: 87 84 sta [vol_name_ptr] ;set name length word
02/1311: c8 iny ;move back to ':' or '\0'
02/1312: b7 3a lda [fcr_path1_vptr],y ;get the char
02/1314: 29 ff 00 and #$00ff
02/1317: d0 1e bne :VolAndName ;if it's ':', there's a filename too; branch
; Volume name only. Create empty file name.
02/1319: a9 00 00 lda #$0000
02/131c: a0 01 00 ldy #$0001
02/131f: 91 b2 sta (file_name_ptr),y ;set first 3 bytes of filename buf to zero
02/1321: 92 b2 sta (file_name_ptr)
; Do a simple check on pathname2, which will only be set for ChangePath.
02/1323: a5 42 :Check2 lda path_flag ;check status of pathname2
02/1325: 29 40 00 and #$0040
02/1328: d0 04 bne :Check2Len ;nonzero, pathname2 is non-null
02/132a: 18 clc ;no second path, return success
02/132b: 60 rts
02/132c: 38 :Fail sec
02/132d: 60 rts
02/132e: a5 46 :Check2Len lda span2 ;check max distance between separators in path2
02/1330: f0 fa beq :Fail ;is zero, fail
02/1332: c9 1f 00 cmp #DOS_MAX_NAME_LEN+1 ;return with an error if it's > 30 chars
02/1335: 60 rts
; pathname1 includes a file name. Y-reg is the index at (or a byte before) the
; start of the filename in pathname1.
.rwid shortm
02/1336: 88 :FileNameOnly dey ;at start; decr to compensate for incr below
02/1337: a2 02 00 :VolAndName ldx #$0002
02/133a: 8e 72 13 stx :output_index ;set output index, leaving room for length word
02/133d: e2 20 sep #$20
02/133f: c8 :NameCopyLoop iny
02/1340: b7 3a lda [fcr_path1_vptr],y ;get filename char
02/1342: f0 1c beq :FileNameEnd ;if '\0', end reached; branch
02/1344: c9 3a cmp #‘:’ ;is it a colon?
02/1346: f0 26 beq :Fail ;yes, but DOS isn't hierarchical; fail
02/1348: bb tyx ;preserve Y-reg
02/1349: ac 72 13 ldy :output_index ;get output index
02/134c: c9 2c cmp #‘,’ ;was the char a ','?
02/134e: d0 02 bne :NotComma
02/1350: a9 3a lda #‘:’ ;they really meant ':', so drop that in
02/1352: 91 b2 :NotComma sta (file_name_ptr),y ;copy to output buffer
02/1354: c8 iny ;advance to next byte
02/1355: c0 22 00 cpy #DOS_MAX_NAME_LEN+4 ;check length (why isn't this +2?)
02/1358: b0 14 bcs :Fail ;too long, fail
02/135a: 8c 72 13 sty :output_index ;update output index
02/135d: 9b txy ;restore Y-reg
02/135e: 80 df bra :NameCopyLoop
02/1360: ac 72 13 :FileNameEnd ldy :output_index
02/1363: 91 b2 sta (file_name_ptr),y ;add null terminator (A-reg is zero)
02/1365: c2 20 rep #$20
.rwid longm
02/1367: 98 tya
02/1368: 3a dec A ;deduct two for length word
02/1369: 3a dec A
02/136a: 92 b2 sta (file_name_ptr) ;set filename length in output buffer
02/136c: 80 b5 bra :Check2
.rwid shortm
02/136e: c2 20 :Fail rep #$20
.rwid longm
02/1370: 38 sec
02/1371: 60 rts
02/1372: 00 00 :output_index .fill 2,$00
;
; Gets the length and aux type of a file. This can require reading the first
; sector, or scanning through the file.
;
; On entry:
; A-reg: track/sector of first TS list
; $0310-0311: DOS file type byte
;
; On exit:
; Carry flag set on error
; X-reg: file length (low)
; Y-reg: file length (high)
; A-reg: aux type (load address), or total number of sectors used
; $02e2: T/S of first or last sector of file
;
02/1374: a6 98 GetLengthAndAux ldx gbuf_ptr ;use GBUF as read buffer
02/1376: 86 04 stx drvr_buf_ptr
02/1378: a6 9a ldx gbuf_ptr+2
02/137a: 86 06 stx drvr_buf_ptr+2
02/137c: 9c 59 15 stz :ts_ent_count
02/137f: 9c 5b 15 stz :ts_list_count
02/1382: 9c 55 15 stz :file_len
02/1385: 9c 57 15 stz :file_len+2
02/1388: 48 pha ;preserve track/sector
02/1389: 5a phy ;preserve Y-reg (why?)
02/138a: a0 08 00 ldy #O_VCR_OPEN_CNT
02/138d: b7 88 lda [vcr_ptr],y ;bump up the VCR's open count
02/138f: 1a inc A
02/1390: 97 88 sta [vcr_ptr],y
02/1392: 7a ply ;restore Y-reg
02/1393: 68 pla ;restore track/sector
02/1394: 20 8c 0e jsr ReadSector ;read the first TS list
02/1397: 08 php ;preserve result
02/1398: 48 pha
02/1399: 5a phy ;preserve Y-reg (why?)
02/139a: a0 08 00 ldy #O_VCR_OPEN_CNT
02/139d: b7 88 lda [vcr_ptr],y ;restore VCR open count
02/139f: 3a dec A
02/13a0: 97 88 sta [vcr_ptr],y
02/13a2: 7a ply ;restore Y-reg
02/13a3: 68 pla ;restore error code and status
02/13a4: 28 plp
02/13a5: 90 01 bcc :GotTS
02/13a7: 60 rts ;failed
02/13a8: ad 10 03 :GotTS lda file_dos_type ;check the file type
02/13ab: f0 64 beq :IsTxt ;TXT file, branch
02/13ad: 29 17 00 and #%0000000000010111 ;INT/BAS/BIN/REL?
02/13b0: f0 03 beq :IsNon ;'S'/'A'/'B' (NON), branch
02/13b2: 82 2a 01 brl :GetFrom1st ;get from first sector
; File type is NON ('S'/'A'/'B'), so just count up T/S list entries.
02/13b5: a0 0c 00 :IsNon ldy #O_DOSTS_LIST ;offset to start of T/S list entries
02/13b8: ee 5b 15 inc :ts_list_count ;inc number of T/S sectors
02/13bb: b7 04 :NonLoop lda [drvr_buf_ptr],y ;get entry
02/13bd: f0 36 beq :NonDone ;if zero, we're done
02/13bf: ee 59 15 inc :ts_ent_count ;inc number of entries in list
02/13c2: c8 iny ;advance pointer
02/13c3: c8 iny
02/13c4: c0 00 01 cpy #$0100 ;did we reach end of sector buffer?
02/13c7: d0 f2 bne :NonLoop ;not yet, branch
02/13c9: a9 02 00 lda #DRIVER_READ
02/13cc: 85 02 sta drvr_call_num
02/13ce: a0 01 00 ldy #O_DOSTS_NXTTRK
02/13d1: b7 98 lda [gbuf_ptr],y ;get next track/sector
02/13d3: f0 20 beq :NonDone ;if zero, no additional T/S lists
02/13d5: 48 pha
02/13d6: 5a phy ;preserve Y-reg (why? it holds 1)
02/13d7: a0 08 00 ldy #O_VCR_OPEN_CNT
02/13da: b7 88 lda [vcr_ptr],y ;bump up the open count
02/13dc: 1a inc A
02/13dd: 97 88 sta [vcr_ptr],y
02/13df: 7a ply ;restore Y-reg
02/13e0: 68 pla
02/13e1: 20 8c 0e jsr ReadSector ;read the next T/S list
02/13e4: 08 php ;preserve result
02/13e5: 48 pha
02/13e6: 5a phy ;preserve Y-reg
02/13e7: a0 08 00 ldy #O_VCR_OPEN_CNT
02/13ea: b7 88 lda [vcr_ptr],y ;drop the open count
02/13ec: 3a dec A
02/13ed: 97 88 sta [vcr_ptr],y
02/13ef: 7a ply ;restore Y-reg
02/13f0: 68 pla ;restore result
02/13f1: 28 plp
02/13f2: 90 c1 bcc :IsNon
02/13f4: 60 rts ;failed
02/13f5: ad 59 15 :NonDone lda :ts_ent_count ;get number of T/S list entries
02/13f8: eb xba ;multiply by 256
02/13f9: aa tax ;preserve
02/13fa: 29 00 ff and #$ff00 ;high byte holds value * 256
02/13fd: 8d 55 15 sta :file_len ;store in low word
02/1400: 8a txa ;restore
02/1401: 29 ff 00 and #$00ff ;low byte holds value * 65536
02/1404: 8d 57 15 sta :file_len+2 ;store in high word
02/1407: ad 59 15 lda :ts_ent_count ;get the number of T/S entries
02/140a: 18 clc
02/140b: 6d 5b 15 adc :ts_list_count ;add the number of T/S sectors
02/140e: 82 3c 01 brl :Finish ; to get the total number of sectors used
; File type is TXT. The length is determined by finding the last sector and
; stopping at the last non-$00. (I believe this is incorrect -- should stop at
; the first $00.) We don't attempt to identify random-access text files, but we
; do deal with "sparse" files, which have zeroed T/S entries in the middle.
02/1411: a2 0c 00 :IsTxt ldx #O_DOSTS_LIST ;reset X-reg/Y-reg to start of T/S list
02/1414: a0 0c 00 ldy #O_DOSTS_LIST
02/1417: ee 5b 15 inc :ts_list_count ;increment number of T/S list sectors
02/141a: b7 04 :TxtLoop lda [drvr_buf_ptr],y ;get T/S entry
02/141c: f0 04 beq :TxtZero ;zero, sparse or past end; branch
02/141e: bb tyx ;remember this index
02/141f: ee 59 15 inc :ts_ent_count ;increment number of T/S entries
02/1422: c8 :TxtZero iny ;advance to next entry
02/1423: c8 iny
02/1424: c0 00 01 cpy #$0100 ;reached end of T/S sector?
02/1427: d0 f1 bne :TxtLoop ;not yet, branch
02/1429: a9 02 00 lda #DRIVER_READ
02/142c: 85 02 sta drvr_call_num
02/142e: a0 01 00 ldy #O_DOSTS_NXTTRK
02/1431: b7 98 lda [gbuf_ptr],y ;get track/sector of the next T/S list
02/1433: f0 20 beq :TxtDone ;if it's zero, we're done
02/1435: 48 pha ;preserve track/sector
02/1436: 5a phy ;preserve Y-reg (why?)
02/1437: a0 08 00 ldy #O_VCR_OPEN_CNT
02/143a: b7 88 lda [vcr_ptr],y ;bump up the open count
02/143c: 1a inc A
02/143d: 97 88 sta [vcr_ptr],y
02/143f: 7a ply ;restore Y-reg
02/1440: 68 pla ;restore track/sector
02/1441: 20 8c 0e jsr ReadSector ;read the next T/S sector
02/1444: 08 php ;preserve status
02/1445: 48 pha
02/1446: 5a phy ;preserve Y-reg (why?)
02/1447: a0 08 00 ldy #O_VCR_OPEN_CNT
02/144a: b7 88 lda [vcr_ptr],y ;drop the VCR open count
02/144c: 3a dec A
02/144d: 97 88 sta [vcr_ptr],y
02/144f: 7a ply ;restore Y-reg
02/1450: 68 pla ;restore status
02/1451: 28 plp
02/1452: 90 bd bcc :IsTxt
02/1454: 60 rts ;failed
02/1455: 8a :TxtDone txa ;get position of last valid T/S entry
02/1456: 38 sec
02/1457: e9 0c 00 sbc #O_DOSTS_LIST ;convert to index
02/145a: 4a lsr A
02/145b: eb xba ;multiply by 256
02/145c: 8d 55 15 sta :file_len ;init file len with it
02/145f: a5 04 lda drvr_buf_ptr ;move the read buffer forward 256 bytes
02/1461: 69 00 01 adc #$0100
02/1464: 85 04 sta drvr_buf_ptr
02/1466: 90 02 bcc :NoInc
02/1468: e6 06 inc drvr_buf_ptr+2
02/146a: a9 02 00 :NoInc lda #DRIVER_READ
02/146d: 85 02 sta drvr_call_num
02/146f: 9b txy ;get position of last valid T/S entry
02/1470: b7 98 lda [gbuf_ptr],y ;get T/S value
02/1472: 8d 2e 02 sta trk_sct_val ;save it
02/1475: d0 05 bne :GotLastSct
02/1477: a0 ff ff ldy #$ffff ;whoops, last T/S list had only zero (sparse) entries
02/147a: 80 2c bra :TxtNonZ ; so report zero additional bytes in last sector
02/147c: 48 :GotLastSct pha ;preserve track/sector
02/147d: 5a phy ;preserve Y-reg (why?)
02/147e: a0 08 00 ldy #O_VCR_OPEN_CNT
02/1481: b7 88 lda [vcr_ptr],y ;bump up the open count
02/1483: 1a inc A
02/1484: 97 88 sta [vcr_ptr],y
02/1486: 7a ply ;restore Y-reg (why not leave on stack?)
02/1487: 68 pla ;restore track/sector
02/1488: 20 8c 0e jsr ReadSector ;read the last data sector in the text file
02/148b: 08 php ;preserve status
02/148c: 48 pha
02/148d: 5a phy ;preserve Y-reg
02/148e: a0 08 00 ldy #O_VCR_OPEN_CNT
02/1491: b7 88 lda [vcr_ptr],y ;drop VCR open count
02/1493: 3a dec A
02/1494: 97 88 sta [vcr_ptr],y
02/1496: 7a ply ;restore Y-reg
02/1497: 68 pla ;restore status
02/1498: 28 plp
02/1499: 90 01 bcc :TxtLastOk
02/149b: 60 rts ;failed
02/149c: a0 ff 00 :TxtLastOk ldy #$00ff ;start at last byte in sector
02/149f: e2 20 sep #$20
.rwid shortm
02/14a1: b7 04 :TxtScanLoop lda [drvr_buf_ptr],y ;read data byte
02/14a3: d0 03 bne :TxtNonZ ;found nonzero value, stop here
02/14a5: 88 dey
02/14a6: 10 f9 bpl :TxtScanLoop
02/14a8: c2 21 :TxtNonZ rep #$21 ;long A-reg, clear carry
.rwid longm
02/14aa: c8 iny ;inc to convert index to count
02/14ab: bb tyx ;save it in X-reg
02/14ac: a0 05 00 ldy #O_DOSTS_SCTOFF ;factor in "sector offset of first sector",
02/14af: b7 98 lda [gbuf_ptr],y ; which is number of T/S entries in previous lists
02/14b1: eb xba ;multiply by 256
02/14b2: a8 tay ;save in Y-reg
02/14b3: 29 00 ff and #$ff00 ;get low byte
02/14b6: 6d 55 15 adc :file_len ;update file length
02/14b9: 8d 55 15 sta :file_len
02/14bc: 98 tya
02/14bd: 29 ff 00 and #$00ff ;get high byte
02/14c0: 6d 57 15 adc :file_len+2 ;update file length
02/14c3: 8d 57 15 sta :file_len+2
02/14c6: 8a txa ;get bytes in last sector
02/14c7: 6d 55 15 adc :file_len ;update file length
02/14ca: 8d 55 15 sta :file_len
02/14cd: ad 57 15 lda :file_len+2
02/14d0: 69 00 00 adc #$0000
02/14d3: 8d 57 15 sta :file_len+2
02/14d6: ad 59 15 lda :ts_ent_count ;get number of non-sparse T/S list entries
02/14d9: 18 clc
02/14da: 6d 5b 15 adc :ts_list_count ;add number of T/S lists
02/14dd: 80 6e bra :Finish
; For INT/BAS/BIN/REL, we get the file length, and possibly the aux type, from
; the first sector of the file.
02/14df: a5 04 :GetFrom1st lda drvr_buf_ptr ;advance the read buffer 256 bytes
02/14e1: 18 clc
02/14e2: 69 00 01 adc #$0100
02/14e5: 85 04 sta drvr_buf_ptr
02/14e7: a5 06 lda drvr_buf_ptr+2
02/14e9: 69 00 00 adc #$0000
02/14ec: 85 06 sta drvr_buf_ptr+2
02/14ee: a9 02 00 lda #DRIVER_READ
02/14f1: 85 02 sta drvr_call_num
02/14f3: a0 0c 00 ldy #O_DOSTS_LIST
02/14f6: b7 98 lda [gbuf_ptr],y ;get first track/sector in T/S list
02/14f8: 8d 2e 02 sta trk_sct_val ;save it
02/14fb: 48 pha ;save it some more
02/14fc: 5a phy ;preserve Y-reg (this must be a macro)
02/14fd: a0 08 00 ldy #O_VCR_OPEN_CNT
02/1500: b7 88 lda [vcr_ptr],y ;bump up the open count
02/1502: 1a inc A
02/1503: 97 88 sta [vcr_ptr],y
02/1505: 7a ply ;restore Y-reg
02/1506: 68 pla ;restore track/sector
02/1507: 20 8c 0e jsr ReadSector ;read first data sector
02/150a: 08 php ;preserve error status
02/150b: 48 pha
02/150c: 5a phy ;preserve Y-reg (why?)
02/150d: a0 08 00 ldy #O_VCR_OPEN_CNT
02/1510: b7 88 lda [vcr_ptr],y ;drop the VCR open count
02/1512: 3a dec A
02/1513: 97 88 sta [vcr_ptr],y
02/1515: 7a ply ;restore Y-reg
02/1516: 68 pla ;restore error status
02/1517: 28 plp
02/1518: 90 01 bcc :Read1stOk
02/151a: 60 rts ;failed
02/151b: ad 10 03 :Read1stOk lda file_dos_type ;get file type byte
02/151e: a0 00 00 ldy #$0000 ;get length from +$00
02/1521: 29 7c 00 and #%0000000001111100 ;BIN/'S'/REL/'A'/'B' ?
02/1524: f0 03 beq :IntBas ;no, INT/BAS; branch
02/1526: a0 02 00 ldy #$0002 ;get length from +$02 instead
02/1529: b7 04 :IntBas lda [drvr_buf_ptr],y ;get length
02/152b: 8d 55 15 sta :file_len ;save it
02/152e: a9 00 00 lda #$0000 ;set high word to zero
02/1531: 8d 57 15 sta :file_len+2
02/1534: ad 10 03 lda file_dos_type ;get file type again
02/1537: 29 7b 00 and #%0000000001111011 ;mask BIN
02/153a: f0 0f beq :CopyAddr ;is BIN, branch (wouldn't be here for TXT)
02/153c: c9 01 00 cmp #$0001 ;INT?
02/153f: d0 05 bne :IsBas ;no, branch
02/1541: a9 00 0c lda #$0c00 ;start address $0c00
02/1544: 80 07 bra :Finish
02/1546: a9 01 08 :IsBas lda #$0801 ;start address $0801
02/1549: 80 02 bra :Finish
02/154b: a7 04 :CopyAddr lda [drvr_buf_ptr] ;get BIN load addr
; For TXT/NON, A-reg holds the total number of sectors used.
; For INT/BAS/BIN/REL, A-reg holds the aux type (load address).
02/154d: ae 55 15 :Finish ldx :file_len
02/1550: ac 57 15 ldy :file_len+2
02/1553: 18 clc
02/1554: 60 rts
02/1555: 00 00 00 00 :file_len .fill 4,$00 ;length of file
02/1559: 00 00 :ts_ent_count .fill 2,$00 ;number of T/S entries in T/S lists
02/155b: 00 00 :ts_list_count .fill 2,$00 ;number of T/S sectors
;
; Reads the rest of the sectors in the catalog into the work buffer. We already
; have the VTOC and first catalog sector in there. This reads at most 14
; additional sectors.
;
; On entry:
; $8c-8f: pointer to work buffer
;
; On exit:
; Carry set on error
; $01ea: number of sectors in catalog
;
02/155d: a5 8c ReadCatTrack lda work_buf_ptr2 ;set drvr_buf_ptr to work buffer +256, so
02/155f: 18 clc ; it points at the first catalog sector
02/1560: 69 00 01 adc #$0100
02/1563: 85 04 sta drvr_buf_ptr
02/1565: a5 8e lda work_buf_ptr2+2
02/1567: 69 00 00 adc #$0000
02/156a: 85 06 sta drvr_buf_ptr+2
02/156c: a2 0e 00 ldx #14 ;read at most 14 more catalog sectors
02/156f: 8e a5 15 stx :cat_sct_counter
02/1572: a0 01 00 :Loop ldy #O_DOSCAT_NXTTRK
02/1575: b7 04 lda [drvr_buf_ptr],y ;get track/sector of next catalog sector
02/1577: f0 20 beq :CatEnd ;zero, done
02/1579: 48 pha ;preserve it
02/157a: a5 04 lda drvr_buf_ptr ;advance the buffer pointer 256 bytes
02/157c: 69 00 01 adc #$0100
02/157f: 85 04 sta drvr_buf_ptr
02/1581: a5 06 lda drvr_buf_ptr+2
02/1583: 69 00 00 adc #$0000
02/1586: 85 06 sta drvr_buf_ptr+2
02/1588: a9 02 00 lda #DRIVER_READ
02/158b: 85 02 sta drvr_call_num
02/158d: 68 pla ;restore track/sector
02/158e: 20 8c 0e jsr ReadSector ;read catalog sector
02/1591: 90 01 bcc :ReadOk
02/1593: 60 rts ;failed
02/1594: ce a5 15 :ReadOk dec :cat_sct_counter ;reached the limit?
02/1597: d0 d9 bne :Loop ;not yet, loop
02/1599: a9 0f 00 :CatEnd lda #15 ;compute (15 - counter)
02/159c: 38 sec
02/159d: ed a5 15 sbc :cat_sct_counter
02/15a0: 8d ea 01 sta num_cat_sectors ;save number of catalog sectors
02/15a3: 18 clc
02/15a4: 60 rts
:cat_sct_counter
02/15a5: 00 00 .fill 2,$00
;
; Finds a file in the catalog, by name.
;
; On entry:
; $b2-b3: pointer to requested file name
;
; On exit:
; Carry set if we found a blank entry and returned in a snit
; $01ee: catalog sector for FDE (1-15)
; $01f0: catalog entry for FDE (0-7, or $ffff if not found)
;
02/15a7: a5 b2 FindFileByName lda file_name_ptr
02/15a9: 85 a0 sta strcmp_arg0
02/15ab: a9 02 00 lda #^req_file_name_buf
02/15ae: 85 a2 sta strcmp_arg0+2
02/15b0: a9 00 00 lda #$0000 ;prepare to walk through the catalog
02/15b3: a2 00 00 :NextSct ldx #$0000
02/15b6: 8d ee 01 :NextEnt sta current_cat_sct
02/15b9: 8e f0 01 stx current_cat_ent
02/15bc: 20 bb 0e jsr SetFdePtr ;get a pointer to the FDE for this sct/ent
02/15bf: 90 01 bcc :GotFde
02/15c1: 60 rts
02/15c2: 20 e1 0e :GotFde jsr GetFdeStatus ;get entry status
02/15c5: f0 4e beq :EndOfCat ;unused entry, end of catalog reached
02/15c7: c9 ff 00 cmp #$00ff ;deleted entry?
02/15ca: f0 37 beq :MoveToNext ;yes, skip to next
02/15cc: a5 a8 lda fde_ptr ;note carry flag is now clear
02/15ce: 69 01 00 adc #$0001
02/15d1: 85 a4 sta strcmp_arg1 ;set strcmp arg to FDE ptr + 1
02/15d3: a5 aa lda fde_ptr+2 ;name is at +3, but we want to line up with
02/15d5: 69 00 00 adc #$0000 ; requested name, which has length word
02/15d8: 85 a6 sta strcmp_arg1+2
02/15da: a0 1f 00 ldy #DOS_MAX_NAME_LEN+1 ;+2 because of length word, -1 so we index last char
02/15dd: a7 a4 lda [strcmp_arg1] ;get +1/+2 (sector and file type)
02/15df: 48 pha ;preserve on stack
02/15e0: e2 20 sep #$20
.rwid shortm
02/15e2: b7 a4 :LenLoop lda [strcmp_arg1],y ;find end of FDE filename
02/15e4: c9 a0 cmp #“ ”
02/15e6: d0 08 bne :FoundEnd
02/15e8: 88 dey
02/15e9: 10 f7 bpl :LenLoop
02/15eb: fa plx ;pop this off
02/15ec: c2 30 rep #$30
.rwid longm
02/15ee: 38 sec ;GetFdeStatus should have caught blank entry
02/15ef: 60 rts
.rwid shortm
02/15f0: 88 :FoundEnd dey
02/15f1: a9 7f lda #$7f ;request is low ASCII, FDE is high ASCII
02/15f3: 8d ec 01 sta strcmp_mask ;so strip high bits when comparing
02/15f6: c2 20 rep #$20
.rwid longm
02/15f8: 98 tya
02/15f9: 87 a4 sta [strcmp_arg1] ;store the length at the start of the FDE name
02/15fb: 20 0e 0f jsr StringCompare ;compare the strings
02/15fe: 68 pla
02/15ff: 87 a4 sta [strcmp_arg1] ;restore original values in FDE
02/1601: 90 1a bcc :Success ;if they matched, we're done; branch
02/1603: ae f0 01 :MoveToNext ldx current_cat_ent ;get current sct/ent
02/1606: ad ee 01 lda current_cat_sct
02/1609: e8 inx ;next entry
02/160a: e0 07 00 cpx #$0007
02/160d: 90 a7 bcc :NextEnt
02/160f: 1a inc A ;next sector
02/1610: cd ea 01 cmp num_cat_sectors
02/1613: 90 9e bcc :NextSct
02/1615: a9 ff ff :EndOfCat lda #$ffff ;report file not found
02/1618: 8d f0 01 sta current_cat_ent
02/161b: 18 clc
02/161c: 60 rts
02/161d: 18 :Success clc
02/161e: 60 rts
;
; Outputs an option list.
;
; On entry:
; Y-reg: offset of optionList field in parm block
;
; On exit:
; Carry set on error
;
]ol_ptr .var $b4 {addr/4}
OutputOptionList
02/161f: b7 32 lda [param_blk_ptr],y ;get pointer to user's optionList buffer
02/1621: 85 b4 sta ]ol_ptr
02/1623: c8 iny
02/1624: c8 iny
02/1625: b7 32 lda [param_blk_ptr],y
02/1627: 85 b6 sta ]ol_ptr+2
02/1629: 05 b4 ora ]ol_ptr ;check for null
02/162b: f0 13 beq :Done ;nothing to do
02/162d: a7 b4 lda []ol_ptr] ;get length word
02/162f: c9 06 00 cmp #$0006 ;room for 6 bytes?
02/1632: b0 02 bcs :GotRoom ;yes, continue
02/1634: 38 sec
02/1635: 60 rts
02/1636: a9 02 00 :GotRoom lda #$0002
02/1639: a8 tay ;reqSize field
02/163a: 97 b4 sta []ol_ptr],y ;set outputLength to 2
02/163c: c8 iny
02/163d: c8 iny ;fileSysID field
02/163e: 97 b4 sta []ol_ptr],y ;set that to 2 (DOS 3.3)
02/1640: 18 :Done clc
02/1641: 60 rts
;
; Handles FSTSpecific function set_text_mode.
; (Programmer's Reference for System 6.0, page 332)
;
• Clear variables
02/1642: ad f4 01 FstSetTextMode lda pcount
02/1645: c9 03 00 cmp #$0003 ;verify pCount == 3
02/1648: f0 05 beq :CountOk
02/164a: a9 04 00 lda #E_INVALID_PCOUNT
02/164d: 38 sec
02/164e: 60 rts
02/164f: a0 06 00 :CountOk ldy #$0006 ;text_mode field
02/1652: b7 32 lda [param_blk_ptr],y ;get value
02/1654: c9 02 00 cmp #$0002 ;must be $0000 or $0001
02/1657: 90 05 bcc :Success
02/1659: a9 53 00 lda #E_PARM_RANGE_ERR
02/165c: 38 sec
02/165d: 60 rts
02/165e: 8d 38 02 :Success sta text_mode ;save value
02/1661: a9 00 00 lda #$0000 ;return success
02/1664: 18 clc
02/1665: 60 rts
;
; Handles FSTSpecific function get_text_mode.
; (Programmer's Reference for System 6.0, page 332)
;
02/1666: ad f4 01 FstGetTextMode lda pcount
02/1669: c9 03 00 cmp #$0003 ;verify pCount == 3
02/166c: f0 05 beq :CountOk
02/166e: a9 04 00 lda #E_INVALID_PCOUNT
02/1671: 38 sec
02/1672: 60 rts
02/1673: ad 38 02 :CountOk lda text_mode ;get current text mode
02/1676: a0 06 00 ldy #$0006 ;text_mode field
02/1679: 97 32 sta [param_blk_ptr],y ;set return value
02/167b: a9 00 00 lda #$0000 ;return success
02/167e: 18 clc
02/167f: 60 rts
;
; Get various file attributes, from the FDE and the file itself. A T/S list
; from the file will be left in the FCR's read buffer.
;
; On entry:
; $a8-ab: pointer to the file's FDE
;
; On exit:
; $0310-0311: DOS file type (lock flag cleared)
; $0312-0313: ProDOS file type
; $0314-0315: ProDOS access flags
; $0316-0317: ProDOS aux type
; $0318-031b: file length
;
02/1680: a9 01 00 GetFileAttrs lda #$0001 ;ordinary file (not DIR)
02/1683: 8d 20 03 sta file_storage_type
02/1686: a0 02 00 ldy #O_DOSFDE_TYPE
02/1689: b7 a8 lda [fde_ptr],y ;get DOS file type byte
02/168b: aa tax ;preserve in X-reg
02/168c: 29 80 00 and #$0080 ;check locked flag
02/168f: d0 05 bne :IsLocked
02/1691: a9 c3 00 lda #$00c3 ;unlocked: rename/delete/read/read
02/1694: 80 03 bra :Cont
02/1696: a9 01 00 :IsLocked lda #$0001 ;locked: read
02/1699: 8d 14 03 :Cont sta file_access
02/169c: 8a txa ;restore DOS file type byte
02/169d: 29 7f 00 and #$007f ;remove "locked" flag
02/16a0: 8d 10 03 sta file_dos_type
; Convert file type to index.
02/16a3: a2 08 00 ldx #$0008
02/16a6: 4a :Loop lsr A
02/16a7: b0 03 bcs :Got1
02/16a9: ca dex
02/16aa: d0 fa bne :Loop
02/16ac: 8a :Got1 txa
02/16ad: 0a asl A ;double for index into 16-bit values
02/16ae: aa tax
02/16af: bd f3 16 lda file_type_map,x ;get ProDOS file type equivalent
02/16b2: 8d 12 03 sta file_pro_type ;save it
02/16b5: ad 10 03 lda file_dos_type ;get the DOS type byte again
02/16b8: 29 17 00 and #%0000000000010111 ;mask INT/BAS/BIN/REL
02/16bb: f0 1e beq :TxtNon ;TXT or NON, branch
02/16bd: a0 00 00 ldy #$0000
02/16c0: b7 a8 lda [fde_ptr],y ;get track/sector of first TS list
02/16c2: 20 74 13 jsr GetLengthAndAux ;get the file's length
02/16c5: 90 01 bcc :GotLen
02/16c7: 60 rts ;failed
02/16c8: 8d 16 03 :GotLen sta file_auxtype
02/16cb: 8e 18 03 stx file_eof
02/16ce: 8c 1a 03 sty file_eof+2
02/16d1: a0 21 00 ldy #O_DOSFDE_LEN
02/16d4: b7 a8 lda [fde_ptr],y ;get length in sectors
02/16d6: 8d 1c 03 sta file_len_in_sct
02/16d9: 18 clc
02/16da: 60 rts
02/16db: a0 00 00 :TxtNon ldy #O_DOSFDE_TRK
02/16de: b7 a8 lda [fde_ptr],y ;get track/sector of first TS list
02/16e0: 20 74 13 jsr GetLengthAndAux ;get the file's length
02/16e3: 90 01 bcc :GotLen2
02/16e5: 60 rts ;failed
02/16e6: 8d 1c 03 :GotLen2 sta file_len_in_sct
02/16e9: 8e 18 03 stx file_eof
02/16ec: 8c 1a 03 sty file_eof+2
02/16ef: 9c 16 03 stz file_auxtype ;sequential text file (or zero for NON)
02/16f2: 60 rts
;
; Map from DOS file type byte to ProDOS file type. Start by converting the DOS
; type to an index:
;
; 00000000 -> 0 (TXT)
; 00000001 -> 8 (INT)
; 00000010 -> 7 (BAS)
; 00000100 -> 6 (BIN)
; 00001000 -> 5 ('S')
; 00010000 -> 4 (REL)
; 00100000 -> 3 ('A')
; 01000000 -> 2 ('B')
;
; Use index 1 for the catalog track (volume directory), which we can identify by
; setting the DOS type byte to $80 (which would normally be a locked text file).
;
; Not sure what entry #9 (R16) is doing here.
;
02/16f3: 04 00 file_type_map .dd2 $0004 ;TXT
02/16f5: 0f 00 .dd2 $000f ;DIR
02/16f7: 00 00 .dd2 $0000 ;NON
02/16f9: 00 00 .dd2 $0000 ;NON
02/16fb: fe 00 .dd2 $00fe ;REL
02/16fd: 00 00 .dd2 $0000 ;NON
02/16ff: 06 00 .dd2 $0006 ;BIN
02/1701: fc 00 .dd2 $00fc ;BAS
02/1703: fa 00 .dd2 $00fa ;INT
02/1705: ee 00 .dd2 $00ee ;R16
;
; Handles the GetDirEntry call.
; (GS/OS ref p.148 and p.384)
;
; The FCR passed in is for the volume directory (catalog).
;
; GS/OS tech note #13 (GS/OS Reference Update) says:
; "On page 386, nameBuffer is described as a pointer to a buffer in which GS/OS
; returns a Pascal string containing the name of the file or directory entry (in
; GetDirEntry). This is incorrect; all versions of GetDirEntry return GS/OS
; (word-length) strings for the directory entry."
;
; The use of GS/OS strings in a ProDOS-16 call is possible because GetDirEntry
; wasn't part of the initial ProDOS-16 definition.
;
; Unlike other calls that return a path or volume name, when returning $4F
; (buffTooSmall), GetDirEntry copies as much of the filename as will fit into
; the output buffer. See p.59 in the GS/OS Reference.
;
; On entry:
; $80-83: pointer to a 4K work buffer (*not* the VCR work buffer)
;
; On exit:
; Carry set on error (ignored by caller)
; A-reg: error code (zero on success)
;
]name_ptr .var $b4 {addr/4}
02/1707: a0 0c 00 DoGetDirEntry ldy #O_VCR_DEV
02/170a: b7 88 lda [vcr_ptr],y ;get device number for this volume
02/170c: 85 00 sta drvr_dev_num
02/170e: 9c 31 19 stz :buf_small_err
02/1711: a0 24 00 ldy #O_FCRX_FILE_TYPE
02/1714: b7 90 lda [fcr_ptr],y ;get the file type
02/1716: c9 0f 00 cmp #$000f ;did they pass a directory?
02/1719: f0 05 beq :IsDir ;yes, branch
02/171b: a9 4a 80 lda #E_BAD_FILE_FORMAT-$8000 ;can't GetDirEntry from a non-directory
02/171e: 38 sec
02/171f: 60 rts
02/1720: ad f2 01 :IsDir lda call_class ;ProDOS-16?
02/1723: f0 1f beq :IsP16 ;yes, branch
02/1725: a0 06 00 ldy #$0006 ;base field (0/1/2)
02/1728: b7 32 lda [param_blk_ptr],y
02/172a: 8d 33 19 sta :base_arg
02/172d: a0 08 00 ldy #$0008 ;displacement field
02/1730: b7 32 lda [param_blk_ptr],y
02/1732: 8d 35 19 sta :displace_arg
02/1735: a0 0a 00 ldy #$000a ;name field
02/1738: b7 32 lda [param_blk_ptr],y
02/173a: 85 b4 sta ]name_ptr
02/173c: c8 iny
02/173d: c8 iny
02/173e: b7 32 lda [param_blk_ptr],y
02/1740: 85 b6 sta ]name_ptr+2
02/1742: 80 26 bra :Cont
02/1744: a0 04 00 :IsP16 ldy #$0004 ;base field
02/1747: b7 32 lda [param_blk_ptr],y
02/1749: 8d 33 19 sta :base_arg
02/174c: a0 06 00 ldy #$0006 ;displacement field
02/174f: b7 32 lda [param_blk_ptr],y
02/1751: 8d 35 19 sta :displace_arg
02/1754: a0 28 00 ldy #O_FCRX_NUM_SCT
02/1757: b7 90 lda [fcr_ptr],y
02/1759: 3a dec A
02/175a: 8d ea 01 sta num_cat_sectors
02/175d: a0 08 00 ldy #$0008 ;nameBuffer field
02/1760: b7 32 lda [param_blk_ptr],y
02/1762: 85 b4 sta ]name_ptr
02/1764: c8 iny
02/1765: c8 iny
02/1766: b7 32 lda [param_blk_ptr],y
02/1768: 85 b6 sta ]name_ptr+2
; Now that we have base+displacement, calculate the entry number.
02/176a: ad 33 19 :Cont lda :base_arg
02/176d: aa tax ;preserve base
02/176e: 0d 35 19 ora :displace_arg ;see if they're both zero
02/1771: f0 0d beq :GetTotalEntries ;they are; go get the total entry count
02/1773: 8a txa ;restore base
02/1774: f0 1d beq :AbsDisp ;branch if base=0 (absolute displacement)
02/1776: 3a dec A
02/1777: f0 31 beq :AddDisp ;branch if base=1 (add displacement)
02/1779: 3a dec A
02/177a: f0 49 beq :SubDisp ;branch if base=2 (subtract displacement)
02/177c: a9 53 00 lda #E_PARM_RANGE_ERR ;base >= 3, fail
02/177f: 60 rts
:GetTotalEntries
02/1780: a0 30 00 ldy #O_FCRX_NUM_ENTRIES
02/1783: b7 90 lda [fcr_ptr],y ;get number of entries in catalog
02/1785: 8d 1e 03 sta file_entry_num ;copy to intermediate area
02/1788: a9 00 00 lda #$0000
02/178b: a0 2c 00 ldy #O_FCRX_CUR_DISP
02/178e: 97 90 sta [fcr_ptr],y ;reset current displacement to zero
02/1790: 82 50 01 brl :GenerateOutput
02/1793: a0 30 00 :AbsDisp ldy #O_FCRX_NUM_ENTRIES
02/1796: b7 90 lda [fcr_ptr],y ;get total number of entries
02/1798: cd 35 19 cmp :displace_arg ;compare to requested displacement
02/179b: b0 05 bcs :RangeOk ;branch if ok
02/179d: a9 61 00 lda #E_END_OF_DIR
02/17a0: 38 sec
02/17a1: 60 rts
02/17a2: ad 35 19 :RangeOk lda :displace_arg
02/17a5: 8d 1e 03 sta file_entry_num ;save it
02/17a8: 80 3d bra :GotDisp
02/17aa: a0 2c 00 :AddDisp ldy #O_FCRX_CUR_DISP
02/17ad: b7 90 lda [fcr_ptr],y ;get current displacement
02/17af: 18 clc
02/17b0: 6d 35 19 adc :displace_arg ;add requested delta
02/17b3: 8d 1e 03 sta file_entry_num ;save result
02/17b6: a0 30 00 ldy #O_FCRX_NUM_ENTRIES
02/17b9: b7 90 lda [fcr_ptr],y ;get total number of entries
02/17bb: cd 1e 03 cmp file_entry_num ;range check
02/17be: b0 27 bcs :GotDisp ;good, branch
02/17c0: a9 61 00 lda #E_END_OF_DIR
02/17c3: 38 sec
02/17c4: 60 rts
02/17c5: a0 2c 00 :SubDisp ldy #O_FCRX_FDE_SCT
02/17c8: b7 90 lda [fcr_ptr],y ;get current displacement
02/17ca: 3a dec A ;check for underflow by comparing (current-1) to
02/17cb: cd 35 19 cmp :displace_arg ; requested displacement; properly handles overflow
02/17ce: 90 12 bcc :BadSub ; (e.g. "10 - 0xffff" is an error, not 11)
02/17d0: 1a inc A ;restore original
02/17d1: 38 sec
02/17d2: ed 35 19 sbc :displace_arg ;subtract requested delta
02/17d5: 8d 1e 03 sta file_entry_num ;save result
02/17d8: a0 30 00 ldy #O_FCRX_NUM_ENTRIES
02/17db: b7 90 lda [fcr_ptr],y ;get total number of entries
02/17dd: cd 1e 03 cmp file_entry_num ;range check (is this necessary?)
02/17e0: b0 05 bcs :GotDisp
02/17e2: a9 61 00 :BadSub lda #E_END_OF_DIR
02/17e5: 38 sec
02/17e6: 60 rts
; We now have the entry number (displacement) of the desired item. Now we need
; to walk through the catalog and find the Nth non-deleted entry.
02/17e7: ad 1e 03 :GotDisp lda file_entry_num
02/17ea: a0 2c 00 ldy #O_FCRX_CUR_DISP
02/17ed: 97 90 sta [fcr_ptr],y ;save current displacement for next time
02/17ef: 8d 37 19 sta :cur_displace
02/17f2: a0 0e 00 ldy #O_VCRX_BUF_VPTR
02/17f5: b7 88 lda [vcr_ptr],y ;get VCR work buffer, which has catalog track
02/17f7: aa tax
02/17f8: c8 iny
02/17f9: c8 iny
02/17fa: b7 88 lda [vcr_ptr],y
02/17fc: a8 tay
02/17fd: 22 38 fc 01 jsl DEREF ;get real pointer
02/1801: 86 8c stx work_buf_ptr2 ;copy to DP
02/1803: 84 8e sty work_buf_ptr2+2
02/1805: a9 00 00 lda #$0000
02/1808: a2 00 00 :LoopNextSct ldx #$0000
02/180b: 8d ee 01 :LoopInSct sta current_cat_sct
02/180e: 8e f0 01 stx current_cat_ent
02/1811: 20 bb 0e jsr SetFdePtr ;calc ptr from sector and index
02/1814: 90 01 bcc :FdePtrOk
02/1816: 60 rts
02/1817: 20 e1 0e :FdePtrOk jsr GetFdeStatus ;check status of entry (normal, deleted, unused)
02/181a: d0 05 bne :IsUsed
02/181c: a9 61 80 lda #E_END_OF_DIR-$8000 ;found an unused entry, must be end of dir
02/181f: 38 sec
02/1820: 60 rts
02/1821: c9 ff 00 :IsUsed cmp #$00ff ;deleted entry?
02/1824: f0 05 beq :IsDel ;yes, ignore it, branch
02/1826: ce 37 19 dec :cur_displace ;count it down
02/1829: f0 12 beq :SearchDone
02/182b: ae f0 01 :IsDel ldx current_cat_ent
02/182e: ad ee 01 lda current_cat_sct
02/1831: e8 inx ;next entry
02/1832: e0 07 00 cpx #$0007 ;catalog sectors hold 7 entries
02/1835: 90 d4 bcc :LoopInSct ;still going in this one
02/1837: 1a inc A ;move to next sector
02/1838: cd ea 01 cmp num_cat_sectors ;reached the end?
02/183b: 90 cb bcc :LoopNextSct ;not yet
; We have a pointer to the desired File Descriptive Entry. (Or we somehow ran
; off the end, which should never happen unless the algorithm that counted the
; total number of entries is broken.)
;
; Before we continue, let's see if the pesky user switched disks on us.
02/183d: a0 02 00 :SearchDone ldy #O_VCR_NAME
02/1840: b7 88 lda [vcr_ptr],y ;get vptr to volume name
02/1842: aa tax
02/1843: c8 iny
02/1844: c8 iny
02/1845: b7 88 lda [vcr_ptr],y
02/1847: a8 tay
02/1848: 22 38 fc 01 jsl DEREF ;convert to real pointer
02/184c: 86 84 stx vol_name_ptr ;copy to DP
02/184e: 84 86 sty vol_name_ptr+2
02/1850: a0 0c 00 ldy #O_VCR_DEV
02/1853: b7 88 lda [vcr_ptr],y ;get device number
02/1855: 8d e0 01 sta dev_num_arg
02/1858: 20 b0 11 jsr FindDosVolByName ;read VTOC into buffer provided by caller
02/185b: 90 04 bcc :ReadVtocOk
02/185d: a9 45 00 lda #E_VOL_NOT_FOUND ;disk vanished, fail
02/1860: 60 rts
02/1861: a0 0c 00 :ReadVtocOk ldy #O_VCR_DEV
02/1864: b7 88 lda [vcr_ptr],y ;get device number for VCR passed in
02/1866: c5 00 cmp drvr_dev_num ;compare to device where we found volume
02/1868: f0 07 beq :SameDev ;no change, branch
02/186a: a0 0c 00 ldy #O_VCR_DEV ;we found the disk, but in a different device
02/186d: a5 00 lda drvr_dev_num ;get the new device number
02/186f: 97 88 sta [vcr_ptr],y ;update the VCR
; We've got all the pieces, time to get the file info.
02/1871: 20 80 16 :SameDev jsr GetFileAttrs ;get file attributes
02/1874: 90 04 bcc :AttrsOk
02/1876: a9 51 00 lda #E_DIR_ERROR
02/1879: 60 rts
; Strip trailing spaces.
02/187a: e2 20 :AttrsOk sep #$20
.rwid shortm
02/187c: a0 20 00 ldy #O_DOSFDE_NAME+29 ;index of last byte of filename
02/187f: b7 a8 :StripLoop lda [fde_ptr],y ;get filename char
02/1881: c9 a0 cmp #“ ” ;is it a high-ASCII space?
02/1883: d0 03 bne :NotSpc ;no, bail
02/1885: 88 dey
02/1886: d0 f7 bne :StripLoop
02/1888: c2 21 :NotSpc rep #$21 ;long acc, clear carry
.rwid longm
02/188a: c8 iny ;inc to mark end point (and convert index to len)
02/188b: 8c 2d 19 sty :filename_end ;save it
02/188e: 98 tya
02/188f: e9 02 00 sbc #$0002 ;subtract 3 (index is from start of FDE, name at +3)
02/1892: 8d 39 19 sta :filename_len ;save that as filename length
02/1895: 1a inc A ;add 3 back
02/1896: 1a inc A
02/1897: 1a inc A
02/1898: a8 tay ;copy to Y-reg (which already had it?)
02/1899: a7 b4 lda []name_ptr] ;get buffer size (applies for both c0/c1)
02/189b: 8d 3b 19 sta :buffer_size
02/189e: cc 3b 19 cpy :buffer_size ;does the full name fit?
02/18a1: 90 06 bcc :Fits
02/18a3: a9 4f 00 lda #E_BUFF_TOO_SMALL ;doesn't fit, but GetDirEntry copies out as much as
02/18a6: 8d 31 19 sta :buf_small_err ; it can, even when it reports an error
02/18a9: a0 02 00 :Fits ldy #O_DOSFDE_NAME-1 ;name starts at FDE +$03
02/18ac: a9 03 00 lda #$0003 ;output starts at buffer +$04
02/18af: 8d 2f 19 sta :out_index
02/18b2: e2 20 sep #$20
.rwid shortm
02/18b4: c8 :Loop iny
02/18b5: cc 2d 19 cpy :filename_end ;copied entire filename?
02/18b8: f0 1f beq :NameDone ;yes, branch
02/18ba: ae 2f 19 ldx :out_index
02/18bd: e8 inx
02/18be: ec 3b 19 cpx :buffer_size ;output buffer full?
02/18c1: f0 16 beq :NameDone ;yes, branch
02/18c3: 8e 2f 19 stx :out_index
02/18c6: b7 a8 lda [fde_ptr],y ;get char
02/18c8: bb tyx ;preserve Y-reg
02/18c9: ac 2f 19 ldy :out_index
02/18cc: 29 7f and #$7f ;strip high bit
02/18ce: c9 3a cmp #‘:’ ;is it a colon?
02/18d0: d0 02 bne :NotColon
02/18d2: a9 2c lda #‘,’ ;yes, transform to comma (semi-illegal in DOS 3.3)
02/18d4: 97 b4 :NotColon sta []name_ptr],y ;write it
02/18d6: 9b txy ;restore Y-reg
02/18d7: 80 db bra :Loop
02/18d9: c2 21 :NameDone rep #$21 ;long A-reg, clear carry
.rwid longm
02/18db: ad 39 19 lda :filename_len
02/18de: a0 02 00 ldy #$0002 ;output buffer +2 is string len
02/18e1: 97 b4 sta []name_ptr],y ;write the filename length
; Filename is done. Set the other parameters.
02/18e3: ad f2 01 :GenerateOutput lda call_class ;ProDOS-16?
02/18e6: d0 18 bne :NotP16 ;no, branch
02/18e8: a9 00 00 lda #$0000
02/18eb: a0 02 00 ldy #$0002 ;flags field
02/18ee: 97 32 sta [param_blk_ptr],y ;set to zero
02/18f0: a9 01 00 lda #$0001
02/18f3: 8d f4 01 sta pcount ;set pcount=1 for copy function
02/18f6: a9 1e 0e lda #parm_c0_gde
02/18f9: 85 c2 sta rslt_cmd_ptr
02/18fb: 20 b2 0d jsr CopyFileParamsOut ;do the copy
02/18fe: 80 26 bra :Finish
02/1900: a9 00 00 :NotP16 lda #$0000
02/1903: a0 04 00 ldy #$0004 ;flags field
02/1906: 97 32 sta [param_blk_ptr],y ;set to zero
02/1908: a9 4e 0e lda #parm_c1_gde
02/190b: 85 c2 sta rslt_cmd_ptr
02/190d: 20 b2 0d jsr CopyFileParamsOut ;do the copy
02/1910: ad f4 01 lda pcount
02/1913: c9 0f 00 cmp #$000f ;is pCount< 15?
02/1916: 90 0e bcc :Finish ;yes, bail
02/1918: a0 32 00 ldy #$0032 ;optionList field
02/191b: 20 1f 16 jsr OutputOptionList ;write it
02/191e: 90 06 bcc :Finish ;all good, bail
02/1920: a9 4f 00 lda #$004f ;not enough room for option list
02/1923: 8d 31 19 sta :buf_small_err
02/1926: ad 31 19 :Finish lda :buf_small_err ;report "buff to small" if necessary
02/1929: c9 01 00 cmp #$0001 ;set carry flag if A-reg is nonzero
02/192c: 60 rts
02/192d: 00 00 :filename_end .fill 2,$00
02/192f: 00 00 :out_index .fill 2,$00
02/1931: 00 00 :buf_small_err .fill 2,$00
02/1933: 00 00 :base_arg .fill 2,$00
02/1935: 00 00 :displace_arg .fill 2,$00
02/1937: 00 00 :cur_displace .fill 2,$00
02/1939: 00 00 :filename_len .fill 2,$00
02/193b: 00 00 :buffer_size .fill 2,$00
;
; Handle NewLine mode. This will terminate a Read call if a newline character
; is encountered.
;
; Do not call here if the NewLine mask is zero (disabled).
;
; On entry:
; $ae-b1: pointer to input data
; $b4-b7: pointer to output data
; $01fe-01ff: newline list len (from FCR)
; $0216-0217: length of data in input buffer
;
; On exit:
; Carry set if we found stopped after encountering a newline char
; $0218-0219: length of data output
;
• Clear variables
]srcptr .var $ae {addr/4}
]dstptr .var $b4 {addr/4}
02/193d: a0 0c 00 CheckNewLine ldy #O_FCR_NEWLINE
02/1940: b7 90 lda [fcr_ptr],y ;get pointer to newline character list
02/1942: aa tax
02/1943: c8 iny
02/1944: c8 iny
02/1945: b7 90 lda [fcr_ptr],y
02/1947: a8 tay
02/1948: 22 38 fc 01 jsl DEREF ;convert to real pointer
02/194c: 86 ba stx newline_ptr ;copy to DP
02/194e: 84 bc sty newline_ptr+2
02/1950: a0 12 00 ldy #O_FCR_MASK
02/1953: b7 90 lda [fcr_ptr],y ;get newline mask
02/1955: 85 be sta newline_mask ;copy to DP
02/1957: ad fe 01 lda newline_len ;get list length
02/195a: 3a dec A ;is length == 1?
02/195b: d0 48 bne :LongList ;no, handle generally
; Scan the data buffer for a specific character value.
02/195d: a7 ba lda [newline_ptr] ;get the one newline char
02/195f: 29 ff 00 and #$00ff
02/1962: 85 c0 sta newline_char ;save it in DP
02/1964: ac 16 02 ldy data_remain_count ;any data left?
02/1967: f0 7c beq :Return ;no, bail
02/1969: a0 00 00 ldy #$0000 ;start at the start
02/196c: e2 30 sep #$30
.rwid shortm,shortx
02/196e: 5a :FastLoop phy
02/196f: c2 30 rep #$30
.rwid longm,longx
02/1971: a0 26 00 ldy #O_FCRX_DOS_TYPE
02/1974: b7 90 lda [fcr_ptr],y ;check if this is a TXT file (in an expensive way)
02/1976: e2 30 sep #$30
.rwid shortm,shortx
02/1978: d0 09 bne :NotText ;not TXT (which has type 0)
02/197a: 7a ply
02/197b: b7 ae lda []srcptr],y ;copy one char
02/197d: 97 b4 sta []dstptr],y
02/197f: 29 7f and #$7f ;text file, always strip high ASCII
02/1981: 80 05 bra :CheckChar
02/1983: 7a :NotText ply
02/1984: b7 ae lda []srcptr],y ;copy one char
02/1986: 97 b4 sta []dstptr],y
02/1988: ee 18 02 :CheckChar inc data_copy_count ;update output count
02/198b: 25 be and newline_mask ;bitwise AND the mask
02/198d: c5 c0 cmp newline_char ;see if it matches
02/198f: f0 0f beq :FoundNewLine ;it does, branch
02/1991: c2 30 rep #$30
.rwid longm,longx
02/1993: c8 iny ;update index
02/1994: cc 16 02 cpy data_remain_count ;done yet?
02/1997: e2 30 sep #$30
.rwid shortm,shortx
02/1999: 90 d3 bcc :FastLoop ;no, loop
02/199b: c2 30 rep #$30
.rwid longm,longx
02/199d: 18 clc ;all done, clear carry to indicate no newline found
02/199e: 80 43 bra :RetFixReg
.rwid shortm,shortx
02/19a0: c2 30 :FoundNewLine rep #$30
.rwid longm,longx
02/19a2: 38 sec
02/19a3: 80 3e bra :RetFixReg
; Instead of a single newline char we have a list of possibilities. This is the
; general solution, but slower for the common case where there's only one
; newline char.
02/19a5: a0 00 00 :LongList ldy #$0000
02/19a8: e2 20 sep #$20
.rwid shortm
02/19aa: 5a :SlowLoop phy
02/19ab: c2 20 rep #$20
.rwid longm
02/19ad: a0 26 00 ldy #O_FCRX_DOS_TYPE
02/19b0: b7 90 lda [fcr_ptr],y ;get the DOS file type
02/19b2: e2 20 sep #$20
.rwid shortm
02/19b4: d0 09 bne :NotText2 ;not TXT (which has type 0)
02/19b6: 7a ply
02/19b7: b7 ae lda []srcptr],y ;copy byte
02/19b9: 97 b4 sta []dstptr],y
02/19bb: 29 7f and #$7f ;always strip high byte for text files
02/19bd: 80 05 bra :CheckChar2
02/19bf: 7a :NotText2 ply
02/19c0: b7 ae lda []srcptr],y
02/19c2: 97 b4 sta []dstptr],y
02/19c4: ee 18 02 :CheckChar2 inc data_copy_count
02/19c7: 25 be and newline_mask ;and value with mask
02/19c9: bb tyx ;save offset in X-reg
02/19ca: ac fe 01 ldy newline_len
02/19cd: 88 :ChkLoop dey
02/19ce: 30 06 bmi :ChkDone
02/19d0: d7 ba cmp [newline_ptr],y ;compare to each char in list
02/19d2: d0 f9 bne :ChkLoop
02/19d4: 80 0c bra :RetSetC ;found a match, bail
02/19d6: 9b :ChkDone txy ;return offset to Y-reg
02/19d7: c8 iny ;update it
02/19d8: cc 16 02 cpy data_remain_count ;have we reached the end?
02/19db: d0 cd bne :SlowLoop ;not yet
02/19dd: c2 20 rep #$20
.rwid longm
02/19df: 18 clc
02/19e0: 80 01 bra :RetFixReg
.rwid shortm
02/19e2: 38 :RetSetC sec ;set carry after match
02/19e3: c2 30 :RetFixReg rep #$30
.rwid longm
02/19e5: 60 :Return rts
;
; Handles the GS/OS Read call.
; (p.180 and p.400)
;
; This function is a monster. We need to return the number of bytes requested,
; using data from a previous sector read if available. In NewLine mode, we have
; to screen every byte.
;
; The FCR read buffer holds the contents of a T/S list from the file. This is
; retained across multiple Read calls.
;
; The file position has to be adjusted by a few bytes because we don't want to
; return things like the BAS length in the first sector. This is handled
; internally -- the file mark we track in the FST does not include this
; adjustment.
;
; FSTs do not handle NewLine ($2011, p.170), but are responsible for
; implementing the feature.
;
; You cannot use this call to read the catalog track. You can open the volume
; directory for use with GetDirEntry, but the Open call sets the EOF to zero, so
; any attempt to read fails immediately. (The code here does have a special
; case for reading the directory, but it can't be called.)
;
; On entry:
; $a8-ab: pointer to catalog FDE for this file
;
; On exit:
; Carry set on error
; A-reg: error code
;
• Clear variables
]read_buf_ptr .var $98 {addr/4}
]fde_ptr1 .var $a8 {addr/4}
]newl_buf_ptr .var $ae {addr/4}
]data_buf_ptr .var $b4 {addr/4}
02/19e6: 9c 8e 1e DoRead stz :err_code ;init some values
02/19e9: 9c 28 02 stz txfr_count
02/19ec: 9c 2a 02 stz txfr_count+2
02/19ef: 9c 18 02 stz data_copy_count
; Get offset adjustment, based on file type.
02/19f2: a0 26 00 ldy #O_FCRX_DOS_TYPE
02/19f5: b7 90 lda [fcr_ptr],y
02/19f7: f0 1a beq :NoAdj ;text files have type zero
02/19f9: 29 fc ff and #%1111111111111100 ;check for INT/BAS (types $01/$02)
02/19fc: d0 08 bne :NotBasic
02/19fe: a9 02 00 lda #$0002 ;BASIC puts the length in the first two bytes
02/1a01: 8d fc 01 sta file_mark_adj
02/1a04: 80 10 bra :GotAdj
02/1a06: 29 eb ff :NotBasic and #%1111111111101011 ;check for BIN/REL
02/1a09: d0 08 bne :NoAdj ;must be S/A/B; branch
02/1a0b: a9 04 00 lda #$0004 ;BIN has load address and length in first 4 bytes
02/1a0e: 8d fc 01 sta file_mark_adj
02/1a11: 80 03 bra :GotAdj
02/1a13: 9c fc 01 :NoAdj stz file_mark_adj ;no adjustment for TXT/'S'/'A'/'B'
02/1a16: a0 12 00 :GotAdj ldy #O_FCR_MASK
02/1a19: b7 90 lda [fcr_ptr],y ;get newline char mask
02/1a1b: 85 be sta newline_mask ;(will be zero if NewLine mode is disabled)
02/1a1d: a0 10 00 ldy #O_FCR_NEWLINE_LEN
02/1a20: b7 90 lda [fcr_ptr],y ;get length of newline char list
02/1a22: 8d fe 01 sta newline_len
02/1a25: a0 1a 00 ldy #O_FCRX_EOF
02/1a28: b7 90 lda [fcr_ptr],y ;copy the file length in
02/1a2a: 8d 18 03 sta file_eof
02/1a2d: c8 iny
02/1a2e: c8 iny
02/1a2f: b7 90 lda [fcr_ptr],y
02/1a31: 8d 1a 03 sta file_eof+2
02/1a34: a0 20 00 ldy #O_FCRX_MARK
02/1a37: b7 90 lda [fcr_ptr],y
02/1a39: 8d 1a 02 sta file_mark ;copy the file mark in
02/1a3c: c8 iny
02/1a3d: c8 iny
02/1a3e: b7 90 lda [fcr_ptr],y
02/1a40: 8d 1c 02 sta file_mark+2
02/1a43: a0 16 00 ldy #O_FCRX_BUF_VPTR
02/1a46: b7 90 lda [fcr_ptr],y ;get the read buffer pointer
02/1a48: aa tax ;(this is the 4K work buffer for a volume dir, but
02/1a49: c8 iny ; the EOF is zero, so we'll exit before trying to
02/1a4a: c8 iny ; read any data into it)
02/1a4b: b7 90 lda [fcr_ptr],y
02/1a4d: a8 tay
02/1a4e: 22 38 fc 01 jsl DEREF ;convert to a real pointer
02/1a52: 86 98 stx ]read_buf_ptr ;copy to DP
02/1a54: 84 9a sty ]read_buf_ptr+2
; Extract the Read call parameters.
02/1a56: ad f2 01 lda call_class ;ProDOS-16?
02/1a59: f0 1e beq :IsP16 ;yes, branch
02/1a5b: a0 08 00 ldy #$0008 ;requestCount field
02/1a5e: b7 32 lda [param_blk_ptr],y
02/1a60: 8d 1e 02 sta request_count
02/1a63: c8 iny
02/1a64: c8 iny
02/1a65: b7 32 lda [param_blk_ptr],y
02/1a67: 8d 20 02 sta request_count+2
02/1a6a: a0 04 00 ldy #$0004 ;dataBuffer field
02/1a6d: b7 32 lda [param_blk_ptr],y ;get pointer
02/1a6f: 85 b4 sta ]data_buf_ptr ;save in DP
02/1a71: c8 iny
02/1a72: c8 iny
02/1a73: b7 32 lda [param_blk_ptr],y
02/1a75: 85 b6 sta ]data_buf_ptr+2
02/1a77: 80 1c bra :Cont
02/1a79: a0 06 00 :IsP16 ldy #$0006 ;requestCount field
02/1a7c: b7 32 lda [param_blk_ptr],y
02/1a7e: 8d 1e 02 sta request_count
02/1a81: c8 iny
02/1a82: c8 iny
02/1a83: b7 32 lda [param_blk_ptr],y
02/1a85: 8d 20 02 sta request_count+2
02/1a88: a0 02 00 ldy #$0002 ;dataBuffer field
02/1a8b: b7 32 lda [param_blk_ptr],y
02/1a8d: 85 b4 sta ]data_buf_ptr
02/1a8f: c8 iny
02/1a90: c8 iny
02/1a91: b7 32 lda [param_blk_ptr],y
02/1a93: 85 b6 sta ]data_buf_ptr+2
; Check to see if we're going to run off the end.
02/1a95: ad 1a 02 :Cont lda file_mark ;is mark == EOF?
02/1a98: cd 18 03 cmp file_eof
02/1a9b: d0 11 bne :NotAtEnd
02/1a9d: ad 1c 02 lda file_mark+2
02/1aa0: cd 1a 03 cmp file_eof+2
02/1aa3: d0 09 bne :NotAtEnd
02/1aa5: a9 4c 00 lda #E_END_OF_FILE ;at end, no further data can be returned
02/1aa8: 8d 8e 1e sta :err_code
02/1aab: 82 c0 03 brl :Finish
; Reduce the requestCount if it exceeds the EOF.
02/1aae: ad 1a 02 :NotAtEnd lda file_mark ;get current mark
02/1ab1: 18 clc
02/1ab2: 6d 1e 02 adc request_count ;add the request length
02/1ab5: 8d 04 02 sta read_end
02/1ab8: ad 1c 02 lda file_mark+2
02/1abb: 6d 20 02 adc request_count+2
02/1abe: b0 12 bcs :TooMuch ;rolled over 32-bit value; cap to EOF
02/1ac0: 8d 06 02 sta read_end+2
02/1ac3: cd 1a 03 cmp file_eof+2 ;is mark + requestCount > EOF?
02/1ac6: 90 16 bcc :LenOk
02/1ac8: d0 08 bne :TooMuch
02/1aca: ad 18 03 lda file_eof
02/1acd: cd 04 02 cmp read_end
02/1ad0: b0 0c bcs :LenOk
02/1ad2: ad 1a 03 :TooMuch lda file_eof+2 ;limit size of request to length of file
02/1ad5: 8d 06 02 sta read_end+2
02/1ad8: ad 18 03 lda file_eof
02/1adb: 8d 04 02 sta read_end
02/1ade: ad 04 02 :LenOk lda read_end ;compute the limited requestCount value
02/1ae1: 38 sec
02/1ae2: ed 1a 02 sbc file_mark
02/1ae5: 8d 10 02 sta read_req_cnt
02/1ae8: ad 06 02 lda read_end+2
02/1aeb: ed 1c 02 sbc file_mark+2
02/1aee: 8d 12 02 sta read_req_cnt+2
; Adjust the file mark to skip the file header.
02/1af1: ad 1a 02 lda file_mark
02/1af4: 18 clc
02/1af5: 6d fc 01 adc file_mark_adj
02/1af8: 8d 08 02 sta read_pos
02/1afb: ad 1c 02 lda file_mark+2
02/1afe: 69 00 00 adc #$0000
02/1b01: 8d 0a 02 sta read_pos+2
; Check for directory read.
02/1b04: a0 24 00 ldy #O_FCRX_FILE_TYPE
02/1b07: b7 90 lda [fcr_ptr],y ;get file type
02/1b09: c9 0f 00 cmp #$000f ;DIR?
02/1b0c: d0 03 bne :NotDir
02/1b0e: 82 80 02 brl :ReadDir
02/1b11: a0 0c 00 :NotDir ldy #O_VCR_DEV
02/1b14: b7 88 lda [vcr_ptr],y ;get device number
02/1b16: 85 00 sta drvr_dev_num ;copy to driver area
02/1b18: a9 02 00 lda #DRIVER_READ
02/1b1b: 85 02 sta drvr_call_num
; Find the T/S list for the start of the read operation. Start by computing the
; index of the sector with the start of the data: ((mark + adj) / 256)
02/1b1d: ad 08 02 :ReadLoop lda read_pos ;get file offset, low word
02/1b20: eb xba
02/1b21: 29 ff 00 and #$00ff ;divide by 256
02/1b24: 8d 22 02 sta ts_list_index ;save value
02/1b27: ad 0a 02 lda read_pos+2 ;get file offset, high word
02/1b2a: eb xba ;divide by 256
02/1b2b: aa tax
02/1b2c: 29 00 ff and #$ff00
02/1b2f: 0d 22 02 ora ts_list_index ;merge into index
02/1b32: 8d 22 02 sta ts_list_index
02/1b35: 8a txa
02/1b36: 29 ff 00 and #$00ff
02/1b39: 8d 24 02 sta ts_list_index+2 ;(unused)
02/1b3c: a5 98 :FindTSLoop lda ]read_buf_ptr ;FCR buffer holds T/S list from previous reads
02/1b3e: 85 04 sta drvr_buf_ptr ;might not be the right one
02/1b40: a5 9a lda ]read_buf_ptr+2 ;set driver read buf in case we need to get another
02/1b42: 85 06 sta drvr_buf_ptr+2
02/1b44: ad 22 02 lda ts_list_index ;get sector index
02/1b47: a0 05 00 ldy #O_DOSTS_SCTOFF
02/1b4a: 38 sec
02/1b4b: f7 98 sbc []read_buf_ptr],y ;subtract index of first sector in this T/S list
02/1b4d: 90 16 bcc :ReadFirstTS ;sector was in an earlier T/S list, go back
02/1b4f: c9 7a 00 cmp #122 ;is it in this sector?
02/1b52: 90 1e bcc :GotTSList ;yes, we're good
02/1b54: a0 01 00 ldy #O_DOSTS_NXTTRK ;no, move to next T/S sector
02/1b57: b7 98 lda []read_buf_ptr],y ;get T/S of next sector in T/S list chain
02/1b59: d0 05 bne :ReadNextTS ;exists, branch
02/1b5b: a9 51 00 lda #E_DIR_ERROR ;end of list reached, fail
02/1b5e: 38 sec
02/1b5f: 60 rts
02/1b60: 20 8c 0e :ReadNextTS jsr ReadSector
02/1b63: 80 d7 bra :FindTSLoop
02/1b65: a0 00 00 :ReadFirstTS ldy #O_DOSFDE_TRK
02/1b68: b7 a8 lda []fde_ptr1],y ;get track/sector for first T/S list
02/1b6a: 20 8c 0e jsr ReadSector
02/1b6d: 90 01 bcc :ReadFirstOk
02/1b6f: 60 rts
02/1b70: 80 ca :ReadFirstOk bra :FindTSLoop
02/1b72: 0a :GotTSList asl A ;T/S index is in A-reg; double it
02/1b73: 69 0c 00 adc #O_DOSTS_LIST ;first T/S pair is at +$0c
02/1b76: a8 tay
02/1b77: b7 98 lda []read_buf_ptr],y ;get track/sector
02/1b79: d0 03 bne :NotSparse ;nonzero, has data
02/1b7b: 82 94 01 brl :SparseBlock ;zero, is a sparse sector
02/1b7e: 8d 0e 02 :NotSparse sta track_sect ;save track/sector value
02/1b81: a0 30 00 ldy #O_FCRX_BUF_TS
02/1b84: d7 90 cmp [fcr_ptr],y ;is this the one currently in the read buffer?
02/1b86: f0 4e beq :HaveSect ;yes, work with that
02/1b88: a5 be lda newline_mask ;are we in NewLine mode?
02/1b8a: d0 2a bne :ReadLocal ;yes, read into local buffer
02/1b8c: ad 08 02 lda read_pos ;check the destination
02/1b8f: 29 ff 00 and #$00ff ;is it page-aligned?
02/1b92: d0 22 bne :ReadLocal ;no, read into local buffer
02/1b94: ad 10 02 lda read_req_cnt ;check the request count
02/1b97: c9 01 01 cmp #$0101 ;is it > 256? (why is == 256 bad?)
02/1b9a: 90 1a bcc :ReadLocal ;no, read into local buffer
02/1b9c: a5 b4 lda ]data_buf_ptr ;we can read this directly into the user's buffer,
02/1b9e: 85 04 sta drvr_buf_ptr ; avoiding a copy
02/1ba0: a5 b6 lda ]data_buf_ptr+2
02/1ba2: 85 06 sta drvr_buf_ptr+2
02/1ba4: ad 0e 02 lda track_sect
02/1ba7: 20 8c 0e jsr ReadSector ;do the read
02/1baa: 90 01 bcc :ReadDirectOk
02/1bac: 60 rts
02/1bad: a9 00 01 :ReadDirectOk lda #$0100
02/1bb0: 8d 18 02 sta data_copy_count ;we copied 256 bytes
02/1bb3: 82 e9 00 brl :DataWasRead ;go update counters
; Something prevented us from reading a full sector into the user's buffer.
; Read into the second half of the FCR read buffer.
02/1bb6: a5 98 :ReadLocal lda ]read_buf_ptr ;get the FCR work buffer
02/1bb8: 18 clc
02/1bb9: 69 00 01 adc #$0100 ;read data into the second half
02/1bbc: 85 04 sta drvr_buf_ptr
02/1bbe: a5 9a lda ]read_buf_ptr+2
02/1bc0: 69 00 00 adc #$0000
02/1bc3: 85 06 sta drvr_buf_ptr+2
02/1bc5: ad 0e 02 lda track_sect ;T/S of data sector
02/1bc8: 20 8c 0e jsr ReadSector ;read it
02/1bcb: 90 01 bcc :ReadLocOk
02/1bcd: 60 rts ;read failed
02/1bce: a0 30 00 :ReadLocOk ldy #O_FCRX_BUF_TS
02/1bd1: ad 0e 02 lda track_sect ;get track/sector we just read
02/1bd4: 97 90 sta [fcr_ptr],y ;update FCR
02/1bd6: ad 08 02 :HaveSect lda read_pos ;get current position
02/1bd9: 29 ff 00 and #$00ff ; mod 256
02/1bdc: 8d 14 02 sta sector_offset ;save it
02/1bdf: ac 12 02 ldy read_req_cnt+2 ;need to read >= 65536 more?
02/1be2: d0 09 bne :BigRead ;yes, branch
02/1be4: 18 clc
02/1be5: 6d 10 02 adc read_req_cnt ;add sector start offset to remaining count
02/1be8: c9 01 01 cmp #$0101 ;is it <= 256?
02/1beb: 90 03 bcc :SmallRead ;yes, whole thing is within sector
02/1bed: a9 00 01 :BigRead lda #$0100 ;no, cap it at 256 bytes
02/1bf0: 8d 26 02 :SmallRead sta wronly_0226
02/1bf3: 38 sec
02/1bf4: ed 14 02 sbc sector_offset ;subtract the sector offset to get the amount we
02/1bf7: 8d 16 02 sta data_remain_count ; need to read from this sector
02/1bfa: ad 14 02 lda sector_offset
02/1bfd: 18 clc
02/1bfe: 69 00 01 adc #$0100
02/1c01: 65 98 adc ]read_buf_ptr ;set pointer for input to NewLine scanner
02/1c03: 85 ae sta ]newl_buf_ptr ; or data copy function
02/1c05: a5 9a lda ]read_buf_ptr+2
02/1c07: 69 00 00 adc #$0000
02/1c0a: 85 b0 sta ]newl_buf_ptr+2
02/1c0c: a6 be ldx newline_mask ;is NewLine mode active?
02/1c0e: d0 65 bne :NewLineCopy ;yes, branch
02/1c10: ad 16 02 lda data_remain_count ;check the amount of data to copy
02/1c13: c9 64 00 cmp #100 ;>= 100 bytes? (probably a heuristic)
02/1c16: b0 39 bcs :UseBlkMove ;yes, use block move function
; We're moving relatively few bytes, so we do the copy directly instead of
; paying the overhead of the block move function.
02/1c18: 4a lsr A ;cut the count in half; carry becomes odd/even flag
02/1c19: aa tax ;move half-count to X-reg
02/1c1a: 90 14 bcc :Even
02/1c1c: e2 20 sep #$20
.rwid shortm
02/1c1e: a7 ae lda []newl_buf_ptr] ;copy a single byte
02/1c20: 87 b4 sta []data_buf_ptr]
02/1c22: c2 21 rep #$21
.rwid longm
02/1c24: e6 ae inc ]newl_buf_ptr ;advance the pointers
02/1c26: d0 02 bne :NoInc
02/1c28: e6 b0 inc ]newl_buf_ptr+2
02/1c2a: e6 b4 :NoInc inc ]data_buf_ptr ;we'll need to undo this change to the output buffer
02/1c2c: d0 02 bne :Even ; pointer when we're done
02/1c2e: e6 b6 inc ]data_buf_ptr+2
02/1c30: 8a :Even txa ;restore half-count
02/1c31: 3a dec A ;test for single-byte copy
02/1c32: 30 0a bmi :CopyDone ;it was, we're done
02/1c34: 0a asl A ;double it
02/1c35: a8 tay
02/1c36: b7 ae :CopyLoop lda []newl_buf_ptr],y ;copy data, two bytes at a time
02/1c38: 97 b4 sta []data_buf_ptr],y
02/1c3a: 88 dey
02/1c3b: 88 dey
02/1c3c: 10 f8 bpl :CopyLoop
02/1c3e: ad 16 02 :CopyDone lda data_remain_count ;get original count
02/1c41: 8d 18 02 sta data_copy_count
02/1c44: 4a lsr A ;check odd/even again
02/1c45: 90 58 bcc :DataWasRead ;even, we're good
02/1c47: a5 b4 lda ]data_buf_ptr ;odd, put the data output pointer back where
02/1c49: d0 02 bne :NoDec ; it was before we incremented it
02/1c4b: c6 b6 dec ]data_buf_ptr+2
02/1c4d: c6 b4 :NoDec dec ]data_buf_ptr
02/1c4f: 80 4e bra :DataWasRead
; Copying 101-256 bytes, use system block copier.
02/1c51: d4 b0 :UseBlkMove pei (]newl_buf_ptr+2) ;push source ptr (hi)
02/1c53: d4 ae pei (]newl_buf_ptr) ;push source ptr (lo)
02/1c55: d4 b6 pei (]data_buf_ptr+2) ;push dest ptr (hi)
02/1c57: d4 b4 pei (]data_buf_ptr) ;push dest ptr (lo)
02/1c59: f4 00 00 pea $0000 ;push transfer count (hi)
02/1c5c: ad 16 02 lda data_remain_count ;push transfer count (lo)
02/1c5f: 48 pha
02/1c60: f4 05 08 pea $0805 ;push command (move_sinc_dinc - basic move)
02/1c63: 22 70 fc 01 jsl MOVE_INFO ;copy data
02/1c67: 90 04 bcc :MoveOk
02/1c69: a9 00 80 lda #$8000 ;"whoops"
02/1c6c: 60 rts
02/1c6d: ad 16 02 :MoveOk lda data_remain_count ;update counters
02/1c70: 8d 18 02 sta data_copy_count
02/1c73: 80 2a bra :DataWasRead
02/1c75: 20 3d 19 :NewLineCopy jsr CheckNewLine ;scan for a newline char, copying as we go
02/1c78: 90 25 bcc :DataWasRead ;didn't find one, branch
02/1c7a: ad 18 02 lda data_copy_count ;partial read; update counters
02/1c7d: 18 clc
02/1c7e: 6d 28 02 adc txfr_count
02/1c81: 8d 28 02 sta txfr_count
02/1c84: 90 03 bcc :NoInc
02/1c86: ee 2a 02 inc txfr_count+2
02/1c89: 18 :NoInc clc
02/1c8a: ad 28 02 lda txfr_count
02/1c8d: 6d fc 01 adc file_mark_adj
02/1c90: 8d 28 02 sta txfr_count
02/1c93: ad 2a 02 lda txfr_count+2
02/1c96: 69 00 00 adc #$0000
02/1c99: 8d 2a 02 sta txfr_count+2
02/1c9c: 82 50 01 brl :ReadDone ;return to the caller
; Some data was read (1-256 bytes). Update counters, then read some more.
02/1c9f: ad 18 02 :DataWasRead lda data_copy_count
02/1ca2: 18 clc
02/1ca3: 6d 28 02 adc txfr_count ;update the transfer count
02/1ca6: 8d 28 02 sta txfr_count
02/1ca9: 90 03 bcc :NoInc
02/1cab: ee 2a 02 inc txfr_count+2
02/1cae: ad 10 02 :NoInc lda read_req_cnt ;reduce the request count
02/1cb1: 38 sec
02/1cb2: ed 18 02 sbc data_copy_count
02/1cb5: 8d 10 02 sta read_req_cnt
02/1cb8: b0 03 bcs :NoDec
02/1cba: ce 12 02 dec read_req_cnt+2
02/1cbd: ad 10 02 :NoDec lda read_req_cnt
02/1cc0: 0d 12 02 ora read_req_cnt+2 ;are we there yet?
02/1cc3: d0 16 bne :NotDone ;no, branch
02/1cc5: 18 clc
02/1cc6: ad 28 02 lda txfr_count ;remove file header adjustment from transfer count
02/1cc9: 6d fc 01 adc file_mark_adj
02/1ccc: 8d 28 02 sta txfr_count
02/1ccf: ad 2a 02 lda txfr_count+2
02/1cd2: 69 00 00 adc #$0000
02/1cd5: 8d 2a 02 sta txfr_count+2
02/1cd8: 82 14 01 brl :ReadDone ;finish up
02/1cdb: ad 10 02 :NotDone lda read_req_cnt
02/1cde: 18 clc
02/1cdf: 6d fc 01 adc file_mark_adj
02/1ce2: 8d f3 1c sta :check_tmp ;add header adjustment to request count
02/1ce5: ad 12 02 lda read_req_cnt+2
02/1ce8: 69 00 00 adc #$0000
02/1ceb: 0d f3 1c ora :check_tmp ;are we done now?
02/1cee: d0 05 bne :NotDone1 ;no, not yet
02/1cf0: 82 fc 00 brl :ReadDone
02/1cf3: 00 00 :check_tmp .fill 2,$00
02/1cf5: ad 18 02 :NotDone1 lda data_copy_count ;advance the read buffer
02/1cf8: aa tax
02/1cf9: 18 clc
02/1cfa: 6d 08 02 adc read_pos
02/1cfd: 8d 08 02 sta read_pos
02/1d00: 90 03 bcc :NoInc
02/1d02: ee 0a 02 inc read_pos+2
02/1d05: 8a :NoInc txa
02/1d06: 18 clc
02/1d07: 65 b4 adc ]data_buf_ptr
02/1d09: 85 b4 sta ]data_buf_ptr
02/1d0b: 90 02 bcc :NoInc2
02/1d0d: e6 b6 inc ]data_buf_ptr+2
02/1d0f: 82 0b fe :NoInc2 brl :ReadLoop ;go get the next chunk
; Handle a sparse block, which is filled with $00 bytes. The awkward part is
; that $00 could be a valid NewLine character, so either the block has no
; newlines or is entirely newlines.
02/1d12: a5 be :SparseBlock lda newline_mask ;are we in NewLine mode?
02/1d14: f0 2a beq :NotNlSp ;no, do it the easy way
02/1d16: ac fe 01 ldy newline_len ;scan the NewLine list
02/1d19: e2 20 sep #$20
.rwid shortm
02/1d1b: 88 :NlCheckLoop dey
02/1d1c: 30 22 bmi :NotNlSp ;no '\0' entries found
02/1d1e: b7 ba lda [newline_ptr],y ;check entry
02/1d20: d0 f9 bne :NlCheckLoop ;nonzero, loop
02/1d22: 87 b4 sta []data_buf_ptr] ;put $00 in the output
02/1d24: c2 31 rep #$31 ;long regs, clear carry
.rwid longm
02/1d26: ee 28 02 inc txfr_count ;update the transfer count (+1)
02/1d29: d0 03 bne :NoInc
02/1d2b: ee 2a 02 inc txfr_count+2
02/1d2e: ad 10 02 :NoInc lda read_req_cnt ;update the request count (-1)
02/1d31: 38 sec
02/1d32: e9 01 00 sbc #$0001
02/1d35: 8d 10 02 sta read_req_cnt
02/1d38: b0 03 bcs :NoDec
02/1d3a: ce 12 02 dec read_req_cnt+2
02/1d3d: 82 af 00 :NoDec brl :ReadDone ;all done reading
.rwid shortm
02/1d40: c2 31 :NotNlSp rep #$31
.rwid longm
02/1d42: ad 08 02 lda read_pos ;get the read position
02/1d45: 29 ff 00 and #$00ff ;mod 256
02/1d48: 8d 14 02 sta sector_offset
02/1d4b: ac 12 02 ldy read_req_cnt+2 ;check request count, high word
02/1d4e: d0 09 bne :BigRead ;nonzero, this is a big read
02/1d50: 18 clc
02/1d51: 6d 10 02 adc read_req_cnt ;add request to offset
02/1d54: c9 01 01 cmp #$0101 ;<= 256?
02/1d57: 90 03 bcc :SmallSpRd ;yes, branch
02/1d59: a9 00 01 :BigRead lda #$0100 ;big read, cap at 256
02/1d5c: 8d 26 02 :SmallSpRd sta wronly_0226
02/1d5f: 38 sec
02/1d60: ed 14 02 sbc sector_offset ;subtract the offset
02/1d63: 8d 16 02 sta data_remain_count ;amount in this sector
02/1d66: 4a lsr A ;halve it; carry holds "odd" flag
02/1d67: aa tax
02/1d68: a9 00 00 lda #$0000 ;storing zeroes
02/1d6b: 90 0c bcc :SpEven ;was even, skip this
02/1d6d: e2 20 sep #$20
.rwid shortm
02/1d6f: 87 b4 sta []data_buf_ptr] ;store a single byte
02/1d71: c2 21 rep #$21
.rwid longm
02/1d73: e6 b4 inc ]data_buf_ptr ;advance the pointer
02/1d75: d0 02 bne :SpEven
02/1d77: e6 b6 inc ]data_buf_ptr+2
02/1d79: 8a :SpEven txa ;counter in A-reg
02/1d7a: 3a dec A ;test for zero (len == 1)
02/1d7b: 30 0b bmi :SpZeroDone ;all done
02/1d7d: 0a asl A ;double it
02/1d7e: a8 tay
02/1d7f: a9 00 00 lda #$0000
02/1d82: 97 b4 :SpZeroLoop sta []data_buf_ptr],y ;store two zeroes at a time
02/1d84: 88 dey
02/1d85: 88 dey
02/1d86: 10 fa bpl :SpZeroLoop
02/1d88: ad 16 02 :SpZeroDone lda data_remain_count ;update counters
02/1d8b: 8d 18 02 sta data_copy_count
02/1d8e: 82 0e ff brl :DataWasRead ;go do some more
; Read from a directory. Since the catalog track is already loaded into the VCR
; work buffer, this can be implemented with memory moves.
02/1d91: a5 be :ReadDir lda newline_mask ;is newline mode active?
02/1d93: f0 26 beq :NoNewL ;no, do the easy thing
02/1d95: ad 10 02 lda read_req_cnt ;set up args to newline scanner
02/1d98: 8d 16 02 sta data_remain_count
02/1d9b: 9c 18 02 stz data_copy_count
02/1d9e: ad 1a 02 lda file_mark ;set pointer to correct buffer offset
02/1da1: 18 clc
02/1da2: 65 98 adc ]read_buf_ptr
02/1da4: 85 ae sta ]newl_buf_ptr
02/1da6: ad 1c 02 lda file_mark+2
02/1da9: 65 9a adc ]read_buf_ptr+2
02/1dab: 85 b0 sta ]newl_buf_ptr+2
02/1dad: 20 3d 19 jsr CheckNewLine ;scan for newline chars
02/1db0: ad 18 02 lda data_copy_count
02/1db3: 8d 28 02 sta txfr_count ;copy output count to transfer count
02/1db6: 9c 2a 02 stz txfr_count+2
02/1db9: 80 34 bra :ReadDone ;perform housekeeping updates
02/1dbb: ad 1a 02 :NoNewL lda file_mark ;get pointer to start of data
02/1dbe: 18 clc
02/1dbf: 65 98 adc ]read_buf_ptr
02/1dc1: aa tax
02/1dc2: ad 1c 02 lda file_mark+2
02/1dc5: 65 9a adc ]read_buf_ptr+2
02/1dc7: 48 pha ;push data source
02/1dc8: da phx
02/1dc9: d4 b6 pei (]data_buf_ptr+2) ;push data destination
02/1dcb: d4 b4 pei (]data_buf_ptr)
02/1dcd: f4 00 00 pea $0000 ;push transfer count
02/1dd0: ad 10 02 lda read_req_cnt
02/1dd3: 48 pha
02/1dd4: f4 05 08 pea $0805 ;push command (move_sinc_dinc - basic move)
02/1dd7: 22 70 fc 01 jsl MOVE_INFO ;copy data
02/1ddb: 90 04 bcc :MoveOk
02/1ddd: a9 00 80 lda #$8000 ;error $00 with flag set
02/1de0: 60 rts
02/1de1: ad 10 02 :MoveOk lda read_req_cnt ;set the transfer count
02/1de4: 8d 28 02 sta txfr_count
02/1de7: ad 12 02 lda read_req_cnt+2
02/1dea: 8d 2a 02 sta txfr_count+2
02/1ded: 80 6b bra :UpdateMark
; The read request has been fulfilled. Either we read the full amount, or we
; stopped early when we hit EOF or a NewLine char. Now we need to handle "text
; mode", which is enabled via an FSTSpecific call.
02/1def: 38 :ReadDone sec
02/1df0: ad 28 02 lda txfr_count ;subtract the header adjustment from the txfr count
02/1df3: ed fc 01 sbc file_mark_adj
02/1df6: 8d 28 02 sta txfr_count
02/1df9: ad 2a 02 lda txfr_count+2
02/1dfc: e9 00 00 sbc #$0000
02/1dff: 8d 2a 02 sta txfr_count+2
02/1e02: a0 26 00 ldy #O_FCRX_DOS_TYPE
02/1e05: b7 90 lda [fcr_ptr],y ;check DOS file type
02/1e07: d0 51 bne :UpdateMark ;not text, skip
02/1e09: ad 38 02 lda text_mode ;check text mode
02/1e0c: f0 4c beq :UpdateMark ;not enabled, skip
02/1e0e: ad f2 01 lda call_class ;ProDOS-16?
02/1e11: f0 0f beq :IsP16 ;yes, branch
02/1e13: a0 04 00 ldy #$0004
02/1e16: b7 32 lda [param_blk_ptr],y ;get original dataBuffer arg
02/1e18: 85 b4 sta ]data_buf_ptr
02/1e1a: c8 iny
02/1e1b: c8 iny
02/1e1c: b7 32 lda [param_blk_ptr],y
02/1e1e: 85 b6 sta ]data_buf_ptr+2
02/1e20: 80 0d bra :StripIt
02/1e22: a0 02 00 :IsP16 ldy #$0002
02/1e25: b7 32 lda [param_blk_ptr],y ;get original dataBuffer arg
02/1e27: 85 b4 sta ]data_buf_ptr
02/1e29: c8 iny
02/1e2a: c8 iny
02/1e2b: b7 32 lda [param_blk_ptr],y
02/1e2d: 85 b6 sta ]data_buf_ptr+2
; Strip high bits from data. We may be stripping more than 64K of data, so we
; split the counter into a 64K chunk count and a fractional count.
02/1e2f: ac 2a 02 :StripIt ldy txfr_count+2 ;get high word of transfer count
02/1e32: c8 iny ;add one so first DEC makes it zero
02/1e33: ae 28 02 ldx txfr_count ;get low word
02/1e36: d0 07 bne :Not64kMult ;nonzero, branch
02/1e38: 88 dey ;exact mult of 64K, decr it back
02/1e39: 8a txa
02/1e3a: 0d 2a 02 ora txfr_count+2 ;is it zero bytes?
02/1e3d: f0 19 beq :StripDone ;yes, bail
02/1e3f: 84 c4 :Not64kMult sty strip_counter
02/1e41: a0 00 00 ldy #$0000 ;start at offset zero
02/1e44: e2 20 sep #$20
.rwid shortm
02/1e46: b7 b4 :StripLoop lda []data_buf_ptr],y
02/1e48: 29 7f and #$7f ;strip high bit
02/1e4a: 97 b4 sta []data_buf_ptr],y
02/1e4c: c8 iny
02/1e4d: d0 02 bne :NoInc
02/1e4f: e6 b6 inc ]data_buf_ptr+2 ;completed 65536 iterations, increment ptr hi word
02/1e51: ca :NoInc dex ;decrement byte count
02/1e52: d0 f2 bne :StripLoop ;more to do
02/1e54: c6 c4 dec strip_counter ;decrement 64K chunk count
02/1e56: d0 ee bne :StripLoop ;more to do
02/1e58: c2 20 :StripDone rep #$20
; Update the file mark and report the transfer count.
.rwid longm
02/1e5a: a0 20 00 :UpdateMark ldy #O_FCRX_MARK
02/1e5d: b7 90 lda [fcr_ptr],y ;get the current mark
02/1e5f: 18 clc
02/1e60: 6d 28 02 adc txfr_count ;add the transfer count
02/1e63: 97 90 sta [fcr_ptr],y
02/1e65: c8 iny
02/1e66: c8 iny
02/1e67: b7 90 lda [fcr_ptr],y ;update the high word
02/1e69: 6d 2a 02 adc txfr_count+2
02/1e6c: 97 90 sta [fcr_ptr],y
; Finish up and exit.
02/1e6e: ad f2 01 :Finish lda call_class ;ProDOS-16?
02/1e71: d0 05 bne :NotP16 ;no, branch
02/1e73: a0 0a 00 ldy #$000a ;transferCount field
02/1e76: 80 03 bra :CopyTxfr
02/1e78: a0 0c 00 :NotP16 ldy #$000c ;transferCount field
02/1e7b: ad 28 02 :CopyTxfr lda txfr_count
02/1e7e: 97 32 sta [param_blk_ptr],y
02/1e80: ad 2a 02 lda txfr_count+2
02/1e83: c8 iny
02/1e84: c8 iny
02/1e85: 97 32 sta [param_blk_ptr],y
02/1e87: ad 8e 1e lda :err_code ;get saved error code
02/1e8a: c9 01 00 cmp #$0001 ;set carry if nonzero
02/1e8d: 60 rts
02/1e8e: 00 00 :err_code .fill 2,$00
;
; Checks that the requested access and fork number are valid. Used by the Open
; call.
;
; On exit:
; Carry set on error
; A-reg: error code
;
• Clear variables
CheckAccessAndFork
02/1e90: ad f2 01 lda call_class ;ProDOS-16?
02/1e93: f0 36 beq :Return ;yes, nothing to do
02/1e95: ad f4 01 lda pcount ;check the parameter count
02/1e98: c9 04 00 cmp #$0004 ;is it < 4? (bug? should be 3)
02/1e9b: 90 2e bcc :Return ;yes, bail
02/1e9d: 18 clc
02/1e9e: a0 08 00 ldy #$0008 ;requestAccess field
02/1ea1: b7 32 lda [param_blk_ptr],y ;check what they're asking for
02/1ea3: 29 02 00 and #$0002 ;$0001=read, $0002=write
02/1ea6: f0 05 beq :NoWrite ;didn't ask for write, branch
02/1ea8: a9 4e 00 lda #E_INVALID_ACCESS ;did ask for write, fail
02/1eab: 80 1d bra :Fail
02/1ead: ad f4 01 :NoWrite lda pcount ;check parameter count
02/1eb0: c9 05 00 cmp #$0005 ;is it < 5? (bug? should be 4)
02/1eb3: 90 16 bcc :Return ;yes, bail
02/1eb5: 18 clc
02/1eb6: a0 0a 00 ldy #$000a ;resourceNumber field
02/1eb9: b7 32 lda [param_blk_ptr],y ;which fork (0=data, 1=rsrc)
02/1ebb: f0 0e beq :Return ;data requested, we're good
02/1ebd: c9 01 00 cmp #$0001 ;did they want rsrc?
02/1ec0: f0 05 beq :WantRsrc ;yes, report error
02/1ec2: a9 53 00 lda #E_PARM_RANGE_ERR ;no, they want something weird
02/1ec5: 80 03 bra :Fail
02/1ec7: a9 63 00 :WantRsrc lda #E_RES_NOT_FOUND
02/1eca: 38 :Fail sec
02/1ecb: 60 :Return rts
;
; Handles the GS/OS Open call.
; (GS/OS ref p.173 and p.397)
;
02/1ecc: 9c 3a 02 DoOpen stz alloc_rsrc_flags
02/1ecf: 20 7e 0c jsr PathCallSetup ;separate path components, get VCR (reads cat)
02/1ed2: 90 03 bcc :PathOk
02/1ed4: 82 23 03 brl :FailCleanup
02/1ed7: b2 b2 :PathOk lda (file_name_ptr) ;was a filename specified?
02/1ed9: f0 03 beq :OpenDir ;length is zero, open volume directory
02/1edb: 82 f6 00 brl :OpenRegular ;open file
;
; Open the catalog track as a volume directory.
;
02/1ede: 20 90 1e :OpenDir jsr CheckAccessAndFork ;check requestAccess and resourceNumber fields
02/1ee1: 90 03 bcc :AccessOk
02/1ee3: 82 14 03 brl :FailCleanup
; Make a copy of the generated volume name, with a leading ':'.
02/1ee6: ad aa 02 :AccessOk lda gen_vol_name_buf ;get length
02/1ee9: 1a inc A ;add one
02/1eea: 8d dc 02 sta gen_vol_colon_buf ;save it
02/1eed: e2 20 sep #$20
.rwid shortm
02/1eef: a9 3a lda #‘:’
02/1ef1: a0 02 00 ldy #$0002 ;start past the length word
02/1ef4: 99 dc 02 sta gen_vol_colon_buf,y
02/1ef7: b9 aa 02 :VolCopyLoop lda gen_vol_name_buf,y ;get char
02/1efa: c9 01 cmp #$01 ;set carry if nonzero (string is null-terminated)
02/1efc: c8 iny
02/1efd: 99 dc 02 sta gen_vol_colon_buf,y
02/1f00: b0 f5 bcs :VolCopyLoop
; Allocate an FCR for this.
02/1f02: c2 20 rep #$20
.rwid longm
02/1f04: a2 dc 02 ldx #gen_vol_colon_buf ;vol name with leading colon is our filename
02/1f07: a0 02 00 ldy #^gen_vol_colon_buf
02/1f0a: a9 32 00 lda #$0032 ;allocate with extra space for our stuff
02/1f0d: 22 2c fc 01 jsl ALLOC_FCR
02/1f11: 90 03 bcc :AllocOk
02/1f13: 82 e4 02 brl :FailCleanup
02/1f16: 22 38 fc 01 :AllocOk jsl DEREF ;convert to regular pointer
02/1f1a: 86 90 stx fcr_ptr ;copy to DP
02/1f1c: 84 92 sty fcr_ptr+2
02/1f1e: a9 00 40 lda #$4000
02/1f21: 0c 3a 02 tsb alloc_rsrc_flags ;set flag to indicate we allocated an FCR
02/1f24: a0 06 00 ldy #O_FCR_FST_ID
02/1f27: a9 02 00 lda #FSID_DOS33
02/1f2a: 97 90 sta [fcr_ptr],y ;set the filesystem ID to DOS 3.3
02/1f2c: a0 00 00 ldy #O_VCR_ID
02/1f2f: b7 88 lda [vcr_ptr],y ;get the VCR ID
02/1f31: a0 08 00 ldy #O_FCR_VOL_ID
02/1f34: 97 90 sta [fcr_ptr],y ;copy to FCR
02/1f36: a0 0e 00 ldy #O_VCRX_BUF_VPTR
02/1f39: b7 88 lda [vcr_ptr],y ;get vpointer to VCR work buffer
02/1f3b: aa tax ;vol dir uses that instead of file read buf
02/1f3c: c8 iny
02/1f3d: c8 iny
02/1f3e: b7 88 lda [vcr_ptr],y
02/1f40: a0 18 00 ldy #O_FCRX_BUF_VPTR+2
02/1f43: 97 90 sta [fcr_ptr],y ;copy to FCR
02/1f45: 8a txa
02/1f46: 88 dey
02/1f47: 88 dey
02/1f48: 97 90 sta [fcr_ptr],y
02/1f4a: a0 20 00 ldy #O_FCRX_MARK
02/1f4d: a9 00 00 lda #$0000 ;set file mark to zero
02/1f50: 97 90 sta [fcr_ptr],y
02/1f52: c8 iny
02/1f53: c8 iny
02/1f54: 97 90 sta [fcr_ptr],y
02/1f56: a0 1c 00 ldy #O_FCRX_EOF+2
02/1f59: 97 90 sta [fcr_ptr],y ;set EOF (high) to zero
02/1f5b: a0 1e 00 ldy #O_FCRX_AUXTYPE
02/1f5e: 97 90 sta [fcr_ptr],y ;set auxType to zero
02/1f60: c8 iny
02/1f61: c8 iny
02/1f62: 97 90 sta [fcr_ptr],y
02/1f64: a0 2c 00 ldy #O_FCRX_CUR_DISP ;init GDE current displacement to zero
02/1f67: 97 90 sta [fcr_ptr],y
02/1f69: a0 2a 00 ldy #O_FCRX_NUM_SCT+2
02/1f6c: 97 90 sta [fcr_ptr],y ;zero out sector allocation count
02/1f6e: 88 dey
02/1f6f: 88 dey
02/1f70: 97 90 sta [fcr_ptr],y
02/1f72: eb xba ;(?)
02/1f73: a0 1a 00 ldy #O_FCRX_EOF
02/1f76: 97 90 sta [fcr_ptr],y ;set EOF (low) to zero; prevents Read of dir
02/1f78: a0 24 00 ldy #O_FCRX_FILE_TYPE
02/1f7b: a9 0f 00 lda #$000f ;use ProDOS DIR type for volume directory
02/1f7e: 97 90 sta [fcr_ptr],y
02/1f80: a0 14 00 ldy #O_FCR_ACCESS
02/1f83: a9 c3 00 lda #$00c3 ;access flags (delete/rename/read/write)
02/1f86: 97 90 sta [fcr_ptr],y
; Figure out how many un-deleted entries there are.
02/1f88: 9c 0e 03 stz num_cat_entries ;init number of entries to zero
02/1f8b: a9 00 00 lda #$0000
02/1f8e: a2 00 00 :NextSctLoop ldx #$0000
02/1f91: 8d ee 01 :NextEntLoop sta current_cat_sct ;put sector and entry in A-reg/X-reg
02/1f94: 8e f0 01 stx current_cat_ent
02/1f97: 20 bb 0e jsr SetFdePtr ;set the pointer
02/1f9a: 90 03 bcc :FdePtrOk
02/1f9c: 82 5b 02 brl :FailCleanup
02/1f9f: 20 e1 0e :FdePtrOk jsr GetFdeStatus ;get entry status
02/1fa2: f0 1a beq :CatScanDone ;unused; we've hit the end of the catalog
02/1fa4: c9 ff 00 cmp #$00ff ;deleted?
02/1fa7: f0 03 beq :IsDel ;yes, branch
02/1fa9: ee 0e 03 inc num_cat_entries ;valid entry, inc count
02/1fac: ae f0 01 :IsDel ldx current_cat_ent
02/1faf: ad ee 01 lda current_cat_sct
02/1fb2: e8 inx ;advance to next entry
02/1fb3: e0 07 00 cpx #$0007 ;7 entries per sector
02/1fb6: 90 d9 bcc :NextEntLoop
02/1fb8: 1a inc A ;increment sector
02/1fb9: cd ea 01 cmp num_cat_sectors ;reached max?
02/1fbc: 90 d0 bcc :NextSctLoop
02/1fbe: ad 0e 03 :CatScanDone lda num_cat_entries ;get the number we calculated
02/1fc1: a0 30 00 ldy #O_FCRX_NUM_ENTRIES
02/1fc4: 97 90 sta [fcr_ptr],y ;set the number of entries in the FCR
02/1fc6: a0 08 00 ldy #O_VCR_OPEN_CNT
02/1fc9: b7 88 lda [vcr_ptr],y ;get open count
02/1fcb: 1a inc A ;increment it
02/1fcc: d0 01 bne :NoRoll ;didn't roll over, branch
02/1fce: 1a inc A ;inc it again so it's nonzero (why?)
02/1fcf: 97 88 :NoRoll sta [vcr_ptr],y
02/1fd1: 82 5f 01 brl :SetParams
;
; Open a regular (non-catalog-track) file.
;
02/1fd4: 20 a7 15 :OpenRegular jsr FindFileByName ;find the position in the catalog
02/1fd7: 90 06 bcc :FindOk
02/1fd9: a9 51 00 lda #E_DIR_ERROR ;unexpectedly found blank entry
02/1fdc: 82 1b 02 brl :FailCleanup
02/1fdf: ad f0 01 :FindOk lda current_cat_ent ;check the entry number
02/1fe2: 10 06 bpl :FoundEntry
02/1fe4: a9 46 00 lda #E_FILE_NOT_FOUND ;didn't find it
02/1fe7: 82 10 02 brl :FailCleanup
02/1fea: 20 90 1e :FoundEntry jsr CheckAccessAndFork ;validate requested access and fork
02/1fed: 90 03 bcc :AccessOk
02/1fef: 82 08 02 brl :FailCleanup
; Make a copy of the generated volume name, with a leading ':'.
02/1ff2: ad aa 02 :AccessOk lda gen_vol_name_buf ;get len of generated vol name
02/1ff5: 1a inc A ;increment it so we can insert a ':'
02/1ff6: 8d dc 02 sta gen_vol_colon_buf ;save it
02/1ff9: e2 20 sep #$20
.rwid shortm
02/1ffb: a9 3a lda #‘:’
02/1ffd: a0 02 00 ldy #$0002 ;start past the length word
02/2000: 99 dc 02 sta gen_vol_colon_buf,y
02/2003: b9 aa 02 :CopyLoop lda gen_vol_name_buf,y
02/2006: c9 01 cmp #$01 ;set carry if char is '\0'
02/2008: c8 iny
02/2009: 99 dc 02 sta gen_vol_colon_buf,y
02/200c: b0 f5 bcs :CopyLoop ;continue until we hit the null termination
02/200e: c2 20 rep #$20
.rwid longm
02/2010: a2 dc 02 ldx #gen_vol_colon_buf ;pass volume name as pathname to FCR alloc
02/2013: a0 02 00 ldy #^gen_vol_colon_buf
02/2016: a9 32 00 lda #$0032 ;size of FCR, with our added goodies
02/2019: 22 2c fc 01 jsl ALLOC_FCR
02/201d: 90 06 bcc :FcrOk
02/201f: a9 54 00 lda #E_OUT_OF_MEM
02/2022: 82 d5 01 brl :FailCleanup
02/2025: 22 38 fc 01 :FcrOk jsl DEREF ;convert to pointer
02/2029: 86 90 stx fcr_ptr ;copy to DP
02/202b: 84 92 sty fcr_ptr+2
02/202d: a9 00 40 lda #$4000 ;set flag to note that we allocated an FCR
02/2030: 0c 3a 02 tsb alloc_rsrc_flags
02/2033: a9 00 02 lda #512
02/2036: 22 1c fc 01 jsl ALLOC_SEG ;allocate 512-byte read buffer
02/203a: 90 06 bcc :Alloc512Ok
02/203c: a9 54 00 lda #E_OUT_OF_MEM
02/203f: 82 b8 01 brl :FailCleanup
02/2042: 98 :Alloc512Ok tya
02/2043: a0 18 00 ldy #O_FCRX_BUF_VPTR+2 ;save read buf vptr in FCR
02/2046: 97 90 sta [fcr_ptr],y
02/2048: 8a txa
02/2049: 88 dey
02/204a: 88 dey
02/204b: 97 90 sta [fcr_ptr],y
02/204d: a0 08 00 ldy #O_VCR_OPEN_CNT
02/2050: b7 88 lda [vcr_ptr],y ;increment the VCR open count
02/2052: 1a inc A
02/2053: d0 01 bne :NoRoll
02/2055: 1a inc A ;do it twice if it came up zero
02/2056: 97 88 :NoRoll sta [vcr_ptr],y
; Init state.
02/2058: a9 00 00 lda #$0000
02/205b: a0 20 00 ldy #O_FCRX_MARK
02/205e: 97 90 sta [fcr_ptr],y ;set the file mark to zero
02/2060: c8 iny
02/2061: c8 iny
02/2062: 97 90 sta [fcr_ptr],y
02/2064: a0 2e 00 ldy #O_FCRX_FDE_INDEX
02/2067: ad f0 01 lda current_cat_ent
02/206a: 97 90 sta [fcr_ptr],y ;set the catalog FDE entry
02/206c: a0 2c 00 ldy #O_FCRX_FDE_SCT
02/206f: ad ee 01 lda current_cat_sct
02/2072: 97 90 sta [fcr_ptr],y ;set the catalog FDE sector
02/2074: a0 06 00 ldy #O_FCR_FST_ID
02/2077: a9 02 00 lda #FSID_DOS33
02/207a: 97 90 sta [fcr_ptr],y ;set the file system ID to DOS 3.3
02/207c: a0 00 00 ldy #O_VCR_ID
02/207f: b7 88 lda [vcr_ptr],y ;get VCR ID
02/2081: a0 08 00 ldy #O_FCR_VOL_ID
02/2084: 97 90 sta [fcr_ptr],y ;copy to FCR
02/2086: a0 02 00 ldy #O_DOSFDE_TYPE
02/2089: b7 a8 lda [fde_ptr],y ;get DOS file type byte
02/208b: 29 7f 00 and #$007f ;strip lock flag
02/208e: a0 26 00 ldy #O_FCRX_DOS_TYPE
02/2091: 97 90 sta [fcr_ptr],y ;store in FCR
02/2093: 80 2e bra :Skip
; UNUSED CODE
; Clones an FCR. Only the fields specified by the bitmap are copied.
02/2095: 9c 8a 22 :unref_2095 stz clone_ctr
02/2098: a0 00 00 ldy #$0000
02/209b: ad 8a 22 :unref_209B lda clone_ctr
02/209e: 4a lsr A
02/209f: aa tax
02/20a0: bd 4c 22 lda fcr_clone_bits,x
02/20a3: 8d 88 22 sta clone_bits
02/20a6: a2 10 00 ldx #$0010
02/20a9: 0e 88 22 :unref_20A9 asl clone_bits
02/20ac: 90 04 bcc :unref_20B2
02/20ae: b7 94 lda [fcr_ptr2],y
02/20b0: 97 90 sta [fcr_ptr],y
02/20b2: c8 :unref_20B2 iny
02/20b3: c8 iny
02/20b4: ca dex
02/20b5: d0 f2 bne :unref_20A9
02/20b7: ad 8a 22 lda clone_ctr
02/20ba: d0 05 bne :unref_20c1
02/20bc: ee 8a 22 inc clone_ctr
02/20bf: 80 da bra :unref_209B
02/20c1: 80 70 :unref_20c1 bra :SetParams
; Get file attributes, like length and auxtype, which often require reading data
; from the file.
02/20c3: a0 16 00 :Skip ldy #O_FCRX_BUF_VPTR
02/20c6: b7 90 lda [fcr_ptr],y ;get vptr to read buffer
02/20c8: aa tax
02/20c9: c8 iny
02/20ca: c8 iny
02/20cb: b7 90 lda [fcr_ptr],y
02/20cd: a8 tay
02/20ce: 22 38 fc 01 jsl DEREF ;convert to real pointer
02/20d2: 86 04 stx drvr_buf_ptr ;copy to DP, as driver buffer and gbuf ptr
02/20d4: 86 98 stx gbuf_ptr
02/20d6: 84 06 sty drvr_buf_ptr+2
02/20d8: 84 9a sty gbuf_ptr+2
02/20da: a9 02 00 lda #DRIVER_READ
02/20dd: 85 02 sta drvr_call_num
02/20df: a0 2e 00 ldy #O_FCRX_FDE_INDEX
02/20e2: b7 90 lda [fcr_ptr],y ;get file's FDE index
02/20e4: aa tax
02/20e5: a0 2c 00 ldy #O_FCRX_FDE_SCT
02/20e8: b7 90 lda [fcr_ptr],y ;get file's FDE sector
02/20ea: 20 bb 0e jsr SetFdePtr ;set the FDE pointer
02/20ed: 20 80 16 jsr GetFileAttrs ;get the file's attributes
02/20f0: 90 03 bcc :GetAttrOk
02/20f2: 82 05 01 brl :FailCleanup
02/20f5: a0 30 00 :GetAttrOk ldy #O_FCRX_BUF_TS
02/20f8: ad 2e 02 lda trk_sct_val ;get track/sector of file's first data sector
02/20fb: 97 90 sta [fcr_ptr],y ;save as the track/sector we read last
02/20fd: a0 14 00 ldy #O_FCR_ACCESS
02/2100: ad 14 03 lda file_access ;get file access mode (should be $0001)
02/2103: 97 90 sta [fcr_ptr],y
02/2105: a0 1a 00 ldy #O_FCRX_EOF
02/2108: ad 18 03 lda file_eof ;copy file length
02/210b: 97 90 sta [fcr_ptr],y
02/210d: ad 1a 03 lda file_eof+2
02/2110: c8 iny
02/2111: c8 iny
02/2112: 97 90 sta [fcr_ptr],y
02/2114: a0 1e 00 ldy #O_FCRX_AUXTYPE
02/2117: ad 16 03 lda file_auxtype ;copy auxtype
02/211a: 97 90 sta [fcr_ptr],y
02/211c: a0 24 00 ldy #O_FCRX_FILE_TYPE
02/211f: ad 12 03 lda file_pro_type ;copy ProDOS-equivalent file type
02/2122: 97 90 sta [fcr_ptr],y
02/2124: a0 28 00 ldy #O_FCRX_NUM_SCT
02/2127: ad 1c 03 lda file_len_in_sct ;copy number of sectors allocated to file
02/212a: 97 90 sta [fcr_ptr],y
02/212c: a9 00 00 lda #$0000
02/212f: c8 iny
02/2130: c8 iny
02/2131: 97 90 sta [fcr_ptr],y ;set the high word to zero
;
; We have all the bits and pieces. Copy them to the parameter block.
;
02/2133: ad f2 01 :SetParams lda call_class ;ProDOS-16?
02/2136: d0 03 bne :CopyClass1 ;no, branch
02/2138: 82 88 00 brl :CopyClass0 ;yes, also branch (nearly out of 1-byte rel range)
; Copy parameters according to commands. Note this is slightly different from
; the function at $02/0db2: we decr pCount *before* copying data.
02/213b: ad f4 01 :CopyClass1 lda pcount ;go until we run out of args to copy
02/213e: aa tax
02/213f: a0 fe ff ldy #$fffe ;start at -2
02/2142: c8 :Copy1Loop iny
02/2143: c8 iny
02/2144: b9 50 22 lda parm_c1_open,y
02/2147: 0a asl A ;shift pCount flag into carry
02/2148: 90 03 bcc :NoDecCount ;not set, branch
02/214a: ca dex ;decr pCount
02/214b: 30 0e bmi :BulkCopyDone ;sub-zero, bail
02/214d: 0a :NoDecCount asl A ;shift skip flag into carry
02/214e: b0 f2 bcs :Copy1Loop ;set, go get the next one
02/2150: 4a lsr A ;shift it back, without the flags
02/2151: 4a lsr A
02/2152: 5a phy ;preserve dest index
02/2153: a8 tay ;get src index
02/2154: b7 90 lda [fcr_ptr],y ;get value
02/2156: 7a ply ;restore dest index
02/2157: 97 32 sta [param_blk_ptr],y ;write output value
02/2159: 80 e7 bra :Copy1Loop
; Zero out some parameter fields. Might have been easier to just zero out the
; tail end of the param block before doing the earlier copy.
; (bug? the pCount tests are off-by-one through most of this)
; (bug? not setting the high word of blocksUsed field)
02/215b: ae f4 01 :BulkCopyDone ldx pcount ;get requested arg count
02/215e: e0 08 00 cpx #$0008 ;pCount < 8?
02/2161: 90 6a bcc :Common ;yes, don't need to deal with this stuff
02/2163: a0 12 00 ldy #$0012 ;auxType hi field (arg 7)
02/2166: a9 00 00 lda #$0000
02/2169: 97 32 sta [param_blk_ptr],y ;set to zero
02/216b: e0 09 00 cpx #$0009 ;pCount < 9?
02/216e: 90 5d bcc :Common ;yes, branch
02/2170: a0 24 00 ldy #O_FCRX_FILE_TYPE
02/2173: b7 90 lda [fcr_ptr],y ;get file type
02/2175: c9 0f 00 cmp #$000f ;is it DIR?
02/2178: f0 03 beq :IsDir ;yes; $0f is also the storage type
02/217a: a9 01 00 lda #$0001 ;use storage type $01 for normal file
02/217d: a0 14 00 :IsDir ldy #$0014 ;storageType field (arg 8)
02/2180: 97 32 sta [param_blk_ptr],y
02/2182: e0 0a 00 cpx #$000a ;pCount < 10?
02/2185: 90 46 bcc :Common ;yes, branch
02/2187: a9 00 00 lda #$0000
02/218a: a0 16 00 ldy #$0016 ;createDateTime field (arg 9)
02/218d: 97 32 sta [param_blk_ptr],y
02/218f: c8 iny
02/2190: c8 iny
02/2191: 97 32 sta [param_blk_ptr],y
02/2193: c8 iny
02/2194: c8 iny
02/2195: 97 32 sta [param_blk_ptr],y
02/2197: c8 iny
02/2198: c8 iny
02/2199: 97 32 sta [param_blk_ptr],y
02/219b: e0 0b 00 cpx #$000b ;pCount < 11?
02/219e: 90 2d bcc :Common ;yes, branch
02/21a0: a0 1e 00 ldy #$001e ;modDateTime field (arg 10)
02/21a3: 97 32 sta [param_blk_ptr],y
02/21a5: c8 iny
02/21a6: c8 iny
02/21a7: 97 32 sta [param_blk_ptr],y
02/21a9: c8 iny
02/21aa: c8 iny
02/21ab: 97 32 sta [param_blk_ptr],y
02/21ad: c8 iny
02/21ae: c8 iny
02/21af: 97 32 sta [param_blk_ptr],y
02/21b1: e0 0c 00 cpx #$000c ;pCount < 12?
02/21b4: 90 17 bcc :Common ;yes, branch
02/21b6: a0 26 00 ldy #$0026 ;optionList field (arg 11)
02/21b9: 20 1f 16 jsr OutputOptionList ;generate an option list
02/21bc: 90 0f bcc :Common
02/21be: a9 4f 00 lda #E_BUFF_TOO_SMALL
02/21c1: 80 37 bra :FailCleanup
02/21c3: a0 00 00 :CopyClass0 ldy #O_FCR_REF_NUM
02/21c6: b7 90 lda [fcr_ptr],y ;get refNum
02/21c8: a0 00 00 ldy #$0000
02/21cb: 97 32 sta [param_blk_ptr],y ;copy to output
02/21cd: ad f4 01 :Common lda pcount
02/21d0: c9 0e 00 cmp #$000e ;pCount < 14? (this test is correct)
02/21d3: 90 20 bcc :Success
02/21d5: a0 32 00 ldy #$0032 ;resourceEOF field (arg 14)
02/21d8: a9 00 00 lda #$0000
02/21db: 97 32 sta [param_blk_ptr],y ;set to zero
02/21dd: c8 iny
02/21de: c8 iny
02/21df: 97 32 sta [param_blk_ptr],y
02/21e1: ad f4 01 lda pcount
02/21e4: c9 0f 00 cmp #$000f ;pCount < 15?
02/21e7: 90 0c bcc :Success
02/21e9: a0 36 00 ldy #$0036 ;resourceBlocks field (arg 15)
02/21ec: a9 00 00 lda #$0000
02/21ef: 97 32 sta [param_blk_ptr],y ;set to zero
02/21f1: c8 iny
02/21f2: c8 iny
02/21f3: 97 32 sta [param_blk_ptr],y
02/21f5: 18 :Success clc
02/21f6: a9 00 00 lda #$0000
02/21f9: 60 rts
; Release the FCR and/or VCR after a failure.
02/21fa: 48 :FailCleanup pha ;preserve error code
02/21fb: 2c 3a 02 bit alloc_rsrc_flags ;set N/V flags based on bits 15/14
02/21fe: 08 php ;push flags (we check N later)
02/21ff: 50 34 bvc :NoFreeFcr
02/2201: a0 24 00 ldy #O_FCRX_FILE_TYPE
02/2204: b7 90 lda [fcr_ptr],y ;get file type
02/2206: c9 0f 00 cmp #$000f ;did we open the volume directory?
02/2209: f0 13 beq :NoRel ;yes, don't release the file buffer
02/220b: a0 16 00 ldy #O_FCRX_BUF_VPTR
02/220e: b7 90 lda [fcr_ptr],y ;get vptr to 512-byte read buffer
02/2210: aa tax
02/2211: c8 iny
02/2212: c8 iny
02/2213: 17 90 ora [fcr_ptr],y ;OR with high word
02/2215: f0 07 beq :NoRel ;zero, nothing to release
02/2217: b7 90 lda [fcr_ptr],y
02/2219: a8 tay
02/221a: 22 20 fc 01 jsl RELEASE_SEG ;release the memory
02/221e: a0 00 00 :NoRel ldy #O_FCR_REF_NUM
02/2221: b7 90 lda [fcr_ptr],y ;get refNum
02/2223: 22 30 fc 01 jsl RELEASE_FCR ;discard the FCR
02/2227: a0 08 00 ldy #O_VCR_OPEN_CNT
02/222a: b7 88 lda [vcr_ptr],y ;get open count
02/222c: f0 07 beq :NoFreeFcr ;zero, branch
02/222e: 1a inc A ;add 1 to test for $ffff
02/222f: f0 02 beq :WasNeg ;was -1, branch
02/2231: 3a dec A ;subtract 1 to restore original value
02/2232: 3a dec A ;subtract one more
02/2233: 97 88 :WasNeg sta [vcr_ptr],y ;save new value
02/2235: 28 :NoFreeFcr plp ;restore N flag
02/2236: 10 11 bpl :Finish
02/2238: a0 08 00 ldy #O_VCR_OPEN_CNT
02/223b: a9 00 00 lda #$0000 ;zero out number of open files
02/223e: 97 88 sta [vcr_ptr],y
02/2240: a0 00 00 ldy #O_VCR_ID
02/2243: b7 88 lda [vcr_ptr],y ;get VCR ID
02/2245: 22 28 fc 01 jsl RELEASE_VCR ;release the VCR
02/2249: 68 :Finish pla ;restore error code
02/224a: 38 sec ;report error
02/224b: 60 rts
;
; Control flags for cloning an FCR. Unused.
02/224c: 18 e4 fc 00 fcr_clone_bits .bulk $18,$e4,$fc,$00
;
; Parameter copy commands for Class 1 Open.
;
; As with the tables at $02/0dd0, each entry is an offset, with two flags. The
; offset is a displacement from the start of the FCR. $4000 indicates the
; output should be advanced 2 bytes without copying data, while $8000 means that
; the pCount should be decremented. Copying stops when the pCount reaches -1.
;
; Unlike the earlier code, we update and check pCount *before* copying the data
; value. So the $8000 flag should be set on the first word of a multi-word
; item, rather than the last. Also, this code doesn't use a trick to copy a
; zero value, presumably because they didn't want every FCR to have an extra two
; bytes just to hold zeroes.
02/2250: 00 40 parm_c1_open .dd2 $4000 ;pCount (skip)
02/2252: 00 80 .dd2 $8000 ;refNum (copy from +$00, decr)
02/2254: 00 c0 .dd2 $c000 ;pathname lo (skip, decr)
02/2256: 00 40 .dd2 $4000 ;pathname hi (skip)
02/2258: 00 c0 .dd2 $c000 ;requestAccess (skip, decr)
02/225a: 00 c0 .dd2 $c000 ;resourceNumber (skip, decr)
02/225c: 14 80 .dd2 $8014 ;access (decr)
02/225e: 24 80 .dd2 $8024 ;fileType (decr)
02/2260: 1e 80 .dd2 $801e ;auxType lo (decr)
02/2262: 00 40 .dd2 $4000 ;auxType hi (skip)
02/2264: 00 c0 .dd2 $c000 ;storageType (skip, decr)
02/2266: 00 c0 .dd2 $c000 ;createDateTime (skip, decr)
02/2268: 00 40 .dd2 $4000 ; ...
02/226a: 00 40 .dd2 $4000
02/226c: 00 40 .dd2 $4000
02/226e: 00 c0 .dd2 $c000 ;modDateTime (skip, decr)
02/2270: 00 40 .dd2 $4000 ; ...
02/2272: 00 40 .dd2 $4000
02/2274: 00 40 .dd2 $4000
02/2276: 00 c0 .dd2 $c000 ;optionList lo (skip, decr)
02/2278: 00 40 .dd2 $4000 ;optionList hi
02/227a: 1a 80 .dd2 $801a ;eof lo (decr)
02/227c: 1c 00 .dd2 $001c ;eof hi
02/227e: 28 80 .dd2 $8028 ;blocksUsed lo (decr)
02/2280: 02 40 .dd2 $4002 ;blocksUsed hi (skip; "2" unused)
02/2282: 00 c0 .dd2 $c000 ;resourceEOF lo (skip, decr)
02/2284: 00 c0 .dd2 $c000 ;resourceEOF hi (skip, decr)
02/2286: 00 c0 .dd2 $c000 ;resourceBlocks lo (skip, decr)
;
; Local storage for FCR clone function. Unused.
02/2288: 00 00 clone_bits .fill 2,$00
02/228a: 00 00 clone_ctr .fill 2,$00