******************************************************************************** * 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