back to project page

DOS3.3.FST Disassembly

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

Symbol Table

AppEntry$020a8c
Header$020100
SysEntry$020bbc