Files
Shawn Quick dfcaae62dd Add qlink, the disk tools, and a bootable ProDOS 8 image.
Move the assembler to src/qasm. qlink links OMF 1.0 and 2.0 from a script. makeBin flattens a load file, qomf dumps OMF, and qpro builds ProDOS volumes. APW and MPW assemble through the ORCA front end. Relocatable modules emit OMF objects. Listings stay off unless -l is set.

make boot assembles the GS/OS 6.0.1 ProDOS 8 kernel and BASIC.SYSTEM into an 800K volume. BASIC.SYSTEM accepts ls and pfx, and its splash reports PRODOS BASIC 2.0, copyright 1983-2026. IIGS.601.SRC is the source and the BASIC.SYSTEM oracle that image is built from.
2026-10-16 14:41:55 -07:00

1 line
146 KiB
Plaintext

print nohdr
***********************************************************************************************
* *
* DOS 3.3 File System Translator for GS/OS *
* *
* Copyright Apple Computer Inc. 1988-1992 ALL RIGHTS RESERVED *
* *
* *
***********************************************************************************************
******* REVISION HISTORY: ***************************************************************
*
* August 15, 1988 Monte Benaresh
*
* Created this file and entered basic directives, includes, etc.
* At this point the preliminary ERS, version 0.1, is about to be
* released.
*
* Much of this file is based on the source for the ProDOS FST by
* Rob Turner.
*
* October 27, 1988 Monte Benaresh
*
* All the code needed for the OPEN call is essentially completed.
* Ported dispatch "shell" routines from the ProDOS FST v02.00.
* Existing source assembles, and code is about to be set up
* for testing under the GS/OS exerciser.
*
* November 17, 1988 Monte Benaresh
*
* The OPEN, VOLUME, and GET_FILE_INFO calls are now functional and
* the Finder will mount DOS 3.3 disks.
*
* August 13, 1989 Monte Benaresh
*
* All calls are now implemented, and the FST is just a few hours away
* from being called Alpha.
*
* August 14, 1989 Monte Benaresh
*
* Projectorized the source and created a makefile.
*
* 1.0 d17
* 10/08/90 CAF Changed the code in match_vcr to increment the vcr open count before
* trying to read in some blocks from the disk. I did this because SCM
* was throwing out the VCR when it got the disk_switched error from the
* appledisk5.25, so an FCR would be created with a link to no VCR - when
* you tried to close the FCR, SCM would die. Fixed following bugs:
* EDS213WMP - changed flush to check for pcount max = 2.
* EDS212WMP - changed parameter table to not include pcount as a parm.
* EDS205WMP - uncommented some existing code which fixed it.
* EDS207WMP - added a check for the resource_num parameter.
* EDS208WMP - fixed by match_vcr addition.
* EDS209WMP - fixed by EDS207WMP fix.
* EDS210WMP - changed code in open to preserve error number.
* EDS211WMP - fixed by commenting out code which copied old fcr
* into new fcr in open call.
* EDS222WMP - changed gfi to put total_blocks into aux_type for c0.
*
* 1.0 d18
* 10/15/90 CAF Fixed Bugs:
* EDS224WMP - Changed read code to skip the high bit stripping for dirs.
* 10/17/90 CAF Fixed Bugs:
* APG209WMP - Volume call was getting a $2C error because of a request
* count to the driver of $100, I changed this to a bouncing
* error.
* EDS226WMP - Same fix as APG209WMP
* EDS215WMP - The newline code for a single char was transferring the
* data backwards, so I changed the loop to go forwards. Also
* the local transfer count was not being returned correctly.
* EDS214WMP - The local transfer count in newline was not being returned
* correctly (it was being returned as 0), so the fst would
* sit and spin forever because the request_count was never met.
*
* 1.0 d19 - Beta Candidate
* 11/02/90 CAF Fixed Bugs:
* LYP225LWA - Changed volume size constants used in volume_s to correct values.
* LYP226LWA - Changed class 0 version of volume_s to not divide free block
* count by 2.
* LYP227LWA - Changed parse_path to check for a class 0 separator as well
* as a class 1 separator at the start of the pathname.
* LYP229LWA - Changed class 0 getfileinfo to check to see if the file is a directory
* before stuffing the total_blocks into the aux_type.
*
* 1.0 d20 - Beta Candidate
* 11/06/90 CAF Fixed Bugs:
* LYP227LWA - removed addition to parse_path added on 11/02/90, and changed
* get_dev_num_s to check for a duplicate volume error and mask it
* into a volume not found error.
* LYP230LWA - changed open to zero out the resource_blocks field.
*
* 1.0 d21 - Beta Candidate
* 11/06/90 CAF Fixed Bugs:
* LYP231LWA - changed open_setup to check to see if a volume is a dos
* volume and that it is online before trying to create a vcr
* for it.
* LYP228LWA - same as 231.
* Completely rewrote most of open_setup because the algorithm used in finding
* volumes and building vcr's was not handling the operation correctly.
* Fixed a bug in find_vol_online where it was not returning a vol_not_found
* error and so the call would not bounce to other fsts.
* Fixed get_dir_entry to update the vcr_dev if the disk moves.
* Fixed a bug in get_dir_entry where the drvr_dev_num was not being setup if the end_of_dir
* had been reached, so a subsequent get_last_dev call would not work correctly.
*
* 1.0 d22 - Beta Candidate
* 11/09/90 CAF Fixed a bug in parse_path where it was executing 16 bit code in 8 bit mode.
* Fixed a bug in open_setup where I was not setting up a pointer before a call, and
* so the fst was getting confused enough to cause SCM to commit hari-kari.
* LYP237LWA - Fixed a bug in open_setup that was causing vcrs to be dumped.
*
* 1.0 d23
* 11/28/90 CAF Fixed Bugs:
* LYP243LWA - changed newline code to strip the high bits off of text
* file data during xfer.
* LYP248LWA - changed some 8/16bit A errors in newline read.
* LYP247LWA - same as LYP248LWA
* LYP245LWA - same as LYP248LWA
* LYP246LWA - same as LYP248LWA
* LYP238LWA - changed pblock copy data to null out the blocks_used field.
* LYP241LWA - changed get_dir_entry to check for out of range values
* when base = 2.
*
* 1.0 d24
* 12/06/90 CAF Fixed Bugs:
* LYP249LWA - Fixed a bug in read where it wasn't taking into account
* the data_offset into a file.
* LYP252LWA - same as 249.
* LYP250LWA - Fixed a bug in newline_read where it wasn't using
* long index registers during the newline compare, and
* so any multiple of $100 would give a length of $00.
* Fixed a bug where newline mode with > 1 char would not work on text files.
*
* 1.0 d25
* 12/07/90 CAF Changed volume_s to output the volume name on a dup_vol error.
*
* 1.0 d26
* 12/18/90 CAF Fixed Bugs:
* LYP254LWA - changed open to not output blocks_used parameter.
* LYP255LWA - changed allocate_vcr to allow the starting search
* device (supposed_dev) to be setup by the caller.
*
* 1.0 d27
* 12/19/90 CAF Fixed Bugs:
* LYP256LWA - changed open to special case the directory file.
* LYP257LWA - changed gfi to special case a class 1 call on a directory.
* LYP258LWA - changed not_supported2 and not_supported to return drvr_wr_prot.
* LYP259LWA - changed not_supported2 and not_supported to return drvr_wr_prot.
* LYP260LWA - changed not_supported2 and not_supported to return drvr_wr_prot.
* LYP261LWA - changed not_supported2 and not_supported to return drvr_wr_prot.
* LYP262LWA - changed not_supported2 and not_supported to return drvr_wr_prot.
* LYP263LWA - changed not_supported2 and not_supported to return drvr_wr_prot.
*
* 1.0 b01
* 12/19/90 CAF/JEV Fixed a bug that was causing a vcr to be thrown out during a gfi call.
* Added "support" for judge_name call ($07).
*
* 1.0 b02 jev Changed how volume names are generated. It was felt that just 'DOS 3.3 Vxxxx' was
* all that was needed.
*
* 1.0 b03 jev Fixed Bug:
* BGK053LWA - changed error code from a $60 to $4A
* 3/31/91 jev changed vol_not_found error to unknown vol error if we try to access any
* other device other than an 5.25.
* 3/31/91 jev fixed problem with checking for correct pcount.
* 3/31/91 jev added error priority support. Added new flags in FST header
* to indicate apple format and read-only fst.
* 8/18/91 jev Fixed Bug:
* 86672 - add check in parsepath to check for path2 to see if
* it exsists or is too long even though we don't use it
* because we con't do a changepath. But we need to be a
* good FST and return $40 instead of a $45 if the path is bad.
* 8/19/91 jev Fixed Do_Option_list. Was not setting up the length of the data.
* 8/21/91 jev Changed max-pcnt for Judename call to 6. Fixes a bug reported by
* developer in which DOS3.3 returns a bad pcount error instead of an
* invalidFSTop for a judeName call.
*
* 9/18/91 jev fixed open call to resource fork. Any open call to a resource fork
* passed to this fst would return res_not_found error even the destination
* volume was not a DOS 3.3 volume. Moved this check to after the destination
* was located because this is a non-bouncing error and we don't want to return
* this error unless the destination belongs to us.
*
* 9/19/91 jev Fixed Bug:
* 86425 - open call with invalid access to any target would return
* invalid access even if the target was not a DOS 3.3 file.
* Moved access check to after the destination was located as above.
*
* 11/19/91 GAB Fixed occurances of returning VolumeNotFound errors when that
* particular error shouldn't be returned. (Confuses SCM's error
* priority logic.)
*
* Changed device_id routine so that it no longer performs a Status
* call to the device driver simply to have the dispatcher set the
* DIB pointer on direct page. The dispatcher has a call that will
* perform this service without getting the driver involved.
*
* 12/05/91 GAB Change maximum allowable pCount for ChangePath call to 3.
*
* 01/08/92 GAB Fixed READ call to properly initialize fde_ptr before it tries
* to use it. It was using an invalid pointer when it tried to
* retrieve the first track/sector list location out of the
* directory entry.
*
* Fixed READ call to decrement the destination pointer after
* moving an odd number of bytes out of the sector buffer. When
* moving an odd number of bytes, it moved the first byte, then
* incremented the destination pointer and performed the remainder
* of the move 2 bytes at a time. Once it had moved the remainder
* of the sector buffer to the caller's buffer, it was adjusting
* the destination pointer by the number of bytes moved (which
* skipped one byte, due to the initial increment). It was then
* going back to read the next sector to finish the call, but
* using the invalid (off-by-one) destination pointer.
*
* Changed 'brl' instructions to 'bra' wherever possible
*
* 02/07/92 MSD Changed version number to 01.00 (final).
*
* 02/19/92 - Monte Benaresh
* Added code to the OPEN call to zero-out the resource_eof field
* when the param is active, and changed to param copy tables for
* GET_DIR_ENTRY and GET_FILE_INFO to zero out the resource_eof
* and resource_blocks fields when those params are active.
*
* 19-Feb-92 Dave Lyons
* Rolled version to $0101 and v01.01 for Monte's change.
*
* 02/20/92 - Monte Benaresh
* Fixed problem in find_eof with 0-length text files.
* Bumped version to 1.02.
*
***********************************************************************************************
*
* BEGIN 6.0.1
*
* 26-May-92 Dave Lyons for 6.0.1
*
* Try changing name to "Apple II DOS 3.3" instead of just "DOS 3.3", to avoid
* confusion with MS-DOS.
*
* 7-Nov-92 DAL with jcm, version 1.3d12
*
* Added an entry to the end of fcr_to_pblk so that Open will stop when pcount is 15 instead
* of trashing a variable amount of memory past the end of the parameter block.
*
*
* 2-Apr-93 SS -- Changed version to $0103 for 6.0.1 (final)
*
* 5-May-93 DAL -- for 6.0.1d17
*
* Handle zero-length filenames like deleted directory entries, because the Finder
* gets really confused on a zero-length name, and it doesn't make much sense to
* begin with. Some DOS 3.3 disks have zero-length filenames to make the catalog
* look pretty.
*
***********************************************************************************************
machine M65816
string asis
print off
include 'my.all.macros'
include 'm16.debug'
;load 'C.Floor:MPW:aiigsincludes:my.cmn.d'
include 'my.common.equ'
include 'param.records'
include 'DOS.FST.identifiers'
print on
;======================================================================
;Below is the FST header definition used by GS/OS at boot time
;
;This PROC was adapted directly from the ProDOS FST v02.00 by Rob Turner
;
;======================================================================
EXPORT fst_header
fst_header PROC
import appl_call
import system_call
EXPORT code_start
code_start
DC.B 'FST ' ;Says I'm an FST
DC.L appl_call ;Process a normal application call
DC.L system_call ;Process an FST system call
DC.W my_id ;ID number (DOS 3.3) = $02
DC.W $1803 ;don't uppercase pathnames, not formattable, strip high bit(?).
;read-only,apple II format
DC.W $0103 ;Version 1.03 final
DC.W 256 ;block size
DC.L 560 ;Max Blocks (140K)
DC.L 32 ;Min Blocks-need at least 2 tracks.
DC.L $FFFFFFFF ;max file size in bytes.
DC.L $0000 ;reserved
DC.B @n_end-@n_strt ;length of next field
@n_strt DC.B 'Apple II DOS 3.3'
@n_end
dc.b @c_end-@c_strt ;length of comment
@c_strt DC.B 'DOS 3.3 FST v01.03'
;comment field
@c_end
DC.W 0000 ;reserved field
dc.b 'DOS 3.3 FST. Written by Monte Benaresh, maintained by Cary '
dc.b 'Farrier and Jack Valois. Tested by Linda Pan. '
dc.b 'Copyright 1992 Apple Computer'
ENDP
print off
include 'DOS.FST.storage'
print on
open_s PROCNAME ;shell for open call
import open
stz error
jsl lock_mem
; No need to check for the p_count here anymore. It is all done
; at the app entry point now.
;
;check_pcount
; lda class ;get the class of the call
; beq @class0 ;if class 0, no pcount
; lda p_cnt
; cmp #open1.pmax
; bcc @class0
; lda #invalid_pcount ;too big
; sta error
; bra exit
;@ok
; moved these checks into open call. we need to check to see if this
; is a DOS3.3 volume first before we return a non-bouncing error
; 9/17/91 JEV
;@check_access
; cmp #4 ;enough parameters for request_access?
; bcc @class0 ;no, can't check it
; ldy #open1.request_access
; lda [param_blk_ptr],y ;get the request access word
; and #%0000000000000010 ;check for write access
; beq @ok0 ;no write access
; lda #invalid_access
; sta error
; bra exit
;@ok0 ; opening resource fork?
; bra @class0
;
; lda p_cnt
; cmp #5
; bcc @class0 ; pcount too small ->
; ldy #open1.resource_number
; lda [<param_blk_ptr],y
; beq @class0 ; ok ->
; cmp #1 ; == 1?
; beq @class0 ; ok ->
; bne @range_err ; no ->
;
; lda #res_not_found
; sta error
; bra exit
;@range_err lda #parm_range_err
; sta error
; bra exit
@class0
jsr open
sta error ;save error
lda gen_buf_vp
tax
ora gen_buf_vp+2
beq @skip3
ldy gen_buf_vp+2
jsl release_seg
@skip3
exit
;brk $22
jsl unlock_mem
lda error ;restore error
cmp #1 ;set carry accordingly
bcc @noErr
ora error_priority
@noErr
;brk $11
jml sys_exit
error ds.w 1 ;temp storage for error code
ENDP
volume_s PROC ;shell for volume call
import volume
import free_sectors
import load_cat_secs
import match_vcr
import allocate_vcr
import device_id
;stz gen_buf_vp ;this is now done on entry
;stz gen_buf_vp+2
stz vcr_fcr_flags ;init flags
;brk $13
jsl lock_mem
jsr device_id ; we're looking for a device_id of $00
bcs err_done
beq @device_ok
lda #unknown_vol ; not a 5.25' drive return a bouncing error
bra err_done
@device_ok
lda class
beq @class0
@class1
; lda p_cnt
; cmp #volume1.pmax
; bcc @ok1
; lda #invalid_pcount
; brl exit
@ok1
ldy #volume1.vol_name ;copy pointer to direct page
lda [param_blk_ptr],y
sta <user_ptr
iny
iny
lda [param_blk_ptr],y
sta <user_ptr+2
lda #vol_max_char+4 ;check the buffer size
cmp [user_ptr]
bcc @ok2
lda #vol_max_char+1 ;this is the size we need
ldy #$0002 ;point to string length field
sta [user_ptr],y ;let caller know what we need
lda #buff_too_small
;pha
;php
brl exit
@ok2
bra get_buffs
@class0
ldy #volume0.vol_name ;copy pointer to direct page
lda [param_blk_ptr],y
sta <user_ptr
iny
iny
lda [param_blk_ptr],y
sta <user_ptr+2
get_buffs
lda #vol_name_buf ;get address of this internal buffer
sta <vol_name_ptr
lda #gen_buf_size ;let's get some buffer space for the vtoc & directory
jsl alloc_seg
bcc @ok
lda #out_of_mem ;can't get buffer space
brl done
@ok
stx gen_buf_vp ;4K buffer used by volume routine
sty gen_buf_vp+2
jsl deref
stx <gen_buf_ptr
sty <gen_buf_ptr+2
;lda gbuf_ptr
;sta <gen_buf_ptr
;lda gbuf_ptr+2
;sta <gen_buf_ptr+2
;brk $A9
jsr volume
bcc ok1
@vol_error ;volume identification failed
cmp #drvr_bad_parm ; bad parameter?
beq @mask ; yes ->
cmp #drvr_bad_count ; bad count because of the $100 request count?
bne err_done ; no, return the error back up ->
@mask lda #unknown_vol ;translate to bouncing error
err_done sec
brl done ;if error, pack up and leave
ok1
lda #<vol_name_buf ;set up our volume name for other routines
sta <volname_buf_ptr
lda #^vol_name_buf
sta <volname_buf_ptr+2
lda drvr_dev_num ; look in this device
sta supposed_dev
jsr allocate_vcr
bcc have_vcr
cmp #dup_volume ; duplicate volume error ?
bne err_done ; no ->
lda path_flag ; device name only?
and #$4000
beq name_out ; yes ->
err_bra lda #dup_volume ; no ->
bra err_done
*
* copy the volume name to the output buffer anyways
*
name_out lda class ;output the volume name
beq @class0
@class1
lda (vol_name_ptr) ;get volume name length
inc a ;account for leading character
ldy #2 ;point to start of g-string
sta [user_ptr],y ;set the length
lda <user_ptr
clc
adc #$0003 ;bump pointer to look at g-string plus room for�
;�leading '/' or ':'
sta <user_ptr
bcc @no_carry
inc <user_ptr+2
@no_carry
ldy #$0002 ;point to 1st char
bra @copy_string ;we're now ready to copy
@class0
lda (vol_name_ptr) ;get length
inc a ;account for leading character
sta [user_ptr]
@copy_string
sep #%00100000 ;work with bytes
LONGA OFF
ldy #$0002
@loop
lda (vol_name_ptr),y
beq @done ;if we hit a null, that's the end of the volume name
sta [user_ptr],y
iny
bpl @loop ;use bpl so if something weird happens, the most we'll�
;�trash is 32K!
@done
rep #%00100000
LONGA ON
lda class
bne @sep1
@sep0
ldy #1 ;point to space for '/'
lda [user_ptr],y
and #$FF00 ;erase the garbage
ora #'/' ;place the slash
sta [user_ptr],y
bra err_bra ; up ->
@sep1 ldy #1 ;point to space for ':'
lda [user_ptr],y
and #$FF00 ;erase the garbage
ora #':' ;place the colon
sta [user_ptr],y
bra err_bra ; up ->
****************
have_vcr
lda class ;here we go again!
beq @class0
@class1
lda (vol_name_ptr) ;get volume name length
inc a ;account for leading character
ldy #2 ;point to start of g-string
sta [user_ptr],y ;set the length
lda <user_ptr
clc
adc #$0003 ;bump pointer to look at g-string plus room for�
;�leading '/' or ':'
sta <user_ptr
bcc @no_carry
inc <user_ptr+2
@no_carry
ldy #$0002 ;point to 1st char
bra copy_string ;we're now ready to copy
@class0
lda (vol_name_ptr) ;get length
inc a ;account for leading character
sta [user_ptr]
copy_string
sep #%00100000 ;work with bytes
LONGA OFF
ldy #$0002
@loop
lda (vol_name_ptr),y
beq @done ;if we hit a null, that's the end of the volume name
sta [user_ptr],y
iny
bpl @loop ;use bpl so if something weird happens, the most we'll�
;�trash is 32K!
@done
rep #%00100000
LONGA ON
*
* output the total_blocks and free_blocks
*
lda class
bne @class1
@class0
ldy #1 ;point to space for '/'
lda [user_ptr],y
and #$FF00 ;erase the garbage
ora #'/' ;place the slash
sta [user_ptr],y
ldy #vtoc_sectors ;get # of sector per track
lda [vol_buf_ptr],y
and #32
bne @32a ;there are 32 sectors per track
lda #$230 ;# of 512-byte blocks on 5 1/4 volume
bra @stor
@32a
lda #$640 ;# of 512-byte blocks on a 3 1/2
@stor
ldy #volume0.total_blocks
sta [param_blk_ptr],y
iny
iny
lda #$0000
sta [param_blk_ptr],y ;zero the high word
jsr free_sectors
ldy #volume0.free_blocks
sta [param_blk_ptr],y
iny
iny
lda #$0000
sta [param_blk_ptr],y ;zero the high word
lda #my_id ;FST ID
ldy #volume0.file_sys_id
sta [param_blk_ptr],y
clc ;call successful
bra done
@class1
ldy #1 ;point to space for ':'
lda [user_ptr],y
and #$FF00 ;erase the garbage
ora #':' ;place the colon
sta [user_ptr],y
lda p_cnt
sta param_countdn
dec param_countdn ;this way we can use bmi
dec param_countdn ;dev_name
dec param_countdn ;vol_name
bmi done
;now we want total_blocks
ldy #vtoc_sectors ;get # of sector per track
lda [vol_buf_ptr],y
and #32
bne @32 ;there are 32 sectors per track
lda #$230 ;# of sectors on 5 1/4 volume
bra @store
@32 lda #$640 ;# of sectors on a 3 1/2
@store ldy #volume1.total_blocks
sta [param_blk_ptr],y
iny
iny
lda #$0000
sta [param_blk_ptr],y ;zero the high word
dec param_countdn
bmi done
;get blocks free
jsr free_sectors
ldy #volume1.free_blocks
sta [param_blk_ptr],y
iny
iny
lda #$0000
sta [param_blk_ptr],y ;zero the high word
dec param_countdn
bmi done
lda #my_id ;FST ID
ldy #volume1.file_sys_id
sta [param_blk_ptr],y
dec param_countdn
bmi done
lda #$0100 ;this is our "block" size
ldy #volume1.block_size
sta [param_blk_ptr],y
clc ;call successful
lda #0
exit
done pha
php
;jsl unlock_mem
bcc @no_error ;if no error, let VCR's stay
bit vcr_fcr_flags ;check whether we alocated a VCR
bpl @no_vcr ;high bit tells whether VCR was allocated
@cancel_vcr
ldy #vcr_open_cnt
lda #$0000
sta [vcr_ptr],y ;must zero the open count to release it!
ldy #vcr_id
lda [vcr_ptr],y ;get ID of the VCR
jsl release_vcr ;this invokes sys_remove_vol call to this fst
@no_vcr
@no_error
lda gen_buf_vp ;we may have to release this segment
tax
ora gen_buf_vp+2
beq @skip3
ldy gen_buf_vp+2
jsl release_seg
@skip3
plp
pla
bcc @noErr
ora error_priority
@noErr
;brk $88
jml sys_exit
param_countdn ds.w 1 ;local countdown var
ENDP
free_sectors PROC ;determine the number of free sectors listed in the vtoc
;vtoc must be at vol_buf_ptr
;result is in accumulator
;first set up some looping limits
ldy #vtoc_sectors ;number of sectors per track
lda [vol_buf_ptr],y
and #$00FF ;take low byte
lsr a
lsr a
lsr a
lsr a ;divide by 16 to get # of words of bitmap per track
sta words
stz result ;start with a clean slate
;let the loops begin!
ldy #vtoc_tracks ;# of tracks on the disk
lda [vol_buf_ptr],y
and #$00FF
sta track_index
@track_loop
lda words
sta words_index
lda track_index
dec a
bmi done
sta track_index
asl a
asl a ;multiply by 4 to get byte offset. this should clear carry
adc #vtoc_bitmap ;add offset to start of bitmap. carry should be clear
tay
@word_loop lda [vol_buf_ptr],y ;get a bitmap word
sta bitmap_temp
lda result
ldx #$0010
@bit_loop asl bitmap_temp
adc #$0000 ;carry set if we just shifted a high bit
dex
bne @bit_loop
sta result
dec words_index
beq @track_loop
iny
iny ;bump index to next word
bra @word_loop
done lda result ;this is a good place for the result
rts
words ds 1
result ds 1
track_index ds 1
words_index ds 1
bitmap_temp ds 1
ENDP
get_file_info_s PROC ;shell for get_file_info call
import open_setup
import match_vcr
import load_cat_secs
import search_fcr
import check_dir
import get_file_info
import fill_param
import get_file_info0_f
import get_file_info1_f
import do_option_list
stz temp_vp
stz temp_vp+2
;brk $AC
jsl lock_mem
; lda class
; beq @class0
;@class1
; lda p_cnt
; cmp #get_file_info1.pmax
; bcc @ok
; lda #invalid_pcount
; brl error
;@ok
; bra do_it
;@class0
do_it
jsr open_setup ;among other things, this will find VCR for�
;�this volume name and verify identity
bcc @ok1
brl pack_up
@ok1
;jsr match_vcr ;find VCR for this volume name and verify identity
;bcc @ok2
;brl pack_up ;return with duplicate volume error
@ok2
;beq no_vcr
found_vcr ;we found the volume's vcr
stz good_vcr ;flag to say we have a vcr
ldy #vcr_buf_vp
lda [vcr_ptr],y
tax
iny
iny
lda [vcr_ptr],y
tay
jsl deref
;stx gen_buf_ptr
;sty gen_buf_ptr+2 ;set up correct buffer for check_dir
stx vol_buf_ptr
sty vol_buf_ptr+2 ;set up correct buffer for check_dir
ldy #vcr_cat_sec_count
lda [vcr_ptr],y
sta cat_sec_count ;get that right, too
;bra have_vcr
no_vcr
;lda #$8000
;sta good_vcr ;keep flag to tell whether we have a vcr
;jsr load_cat_secs
;bcc check_file
;brl pack_up
@ok3
have_vcr
check_file ;was there a file to get info on?
lda (file_name_ptr) ;was there a filename?
bne handle_file
handle_vol
;a bunch of things get zeroes
stz aux_type ;fill the globals
stz eof+2
stz blks_used
lda cat_sec_count
inc a ;account for vtoc
ldx class
bne @1
sta blks_used
@1 xba
sta eof
lda #$000F
sta os_type
sta stor_type
lda #access_unlocked
sta access
bra stuff_globals
handle_file
;brk $02
;lda good_vcr ;did we have a valid vcr?
;bne chk_dir ;no, check the directory
ldy #vcr_open_cnt
lda [vcr_ptr],y ;see if any files are already open in this vcr
beq chk_dir ;we'll have to look in the directory
jsr search_fcr ;this routine will return number of fcr, for previous�
;�open of the file named in file_name_ptr, in old_fcr
;bcs chk_dir ;not found, check directory
bcc from_fcr ;fetch all info from fcr
chk_dir ;does this guy exist in the directory?
jsr check_dir ;this sub checks for filename in file_name_ptr
bcc @ok
lda #dir_error ;problem with the directory structure
brl pack_up
@ok
lda fde_index
bpl filename_good
file_not_here ;file not on this volume
lda #file_not_found
brl pack_up
filename_good ;file exists in directory
go_to_disk ;otherwise, we have to get it ourselves from disk
;lda #file_buf_size ;request segments for file buffers ***************
;jsl alloc_seg
;bcc @ok1
;lda #out_of_mem ;we *must* have those buffers!
;bra error
;@ok1
;stx temp_vp
;sty temp_vp+2
;jsl deref
;stx <fcr_buf_ptr
;sty <fcr_buf_ptr+2
;make it look like we have an FCR
;hopefully, the device number will be left over from
;the "volume" routine
lda gbuf_ptr ;use system gbuf
sta <fcr_buf_ptr
lda gbuf_ptr+2
sta <fcr_buf_ptr+2
lda #drvr_read
sta <drvr_call_num
jsr get_file_info ;get all the good stuff
bcc @ok2
bra error ;return with driver error
@ok2
stuff_globals ;now copy from globals to user param list
lda class
bne class1
class0
lda #$0001
sta p_cnt ;this will make the fill_param tables work right
lda os_type
cmp #$0F
bne @cont
lda #$230 ; auxtype = total blocks
sta aux_type
@cont lda #<get_file_info0_f
sta <fill_tabl ;set up for right parameter fill table
jsr fill_param
lda #0
bra pack_up ;all done!
class1
lda #<get_file_info1_f
sta <fill_tabl ;set up for right parameter fill table
jsr fill_param
lda p_cnt
cmp #8 ;do we need option list?
bcc @no_list
ldy #get_file_info1.option_list
jsr do_option_list
bcc @done
lda #buff_too_small
bra pack_up
@no_list
@done
lda #0
bra pack_up
from_fcr ;pull info out of the fcr and into the globals
ldy #fcr_access
lda [old_fcr_ptr],y
sta access
ldy #fcr_eof
lda [old_fcr_ptr],y
sta eof
iny
iny
lda [old_fcr_ptr],y
sta eof+2
ldy #fcr_aux
lda [old_fcr_ptr],y
sta aux_type
ldy #fcr_type
lda [old_fcr_ptr],y
sta os_type
ldy #fcr_blks_used
lda [old_fcr_ptr],y
sta blks_used
bra stuff_globals ;now give it all to the user
error
sec
pack_up
pha
;lda temp_vp
;tax
;ora temp_vp+2
;beq @skip1
;ldy temp_vp+2
;jsl release_seg
@skip1
lda gen_buf_vp
tax
ora gen_buf_vp+2
beq @skip3
ldy gen_buf_vp+2
jsl release_seg
@skip3
exit
;brk $22
jsl unlock_mem
pla
cmp #1
bcc @noErr
ora error_priority
@noErr
jml sys_exit
good_vcr ds 1
ENDP
get_dir_entry_s PROCNAME ;shell for the get_dir_entry call
import get_dir_entry
;brk $ED
jsl lock_mem
ldx <g_fcr_ptr ;SCM provides the FCR & VCR
ldy <g_fcr_ptr+2
jsl deref
stx <fcr_ptr
sty <fcr_ptr+2
ldx <g_vcr_ptr
ldy <g_vcr_ptr+2
jsl deref
stx <vcr_ptr
sty <vcr_ptr+2
lda #gen_buf_size ;let's get some buffer space for the vtoc & directory
jsl alloc_seg
bcc @mem_ok
lda #out_of_mem ;can't get buffer space
rts
@mem_ok
stx gen_buf_vp ;4K buffer used by volume routine
sty gen_buf_vp+2
jsl deref
stx <gen_buf_ptr
sty <gen_buf_ptr+2
;lda #file_buf_size ;request segments for file buffers for find_eof
;jsl alloc_seg
;bcc @ok1
;lda #out_of_mem ;we *must* have those buffers!
;bra exit2
@ok1
;stx temp_vp
;sty temp_vp+2
;jsl deref
;stx <fcr_buf_ptr
;sty <fcr_buf_ptr+2 ;make it look like we have an FCR
lda gbuf_ptr
sta <fcr_buf_ptr ;use system gbuf
lda gbuf_ptr+2
sta <fcr_buf_ptr+2
lda class
; beq @class0
;@class1
; WITH get_dir_entry1
; lda p_cnt
; cmp #pmax
; bcc @ok2
; lda #invalid_pcount
; sec
; bra exit
;@ok2
; ENDWITH
@class0
jsr get_dir_entry
exit
;pha
;lda temp_vp
;tax
;ora temp_vp+2
;beq @skip1
;ldy temp_vp+2
;jsl release_seg
@skip1
;pla
exit2 ;alternate exit which does not deallocate buffer
pha ;save error code
lda gen_buf_vp
tax
ora gen_buf_vp+2 ;is vp valid?
beq @skip3
ldy gen_buf_vp+2
jsl release_seg
@skip3
jsl unlock_mem
pla ;restore error
cmp #1
bcc @noErr
ora error_priority
@noErr
jml sys_exit
ENDP
read_s PROCNAME ;shell for the read call
import read, find_fde
jsl lock_mem
ldx <g_fcr_ptr ;set up real pointers to vp's to fcr & vcr provided�
ldy <g_fcr_ptr+2 ;�by the SCM
jsl deref
stx <fcr_ptr
sty <fcr_ptr+2
ldx <g_vcr_ptr
ldy <g_vcr_ptr+2
jsl deref
stx <vcr_ptr
sty <vcr_ptr+2
; init fde_ptr properly so that if we need to read anything out of the file
; directory entry, we won't be using an invalid pointer (and reading who-knows-what)
ldy #vcr_buf_vp ;set up correct buffer for find_fde
lda [vcr_ptr],y
tax
iny
iny
lda [vcr_ptr],y
tay
jsl deref
stx vol_buf_ptr
sty vol_buf_ptr+2
ldy #fcr_fde_num
lda [fcr_ptr],y ;need these to set up ptr to fde
tax
ldy #fcr_cat_sec
lda [fcr_ptr],y
jsr find_fde ;this sets up fde_ptr
; lda class
; beq @class0
;@class1
; lda p_cnt
; cmp #read1.pmax
; bcc @ok1
; lda #invalid_pcount
; sec
; bra exit
;@ok1
;@class0
jsr read
exit
pha
php
jsl unlock_mem
plp
pla
bcc @noErr
ora error_priority
@noErr
jml sys_exit
ENDP
close_s PROCNAME ;shell for the CLOSE call
import close
jsl lock_mem
ldx <g_fcr_ptr ;set up real pointers to vp's to fcr & vcr provided�
ldy <g_fcr_ptr+2 ;�by the SCM
jsl deref
stx <fcr_ptr
sty <fcr_ptr+2
ldx <g_vcr_ptr
ldy <g_vcr_ptr+2
jsl deref
stx <vcr_ptr
sty <vcr_ptr+2
check_pcnt
; lda class
; beq @class0
;@class1
; WITH close1
; lda p_cnt
; cmp #pmax
; bcc @ok1
; lda #invalid_pcount
; sec
; bra exit
;@ok1
; ENDWITH
@class0
do_close
ldy #fcr_type
lda [fcr_ptr],y
cmp #$000F ;is it a directory?
beq free_fcr ;if directory file then don't release its buffer�
;�because it is shared with the VCR
ldy #fcr_buf_vp
lda [fcr_ptr],y ;get fcr buffer's vp so we can release it
tax
iny
iny
lda [fcr_ptr],y
tay
jsl release_seg
free_fcr
ldy #fcr_ref_num
lda [fcr_ptr],y
jsl release_fcr
bcc @ok1
lda #invalid_ref_num OR $8000 ;internal error
bra exit
@ok1
ldy #vcr_open_cnt
lda [vcr_ptr],y ;get the open count
bne @ok2 ;make sure open count isn't aleady 0
lda #8000 ;generic internal error
bra exit
@ok2
dec a ;one less open file
sta [vcr_ptr],y
no_err ;successful close
clc
lda #$0000
exit
pha
php
jsl unlock_mem
plp
pla
bcc @noErr
ora error_priority
@noErr
jml sys_exit
ENDP
get_dev_num_s PROCNAME ;shell for the GET_DEV_NUM call
import open_setup
import match_vcr
import find_vol_online
;brk $09
;stz gen_buf_vp
;stz gen_buf_vp+2
jsl lock_mem
check_pcnt
; lda class
; beq @class0
;@class1
; WITH get_dev_num1
; lda p_cnt
; cmp #pmax
; bcc @ok1
; lda #invalid_pcount
; sec
; bra exit
;@ok1
; ENDWITH
@class0
check_scm ;see whether SCM has done the work for us yet
lda <dev1_num ;is the number there already?
beq hard_way ;if not, we have to figure it out ourselves
easy_way
ldy #get_dev_num1.dev_num ;anticipate class 1
ldx class
bne @store ;class 1, store the value
@class0
ldy #get_dev_num0.dev_num
@store
sta [param_blk_ptr],y
bra no_err ;that's the whole show!
hard_way ;we have to find the volume ourselves
jsr open_setup ;init vars, set up buffers, put volume name in�
;�volname_buf_ptr
bcc @ok
cmp #dup_volume ; duplicate volume? if yes, that means that someone else
; will claim it...
bne @1
lda #unknown_vol
@1 sec
bra exit ;return with error
@ok
ldy #vcr_dev
lda [vcr_ptr],y ;get device number
bra easy_way ;we got it!
;have_name ;check for vcr with the given name
;jsr match_vcr ;find vcr for volume name in volname_buf_ptr
;bcc @ok
;lda #vol_not_found
;bra exit ;return with error
;@ok
;bne use_old_vcr ;if we got a match, use it
;stz supposed_dev
;jsr find_vol_online ;no vcr, walk the devices
;bcc @ok1
;lda #vol_not_found
;bra exit
@ok1
;lda <drvr_dev_num ;the answer is here
;bra easy_way ;mail it and we're done
use_old_vcr ;we have a vcr for the volume
;vcr_ptr points to it
;lda <dev1_num
;bra easy_way
no_err ;successful get_dev_num
clc
lda #$0000
exit
pha
php
lda gen_buf_vp
tax
ora gen_buf_vp+2
beq @skip3
ldy gen_buf_vp+2
jsl release_seg
@skip3
jsl unlock_mem
plp
pla
bcc @noErr
ora error_priority
@noErr
jml sys_exit
ENDP
get_eof_s PROCNAME ;shell for the GET_EOF call
jsl lock_mem
ldx <g_fcr_ptr ;set up real pointers to vp's to fcr & vcr provided�
ldy <g_fcr_ptr+2 ;�by the SCM
jsl deref
stx <fcr_ptr
sty <fcr_ptr+2
; ldx <g_vcr_ptr
; ldy <g_vcr_ptr+2
; jsl deref
; stx <vcr_ptr
; sty <vcr_ptr+2
check_pcnt
; lda class
; beq @class0
;@class1
; WITH get_eof1
; lda p_cnt
; cmp #pmax
; bcc @ok1
; lda #invalid_pcount
; sec
; bra exit
;@ok1
; ENDWITH
@class0
do_get_eof
ldy #fcr_eof
lda [fcr_ptr],y ;get the eof (of all things)
tax
iny
iny
lda [fcr_ptr],y ;high word
pha ;save accum
lda class
beq @class0
@class1
pla ;get back low word
ldy #get_eof1.eof+2
sta [param_blk_ptr],y
txa ;restore low word
dey
dey
sta [param_blk_ptr],y
bra no_err ;that's it!
@class0
pla ;get back low word
ldy #get_eof0.eof+2
sta [param_blk_ptr],y
txa ;restore low word
dey
dey
sta [param_blk_ptr],y
no_err
clc
lda #$0000
exit
pha
php
jsl unlock_mem
plp
pla
bcc @noErr
ora error_priority
@noErr
jml sys_exit
ENDP
get_mark_s PROCNAME ;shell for the GET_MARK call
jsl lock_mem
ldx <g_fcr_ptr ;set up real pointers to vp's to fcr & vcr provided�
ldy <g_fcr_ptr+2 ;�by the SCM
jsl deref
stx <fcr_ptr
sty <fcr_ptr+2
; ldx <g_vcr_ptr
; ldy <g_vcr_ptr+2
; jsl deref
; stx <vcr_ptr
; sty <vcr_ptr+2
check_pcnt
; lda class
; beq @class0
;@class1
; lda p_cnt
; cmp #get_mark1.pmax
; bcc @ok1
; lda #invalid_pcount
; sec
; bra exit
;@ok1
;@class0
do_get_mark
ldy #fcr_mark
lda [fcr_ptr],y ;get the mark (of all things)
tax
iny
iny
lda [fcr_ptr],y ;high word
pha ;save accum
lda class
beq @class0
@class1
pla ;get back low word
ldy #get_mark1.position+2
sta [param_blk_ptr],y
txa ;restore low word
dey
dey
sta [param_blk_ptr],y
bra no_err ;that's it!
@class0
pla ;get back low word
ldy #get_mark0.position+2
sta [param_blk_ptr],y
txa ;restore low word
dey
dey
sta [param_blk_ptr],y
no_err
clc
lda #$0000
exit
pha
php
jsl unlock_mem
plp
pla
bcc @noErr
ora error_priority
@noErr
jml sys_exit
ENDP
set_mark_s PROCNAME ;shell for the SET_MARK call
;brk $EF
jsl lock_mem
ldx <g_fcr_ptr ;set up real pointers to vp's to fcr & vcr provided�
ldy <g_fcr_ptr+2 ;�by the SCM
jsl deref
stx <fcr_ptr
sty <fcr_ptr+2
; ldx <g_vcr_ptr
; ldy <g_vcr_ptr+2
; jsl deref
; stx <vcr_ptr
; sty <vcr_ptr+2
check_pcnt
lda class
beq @class0
@class1
ldy #set_mark1.displacement
lda [param_blk_ptr],y
sta disp
iny
iny
lda [param_blk_ptr],y
sta disp+2
; lda p_cnt
; cmp #set_mark1.pmax
; bcc get_base
bra get_base
; lda #invalid_pcount
; sec
; brl exit
;@ok1
@class0
ldy #set_mark0.position
lda [param_blk_ptr],y
sta disp
iny
iny
lda [param_blk_ptr],y
sta disp+2
bra absolute ;class 0 displacement is always absolute
get_base ;fetch displacement from param block
ldy #set_mark1.base
lda [param_blk_ptr],y
beq absolute ;base = 0 --> mark = displacement
dec a
beq minus ;base = 1 --> mark = eof - displacement
dec a
beq add ;base = 2 --> mark = mark + displacement
dec a
beq subtract ;base = 3 --> mark = mark - displacement
bad_base ;if we get here, base had an invalid value
lda #parm_range_err ;base is out of range
brl exit ;can't continue
absolute ;set mark to (displacement)
lda disp
sta new_mark
lda disp+2
sta new_mark+2
bra validate_new_mark ;make sure it's in range, and we're done!
minus ;set mark to (eof - displacement)
ldy #fcr_eof
lda [fcr_ptr],y ;get eof
sec
sbc disp
sta new_mark
iny
iny
lda [fcr_ptr],y ;get high word of eof
sbc disp+2
bcs @no_underflow
lda #out_of_range ;position fell below 0
bra exit
@no_underflow
sta new_mark+2
bra set_new_mark ;new mark is guaranteed to be valid now
subtract ;set mark to (mark - displacement)
ldy #fcr_mark
lda [fcr_ptr],y
sec
sbc disp
sta new_mark
iny
iny
lda [fcr_ptr],y ;get high word of present mark
sbc disp+2
bcs @no_underflow
lda #out_of_range ;mark would go below 0
bra exit
@no_underflow
sta new_mark+2
bra set_new_mark ;new mark is guaranteed to be valid now
add ;set mark to (mark + displacement)
ldy #fcr_mark
lda [fcr_ptr],y
clc
adc disp
sta new_mark
iny
iny
lda [fcr_ptr],y ;get high word of mark
adc disp+2
bcc @no_overflow
lda #out_of_range ;new mark overflowed 32-bit range
bra exit
@no_overflow
sta new_mark+2
validate_new_mark ;check value of new mark against eof
lda new_mark+2
ldy #fcr_eof+2
cmp [fcr_ptr],y ;compare to high word of eof
bcc @in_range
bne @out_of_range
@maybe ;now check low words
dey
dey
lda [fcr_ptr],y ;get low word of eof
cmp new_mark
bcs @in_range
@out_of_range
lda #out_of_range
bra exit
@in_range
set_new_mark
lda new_mark
ldy #fcr_mark
sta [fcr_ptr],y ;set new mark
lda new_mark+2
iny
iny
sta [fcr_ptr],y ;that completes the new mark setting!
no_err
clc
lda #$0000
exit
pha
jsl unlock_mem
pla
cmp #1
bcc @noErr
ora error_priority
@noErr
jml sys_exit
disp ds.l 1 ;local copy of user parameter "displacement"
new_mark ds.l 1 ;scratch space for new mark value
ENDP
flush_s PROCNAME ;handle the FLUSH call
check_pcnt ;this is about all we do?
lda class
beq @class0
@class1
lda p_cnt
cmp #2+1 ;max pcount for FLUSH is 2
bcc @ok1
lda #invalid_pcount
sec
bra exit
@ok1
@class0
no_err
clc
lda #$0000
exit
bcc @noErr
ora error_priority
@noErr
jml sys_exit
ENDP
fst_specific_s PROCNAME ;shell for fst-specific calls
import set_text_mode
import get_text_mode
WITH fst_specific
ldy #command
lda [param_blk_ptr],y ;get command parameter
beq @bad_command ;0 not a valid command
dec a
bne @next1
jsr set_text_mode ;command = 1
bra exit
@next1
dec a
bne @next2
jsr get_text_mode ;command = 2
bra exit
@next2
@bad_command ;command number not valid
lda #invalid_fst_op
sec
exit
cmp #1
bcc @noErr
ora error_priority
@noErr
jml sys_exit
ENDWITH
ENDP
not_supported2 PROCNAME ;handle FST calls which are not supported because they require�
;�write operations
;this routine handles calls which take a pathname as a parameter
;if the pathname refers to a DOS 3.3 disk, we return an�
;�invalid_fst_op error
;if the pathname refers to a non-DOS 3.3 disk, we return a�
;�unknown_vol error, which allows the call to bounce to other�
;�FST's
import open_setup
;brk $AC
jsl lock_mem
check_volume ;the main thing is to know whom the volume belongs to
jsr open_setup ;this routine allocates a buffer, sets up name buffer�
;�pointers, parses the path provided by SCM,
bcs pack_up ;return with unknown_vol, or pathalogical error
@ok1
lda #drvr_wr_prot ;call was meant for us
pack_up
pha
lda gen_buf_vp
tax
ora gen_buf_vp+2
beq @skip3
ldy gen_buf_vp+2
jsl release_seg
@skip3
exit
;brk $22
jsl unlock_mem
pla
cmp #1
bcc @noErr
ora error_priority
@noErr
jml sys_exit
ENDP
;======================================================================
;Below is the main entry point for ALL standard operating system calls.
;
;This PROC was adapted directly from the ProDOS FST v02.00 by Rob Turner
;
;Input: X = Call number * 2
; Y = Class number * 2
; A = undefined
;======================================================================
longa on
longi on
EXPORT appl_call
appl_call PROCNAME
import max_call
sta $FF0500 ;debug tag
phk ;set up our bank register
plb
rep #$30 ; 16 bit A & X
stz p_cnt ; zero it out
stz error_priority ; for bouncing errors
sty class ;keep this around for future reference
cpy #0
beq @class0
@class1
lda [param_blk_ptr] ;first word of class1 is always pcount
sta p_cnt ;save for future reference
lda pcount_tbl,x ; get the min/max pcount for this call
sep #$20 ; 8 bit accum. please
LONGA OFF
sta p_max_cnt
xba
sta p_min_cnt
lda p_cnt
cmp p_max_cnt ; Max pcount - pcount
bcs pcnt_err ; pcount too big
cmp p_min_cnt ; Min pcount - pcount
blt pcnt_err ; pcount too small
rep #$20 ; back to 16 bit accu.
LONGA ON
@class0
stz gen_buf_vp ;invalidate vp's until buf gets allocated
stz gen_buf_vp+2
cpx #end_tbl+1 ;check command number
bcs cmd_error
jmp (cmd_tbl,x) ;call the function
pcnt_err ; bad pcount in the call
rep #$20 ; back to 16 bit accu.
LONGA ON
lda #invalid_pcount
bra err
cmd_error ;bad command number, or not FST call
lda #bad_system_call
err sec
ora error_priority
jml sys_exit
not_supported ;call not supported by this FST
;we get here if the call takes a ref num as a parameter, in�
;�which case we are certain the call was meant for this FST
lda #drvr_wr_prot
bra err
not_implemented ; no handler for this call
lda #invalid_fst_op
bra err
cmd_tbl dc.w cmd_error ;undefined 00
DC.W not_supported2 ;CREATE 01
DC.W not_supported2 ;DESTROY 02
DC.W cmd_error ;OS_SHUTDOWN 03
DC.W not_supported2 ;CHANGE_PATH 04
DC.W not_supported2 ;SET_FILE_INFO 05
DC.W get_file_info_s ;GET_FILE_INFO 06
DC.W not_implemented ;JUDGE_NAME 07
DC.W volume_s ;VOLUME 08
DC.W cmd_error ;SET_PREFIX 09
DC.W cmd_error ;GET_PREFIX 0A
DC.W not_supported2 ;CLEAR_BACKUP 0B
DC.W cmd_error ;SET_SYS_PREFS 0C
DC.W cmd_error ;NULL 0D
DC.W cmd_error ;EXPAND_PATH 0E
DC.W cmd_error ;GET_SYS_PREFS 0F
DC.W open_s ;OPEN 10
DC.W cmd_error ;NEWLINE 11
DC.W read_s ;READ 12
DC.W not_supported ;WRITE 13
DC.W close_s ;CLOSE 14
DC.W flush_s ;FLUSH 15
DC.W set_mark_s ;SET_MARK 16
DC.W get_mark_s ;GET_MARK 17
DC.W not_supported ;SET_EOF 18
DC.W get_eof_s ;GET_EOF 19
DC.W cmd_error ;SET_LEVEL 1A
DC.W cmd_error ;GET_LEVEL 1B
DC.W get_dir_entry_s ;GET_DIR_ENTRY 1C
DC.W cmd_error ;BEGIN_SESSION 1D
DC.W cmd_error ;END_SESSION 1E
DC.W cmd_error ;SESSION_STATUS 1F
DC.W get_dev_num_s ;GET_DEV_NUM 20
DC.W cmd_error ;GET_LAST_DEV 21
DC.W cmd_error ;READ_BLOCK 22
DC.W cmd_error ;WRITE_BLOCK 23
DC.W not_supported ;FORMAT 24
DC.W not_supported ;ERASE_DISK 25
DC.W cmd_error ;RESET_CACHE 26
DC.W cmd_error ;GET_NAME 27
DC.W cmd_error ;GET_BOOT_VOL 28
DC.W cmd_error ;QUIT 29
DC.W cmd_error ;GET_VERSION 2A
DC.W cmd_error ;GET_FST_INFO 2B
DC.W cmd_error ;D_INFO 2C
DC.W cmd_error ;D_STATUS 2D
DC.W cmd_error ;D_CONTROL 2E
DC.W cmd_error ;D_READ 2F
DC.W cmd_error ;D_WRITE 30
DC.W cmd_error ;ALLOC_INT and BIND_INT 31
DC.W cmd_error ;DEALLOC_INT and UNBIND_INT 32
DC.W fst_specific_s ;FST_SPECIFIC 33
;EXPORT end_tbl
end_tbl equ * - cmd_tbl
;=====================================================================
; Below is the table used to check the pcount on the calls that are
; not supported. This should have been done in general for all the
; calls instead of doing it inline in each call. Oh well I didn't
; start this thing and "to each his own". If I had time I would go back
; and change all the calls, but we're too close to beta to do it now.
; jev 3/26/91
;=====================================================================
pcount_tbl DC.W cmd_error
DC.W max_pcnt7+min_pcnt1 ;CREATE 01
DC.W max_pcnt1+min_pcnt1 ;DESTROY 02
DC.W max_pcnt1+min_pcnt1 ;OS_SHUTDOWN 03
DC.W max_pcnt3+min_pcnt2 ;CHANGE_PATH 04
DC.W max_pcnt12+min_pcnt2 ;SET_FILE_INFO 05
DC.W max_pcnt12+min_pcnt1 ;GET_FILE_INFO 06
DC.W max_pcnt6+min_pcnt1 ;JUDGE_NAME 07
DC.W max_pcnt6+min_pcnt2 ;VOLUME 08
DC.W max_pcnt2+min_pcnt2 ;SET_PREFIX 09
DC.W max_pcnt2+min_pcnt2 ;GET_PREFIX 0A
DC.W max_pcnt1+min_pcnt1 ;CLEAR_BACKUP 0B
DC.W max_pcnt1+min_pcnt1 ;SET_SYS_PREFS 0C
DC.W max_pcnt0+min_pcnt0 ;NULL 0D
DC.W max_pcnt3+min_pcnt2 ;EXPAND_PATH 0E
DC.W max_pcnt1+min_pcnt1 ;GET_SYS_PREFS 0F
DC.W max_pcnt15+min_pcnt2 ;OPEN 10
DC.W max_pcnt4+min_pcnt4 ;NEWLINE 11
DC.W max_pcnt5+min_pcnt4 ;READ 12
DC.W max_pcnt5+min_pcnt4 ;WRITE 13
DC.W max_pcnt1+min_pcnt1 ;CLOSE 14
DC.W max_pcnt1+min_pcnt1 ;FLUSH 15
DC.W max_pcnt3+min_pcnt3 ;SET_MARK 16
DC.W max_pcnt2+min_pcnt2 ;GET_MARK 17
DC.W max_pcnt3+min_pcnt3 ;SET_EOF 18
DC.W max_pcnt2+min_pcnt2 ;GET_EOF 19
DC.W max_pcnt1+min_pcnt1 ;SET_LEVEL 1A
DC.W max_pcnt1+min_pcnt1 ;GET_LEVEL 1B
DC.W max_pcnt17+min_pcnt5 ;GET_DIR_ENTRY 1C
DC.W max_pcnt0+min_pcnt0 ;BEGIN_SESSION 1D
DC.W max_pcnt0+min_pcnt0 ;END_SESSION 1E
DC.W max_pcnt1+min_pcnt1 ;SESSION_STATUS 1F
DC.W max_pcnt2+min_pcnt2 ;GET_DEV_NUM 20
DC.W max_pcnt0+min_pcnt0 ;GET_LAST_DEV 21
DC.W max_pcnt0+min_pcnt0 ;READ_BLOCK 22
DC.W max_pcnt0+min_pcnt0 ;WRITE_BLOCK 23
DC.W max_pcnt4+min_pcnt3 ;FORMAT 24
DC.W max_pcnt4+min_pcnt3 ;ERASE_DISK 25
DC.W max_pcnt0+min_pcnt0 ;RESET_CACHE 26
DC.W max_pcnt1+min_pcnt1 ;GET_NAME 27
DC.W max_pcnt1+min_pcnt1 ;GET_BOOT_VOL 28
DC.W max_pcnt2+min_pcnt0 ;QUIT 29
DC.W max_pcnt1+min_pcnt1 ;GET_VERSION 2A
DC.W max_pcnt8+min_pcnt2 ;GET_FST_INFO 2B
DC.W max_pcnt11+min_pcnt2 ;D_INFO 2C
DC.W max_pcnt5+min_pcnt5 ;D_STATUS 2D
DC.W max_pcnt5+min_pcnt5 ;D_CONTROL 2E
DC.W max_pcnt6+min_pcnt6 ;D_READ 2F
DC.W max_pcnt6+min_pcnt6 ;D_WRITE 30
DC.W max_pcnt0+min_pcnt0 ;ALLOC_INT and BIND_INT 31
DC.W max_pcnt0+min_pcnt0 ;DEALLOC_INT and UNBIND_INT 32
DC.W max_pcnt17+min_pcnt1 ;FST_SPECIFIC 33
;EXPORT pend_tbl
pend_tbl equ * - pcount_tbl
ENDP
;======================================================================
;Below is the main entry point for ALL FST system calls
;
;This PROC was adapted directly from the ProDOS FST v02.00 by Rob Turner
;
;
;Input: X = Call number * 2
; Y = Class number * 2
; A = undefined
;======================================================================
EXPORT system_call
system_call PROCNAME
sta $FF0510 ;debug tag
phk ;setup our bank register
plb
cpx #max_sys+1 ;maximum system calls
bcs sys_err
jmp (sys_tbl,x) ;call system routine
sys_err
lda #bad_system_call
rtl ;return error to the SCM
startup
jsl get_sys_gbuf ;get pointer to general 1K buffer
stx gbuf_ptr
sty gbuf_ptr+2
lda #$0001 ;set default text mode to clear high bits
sta text_flag
bra exit
shutdown
deferred_flush
exit
lda #$0000
clc
rtl
sys_remove_vol ;clean up a vcr before SCM throws it away
sta >$FE0358
ldx <g_vcr_ptr
ldy <g_vcr_ptr+2
stx <vcr_ptr
sty <vcr_ptr+2
ldy #vcr_buf_vp ;point to vp for the segment owned by the vcr
lda [g_vcr_ptr],y ;g_vcr_ptr contains a real pointer
tax
iny
iny
ora [g_vcr_ptr],y ;check that it's not 0
beq @invalid_vp
lda [g_vcr_ptr],y
tay
jsl release_seg ;throw away the 4K buffer
@invalid_vp
rtl ;return with any error
sys_tbl dc.w sys_err
DC.W startup ;set ready for work
DC.W shutdown ;get ready to quit
DC.W sys_remove_vol ;remove local data from VCR
DC.W deferred_flush ;flush deferred blocks.
max_sys equ * - sys_tbl
ENDP
match_vcr PROCNAME ;given a volume name, try to find a matching vcr.
;volume name in volname_buf_ptr
import throw_away
import find_vol_online
import cmp_vol_vcr
;this is a complicated task, so here is the pseudocode:
; try to find a VCR with the same volume name
;
; NOT FOUND: return with no match
;
; FOUND:
; is it mine?
; NO: try to discard it (no open files)
; SUCCESSFUL discard: return with no match
; CAN'T discard: return w/duplicate_vol error
;
; YES: find the disk online, checking in dev_num or last known
; device (error if failure)
;
; is the disk really the same as one that VCR refers to?
; YES: return with match
; NO: try to discard it (no open files)
; SUCCESSFUL discard: return with no match
; CAN'T discard: return w/dup_vol error
;check for vcr with same name
;brk $03
ldx <volname_buf_ptr
ldy <volname_buf_ptr+2
lda #$0000 ;tell find_vcr to search by name
jsl find_vcr ;see if a vcr exists for this volume
bcc same_name ;same name, see if it's mine
no_match
lda #0 ;report no match
clc
rts
same_name
jsl deref
stx <vcr_ptr
sty <vcr_ptr+2
ldy #vcr_fst_id
lda [vcr_ptr],y ;see if it's mine
cmp #my_id
beq mine
jsr throw_away
bcc no_match ;successful discard, return no match
dup_vol ;return with error
lda #dup_volume ;duplicate volume
sec
rts
mine ;it's mine
ldy #vcr_open_cnt ; <CAF> By incrementing the vcr open count, I
lda [<vcr_ptr],y ; fool SCM into not throwing away the vcr for
inc a ; this volume when the disk is accessed.
sta [<vcr_ptr],y
lda <dev1_num ;was a device number given by SCM?
bne verify_same ;if so, see if the vcr matches the online disk
;no dev_num given, search devices starting with the�
;�one it was last seen in
ldy #vcr_dev
lda [vcr_ptr],y ;find where it was last seen
sta supposed_dev
jsr find_vol_online ;find the physical disk
bcc verify_same ;we found one, check it
ldy #vcr_open_cnt ; <CAF> Restore the previous open count.
lda [<vcr_ptr],y
dec a
sta [<vcr_ptr],y
lda #vol_not_found
sec
rts
verify_same ;we have a disk with the same name, but let's check two full�
;�sectors' worth of data
ldy #vcr_open_cnt ; <CAF> Restore the previous open count.
lda [<vcr_ptr],y
dec a
sta [<vcr_ptr],y
jsr cmp_vol_vcr
bcs try_discard ;it's not the same, try to get rid of the VCR
match ;we have a match!
ldy #vcr_id
lda [vcr_ptr],y
clc
rts ;return with vcr_id in accum
try_discard
jsr throw_away ;try to throw away the incorrect vcr
bcs dup_vol ;can't, report duplicate volume
bra no_match ;otherwise, report no match
ENDP
export open_setup
open_setup PROCNAME ;perform basic setup for OPEN and GET_FILE_INFO
import parse_path
import volume
import find_vol_online
import allocate_vcr
import cmp_vol_vcr
import device_id
stz supposed_dev ;init to 0
stz valid_vcr
stz gen_buf_vp
stz gen_buf_vp+2
lda #gen_buf_size ;let's get some buffer space for the vtoc & directory
jsl alloc_seg
bcc @ok
lda #out_of_mem ;can't get buffer space
rts
@ok
stx gen_buf_vp ;4K buffer used by volume routine
sty gen_buf_vp+2
jsl deref
stx <gen_buf_ptr
sty <gen_buf_ptr+2
lda #name_buf
sta <volname_buf_ptr
lda #^name_buf
sta <volname_buf_ptr+2
lda #<file_buf ;get address of this internal buffer
sta <file_name_ptr
lda #^file_buf
sta <file_name_ptr+2
lda #<vol_name_buf ;get address of this internal buffer
sta <vol_name_ptr
lda #^vol_name_buf
sta <vol_name_ptr+2
;brk $12
jsr parse_path ;check path and break off filename and volume name
bcc @ok4
lda #bad_path_syntax
rts
* <CAF> 11/08/90
* ok, at this point we have allocated any buffers and broken down the pathname, and it
* is a valid dos pathname. now we must attempt to find the volume which is the target
* of this call. the target may actually belong to another FST, so this is what must
* be done:
*
* A. does a vcr exist?
* 1. yes -
* a. is it DOS?
* 1. yes -
* a. is it same volume?
* 1. yes - verify that has not been switched.
* a. if not switched, then done (goto B)
* b. if switched, start scanning from last seen device (goto A.2.a)
* 2. no - if no open files, then proceed as if it was not DOS (goto A.1.b)
* 2. no - act as if a vcr does not exist, so that if a volume is found, we can try
* to throw this vcr out (goto A.2.a)
* 2. no -
* a. find the dos volume in one of the devices. If the vcr exists, attempt to throw
* it out. If it was thrown out, then create the new vcr for the dos volume.
* done. (goto B)
* B. done
*
@ok4
lda <dev1_num
beq have_vol_name ;if 0 dev_num then we have our volume pointed�
;�to by volname_buf_ptr
sta <drvr_dev_num
jsr device_id ;check to see if this is a 5.25 device
bcs @exit ;failed to find the device
bne @exit ;can't deal with non-5.25 drives
jsr volume ;get name of volume in that device
;that routine will return a g-string in vol_name_ptr
bcc @ok5 ;can't recognize volume
@exit sec
lda #unknown_vol
rts ;return with bouncing error
@ok5
lda #<vol_name_buf ;this is our volume name for sure
sta <volname_buf_ptr
lda #^vol_name_buf
sta <volname_buf_ptr+2
* A. does a vcr exist?
have_vol_name
ldx <volname_buf_ptr
ldy <volname_buf_ptr+2
lda #$0000 ;tell find_vcr to search by name
jsl find_vcr ;see if a vcr exists for this volume
* 2. no -
bcs find_vol ; no vcr, so go look for volume ->
* 1. yes -
same_name
jsl deref
stx <vcr_ptr
sty <vcr_ptr+2
* a. is it DOS?
ldy #vcr_fst_id ; is it a vcr for a dos volume?
lda [vcr_ptr],y ;
cmp #my_id
* 1. yes -
beq mine ; yes ->
* 2. no - act as if a vcr does not exist, so that if a volume is found, we can try
* to throw this vcr out (goto A.2.a)
lab112 ldy #vcr_open_cnt ; no, any open files?
lda [<vcr_ptr],y
beq find_vol ; no, so lets act as if I haven't seen the vcr, and go try
; to find a dos volume with the same name.
*
* ok, I've found a vcr for a volume that has open files. this means that even if
* I were to find a dos volume of the same name, I couldn't mount it because of this volume.
* that means that now I must return either dup_vol or vol_not_found.
*
* dup_vol -> I found a dos volume online but couldn't mount it.
* vol_not_found -> couldn't find a dos volume of this name.
*
* what are the side effects of my just returning vol_not_found?
* - won't be able to tell if a duplicate volume has been inserted
*
*
stz supposed_dev ; we have no clue what device it might be in
jsr find_vol_online
lda #vol_not_found ; assume that we couldn't find it
bcc @1
brl exit ; couldn't find it ->
@1 lda #dup_volume ; was found, so error is a dup_volume error
brl exit ; ->
*
* I've found a vcr for a dos volume. check to see if it is the same volume.
*
* a. is it same volume?
mine ldy #vcr_buf_vp ;set up correct buffer for check_dir
lda [vcr_ptr],y
tax
iny
iny
lda [vcr_ptr],y
tay
jsl deref
stx vol_buf_ptr
sty vol_buf_ptr+2
jsr cmp_vol_vcr ; same?
bcs @not_same ; no ->
* 1. yes - verify that has not been switched.
@same ldy #vcr_status
lda [<vcr_ptr],y
and #vcr_swapped
* a. if not switched, then done (goto B)
beq done ; not switched ->
* b. if switched, start scanning from last seen device (goto A.2.a)
ldy #vcr_dev ; get the last device it was in
lda [<vcr_ptr],y
sta supposed_dev
bra find_vol2 ; switched, so go find it ->
* 2. no - if no open files, then proceed as if it was not DOS (goto A.1.b)
@not_same bra lab112 ; lab112 checks vcr_open_cnt ->
* 2. no -
* a. find the (possibly) dos volume in one of the devices. If the vcr exists, attempt to throw
* it out. If it was thrown out, then create the new vcr for the dos volume.
* done. (goto B)
find_vol ; we have a volume name, but make sure that it is a dos disk in
; one of the drives
stz vcr_ptr ; zero out the vcr pointer so that I know if I only need to
stz vcr_ptr+2 ; update the device number
stz supposed_dev ; start at first device
find_vol2 stz myflag1 ; assume no prior vcr
lda vcr_ptr ; vcr?
ora vcr_ptr+2
beq @1 ; no ->
inc myflag1 ; set flag indicating that I have bumped the open count
ldy #vcr_open_cnt ; increment the open count so volume won't be lost
lda [<vcr_ptr],y ; the only time that this might not work would be if we are
inc a ; searching for a duplicate dos 3.3 volume, but that is not
sta [<vcr_ptr],y ; very likely.
@1 jsr find_vol_online
php
pha
lda myflag1
beq @2
ldy #vcr_open_cnt ; decrement the open count
lda [<vcr_ptr],y
dec a
sta [<vcr_ptr],y
stz myflag1
@2 pla
plp
bcs exit ; couldn't find it ->
lda vcr_ptr ; found a volume, did we have a vcr already?
ora vcr_ptr+2
beq @alloc ; no, go create one ->
ldy #vcr_dev ; yes, update the device number in the vcr
lda drvr_dev_num
sta [<vcr_ptr],y
ldy #vcr_status ; clear the disk swapped flag
lda [<vcr_ptr],y
and #$BFFF
sta [<vcr_ptr],y
bra done
@alloc jsr allocate_vcr ; found volume, allocate the vcr
bcs exit ; error ->
* B. done
done ldy #vcr_dev ; setup the drvr_dev_num
lda [<vcr_ptr],y
sta drvr_dev_num
clc
exit rts
myflag1 dc.w 0
ENDP
export fill_param
fill_param PROCNAME ;fill the user param list according to the table pointed to�
;�by fill_tabl. p_cnt must be correct
ldy #$0000-2 ;init index
ldx p_cnt ;keep countdown in x
@cpy_loop
iny
iny ;move to next word entry
lda (fill_tabl),y ;get the table entry
asl a ;test 2 highest bits
php ;save the results in carry and neg flags
bmi @no_copy ;if original bit 14 was set, don't copy
lsr a ;shift things back into place
phy ;save the present index
tay
lda global_params,y
ply
sta [param_blk_ptr],y
@no_copy
plp ;restore the first test result
bcc @cpy_loop ;should we decrement pcount?
dex ;decrement pcount
bne @cpy_loop ;if we're not done, keep looping
@done
rts
;now we have the param list fill tables:
;table to specify what data to copy from the fcr to the user
;parameter block. each position corresponds to the same position
;in the user parameter block.
;each word entry contains:
; bit 15 = decrement pcount--high for the 1st word in each parameter
; bit 14 = copy inhibit--if high, nothing gets copied for this word position
no_copy equ %0100000000000000
dec equ %1000000000000000
;the label 'g' is short for 'global_params' and is the address of the start�
;�of the table of global parameters. It is needed in the offset expression�
;�for each entry that represents a param to be copied, so I used a one-letter�
;�label for convenience.
*************************************************************
EXPORT get_file_info0_f
get_file_info0_f
dc.w no_copy ;pathname
dc.w no_copy
dc.w access - g ;access
dc.w os_type - g ;file_type
dc.w aux_type - g ;aux_type
dc.w null - g
dc.w stor_type - g ;storage_type
dc.w null - g ;create_date
dc.w null - g ;create_time
dc.w null - g ;mod_date
dc.w null - g ;mod_time
dc.w blks_used - g ;blocks_used
dc.w null - g + dec
*************************************************************
EXPORT get_file_info1_f
get_file_info1_f
dc.w no_copy ;pcount
dc.w no_copy ;pathname
dc.w no_copy + dec
dc.w access - g + dec ;access
dc.w os_type - g + dec ;file_type
dc.w aux_type - g ;aux_type
dc.w null - g + dec
dc.w stor_type - g + dec ;storage_type
dc.w null - g ;create_date_time
dc.w null - g
dc.w null - g
dc.w null - g + dec
dc.w null - g ;mod_date_time
dc.w null - g
dc.w null - g
dc.w null - g + dec
dc.w no_copy ;option_list
dc.w no_copy + dec
dc.w eof - g ;eof
dc.w eof+2 - g + dec
dc.w blks_used - g ;blocks_used
dc.w null - g + dec
;dc.w no_copy ;resource_eof
;dc.w no_copy + dec
;dc.w no_copy ;resource_blocks
;dc.w no_copy + dec
; Resource fork info now gets zeroed
; -- Monte Benaresh 2/19/92 for 6.0.1
dc.w null - g ;resource_eof
dc.w null - g + dec
dc.w null - g ;resource_blocks
dc.w null - g + dec
*************************************************************
EXPORT volume0_f
volume0_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT volume1_f
volume1_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT open0_f
open0_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT open1_f
open1_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT read0_f
read0_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT read1_f
read1_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT close0_f
close0_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT close1_f
close1_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT set_mark0_f
set_mark0_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT set_mark1_f
set_mark1_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT get_mark0_f
get_mark0_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT get_mark1_f
get_mark1_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT get_eof0_f
get_eof0_f ;this call is mostly special-case, so this routine never gets called for it.
*************************************************************
EXPORT get_eof1_f
get_eof1_f ;this call is mostly special-case, so this routine never gets called for it.
*************************************************************
EXPORT get_dir_entry0_f
get_dir_entry0_f
dc.w no_copy ;ref_num
dc.w no_copy ;flags
dc.w no_copy ;base
dc.w no_copy ;displacement
dc.w no_copy ;name
dc.w no_copy
dc.w entry_num - g ;entry_num
dc.w os_type - g ;file_type
dc.w eof - g ;eof
dc.w eof+2 - g
dc.w blks_used - g ;block_count
dc.w null - g
dc.w null - g ;create_date_time
dc.w null - g
dc.w null - g
dc.w null - g
dc.w null - g ;mod_date_time
dc.w null - g
dc.w null - g
dc.w null - g
dc.w access - g ;access
dc.w aux_type - g ;aux_type
dc.w null - g
dc.w fst_id - g + dec ;file_sys_id
*************************************************************
EXPORT get_dir_entry1_f
get_dir_entry1_f
dc.w no_copy ;pcount
dc.w no_copy + dec ;ref_num
dc.w no_copy + dec ;flags
dc.w no_copy + dec ;base
dc.w no_copy + dec ;displacement
dc.w no_copy ;name
dc.w no_copy + dec
dc.w entry_num - g + dec ;entry_num
dc.w os_type - g + dec ;file_type
dc.w eof - g ;eof
dc.w eof+2 - g + dec
dc.w blks_used - g ;block_count
dc.w null - g + dec
dc.w null - g ;create_date_time
dc.w null - g
dc.w null - g
dc.w null - g + dec
dc.w null - g ;mod_date_time
dc.w null - g
dc.w null - g
dc.w null - g + dec
dc.w access - g + dec ;access
dc.w aux_type - g ;aux_type
dc.w null - g + dec
dc.w fst_id - g + dec ;file_sys_id
dc.w no_copy ;option_list
dc.w no_copy + dec
;dc.w no_copy ;resource_eof
;dc.w no_copy + dec
;dc.w no_copy ;resource_blocks
;dc.w no_copy + dec
; Resource fork info now gets zeroed
; -- Monte Benaresh 2/19/92 for 6.0.1
dc.w null - g ;resource_eof
dc.w null - g + dec
dc.w null - g ;resource_blocks
dc.w null - g + dec
*************************************************************
EXPORT get_dev_num0_f
get_dev_num0_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
EXPORT get_dev_num1_f
get_dev_num1_f ;this call is all special-case, so this routine never gets called for it.
*************************************************************
ENDP
;======================================================================
;new_rwts Read/write a given track/sector.
; Calculates: block = (16 * track) + sector
;
;Enter: JSR
;
; sector | track
;Input: A =|F|E|D|C|B|A|9|8|7|6|5|4|3|2|1|0|
; This is what you get if you do a 16-bit load on a
; track/sector entry typically found in a DOS 3.3
; data structure.
;
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..0
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..|___ high = device error, low = successful
; b = k
;
;uses: Good for reading sectors.
;
;======================================================================
longa on
longi on
new_rwts PROCNAME ;read/write a track/sector
xba ;switch track byte/sector byte
sep #%00100000 ;work with bytes
LONGA OFF
asl a ;get sector nibble flush left in byte
asl a
asl a
asl a
rep #%00100000 ;now open up to 16 bits
LONGA ON
lsr a
lsr a
lsr a
lsr a ;calculation done--we have a block number
sta <drvr_blk_num ;put on parm list
stz <drvr_blk_num+2 ;clear high word
;make this call lda #drvr_read
;more general sta <drvr_call_num
lda #$0100 ;256-byte "blocks"
sta <drvr_blk_size
sta <drvr_req_cnt ;we only deal in whole blocks�
stz <drvr_req_cnt+2 ;�and only one at a time!
lda #my_id
sta <drvr_fst_num
stz <drvr_cache ;to hell with caching
@retry jsl dev_dispatcher ;go read
bcs @err
rts
@err
cmp #drvr_disk_sw
beq @retry
sec
rts
ENDP
;======================================================================
;find_fde Find the file descriptive entry #n in catalog sector #k
; in vol_name_buf.
;
;
;Enter: JSR
;
;
;Input: A = k, as in "catalog sector #k"
;
; X = n, as in "fde #n"
; Y = undefined
; P = nvmxdizc
; ..000..0
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..|___ high = error, low = successful
; b = k
; fde_ptr = longword pointer to first byte of that fde
;
;uses:
;
;======================================================================
find_fde PROCNAME ;find a file descriptive entry
cmp #max_cat_sec+1 ;see if in range
bcs @error ;range error
xba ;we now have a byte offset to start of that cat sector
adc #cat_strt_off ;add offset to first cat sector
adc #base_fde ;add base fde offset within catalog sector
cpx #max_fde+1 ;check range
bcs @error
@add dex ;decrement fde offset
bmi @fde_done ;we're done when we get below zero--loop 0 or more times
adc #fde_len ;add byte length of each fde
bra @add
@fde_done ;clc hopefully, carry will be clear
adc vol_buf_ptr ;finished product is address of fde in contiguous�
sta fde_ptr ;�buffer of catalog sectors
lda vol_buf_ptr+2 ;get high part
adc #$0000
sta fde_ptr+2
rts
@error
rts
ENDP
;======================================================================
;
; load_fde_flag (added 6-May-93 DAL)
;
; This routine is similar to
; lda [fde_ptr]
; and #$00ff
; That first byte of an fde is $FF for a deleted entry.
;
; We also return $FF for a non-deleted entry if the filename is
; all blanks, which would be a zero-length filename.
;
;Enter: JSR
;
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; fde_ptr = points to a file-descriptive entry
;
;Output: A = first byte of fde, or $FF if the filename is blanks
; X = preserved
; Y = preserved
; P = nvmxdizc
; ..000.|.___ Z is conditioned for A
; b = k
;
;======================================================================
load_fde_flag PROC
lda [fde_ptr]
and #$00ff
beq @done ;beyond end of catalog--done
cmp #$00ff
beq @done ;a real deleted entry--done
*** If all 30 filename characters are blank, simulate a deleted entry
phy
phx
ldx #15
ldy #fde_name
@search lda [fde_ptr],y
cmp #$A0A0 ;blanks?
bne @nonBlank
iny
iny
dex
bne @search
lda #$00ff ;all blanks--simulate a deleted entry
bra @leave
@nonBlank lda [fde_ptr]
@leave plx
ply
@done and #$00FF
rts
ENDP
;======================================================================
;cmp_string Compare two class 1 strings pointed to by str1 and str2.
; Str2 is masked with cmp_mask. Maximum length is $8000
; bytes.
;
;Enter: JSR
;
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..0
; b = k
;
; str1_ptr = longword pointer to one string, usually from GS/OS
; str2_ptr = longword pointer to other string, possibly with high bits set,
; usually from DOS. This string gets masked with cmp_mask.
; cmp_mask = 8-bit value to be ANDed with each character in str2.
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..|___ high = compare false, low = compare true
; b = k
;
;uses: Good for comparing two strings.
;
;======================================================================
;let's try this using LONG accumulator!
cmp_string PROCNAME ;compare two strings
lda [str1_ptr] ;get string length
cmp [str2_ptr]
bne @different ;if they're not even the same length...
tay
bpl @ok
ldy #$7FFF ;in the unlikely event that y is greater than $7FFF, it�
;�is truncated to $7FFF
@ok iny ;point to last character
sep #%00100000 ;work with bytes
LONGA OFF
@loop lda [str2_ptr],y ;y must be signed POSITIVE
and cmp_mask ;user mask, maybe to strip high bit?
cmp [str1_ptr],y
bne @different
dey
bpl @loop
rep #%00100001 ;16-bit accum, carry clear
rts
@different sec
rep #%00100000 ;16-bit accum
rts
LONGA ON
ENDP
allocate_vcr PROCNAME ;find old VCR or allocate new one for volume�
;�found by the "volume" subroutine
;enter with
; volname_buf_ptr points to volume name
import load_cat_secs
import find_vol_online
;brk $A4
;do we need to allocate a VCR for this volume?
jsr match_vcr ;find vcr for volume name in volname_buf_ptr
bcc @ok
lda #dup_volume ;return with duplicate_volume error
rts
@ok
beq find_vol ;no match, find volume and allocate a new VCR
brl have_vcr ;if we got a match, don't allocate new VCR
find_vol
jsr find_vol_online ; supposed_dev is setup by the caller of allocate_vcr
bcc req_vcr
lda #vol_not_found
rts
req_vcr
lda #vcr_size
;ldx #<vol_name_buf
;ldy #^vol_name_buf
ldx <volname_buf_ptr
ldy <volname_buf_ptr+2
jsl alloc_vcr
;brk $AA
bcc @ok
lda #out_of_mem ;can't get new vcr
rts ;return carry set
@ok
jsl deref
stx <vcr_ptr
sty <vcr_ptr+2
lda #$8000
tsb vcr_fcr_flags ;set flag to say we allocated a VCR
fill_new_vcr
lda gen_buf_vp
ora gen_buf_vp+2 ;is there a valid buffer?
bne @have_gen_buf
@get_gen_buf
lda #gen_buf_size ;let's get some buffer space for the vtoc & directory
jsl alloc_seg
bcc @got_buf
lda #out_of_mem ;can't get buffer space
bra vol_vcr_err
@got_buf
stx gen_buf_vp ;4K buffer used by volume routine
sty gen_buf_vp+2
@have_gen_buf
ldy #vcr_fst_id
lda #my_id
sta [vcr_ptr],y
ldy #vcr_open_cnt
;lda #$FFFF ;this value will prevent a swap-out, but we understand�
;�it to be 0
lda #0
sta [vcr_ptr],y ;set open count to 0 since we just allocated this vcr
ldy #vcr_dev
lda <drvr_dev_num ;where did we find the disk?
sta [vcr_ptr],y
ldy #vcr_buf_vp ;index to virtual pointer to vtoc & directory buffers
lda gen_buf_vp ;transfer the vp's for the general buffer�
;�to the vol_buf now that we know�
;�that we want to keep them
tax ;save for deref
sta [vcr_ptr],y
iny
iny
lda gen_buf_vp+2
sta [vcr_ptr],y
tay ;set up for deref
jsl deref
stx vol_buf_ptr ;set up for rest of the FST
sty vol_buf_ptr+2
stz gen_buf_vp ;nail the original vp's so that we know�
stz gen_buf_vp+2 ;�not to release them later
load
ldy #vcr_open_cnt ; prevent swap out of vcr
lda [<vcr_ptr],y
inc a
sta [<vcr_ptr],y
jsr load_cat_secs ;load catalog sectors into vol_buf
php
pha
ldy #vcr_open_cnt ; restore original count
lda [<vcr_ptr],y
dec a
sta [<vcr_ptr],y
pla
plp
bcc load_ok
vol_vcr_err
pha ;save error code
@cancel_vcr
ldy #vcr_open_cnt
lda #$0000
sta [vcr_ptr],y ;must zero the open count to release it!
ldy #vcr_id
lda [vcr_ptr],y ;get ID of the VCR
jsl release_vcr ;this invokes sys_remove_vol call to this fst
lda #$8000
trb vcr_fcr_flags ;clear flag since we deallocated the VCR
pla ;restore error
sec
rts
load_ok
lda cat_sec_count ;enter catalog sector count into VCR
ldy #vcr_cat_sec_count
sta [vcr_ptr],y
bra vol_exit
have_vcr
;update old VCR
ldy #vcr_buf_vp ;get the vcr's buffer set up
lda [vcr_ptr],y
tax
iny
iny
lda [vcr_ptr],y
tay
jsl deref
stx vol_buf_ptr
sty vol_buf_ptr+2
ldy #vcr_dev
lda <drvr_dev_num ;where did we find the disk?
sta [vcr_ptr],y
vol_exit
;lda gen_buf_vp ;we may have to release this segment
;tax ;save in case it's valid
;ora gen_buf_vp+2 ;null vp?
;beq @skip3
;ldy gen_buf_vp+2
;jsl release_seg ;release unused gen buf
@skip3
clc
rts ;that's all, folks!
ENDP
volume PROCNAME ;check a device for a valid DOS 3.3 disk
;caller must set device in drvr_dev_num
;returns a volume name in vol_name_ptr
;carry set means that a DOS 3.3 disk was�
;�not found.
lda <gen_buf_ptr
sta <drvr_buf_ptr
lda <gen_buf_ptr+2
sta <drvr_buf_ptr+2
lda #drvr_read
sta <drvr_call_num
lda #$0011 ;get VTOC, always at track 17, sector 0
jsr new_rwts ;make driver call
bcc @ok
rts ;return error
@ok ;so, is it a DOS volume?
;brk 99
sep #$30 ;work with bytes
LONGI OFF
LONGA OFF
ldx #id_tbl_end-id_values-1 ;point to last entry in id tables
@loop1 lda id_offset,x ;get offset into VTOC
tay
@loop2 lda [drvr_buf_ptr],y ;get the actual value
cmp id_values,x ;compare to the table value
bne @false ;not the same
@loop3 dex
bmi confirmed ;table finished
tya
cmp id_offset,x ;skip over other entries for same position
beq @loop3
bra @loop1 ;move on to next compare
@false dex
bmi @failed
tya
cmp id_offset,x ;see if there's an alternate value for that offset
beq @loop2 ;yes, get a second chance
@failed sec
rep #$30 ;word accum
LONGA ON
LONGI ON
lda #unknown_vol ;return bouncing error
rts ;sorry, not a DOS volume
confirmed ;well, it looks like a DOS 3.3 volume!
rep #$31 ;word accum, carry clear
LONGA ON
LONGI ON
;now we must load the first directory sector!
ldy #vtoc_cat_link ;get the link to first catalog sector
lda [drvr_buf_ptr],y
bne @ok
lda #dir_error ;this is a weird error
rts
@ok
pha ;save this for a minute
lda <drvr_buf_ptr
sta <gen_buf_ptr
clc
adc #$0100 ;bump by a page
sta <drvr_buf_ptr
lda <drvr_buf_ptr+2
sta <gen_buf_ptr+2
adc #$0000
sta <drvr_buf_ptr+2
lda #drvr_read
sta <drvr_call_num
pla ;get back the track/sector
jsr new_rwts ;read the sector
bcc @ok1
; lda #error ;device error
rts
@ok1
generate_name ;now generate the name
ldy #$01FE ;point to end of area to be checksummed
lda #$0000 ;seed the checksum
clc ;this is part of the seed
@loop eor [gen_buf_ptr],y
ror a ;scramble bits
bpl @1 ;random condition
eor #$7FFF ;conditional flip of all but high bit
@1 dey
dey
bpl @loop
;we now have a 16-bit checksum in the accumulator
sta chksum
; and #$F800 ;take the high five bits
; lsr a
; lsr a ;use it as an index into word table
; xba
; tax
ldx #0 ;we no longer use the table of names
lda name_tabl,x ;this table contains word pointers to 2 bytes�
sta <namegen_ptr ;�before name strings
copy_alpha ;now copy the alphabetic part of the generated name to the buffer
sep #%00100000 ;work with bytes
LONGA OFF
ldy #$0002 ;we need this offset for the g-string
@loop
lda (namegen_ptr),y
beq @done
sta (vol_name_ptr),y
iny
cpy #alpha_len_limit ;just in case, we don't want to overflow our buffer
bne @loop
@done
rep #%00100000
LONGA ON
bcd stz bcd_result ;we're going to do a BCD calculation
stz bcd_result+2 ;1 *byte*/digit
lda chksum
and #$07FF ;get lowest 11 bits
sta bin_temp
@1000 sec
sbc #1000
bcc @100
sta bin_temp
lda bcd_result
adc #$1-1 ;carry is set
sta bcd_result
lda bin_temp
bra @1000
@100 lda bin_temp
sec
sbc #100
bcc @10
sta bin_temp
lda bcd_result
adc #$100-1 ;carry is set
sta bcd_result
bra @100
@10 lda bin_temp
sec
sbc #10
bcc @almost_done
sta bin_temp
lda bcd_result+2
adc #$1-1 ;carry is set
sta bcd_result+2
bra @10
@almost_done
;brk $ED
lda bin_temp
xba
adc bcd_result+2 ;take care of ones place. carry is clear
sta bcd_result+2
;we should now have the bcd value of lower 11 bits of the checksum in bcd_result.
lda bcd_result
adc #'00' ;get ascii for this digit. carry is clear.
sta (vol_name_ptr),y
iny
iny
lda bcd_result+2
adc #'00' ;get ascii for this digit. carry is clear.
sta (vol_name_ptr),y
iny
iny
lda #$0000
sta (vol_name_ptr),y
@done2
dey
dey ;adjust to find name length
tya
sta (vol_name_ptr) ;this completes the g-string
;we are done generating the name
rep #$31 ;long accum, carry clear
LONGA ON
LONGI ON
lda #$8000
sta error_priority ;any error now has priority
rts
; 1/4/91 jev Removed table of volume names. It was felt that it would not be
; appropriate to use the names from Prinston and that just DOS 3.3
; was what was aggreed on.
name_tabl ;this table contains word pointers to 3 bytes before the name strings
dc.w name1 - 2
; dc.w name2 - 2
; dc.w name3 - 2
; dc.w name4 - 2
; dc.w name5 - 2
; dc.w name6 - 2
; dc.w name7 - 2
; dc.w name8 - 2
; dc.w name9 - 2
; dc.w name10 - 2
; dc.w name11 - 2
; dc.w name12 - 2
; dc.w name13 - 2
; dc.w name14 - 2
; dc.w name15 - 2
; dc.w name16 - 2
; dc.w name17 - 2
; dc.w name18 - 2
; dc.w name19 - 2
; dc.w name20 - 2
; dc.w name21 - 2
; dc.w name22 - 2
; dc.w name23 - 2
; dc.w name24 - 2
; dc.w name25 - 2
; dc.w name26 - 2
; dc.w name27 - 2
; dc.w name28 - 2
; dc.w name29 - 2
; dc.w name30 - 2
; dc.w name31 - 2
; dc.w name32 - 2
;now we have the actual name data:
string C
name1 dc.b 'DOS 3.3 v'
; name2 dc.b 'HAMILTON'
; name3 dc.b 'DOD'
; name4 dc.b 'HENRY'
; name5 dc.b 'EDWARDS'
; name6 dc.b 'HOLDER'
; name7 dc.b 'WITHERSPOON'
; name8 dc.b 'PYNE'
; name9 dc.b 'SPELMAN'
; name10 dc.b 'MCCOSH'
; name11 dc.b 'WOOLWORTH'
; name12 dc.b 'PALMER'
; name13 dc.b 'JADWIN'
; name14 dc.b 'DILLON'
; name15 dc.b 'PROSPECT'
; name16 dc.b 'ALEXANDER'
; name17 dc.b 'FINE'
; name18 dc.b 'EQUAD'
; name19 dc.b 'FIRESTONE'
; name20 dc.b 'MATHEY'
; name21 dc.b 'NASSAU'
; name22 dc.b 'KRESGE'
; name23 dc.b 'MCCORMICK'
; name24 dc.b 'CLAPP'
; name25 dc.b 'WAWA'
; name26 dc.b 'TI'
; name27 dc.b 'CHARTER'
; name28 dc.b 'ELM'
; name29 dc.b 'COLONIAL'
; name30 dc.b 'QUAD'
; name31 dc.b 'TERRACE'
; name32 dc.b 'CAMPUS'
id_offset ;these are offsets into the VTOC of bytes which are checked in order
;to determine whether a disk is DOS 3.3 format:
dc.b $3 ;DOS release #
dc.b $27 ;# of T/S pairs per T/S list sector
dc.b $36,$37 ;# of bytes per sector
dc.b $31,$31 ;direction of track allocation (2 possible values)
dc.b $34,$34 ;# of tracks per disk (2 possible values??)
dc.b $35,$35 ;# of sectors per track (2 possible values??)
id_values ;these are the actual values required for a DOS 3.3 disk:
dc.b 3 ;DOS release #
dc.b $7A ;# of T/S pairs per T/S list sector
dc.b $00,$01 ;# of bytes per sector
dc.b $01,$FF ;direction of track allocation (2 possible values)
dc.b 35,50 ;# of tracks per disk (2 possible values??)
dc.b 16,32 ;# of sectors per track (2 possible values??)
id_tbl_end
;storage area
chksum ds 1
bin_temp ds 1
bcd_result ds.l 1
ENDP
throw_away PROCNAME ;try to throw away the current vcr
ldy #vcr_open_cnt
lda [vcr_ptr],y ;see if I can throw this away
beq go_ahead
sec ;we can't throw it away
rts
go_ahead ldy #vcr_id
lda [vcr_ptr],y
jsl release_vcr
clc
rts
ENDP
cmp_vol_vcr PROCNAME ;compare vcr buffers to region read in by "volume" routine
ldy #vcr_buf_vp
lda [vcr_ptr],y ;set up some real pointers to this vcr's buffers
tax
iny
iny
lda [vcr_ptr],y
tay
jsl deref
stx <ptr1
sty <ptr1+2 ;we are now ready to compare buffers
ldy #$01FE ;set index for compare
@loop2
lda [vol_buf_ptr],y ;get word from VOLUME routine
cmp [ptr1],y ;match with word from VCR
bne @different ;if any difference, go on to next dev
dey
dey
bpl @loop2
@same
clc ;if we get this far then they're identical
rts
@different
sec
rts
ENDP
device_id PROCNAME ;get the device ID of given device
;on entry:
; <drvr_dev_num must contain the number of the device
;on exit:
; carry clear: Accumulator contains device ID
; carry set: Accumulator contains device error
if 0 then
lda #drvr_status
sta <drvr_call_num
lda #$0002
sta <drvr_req_cnt ;we'd like just a word
stz <drvr_req_cnt+2
lda #ptr1 OR direct_base ;use this direct-page loc for the lousy 2 bytes
sta <drvr_slist_ptr
stz <drvr_slist_ptr+2
stz <drvr_stat_code ;$0000 means get device status
jsl dev_dispatcher
bcc @ok
rts ;return with driver error
@ok
ldy #$34 ;offset to device ID in DIB
lda [drvr_dib_ptr],y ;get the device id
rts
else
lda <drvr_dev_num
pha ;save for restoration
sta <drvr_buf_ptr ;store for dispatcher's use
stz <drvr_dev_num ;call the dispatcher directly
lda #$0001 ;call to get_dib_ptr
sta <drvr_call_num
jsl dev_dispatcher ;call the dispatcher
ply ;restore device number
sty <drvr_dev_num
bcs @done ;just punt on error
ldy #$34 ;offset to device ID in DIB
lda [drvr_dib_ptr],y ;get the device id
@done
rts
endif
ENDP
find_vol_online PROCNAME ;look for a volume in online devices
;enter with:
; volume name as g-string in volname_buf_ptr
; supposed_dev contains the number of the device where we expect�
; �to find the volume or 0 to walk all devices
;brk $05
ldx supposed_dev
bne recover_loop ;this is the case where we look for a lost disk
simple_loop ;this is the easy case: walk the device list
@dev_loop
inx
stx <drvr_dev_num
jsr volume ;who's in that drive?
bcc @chk_name ;my disk, verify that it's the right name
cmp #invalid_dev_num ;see if out of devices
;bne @next
beq not_found ;end of devs
@next
ldx <drvr_dev_num ;restore
bra @dev_loop
@chk_name
lda volname_buf_ptr ;set up pointers for cmp_string
sta <str1_ptr
lda volname_buf_ptr+2
sta <str1_ptr+2
lda vol_name_ptr
sta <str2_ptr
lda #^vol_name_buf
sta <str2_ptr+2
lda #$00FF
sta cmp_mask ;bit mask for ASCII characters
jsr cmp_string ;are the names the same?
bcc found ;right disk!
stz error_priority ;wrong disk, clear the error proirity
bra @next ;look for another
recover_loop ;find a lost disk
stx <drvr_dev_num ;start with the suspected location. X contains supposed_dev
jsr device_id ;get that device's ID
bcc @ok
rts ;return with driver error
@ok
sta dev_id ;save this value
@loop
jsr volume
bcc @chk_name2
cmp #invalid_dev_num ;see if out of devices
bne @next2
@wrap
stz <drvr_dev_num ;wrap down to start of list
@next2
ldx <drvr_dev_num
inx
cpx supposed_dev ;have we come full circle?
beq not_found ;if so, we're done
stx <drvr_dev_num
jsr device_id ;get that device's ID
bcc @ok1
cmp #invalid_dev_num
beq @wrap
rts ;return with driver error
@ok1
cmp dev_id ;same type of device?
beq @loop
bra @next2
@chk_name2
lda volname_buf_ptr ;set up pointers for cmp_string
sta str1_ptr
lda volname_buf_ptr+2
sta str1_ptr+2
lda vol_name_ptr
sta str2_ptr
lda #^vol_name_buf
sta str2_ptr+2
lda #$00FF
sta cmp_mask
jsr cmp_string ;are the names the same?
bcs @next2 ;wrong disk
found ;right disk
clc
rts
not_found lda #vol_not_found
sec
rts
dev_id ds 1
ENDP
search_fcr PROCNAME ;find an fcr by name in file_name_ptr
;checks that fcr belongs to vcr in vcr_ptr
stz old_fcr ;if we find a dup fcr, we'll save its number here
stz fcr_index ;walk the fcr's
loop inc fcr_index
lda fcr_index
jsl get_fcr
bcs not_found ;no match
@ok1 jsl deref
stx old_fcr_ptr
sty old_fcr_ptr+2
ldy #fcr_fst_id
lda [old_fcr_ptr],y ;is it mine?
cmp #my_id
bne loop
ldy #fcr_vcr_id
lda [old_fcr_ptr],y
ldy #vcr_id
cmp [vcr_ptr],y ;does it belong to the right volume?
bne loop
ldy #fcr_type
lda [old_fcr_ptr],y ;get filetype
cmp #$000F ;is it a directory file?
beq loop
ldy #fcr_path_name
lda [old_fcr_ptr],y ;get virtual pointer to the pathname
tax
iny
iny
lda [old_fcr_ptr],y
tay
jsl deref
stx src_scratch
sty src_scratch+2
parse ;now parse off file name
sep #$20
longa off
ldy #4-1 ;start with second valid character
@loop2
iny
lda [src_scratch],y
cmp #':' ;look for colon before filename
bne @loop2
ldx #2-1 ;init ptr to dest string
@loop3
iny ;now copy filename to scratch buffer
inx
lda [src_scratch],y
sta scratch_str_buf,x
bne @loop3 ;if last character wasn't null, keep going
rep #$30
longa on
txa ;get final pointer
dec a
dec a ;adjust to string length
sta scratch_str_buf ;set length
ldx #<scratch_str_buf
ldy #^scratch_str_buf
stx <str2_ptr
sty <str2_ptr+2
;now set up for compare and see if the name matches
lda <file_name_ptr
sta <str1_ptr
lda #^file_buf
sta <str1_ptr+2
jsr cmp_string
bcs loop ;try next fcr in vcr's list
;at this point, we've found an fcr for a previous open of this file
lda fcr_index
sta old_fcr
@found_exit
clc
not_found
rts
ENDP
parse_path PROCNAME ;parse off the path1 string into volume and filename.
;outputs are g-strings pointed to by volname_buf_ptr (vol name)�
;�and file_name_ptr. These strings will contain null length�
;�words if they do not exist.
;this proc also checks length and syntax and returns�
;�with carry set if they are not valid.
lda <path_flag
and #$4000 ;make sure path1 is valid
bne @continue
@null rep #$30 ; ensure 16bit mode
lda #$0000 ;null out the strings
ldy #$0001
sta [volname_buf_ptr],y ;this takes care of the terminator nulls
sta (file_name_ptr),y
sta [volname_buf_ptr] ;this takes care of the length words
sta (file_name_ptr)
clc
rts
@continue
sep #%00100000 ;work with bytes
LONGA OFF
ldy #$0002 ;start of path1 string after length word
lda [path1_ptr],y ;get char
beq @null ;if terminator, set strings to null
cmp #':'
bne filename0
vol ;parse volume name
@loop iny
lda [path1_ptr],y
beq vol_end ;null means end of path string
cmp #':'
beq vol_end ;colon means end of the volume name
dey ;adjust for destination index
sta [volname_buf_ptr],y
iny ;adjust back
cpy #vol_max_char+3 ;over the volume name length limit?
bcc @loop
vol_end
dey ;index past last char in dest
lda #$00
sta [volname_buf_ptr],y ;place terminator
rep #%00100001 ;clear carry as well
LONGA ON
tya
sbc #2-1 ;adjust for length. carry is clear
sta [volname_buf_ptr] ;set the length word
iny
lda [path1_ptr],y ;refresh my memory...
and #$00FF
bne filename
lda #$0000 ;null out the strings
ldy #$0001
sta (file_name_ptr),y ;there is no filename
sta (file_name_ptr)
; We need to check to see if there might be a second pathname and if it might be bad.
; We do this because we are a good FST and we need to return the right error for calls
; like change path.
check2 lda path_flag
and #path2_mask ; is there a second path
bne check_path2 ; if so check it out.
clc
rts
bad_span sec
rts
check_path2 lda span2
beq bad_span
cmp #$001F ; carry is set if too big.
rts
filename0 dey ;adjustment
filename
ldx #$0002
stx temp_index ;this will be our index to the destination string
sep #%00100000 ;work with bytes
LONGA OFF
@loop iny
lda [path1_ptr],y
beq fn_end
cmp #':'
beq error ;sorry, can't have subdirectories!
tyx ;save source index
ldy temp_index ;get dest index
cmp #','
bne @not_comma
lda #':' ;translate commas to colons
@not_comma
sta (file_name_ptr),y
iny
cpy #fn_max_char+4
bcs error
sty temp_index
txy
bra @loop
fn_end
ldy temp_index
sta (file_name_ptr),y ;accum contains zero
rep #%00100000
LONGA ON
tya
dec a
dec a ;adjust for length
sta (file_name_ptr) ;set length word
bra check2 ;we're done with path1, now check path 2
error
rep #%00100000
LONGA ON
sec
rts
temp_index ds 1
ENDP
find_eof PROCNAME ;find the eof and aux_type for a file on an online volume
;on entry:
; volume's directory must be in vol_buf_ptr
; dev_num must be set up
; accum should contain T/S of first T/S list sector
;on exit:
; X = eof (low)
; Y = eof (high)
;
; for Text, A-, B-, S-types:
; A = total number of sectors used
;
; for Bin, Asoft, Int, Rel
; A = aux_type
ldx <fcr_buf_ptr ;set up buffer for driver
stx <drvr_buf_ptr
ldx <fcr_buf_ptr+2
stx <drvr_buf_ptr+2
stz data_secs_cnt
stz ts_secs_cnt
stz eof_scratch
stz eof_scratch+2
;load sectors with useful info -->T/S list & 1 data sect
pha
phy
ldy #vcr_open_cnt
lda [<vcr_ptr],y
inc a
sta [<vcr_ptr],y
ply
pla
jsr new_rwts ;read in 1st T/S list sector
php
pha
phy
ldy #vcr_open_cnt
lda [<vcr_ptr],y
dec a
sta [<vcr_ptr],y
ply
pla
plp
bcc @ok1
rts ;return with driver error
@ok1
lda dos_type ;this should contain the dos filetype
beq text ;text may be sparse
and #$0017 ;check for "easy" types: Int, Asoft, Bin, Rel
beq not_easy
brl easy ;go handle easy types
not_easy
@count
ldy #ts_list_start ;point to start of t/s list entries
inc ts_secs_cnt ;count T/S list sector
@loop
lda [drvr_buf_ptr],y ;get the entry
beq @empty
inc data_secs_cnt ;keep count of actual blocks used
iny
iny ;next entry
cpy #$0100
bne @loop ;get next unless we overflowed
lda #drvr_read
sta <drvr_call_num
ldy #ts_link ;offset in T/S sector to link bytes
lda [fcr_buf_ptr],y ;get the links
beq @done ;$0000 means no more links --> last T/S list sector
pha
phy
ldy #vcr_open_cnt
lda [<vcr_ptr],y
inc a
sta [<vcr_ptr],y
ply
pla
jsr new_rwts ;load next T/S list
php
pha
phy
ldy #vcr_open_cnt
lda [<vcr_ptr],y
dec a
sta [<vcr_ptr],y
ply
pla
plp
bcc @count ;if good, count valid t/s entries
rts ;return with driver error
@empty
@done
;assume file is all bytes in all allocated sectors
;calculate: eof = (sectors used) * 256
lda data_secs_cnt ;get the number of data sectors used
xba ;multiply low byte by 256
tax ;save this value
and #$FF00 ;clear (former) high byte
sta eof_scratch
txa ;get back that flipped sector count
and #$00FF ;now we want the other half
sta eof_scratch+2 ;all done!
lda data_secs_cnt
clc
adc ts_secs_cnt ;find total number of sectors used
brl eof_exit
text
@count
ldx #ts_list_start ;init offset to 1st entry in case all are 0
;** Monte Benaresh -- 2/20/92
ldy #ts_list_start ;point to start of T/S list entries
inc ts_secs_cnt ;count T/S list sector
@loop
lda [drvr_buf_ptr],y ;get the entry
beq @empty
tyx ;save in case it's the last non-zero
inc data_secs_cnt
@empty
iny
iny ;next entry
cpy #$0100
bne @loop ;get next unless we overflowed
lda #drvr_read
sta <drvr_call_num
ldy #ts_link ;offset in T/S sector to link bytes
lda [fcr_buf_ptr],y ;get the links
beq @done ;$0000 means no more links --> last T/S list sector
pha
phy
ldy #vcr_open_cnt
lda [<vcr_ptr],y
inc a
sta [<vcr_ptr],y
ply
pla
jsr new_rwts ;next t/s list
php
pha
phy
ldy #vcr_open_cnt
lda [<vcr_ptr],y
dec a
sta [<vcr_ptr],y
ply
pla
plp
bcc @count ;if good, count valid t/s entries
rts ;return with driver error
@done ;we have the last T/S list sector for a text file.
get_eof ;now we have to find the last valid (non-0) sector in the list
txa ;get that entry position so we can calculate
sec
sbc #ts_list_start ;subtract the offset to start of T/S list
lsr a ;divide by 2 to get # of sectors
xba ;turn it into a byte value
sta eof_scratch ;store temporary result = # of bytes in all but last�
;�data sector in this T/S list
lda <drvr_buf_ptr
adc #$0100 ;incr bufer ptr to next pg. carry should be clear
sta <drvr_buf_ptr
bcc @no_carry
inc <drvr_buf_ptr+2
@no_carry
lda #drvr_read
sta <drvr_call_num
txy ;restore the index
lda [fcr_buf_ptr],y
sta last_data_sec ;save this number in case we're successful
;
; next added 2/20/92 by Monte Benaresh
bne @real_sector
@no_sector
ldy #-1 ;will get incremented to 0 for eof
bra found_eof ;calculate now
@real_sector
; end 2/20/92
pha
phy
ldy #vcr_open_cnt
lda [<vcr_ptr],y
inc a
sta [<vcr_ptr],y
ply
pla
jsr new_rwts ;load in that last data sector
php
pha
phy
ldy #vcr_open_cnt
lda [<vcr_ptr],y
dec a
sta [<vcr_ptr],y
ply
pla
plp
bcc @ok
; lda #error return with driver error
rts
@ok
;may need special check for case where data fills last sec exactly!
ldy #$00FF ;offset to last byte
sep #%00100000 ;work with bytes
LONGA OFF
@loop2 lda [drvr_buf_ptr],y
bne found_eof ;when we hit a non-0, that's eof-1
dey ;back up to next one
bpl @loop2 ;keep going as long as we don't hit bottom
;if we hit bottom, just set eof to end at end of last�
;�sector. in effect this causes the fst to ignore the�
;�zero-filled sector.
found_eof rep #%00100001 ;clear carry, too
LONGA ON
iny ;eof points to *next* byte
tyx ;save that byte part
ldy #ts_sec_offset ;offset to offset number of 1st sector in this list
lda [fcr_buf_ptr],y ;get sector offset word
xba
tay
and #$FF00 ;keep high part --> byte offset
adc eof_scratch ;build this value. carry is clear from above
sta eof_scratch
tya
and #$00FF ;this is byte offset for the high word
adc eof_scratch+2 ;build high word with carry from low word
sta eof_scratch+2
txa ;get back that last byte part
adc eof_scratch ;carry should be clear
sta eof_scratch
lda eof_scratch+2
adc #$0000
sta eof_scratch+2
;find blks_used
lda data_secs_cnt
clc
adc ts_secs_cnt ;find total number of sectors used
bra eof_exit
easy ;filetypes Asoft, Int, Bin, and Rel contain their eof's!
lda <drvr_buf_ptr
clc
adc #$0100 ;incr buffer ptr to next pg.
sta <drvr_buf_ptr
lda <drvr_buf_ptr+2
adc #$0000 ;carry bank
sta <drvr_buf_ptr+2
lda #drvr_read
sta <drvr_call_num
ldy #ts_list_start ;offset to start of list entries
lda [fcr_buf_ptr],y ;get first data sector
sta last_data_sec
pha
phy
ldy #vcr_open_cnt
lda [<vcr_ptr],y
inc a
sta [<vcr_ptr],y
ply
pla
jsr new_rwts ;load in that last data sector
php
pha
phy
ldy #vcr_open_cnt
lda [<vcr_ptr],y
dec a
sta [<vcr_ptr],y
ply
pla
plp
bcc @ok
; lda #error return with driver error
rts
@ok
lda dos_type
ldy #$0000 ;offset to length bytes for Applesoft & Integer
and #�$FF83 ;mask out Applesoft, Integer and lock bits
beq @get_len ;get the length bytes
ldy #$0002 ;offset for binary and relocatable
@get_len
lda [drvr_buf_ptr],y
sta eof_scratch ;that's the eof, period.
lda #$0000
sta eof_scratch+2 ;never a high word for these filetypes
@get_aux ;find the aux type
lda dos_type
and #�$FF84 ;mask out Binary
beq @bin
cmp #$0001 ;is it integer?
bne @apsoft
lda #$0C00
bra eof_done
@apsoft
lda #$0801
bra eof_done
@bin lda [drvr_buf_ptr] ;for reloc & binary, 1st word is aux_type!
eof_done ;we now have the eof, for every type, stored safely in eof_scratch
eof_exit ldx eof_scratch
ldy eof_scratch+2 ;load eof into X & Y for caller
;on exit, A should contain either aux_type or blks_used
clc
rts
eof_scratch ds.l 1
data_secs_cnt ds 1
ts_secs_cnt ds 1
ENDP
load_cat_secs PROCNAME ;load the catalog sectors into vol_buf_ptr, up to�
;�max of 15 total
;at this point, the vtoc and the first catalog sector
;have been read in by the subroutine "volume"
lda <vol_buf_ptr
clc
adc #$0100 ;set to first cat sector
sta <drvr_buf_ptr
lda <vol_buf_ptr+2
adc #$0000 ;carry bank
sta <drvr_buf_ptr+2
ldx #14 ;max number of additional cat sectors
stx countdn
@loop
ldy #$0001 ;offset to LINK BYTES in LAST CAT SECTOR
lda [drvr_buf_ptr],y ;get track/sector of next cat sector
beq @cat_done ;last cat sector
;range checking?
pha
lda <drvr_buf_ptr
adc #$0100 ;bump to next sector. carry should be clear
sta <drvr_buf_ptr
lda <drvr_buf_ptr+2
adc #$0000 ;carry bank
sta <drvr_buf_ptr+2
lda #drvr_read
sta <drvr_call_num
pla ;restore track/sector request
jsr new_rwts ;go get it
bcc @ok
; lda #error return with driver error
rts
@ok dec countdn
bne @loop
@cat_done
lda #15
sec
sbc countdn ;total # of cat sectors
sta cat_sec_count ;this is the number of catalog sectors read in
clc
rts
countdn ds 1
ENDP
check_dir PROCNAME ;check the directory sectors in vol_buf_ptr for�
;�the filename in file_name_ptr
lda file_name_ptr
sta str1_ptr
lda #^file_buf
sta str1_ptr+2
lda #$0000 ;cat sector numbers start at 0
@cat_loop ldx #$0000 ;same for fde's
@fde_loop sta cat_index
stx fde_index ;save these
jsr find_fde
bcc @ok
; lda #parm_range_err OR $8000 ;internal range error
rts ;error
@ok
*** changed 6-May-93 DAL
;;; lda [fde_ptr] ;first byte tells whether in use
;;; and #$00FF
jsr load_fde_flag
*** end 6-May-93
beq file_not_here ;a $00 means beyond last entry
cmp #$00FF
beq @next_fde ;inactive, go to next one
lda <fde_ptr
adc #fde_name-2 ;add offset to name field w/room for length word.
;carry should be clear
sta <str2_ptr
lda <fde_ptr+2
adc #$0000 ;carry bank
sta <str2_ptr+2
ldy #$1D+2 ;offset to last character in name
lda [str2_ptr] ;save this word before we crash it with length
pha
sep #%00100000 ;work with bytes
LONGA OFF
@loop lda [str2_ptr],y ;borrow this pointer before we give it to cmp_string
cmp #' ' OR $80 ;check for spaces used as padding (w/high bit set)
bne @nam_end
dey
bpl @loop ;as long as we don't hit bottom, keep going
; lda #dir_error ;name is all spaces!
plx
rep #$30 ;long a,x
sec
rts
@nam_end
dey ;adjust to string length
lda #$7F
sta cmp_mask ;set the compare mask
rep #%00100000
LONGA ON
tya
sta [str2_ptr] ;str2 is a g-string without the terminator
jsr cmp_string ;str1_ptr should contain file_name_ptr
pla ;restore that one word we stepped on
sta [str2_ptr]
bcc right_fde ;we got it!
@next_fde ldx fde_index
lda cat_index
inx ;next fde
cpx #$0007 ;check limit of 7 fde's per cat sector
bcc @fde_loop ;we're ok, check next fde
ina ;next catalog sector
cmp cat_sec_count ;past our limit?
bcc @cat_loop ;move on to next cat sector
file_not_here ;file not on this volume
lda #$FFFF
sta fde_index ;indicate file not found
clc
rts
right_fde clc ;success
rts
ENDP
export do_option_list
do_option_list PROCNAME ;return option list data to user buffer
;enter with:
; Y = offset in param block to option list ptr
;brk $05
lda [param_blk_ptr],y
sta <user_ptr
iny
iny
lda [param_blk_ptr],y
sta <user_ptr+2
ora <user_ptr ;test for 0
beq done ;if $00000000, don't use it!
list_good
lda [user_ptr] ;get length word
cmp #4+2 ;need 4 bytes for overhead, 2 for data
bcs @ok
@too_small
sec ;this is an error
rts
@ok
lda #my_id ; my id is same as its length = 2
tay
sta [user_ptr],y
iny
iny ;offset to actual buffer
sta [user_ptr],y ;give user our FST ID
done
clc
rts ;that's all there is to it!
ENDP
export set_text_mode
set_text_mode PROCNAME ;handle FST-specific call
WITH fst_specific
lda p_cnt
cmp #3 ;check pcount
beq @pcount_ok
lda #invalid_pcount
sec
rts
@pcount_ok
ldy #param1 ;get parameter
lda [param_blk_ptr],y
cmp #1+1 ;range check
bcc @mode_ok
@bad_mode
lda #parm_range_err
sec
rts
@mode_ok
sta text_flag
lda #0 ;no error
clc
rts
ENDWITH
ENDP
export get_text_mode
get_text_mode PROCNAME ;handle FST-specific call
WITH fst_specific
lda p_cnt
cmp #3 ;check pcount
beq @pcount_ok
lda #invalid_pcount
sec
rts
@pcount_ok
lda text_flag
ldy #param1 ;get parameter
sta [param_blk_ptr],y
lda #0 ;no error
clc
rts
ENDWITH
ENDP
get_file_info PROCNAME ;get all necessary info for a file and stuff it into�
;�global variables
;fde_ptr must point to the file's fde
lda #storage_type ;if it's not the root directory, it gets this storage type
sta stor_type
ldy #fde_type
lda [fde_ptr],y ;get the filetype
tax
and #$0080 ;look at the lock bit
bne @locked
lda #access_unlocked
bra @store
@locked
lda #access_locked
@store
sta access
txa ;get back original fetch
and #$007F ;mask out high byte and lock bit
sta dos_type
translate_type ;determine corresponding filetype
ldx #8
@loop
lsr a
bcs @lookup
dex
bne @loop
@lookup
txa
asl a ;multiply by two for table
tax
lda type_table,x
sta os_type
lda dos_type
and #$0017 ;is it basic, Bin, or Rel?
beq not_easy
easy
ldy #fde_ts_link
lda [fde_ptr],y ;find_eof needs this
jsr find_eof
bcc @ok
rts ;return with driver error
@ok
sta aux_type
stx eof
sty eof+2
ldy #fde_sec_used
lda [fde_ptr],y ;we can trust the directory for these filetypes
sta blks_used
clc
rts
not_easy
ldy #fde_ts_link
lda [fde_ptr],y ;find_eof needs this
jsr find_eof
bcc @ok
rts ;return with driver error
@ok sta blks_used
stx eof
sty eof+2
stz aux_type
rts ;carry is clear
type_table ;lookup table to translate DOS filetypes to GS/OS(TM)
; GS/OS type DOS 3.3 type
; ---------- ------------
dc.w $04 ; ASCII Text File Text
dc.w $0F ; Directory (undefined)
dc.w $00 ; uncatgegorized B-type
dc.w $00 ; uncatgegorized A-type
dc.w $FE ; EDASM Relocatable Code File Relocatable
dc.w $00 ; uncatgegorized S-type
dc.w $06 ; Binary File Binary
dc.w $FC ; Applesoft Basic Program Applesoft Basic
dc.w $FA ; Integer Basic Program Integer Basic
dc.w $EE ; Classic Auto E-type
ENDP
get_dir_entry PROCNAME
;brk $1C
ldy #vcr_dev ; even if we don't access the device, set this up
lda [<vcr_ptr],y ; for get_last_dev.
sta drvr_dev_num
stz error ;we store the non-fatal error here
ldy #fcr_type
lda [fcr_ptr],y ;SCM promises to give me the right fcr
cmp #$000F ;is it a directory file?
beq @ok
lda #version_error OR $8000 ;internal error
sec
rts
@ok
lda class
beq @class0
@class1
ldy #get_dir_entry1.base
lda [param_blk_ptr],y ;get the base
sta base1
ldy #get_dir_entry1.displacement
lda [param_blk_ptr],y ;get displacement
sta displacement1
ldy #get_dir_entry1.name ;copy pointer to direct page
lda [param_blk_ptr],y
sta <user_ptr
iny
iny
lda [param_blk_ptr],y
sta <user_ptr+2
bra chk_entry ;go validate the entry num
@class0
ldy #get_dir_entry0.base
lda [param_blk_ptr],y ;get the base
sta base1
ldy #get_dir_entry0.displacement
lda [param_blk_ptr],y ;get displacement
sta displacement1
ldy #fcr_blks_used
lda [fcr_ptr],y ;get number of sectors owned by this directory
dea ;account for vtoc
sta cat_sec_count ;we need this below
ldy #get_dir_entry0.name ;copy pointer to direct page
lda [param_blk_ptr],y
sta <user_ptr
iny
iny
lda [param_blk_ptr],y
sta <user_ptr+2
;brk $1d
chk_entry
lda base1
tax ;save for a moment
ora displacement1 ;are they both zero?
beq special ;special funtion for base = displacement = 0
txa ;restore original base value
beq absolute
dea
beq add
dea
beq subtract
lda #parm_range_err
rts
special ;base and displacement are both zero
ldy #fcr_tot_ent
lda [fcr_ptr],y ;get the total number of valid entries
sta entry_num ;user gets this value here
lda #0
ldy #fcr_entry_num
sta [fcr_ptr],y ;reset entry number for this directory
brl fill_param_blk ;that all we do!
absolute
ldy #fcr_tot_ent
lda [fcr_ptr],y ;get the total number of valid entries
cmp displacement1
bcs @ok
lda #end_of_dir ;entry number too high
sec
rts
@ok
lda displacement1
sta entry_num
bra find_entry
add
ldy #fcr_entry_num
lda [fcr_ptr],y ;get the current entry number
clc
adc displacement1
sta entry_num
ldy #fcr_tot_ent
lda [fcr_ptr],y ;get the total number of valid entries
cmp entry_num ;range check
bcs find_entry
lda #end_of_dir
sec
rts
subtract
ldy #fcr_entry_num
lda [fcr_ptr],y ;get the current entry number
dec a
cmp displacement1
bcc @err ; out of range, displacement would make entry < 1
inc a
sec
sbc displacement1
sta entry_num
ldy #fcr_tot_ent
lda [fcr_ptr],y ;get the total number of valid entries
cmp entry_num ;range check
bcs find_entry
@err lda #end_of_dir
sec
rts
find_entry ;cycle through to nth directory entry
lda entry_num
ldy #fcr_entry_num
sta [fcr_ptr],y
sta countdown ;set this up for the loop
ldy #vcr_buf_vp ;get the vcr's buffer set up
lda [vcr_ptr],y
tax
iny
iny
lda [vcr_ptr],y
tay
jsl deref
stx vol_buf_ptr
sty vol_buf_ptr+2
;now loop through valid entries
lda #$0000 ;cat sector numbers start at 0
@cat_loop
ldx #$0000 ;same for fde's
@fde_loop
sta cat_index
stx fde_index ;save these
jsr find_fde
bcc @ok
rts ;error
@ok
*** changed 6-May-93 DAL
;;; lda [fde_ptr] ;first byte tells whether in use
;;; and #$00FF
jsr load_fde_flag
*** end 6-May-93
bne @cont ;a $00 means beyond last entry
lda #end_of_dir OR $8000 ;internal error
sec
rts
@cont
cmp #$00FF
beq @inactive ;inactive, don't count it
dec countdown
beq right_fde ;if countdown hits 0, we've got our man
@inactive
ldx fde_index
lda cat_index
inx ;next fde
cpx #$0007 ;check limit of 7 fde's per cat sector
bcc @fde_loop ;we're ok, check next fde
ina ;next catalog sector
cmp cat_sec_count ;past our limit?
bcc @cat_loop ;move on to next cat sector
right_fde ;fde_ptr now points to the fde in question!
;now, alas, we have to go to disk for some of the info
ldy #vcr_name
lda [vcr_ptr],y ;get the vp's to volume name
tax
iny
iny
lda [vcr_ptr],y
tay
jsl deref
stx volname_buf_ptr
sty volname_buf_ptr+2
ldy #vcr_dev
lda [vcr_ptr],y ;get last known device
sta supposed_dev
jsr find_vol_online
bcc @ok
lda #vol_not_found ;we must have the device online
rts
@ok ldy #vcr_dev ; has the volume moved?
lda [<vcr_ptr],y
cmp drvr_dev_num
beq @ok1 ; no ->
ldy #vcr_dev ; update the dev_num
lda drvr_dev_num
sta [<vcr_ptr],y
@ok1 jsr get_file_info
bcc @ok2
lda #dir_error
rts
@ok2 ;we now have all our file info in global variables
do_name
sep #%00100000 ;work with bytes
LONGA OFF
ldy #fde_sec_used-1 ;this points to the last byte of the filename
@loop
lda [fde_ptr],y
cmp #' ' OR $80 ;is it a space?
bne @set_len ;if not, we've found the last character
dey
bne @loop ;use bne just in case...
@set_len
rep #%00100001 ;16-bit accum, carry clear
LONGA ON
iny ;get past end
sty src_end
tya
sbc #fde_name-1 ;adjust for name len. note: carry is clear
sta name_len
ina
ina
ina ;add three
tay
lda [user_ptr]
sta usr_len ;this will be a worthwile investment
cpy usr_len
bcc @big_enough
lda #buff_too_small
sta error ;non-fatal
@big_enough
ldy #fde_name-1 ;this is our source pointer
lda #$0004-1
sta dest_ptr
sep #%00100000 ;work with bytes
LONGA OFF
@loop1
iny
cpy src_end
beq @name_done
ldx dest_ptr
inx
cpx usr_len ;check for end of user buffer
beq @name_done
stx dest_ptr
lda [fde_ptr],y
tyx
ldy dest_ptr
and #$7F ;strip high bits
cmp #':'
bne @not_colon
lda #',' ;replace colons with commas
@not_colon
sta [user_ptr],y
txy ;restore source pointer
bra @loop1
@name_done
rep #%00100001 ;16-bit accum, carry clear
LONGA ON
lda name_len
ldy #$0002
sta [user_ptr],y ;that sets the length word
fill_param_blk ;now fill in the user param block
lda class
bne @class1
@class0
lda #$0000
ldy #get_dir_entry0.flags
sta [param_blk_ptr],y ;indicate not extended file
lda #$0001
sta p_cnt ;this will make the fill_param tables work right
lda #<get_dir_entry0_f
sta <fill_tabl ;set up for right parameter fill table
jsr fill_param
bra all_done ;now take care of the name
@class1
lda #$0000 ;non_extended file
ldy #get_dir_entry1.flags
sta [param_blk_ptr],y
lda #<get_dir_entry1_f
sta <fill_tabl ;set up for right parameter fill table
jsr fill_param
lda p_cnt
cmp #15 ;do we need option list?
bcc @no_list
ldy #get_dir_entry1.option_list
jsr do_option_list
bcc @list_ok
lda #buff_too_small
sta error
@no_list
@list_ok
all_done
lda error
cmp #$0001 ;set carry
rts
src_end ds 1
dest_ptr ds 1
error ds 1
base1 ds 1
displacement1 ds 1
countdown ds 1
name_len ds 1
usr_len ds 1
ENDP
read_newline PROCNAME ;transfer a range of bytes according to newline
;pointer at src_scratch must be set to the start of the�
;�source range
;pointer at usr_ptr must be set to destination buffer
;byte_cnt must contain the maximum number of bytes�
;�to be transferred
;recent_xfer gets updated
;on exit, carry set means a newline character was encountered
newline
ldy #fcr_newline ;get the newline pointer
lda [fcr_ptr],y
tax
iny
iny
lda [fcr_ptr],y
tay
jsl deref
stx nl_list_ptr
sty nl_list_ptr+2
ldy #fcr_mask
lda [fcr_ptr],y
sta <nl_msk
lda nl_len ;how many nl characters are we dealing with?
dea
bne slow_nl ;if more than 1 newline char, things slow down
fast_nl
lda [nl_list_ptr]
and #$00FF ;there's only one real byte there
sta <nl_char
ldy byte_cnt
beq end_nl2 ;check for degenerate case
ldy #0
sep #$30 ;short registers and accumulator
LONGA OFF
LONGI OFF
@loop phy
rep #$30
LONGA ON
LONGI ON
ldy #fcr_dos_type
lda [fcr_ptr],y ;get the DOS 3.3 filetype
sep #$30
LONGA OFF
LONGI OFF
bne @1 ;if NOT 0 (TEXT file) don't touch the data
ply
lda [<src_scratch],y ; xfer char
sta [<user_ptr],y
and #$7F ; strip the high bit for newline compare
bra @2 ; continue ->
@1 ply
lda [src_scratch],y
sta [user_ptr],y
@2 inc recent_xfer
and <nl_msk
cmp <nl_char
beq @hit_nl
rep #$30
LONGA ON
LONGI ON
iny
cpy byte_cnt
sep #$30
LONGA OFF
LONGI OFF
bcc @loop
rep #$30 ;16-bit accum, carry clear
LONGA ON
LONGI ON
clc
bra end_nl
@hit_nl
rep #$30 ;16-bit accum
LONGA ON
LONGI ON
sec ;indicate that newline was encountered
bra end_nl
slow_nl ldy #$0000
sep #$20 ;short accumulator
LONGA OFF
@loop phy
rep #$20
LONGA ON
ldy #fcr_dos_type
lda [fcr_ptr],y ;get the DOS 3.3 filetype
sep #$20
LONGA OFF
bne @1 ;if NOT 0 (TEXT file) don't touch the data
ply
lda [<src_scratch],y ; xfer char
sta [<user_ptr],y
and #$7F ; strip the high bit for newline compare
bra @2 ; continue ->
@1 ply
lda [src_scratch],y
sta [user_ptr],y
@2 inc recent_xfer
and <nl_msk
tyx
ldy nl_len
@cmp_loop dey
bmi @not_end ;we've run out of newline characters
cmp [nl_list_ptr],y
bne @cmp_loop
bra @hit_nl ;we've hit a newline character
@not_end txy ;restore buffer position
iny
cpy byte_cnt ;are we out of bytes anyway?
bne @loop
rep #$20 ;16-bit accum
LONGA ON
clc
bra end_nl
@hit_nl sec ;indicate that newline was encountered
end_nl rep #$30
LONGA ON
LONGI ON
end_nl2 rts
ENDP
read PROCNAME ;handle the READ call
stz error ;errors go here
stz bytes_sent
stz bytes_sent+2
stz recent_xfer
;brk $42
find_offset ;figure out what the "data_offset" value is for this file
ldy #fcr_dos_type
lda [fcr_ptr],y
beq @pure_data ;0 means text
and #�$0003 ;mask off bits for Asoft & Integer
bne @not_basic
lda #$0002 ;basic files have 2 bytes before the real data
sta data_offset
bra fetch_fcr
@not_basic
and #�$0014 ;bits for binary & relocatable
bne @not_bin
lda #$0004 ;these types have 4 bytes before real data
sta data_offset
bra fetch_fcr
@not_bin
@pure_data ;the only types left are considered to have pure data
stz data_offset
fetch_fcr ;fetch stuff from fcr for handy reference
ldy #fcr_mask
lda [fcr_ptr],y ;get newline mask
sta nl_msk
ldy #fcr_newline_len
lda [fcr_ptr],y ;get newline list length
sta nl_len
ldy #fcr_eof
lda [fcr_ptr],y
sta eof
iny
iny
lda [fcr_ptr],y
sta eof+2
ldy #fcr_mark
lda [fcr_ptr],y
sta mark
iny
iny
lda [fcr_ptr],y
sta mark+2
ldy #fcr_buf_vp ;set up the pointer to the buffers
lda [fcr_ptr],y
tax
iny
iny
lda [fcr_ptr],y
tay
jsl deref
stx <fcr_buf_ptr
sty <fcr_buf_ptr+2
;txa
lda class
beq @class0
@class1
ldy #read1.request_count
lda [param_blk_ptr],y
sta req_cnt
iny
iny
lda [param_blk_ptr],y
sta req_cnt+2
ldy #read1.data_buffer
lda [param_blk_ptr],y
sta <user_ptr
iny
iny
lda [param_blk_ptr],y
sta <user_ptr+2
bra @fetch_done
@class0
ldy #read0.request_count
lda [param_blk_ptr],y
sta req_cnt
iny
iny
lda [param_blk_ptr],y
sta req_cnt+2
ldy #read0.data_buffer
lda [param_blk_ptr],y
sta <user_ptr
iny
iny
lda [param_blk_ptr],y
sta <user_ptr+2
@fetch_done
check_mark ;must return error if mark = eof
lda mark
cmp eof
bne @not_equal
lda mark+2
cmp eof+2
bne @not_equal
lda #end_of_file
sta error
brl store_tcount ;return transfer count and exit
@not_equal
check_limits
;brk $A1
lda mark
clc
adc req_cnt
sta endmark
lda mark+2
adc req_cnt+2
bcs @too_big ;overflowed 32 bits!
sta endmark+2
cmp eof+2
bcc limit_ok
bne @too_big
@maybe
lda eof
cmp endmark
bcs limit_ok
@too_big
;lda #end_of_file
;sta error ;remember, it's non-fatal
lda eof+2
sta endmark+2
lda eof
sta endmark ;that's as far as we can go!
limit_ok
;now calculate needed scratch values
lda endmark
sec
sbc mark
sta bytes_left ;this is the maximum number of bytes left to transfer
lda endmark+2
sbc mark+2
sta bytes_left+2 ;this is the transfer count based on lesser of�
;�request count and (eof minus mark)
lda mark
clc
adc data_offset
sta phys_mark ;this is the "mark" for disk sectors
lda mark+2
adc #$0000
sta phys_mark+2
ldy #fcr_type
lda [fcr_ptr],y ;check for directory file
cmp #$000F
bne @normal
brl directory_file ;some weirdo wants to read the directory file
@normal
init_drvr ;initialize direct page driver vars
ldy #vcr_dev
lda [vcr_ptr],y ;where did we see the disk last?
sta <drvr_dev_num ;we'll trust this number for now
lda #drvr_read ;set up for reading sectors
sta <drvr_call_num
;now we have to locate the t/s list sector we need
find_sector ;this is the return point for the big sector loop
;bytes_sent, bytes_left, and phys_mark should be updated�
;�before iterating
;brk $49
lda phys_mark
xba
and #$00FF ;this is now low byte of sector num which contains mark
sta sec_num
lda phys_mark+2
xba
tax
and #$FF00
ora sec_num
sta sec_num
txa
and #$00FF
sta sec_num+2 ;this completes a 24-bit value for sec_num
;lda phys_mark
;ldy #$0000
check_ts
lda fcr_buf_ptr ;t/s list sectors go here
sta drvr_buf_ptr
lda fcr_buf_ptr+2
sta drvr_buf_ptr+2
lda sec_num
ldy #ts_sec_offset
sec
sbc [fcr_buf_ptr],y ;do we have the right t/s list?
bcc start_over ;the one we have is too far, start from top
cmp #122 ;are we within the range of the one we want?
bcc have_ts ;if the difference is less than the capacity of a�
;�t/s list, then we have the right one
walk_ahead
ldy #ts_link
lda [fcr_buf_ptr],y
bne @ok1
lda #dir_error ;the linked list is bad
sec
rts
@ok1 jsr new_rwts ;go get that next t/s list sector
bra check_ts
start_over
ldy #fde_ts_link ;start from the first t/s list sector
lda [fde_ptr],y ;get the track/sector of first t/s list sector
jsr new_rwts
bcc @ok
rts ;return with driver error
@ok
bra check_ts
have_ts
;brk $43
;the accumulator contains the t/s entry offset number
asl a ;convert to word offset
adc #ts_list_start ;carry is clear
tay
lda [fcr_buf_ptr],y ;get the entry
bne @real ;if non-0 then it's a real sector
brl null_sect ;if the entry is zero, hopefully it's because we've hit�
;�a null portion of a sparse file
@real
sta data_sect ;save entry
ldy #fcr_data_sect
cmp [fcr_ptr],y ;is our sector already there?
beq xfer_data ;if it is, skip the driver call!
lda nl_msk ;is newline mode set?
bne double_buf ;if so, we have to bite the bullet on this one!
lda phys_mark
and #$00FF ;look at least sig byte
bne double_buf ;are we sector aligned?
lda bytes_left
cmp #$101
bcc double_buf ;if user gets partial sector, must double buffer
single_buf ;read the puppy right into the user's buffer!
lda user_ptr
sta drvr_buf_ptr
lda user_ptr+2
sta drvr_buf_ptr+2
lda data_sect ;this was from the t/s list
jsr new_rwts
bcc @ok1
rts ;return with driver error
@ok1
lda #$0100
sta recent_xfer ;this is what we've transfered
brl next_sector ;we're done with this sector
double_buf
lda <fcr_buf_ptr
clc
adc #$100 ;second page of buffer is for data sectors
sta drvr_buf_ptr
lda <fcr_buf_ptr+2
adc #$0000
sta drvr_buf_ptr+2
lda data_sect
jsr new_rwts ;read in the sector
bcc @ok
rts ;return with driver error
@ok
ldy #fcr_data_sect
lda data_sect
sta [fcr_ptr],y ;make a record that we have this sector in the fcr buffer
xfer_data
lda phys_mark
and #$00FF ;this is the physical start position
sta sec_start ;save for later
ldy bytes_left+2
bne @rest_of_sec ;if this isn't 0, we're definitely going to the end of�
;�this sector
clc
adc bytes_left ;find physical end position
cmp #$0100+1 ;does it go past the end of the sector?
bcc @store
@rest_of_sec
lda #$0100
@store
sta sec_end ;the physical end position for this particular sector
sec
sbc sec_start ;now we have the number of bytes to be transfered�
;�from this sector
sta byte_cnt
lda sec_start ;get back least significant byte of start addr
clc
adc #$0100 ;bump to second page of fcr buffer
adc <fcr_buf_ptr
sta <src_scratch
lda <fcr_buf_ptr+2
adc #$0000
sta <src_scratch+2
ldx nl_msk
bne newline ;go do it the ugly way
;brk $47
lda byte_cnt ;this is the # of bytes to be moved from this sector
cmp #move_info_be ;check transfer value against the break-even point�
;�for using the move_info call
my_move
bcs use_mi
lsr a ;divide by 2
tax
bcc @even ;if no remainder, go move by word
;otherwise we transfer the odd byte
sep #%00100000 ;work with bytes
LONGA OFF
lda [src_scratch]
sta [user_ptr] ;that takes care of the blasted odd byte!
rep #%00100001 ;16-bit accum, carry clear
LONGA ON
inc src_scratch
bne @no_carry
inc src_scratch+2
@no_carry
inc user_ptr
bne @no_carry2
inc user_ptr+2
@no_carry2
@even
txa ;this is a word count
dea ;decrement effectively by 2 to point to the word
bmi @done
asl a
tay
@loop lda [src_scratch],y
sta [user_ptr],y
dey
dey
bpl @loop
@done ;transfer complete!
lda byte_cnt
sta recent_xfer ;the whole amount was transferred
lsr a ;did we increment user_ptr earlier?
bcc next_sector ;no, no need to fix it up...
lda user_ptr ;else we must decrement it once to account for
bne @1 ; the increment that we did above (for odd-size
dec user_ptr+2 ; move)
@1 dec user_ptr ;This ensures proper math when we update the pointer
bra next_sector ; at the end of the loop.
use_mi ;use move_info
pei src_scratch+2 ;high word of source pointer
pei src_scratch ;low word " " "
pei user_ptr+2 ;destination
pei user_ptr
pea $0000 ;high word of transfer count is always 0
lda byte_cnt
pha
pea move_sinc_dinc ;command word
jsl move_info
bcc @ok1
lda #$8000 ;internal generic error
rts
@ok1
lda byte_cnt
sta recent_xfer ;the whole amount was transferred
bra next_sector
newline
jsr read_newline
bcc next_sector ;we ran out of bytes, so get more
lda recent_xfer
clc
adc bytes_sent
sta bytes_sent
bcc @no_carry
inc bytes_sent+2
@no_carry clc ; all done, so compensate for the subtraction that
lda bytes_sent ; is performed during post_process for newline mode.
adc data_offset
sta bytes_sent
lda bytes_sent+2
adc #0
sta bytes_sent+2
brl post_process ;we hit a newline char, so return to caller
next_sector ;at this point, only recent_xfer should reflect what has been�
;�sent to the caller
lda recent_xfer
clc
adc bytes_sent
sta bytes_sent
bcc @no_carry
inc bytes_sent+2
@no_carry
lda bytes_left
sec
sbc recent_xfer
sta bytes_left
bcs @no_borrow
dec bytes_left+2
@no_borrow lda bytes_left ; done?
ora bytes_left+2
bne @1 ; maybe ->
clc ; all done, so compensate for the subtraction that
lda bytes_sent ; is performed during post_process for newline mode.
adc data_offset
sta bytes_sent
lda bytes_sent+2
adc #0
sta bytes_sent+2
brl post_process
; if in newline mode, then it will go negative when
; it is done, but only by the amount of data_offset...
@1 lda bytes_left ; check to see if there are any more bytes to xfer.
clc ; must add the data_offset or else the value may be
adc data_offset ; < 0.
sta @temp
lda bytes_left+2
adc #0
ora @temp
bne @more ;no more bytes left to transfer!
brl post_process
@temp dc.w 0
@more
lda recent_xfer
tax ;save for other calculations
clc
adc phys_mark ;update the physical mark
sta phys_mark
bcc @no_carry1
inc phys_mark+2
@no_carry1
txa ;get back recent_xfer
clc
adc user_ptr ;update user_ptr
sta user_ptr
bcc @no_carry3
inc user_ptr+2
@no_carry3
brl find_sector ;we're set to iterate
null_sect
lda nl_msk
beq @no_nl ;no newline if mask is 0
ldy nl_len
sep #%00100000 ;work with bytes
LONGA OFF
@loop dey
bmi @no_nl
lda [nl_list_ptr],y ;determine whether 0 is on the newline list
bne @loop ;fall through on a zero
sta [user_ptr] ;store the single zero in user's buffer
rep #%00110001 ;16-bit accum, carry clear
LONGA ON
inc bytes_sent
bne @no_carry
inc bytes_sent+2
@no_carry
lda bytes_left
sec
sbc #$0001
sta bytes_left
bcs @no_borrow
dec bytes_left+2
@no_borrow
brl post_process ;all done!
@no_nl
rep #%00110001 ;16-bit accum, carry clear
LONGA ON
lda phys_mark
and #$00FF ;this is the physical start position
sta sec_start ;save for later
ldy bytes_left+2
bne @rest_of_sec ;if this isn't 0, we're definitely going to the end of�
;�this sector
clc
adc bytes_left ;find physical end position
cmp #$0100+1 ;does it go past the end of the sector?
bcc @store
@rest_of_sec
lda #$0100
@store
sta sec_end ;the physical end position for this particular sector
sec
sbc sec_start ;now we have the number of bytes to be transfered�
;�from this sector
sta byte_cnt
lsr a ;divide by 2
tax
lda #$0000 ;null sectors contain zeros
bcc @even ;if no remainder, go move by word
sep #%00100000 ;work with bytes
LONGA OFF
sta [user_ptr] ;that takes care of the blasted odd byte!
rep #%00100001 ;16-bit accum, carry clear
LONGA ON
inc user_ptr
bne @no_carry2
inc user_ptr+2
@no_carry2
@even
txa ;this is a word count
dea ;decrement effectively by 2
bmi @done
asl a ;make it a byte value again
tay
lda #$0000
@loop1
sta [user_ptr],y
dey
dey
bpl @loop1
@done
lda byte_cnt
sta recent_xfer ;the whole amount was transferred
brl next_sector
directory_file ;read stuff from the directory file
lda nl_msk ;is newline mode set?
beq dir_no_nl ;if not, just shove the bytes over
@newline
lda bytes_left
sta byte_cnt ;only one word should be non-zero
stz byte_cnt+2
lda mark
clc
adc fcr_buf_ptr
sta src_scratch
lda mark+2
adc fcr_buf_ptr+2
sta src_scratch+2
jsr read_newline
lda recent_xfer
sta bytes_sent
stz bytes_sent+2 ;high word is always 0
bra post_process ;that's all there is to it!
dir_no_nl
lda mark ;get back least significant word of start addr
clc
adc fcr_buf_ptr
tax
lda mark+2
adc fcr_buf_ptr+2
pha ;high word of source pointer
phx ;low word " " "
pei user_ptr+2
pei user_ptr
pea $0000 ;high word of transfer count is always 0
lda bytes_left
pha
pea move_sinc_dinc ;command word
jsl move_info
bcc @ok1
lda #$8000 ;internal generic error
rts
@ok1
lda bytes_left
sta bytes_sent ;the whole amount was transferred
lda bytes_left+2
sta bytes_sent+2
;brl post_process
bra pack_up ; don't strip high bits on directory data ->
post_process sec ; subtract the data_offset from the amount
lda bytes_sent ; actually transferred.
sbc data_offset
sta bytes_sent
lda bytes_sent+2
sbc #0
sta bytes_sent+2
;clear high bit of each byte sent, if specified by mode
ldy #fcr_dos_type
lda [fcr_ptr],y ;get the DOS 3.3 filetype
bne pack_up ;if NOT 0 (TEXT file) don't touch the data
@text
lda text_flag ;this global flag decides the text transfer mode
beq pack_up ;if flag = 0, leave text as-is
;bra pack_up ;shunt-out this code for the time being
lda class
beq @class0
@class1
ldy #read1.data_buffer
lda [param_blk_ptr],y
sta <user_ptr
iny
iny
lda [param_blk_ptr],y
sta <user_ptr+2
bra @clear_bit
@class0
ldy #read0.data_buffer
lda [param_blk_ptr],y
sta <user_ptr
iny
iny
lda [param_blk_ptr],y
sta <user_ptr+2
@clear_bit
ldy bytes_sent+2
iny ;adjust for countdown
ldx bytes_sent
bne @not_0
dey ;account for low part = 0
txa
ora bytes_sent+2 ;are they both zero?
beq @done
@not_0
sty <clear_ctr ;this is the high part of the counter
ldy #0 ;init index
sep #$20
LONGA OFF
@loop
lda [user_ptr],y
and #$7F ;clear that archaic high bit
sta [user_ptr],y ;put it back
iny ;bump ptr
bne @no_carry1
inc <user_ptr+2 ;bump high part of pointer
@no_carry1
dex ;low part of counter
bne @loop
dec <clear_ctr ;high part of counter
bne @loop
@done
rep #$20
LONGA ON
pack_up ;square away all the values for the user param block before�
;�returning
;bytes_sent should reflect the total amount transfered during�
;�the entire call
ldy #fcr_mark
lda [fcr_ptr],y
clc
adc bytes_sent
sta [fcr_ptr],y ;update the file's mark
iny
iny
lda [fcr_ptr],y
adc bytes_sent+2
sta [fcr_ptr],y
store_tcount
lda class
bne @class1
@class0
ldy #read0.transfer_count
bra exit
@class1
ldy #read1.transfer_count
exit
lda bytes_sent
sta [param_blk_ptr],y
lda bytes_sent+2
iny
iny
sta [param_blk_ptr],y
lda error ;there may be a non-fatal error here
cmp #$0001 ;set carry to appropriate sign
rts
error ds 1
ENDP
export check_access
; check_access checks to see if the parameter block is specifying
; an invalid access for the open call. The things that it checks
; for are:
;
; Are we trying to open the resource fork of a file.
; Are we trying to open the file other than read only.
;
; If so it returns with the carry set and the accumulator set to the
; appropriate error. We do this here, instead of in the open_s routine
; because these are non_bouncing errors and we need to check this after
; we have determined that the target is a DOS file.
check_access PROCNAME
lda class ;get the class of the call
beq @done ;if class 0, no resource fork
lda p_cnt
cmp #4 ;enough parameters for request_access?
bcc @done ;no, can't check it
clc
ldy #open1.request_access
lda [param_blk_ptr],y ;get the request access word
and #%0000000000000010 ;check for write access
beq @checkRez ;no write access
lda #invalid_access
bra @access_err
@checkRez
lda p_cnt
cmp #5
bcc @done ; pcount too small ->
clc
ldy #open1.resource_number
lda [<param_blk_ptr],y
beq @done ; ok -> its not the resource fork
cmp #1 ; == to 1
beq @fork_err ; can't open resource fork
lda #parm_range_err ; bad value for ressource_number
bra @access_err
@fork_err
lda #res_not_found
@access_err
sec
@done
rts
ENDP
export open
open PROCNAME ;handle the OPEN call
;brk $49
stz vcr_fcr_flags ;init flags
jsr open_setup ;init vars, set up buffers, put volume name in�
;�volname_buf_ptr, verify that volume is�
;�online, mount the volume
bcc @ok
brl err_exit ;deallocate stuff on the way out
@ok
; we now have a valid vcr for the volume we want
; now see exactly what we're supposed to open
@process_file
;we now have a legal filename and a vcr
;now look for fcrs unless we just created the vcr
;brk $07
lda (file_name_ptr) ;was there a filename?
beq handle_vol
brl handle_file
handle_vol ;this section opens the volume "root" directory file
;first build string of vol name with leading colon
; but first check to see if for some reason the caller
;has specified the resource fork as the target or is trying
;to open this vol readWrite.
jsr check_access
bcc @h_vol ; nope! everythings OK here
brl err_exit ;deallocate stuff on the way out
@h_vol
lda vol_name_buf ;get length
inc a ;account for colon character
sta scratch_str_buf
sep #$20
LONGA OFF
lda #':'
ldy #2
sta scratch_str_buf,y ;place the colon
@loop
lda vol_name_buf,y
cmp #1 ;save zero status in carry
iny ;position one byte over in dest string
sta scratch_str_buf,y
bcs @loop ;if character was non-zero, loop
rep #$20
LONGA ON
ldx #<scratch_str_buf
ldy #^scratch_str_buf
lda #fcr_size
jsl alloc_fcr
bcc @ok
brl err_exit ;can't get FCR
@ok
jsl deref
stx fcr_ptr
sty fcr_ptr+2
lda #$4000
tsb vcr_fcr_flags ;set flag to say we allocated an FCR
;special fcr setup for root directory file
ldy #fcr_fst_id
lda #my_id
sta [fcr_ptr],y
ldy #vcr_id
lda [vcr_ptr],y
ldy #fcr_vcr_id
sta [fcr_ptr],y
ldy #vcr_buf_vp
lda [vcr_ptr],y ;the fcr has the same buffer as the vcr
tax
iny
iny
lda [vcr_ptr],y ;high word
ldy #fcr_buf_vp+2
sta [fcr_ptr],y
txa
dey
dey
sta [fcr_ptr],y
ldy #fcr_mark ;zero the mark
lda #$0000
sta [fcr_ptr],y
iny
iny
sta [fcr_ptr],y
ldy #fcr_eof+2
sta [fcr_ptr],y ;zero high word of eof
ldy #fcr_aux
sta [fcr_ptr],y ;zero aux_type
iny
iny
sta [fcr_ptr],y
ldy #fcr_entry_num
sta [fcr_ptr],y ;zero the entry position for get_dir_entry
ldy #fcr_blks_used+2 ;set blocks used
sta [fcr_ptr],y ;zero high word
dey
dey
sta [fcr_ptr],y ;low word of blocks used
xba ;transform to byte value for EOF
ldy #fcr_eof
sta [fcr_ptr],y ;that's our EOF!
ldy #fcr_type
lda #$000F ;GS/OS directory file
sta [fcr_ptr],y
ldy #fcr_access
lda #access_unlocked
sta [fcr_ptr],y
count_dir ;count entries in the directory
stz dir_cnt
lda #$0000 ;cat sector numbers start at 0
@cat_loop
ldx #$0000 ;same for fde's
@fde_loop
sta cat_index
stx fde_index ;save these
jsr find_fde
bcc @ok
brl err_exit ;error
@ok
*** changed 6-May-93 DAL
;;; lda [fde_ptr] ;first byte tells whether in use
;;; and #$00FF
jsr load_fde_flag
*** 6-May-93
beq @done ;a $00 means beyond last entry
cmp #$00FF
beq @inactive ;inactive, don't count it
inc dir_cnt
@inactive
ldx fde_index
lda cat_index
inx ;next fde
cpx #$0007 ;check limit of 7 fde's per cat sector
bcc @fde_loop ;we're ok, check next fde
ina ;next catalog sector
cmp cat_sec_count ;past our limit?
bcc @cat_loop ;move on to next cat sector
@done
lda dir_cnt
ldy #fcr_tot_ent
sta [fcr_ptr],y ;save this for get_dir_entry
ldy #vcr_open_cnt
lda [vcr_ptr],y
inc a
bne @not_first ;if it was $FFFF, increment it again
inc a
@not_first
sta [vcr_ptr],y
brl stuff_usr_prm_blk
handle_file
;brk $02
; ldy #vcr_open_cnt
; lda [vcr_ptr],y ;see if any files are already open in this vcr
; beq chk_dir ;if no open files, we'll have to look in the directory
;
; inc a ;this field is inited to $FFFF
; beq chk_dir ;check for $FFFF also
;
; jsr search_fcr ;this routine will return number of fcr for previous�
; ;�open of the file in old_fcr (0 means none found)
; bcc req_fcr ;we're ready to get a new fcr
chk_dir ;does this guy exist in the directory?
jsr check_dir ;this sub checks for filename in file_name_ptr
bcc @ok
lda #dir_error ;problem with the directory structure
brl err_exit
@ok
lda fde_index ;if negative, we didn't find the file
bpl req_fcr
file_not_here ;file not on this volume
lda #file_not_found
brl err_exit
;now we have to allocate a new fcr
;but first lets check to see what fork the caller is trying to open
req_fcr jsr check_access ;test for invalid operation
bcc @not_Rez ;if not resource fork
brl err_exit ;cleanup and return the appropriate error.
@not_Rez
;first build string of vol name with leading colon
lda vol_name_buf ;get length
inc a ;account for colon character
sta scratch_str_buf
sep #$20
LONGA OFF
lda #':'
ldy #2
sta scratch_str_buf,y ;place the colon
@loop
lda vol_name_buf,y
cmp #1 ;save zero status in carry
iny ;position one byte over in dest string
sta scratch_str_buf,y
bcs @loop ;if character was non-zero, loop
rep #$20
LONGA ON
ldx #<scratch_str_buf ;alloc_fcr now gets volume name
ldy #^scratch_str_buf
lda #fcr_size
jsl alloc_fcr
bcc @ok1
lda #out_of_mem ;can't get an fcr
brl err_exit
@ok1 ;SCM has successfully allocated the FCR
jsl deref
stx <fcr_ptr
sty <fcr_ptr+2
lda #$4000
tsb vcr_fcr_flags ;set flag to say we allocated an FCR
lda #file_buf_size ;request segments for file buffers
jsl alloc_seg
bcc @ok2
;can't get needed buffers, cancel the OPEN
lda #out_of_mem
brl err_exit
@ok2
tya ;stash the vp's in the fcr
ldy #fcr_buf_vp+2
sta [fcr_ptr],y
txa
dey
dey
sta [fcr_ptr],y
ldy #vcr_open_cnt ;bump the open count in the vcr
lda [vcr_ptr],y
inc a
bne @not_first ;this field is inited to $FFFF
inc a
@not_first
sta [vcr_ptr],y
reset_mark
lda #$0000
ldy #fcr_mark ;offset to mark in fcr
sta [fcr_ptr],y
iny
iny
sta [fcr_ptr],y
; lda old_fcr ;what was the result of the fcr dup search?
; beq fill_new_fcr ;first open for this file
; brl copy_fcr ;let's not reinvent this wheel
fill_new_fcr
ldy #fcr_fde_num ;index to fde number of the file
lda fde_index
sta [fcr_ptr],y
ldy #fcr_cat_sec ;sector where file's directory entry lives
lda cat_index
sta [fcr_ptr],y
ldy #fcr_fst_id ;FST ID number
lda #my_id
sta [fcr_ptr],y
ldy #vcr_id ;link to VCR
lda [vcr_ptr],y
ldy #fcr_vcr_id
sta [fcr_ptr],y
ldy #fde_type ;DOS 3.3 filetype
lda [fde_ptr],y
and #$007F ;mask high byte & lock bit
ldy #fcr_dos_type
sta [fcr_ptr],y
bra get_info ;now get info for param block
copy_fcr ;copy appropriate stuff from old to new fcr
;brk $31
stz copy_fcr_ctr
ldy #0 ;init index
@big_loop
lda copy_fcr_ctr ;get counter of number of bitmapped words
lsr a
tax
lda copy_tbl,x ;get bitmapped word
sta copy_fcr_temp
ldx #16 ;init counter
@loop
asl copy_fcr_temp
bcc @no_copy
lda [old_fcr_ptr],y
sta [fcr_ptr],y
@no_copy
iny
iny ;bump index by a word
dex ;decrement counter
bne @loop ;repeat for each bit
lda copy_fcr_ctr
bne @done ;if not 0, we're done (loop executes twice)
inc copy_fcr_ctr
bra @big_loop
@done
bra stuff_usr_prm_blk
get_info ;call get_file_info
;brk $01
ldy #fcr_buf_vp ;get the vp to the fcr's buffer
lda [fcr_ptr],y
tax
iny
iny
lda [fcr_ptr],y
tay
jsl deref
stx <drvr_buf_ptr
stx <fcr_buf_ptr
sty <drvr_buf_ptr+2
sty <fcr_buf_ptr+2
;hopefully, the device number will be left over from
;the "volume" routine
lda #drvr_read
sta <drvr_call_num
ldy #fcr_fde_num
lda [fcr_ptr],y ;need these to set up ptr to fde
tax
ldy #fcr_cat_sec
lda [fcr_ptr],y
jsr find_fde ;this sets up fde_ptr
jsr get_file_info ;get all the good stuff
bcc @ok1
brl err_exit
@ok1 ;now copy from globals to the fcr
ldy #fcr_data_sect
lda last_data_sec
sta [fcr_ptr],y ;make note of number of sector in buffer
ldy #fcr_access
lda access
sta [fcr_ptr],y
ldy #fcr_eof
lda eof
sta [fcr_ptr],y
lda eof+2
iny
iny
sta [fcr_ptr],y
ldy #fcr_aux
lda aux_type
sta [fcr_ptr],y
ldy #fcr_type
lda os_type
sta [fcr_ptr],y
ldy #fcr_blks_used
lda blks_used
sta [fcr_ptr],y
lda #$0000
iny
iny
sta [fcr_ptr],y ;zero long word of blks_used
stuff_usr_prm_blk ;fill everything the user asked for
;brk $44
lda class ;get the class of the call
bne @class1
brl @class0
@class1
lda p_cnt
tax ;keep countdown in x
ldy #$0000-2 ;init Y for index to table in loop
@cpy_loop
iny
iny ;move to next word entry
lda fcr_to_pblk,y ;get the table entry
asl a ;test 2 highest bits
bcc @no_dec ;should we decrement pcount?
dex
bmi @special ;if we hit bottom, go on to the special cases
@no_dec
asl a ;test next bit
bcs @cpy_loop ;if original bit 14 was set, don't copy
lsr a
lsr a ;shift things back into place
phy ;save the present index
tay
lda [fcr_ptr],y
ply
sta [param_blk_ptr],y
bra @cpy_loop
@special ;handle special case parameters
ldx p_cnt
cpx #8 ;do we need aux_type?
bcc @done
ldy #open1.aux_type+2
lda #$0000
sta [param_blk_ptr],y ;zero the high word of aux_type
cpx #9 ;need storage type?
bcc @done
ldy #fcr_type
lda [fcr_ptr],y
cmp #$000F ;root directory?
beq @store_s_type ;filetype happens to equal storage type
lda #storage_type ;for regular files
@store_s_type
ldy #open1.storage_type
sta [param_blk_ptr],y
cpx #10
bcc @done
lda #$0000
ldy #open1.create_date_time
sta [param_blk_ptr],y
iny
iny
sta [param_blk_ptr],y
iny
iny
sta [param_blk_ptr],y
iny
iny
sta [param_blk_ptr],y
cpx #11
bcc @done
ldy #open1.mod_date_time
sta [param_blk_ptr],y
iny
iny
sta [param_blk_ptr],y
iny
iny
sta [param_blk_ptr],y
iny
iny
sta [param_blk_ptr],y
cpx #12
bcc @done
ldy #open1.option_list
jsr do_option_list ;this routine needs offset in param blk in Y
bcc @done
lda #buff_too_small
bra err_exit
@class0
ldy #fcr_ref_num
lda [fcr_ptr],y ;get ref num from FCR
ldy #open0.ref_num
sta [param_blk_ptr],y ;set reference number
@done
; Added zeroing of res eof
; -- Monte Benaresh 2/19/92 for 6.0.1
lda p_cnt ; res eof included?
cmp #$0E
bcc @d1 ; no ->
ldy #open1.resource_eof
lda #0
sta [<param_blk_ptr],y
iny
iny
sta [<param_blk_ptr],y
lda p_cnt ; res blocks included?
cmp #$0f
bcc @d1 ; no ->
ldy #open1.resource_blocks
lda #0
sta [<param_blk_ptr],y
iny
iny
sta [<param_blk_ptr],y
@d1 clc
lda #$0000
rts ;all done with open!
err_exit ;if an error occurs, return through here
;brk $91
pha ;save error code
bit vcr_fcr_flags ;that's the first time I've used this instruction!
php ;save result for VCR test below
bvc @no_fcr
@cancel_fcr
ldy #fcr_type
lda [fcr_ptr],y
cmp #$000F ;is it a directory?
beq @not_dir ;if directory file then don't release its buffer�
;�because it is shared with the VCR
ldy #fcr_buf_vp
lda [fcr_ptr],y ;get fcr buffer's vp so we can release it
tax
iny
iny
ora [fcr_ptr],y ;check for 0 vp
beq @invalid_vp
lda [fcr_ptr],y
tay
jsl release_seg
@not_dir
@invalid_vp
ldy #fcr_ref_num
lda [fcr_ptr],y ;get ref num
jsl release_fcr ;get rid of the FCR
ldy #vcr_open_cnt
lda [vcr_ptr],y
beq @open_cnt_ok ;if open count is 0, leave it alone
inc a ;check for $FFFF
beq @store_open_cnt ;change $FFFF to 0
@dec_open_cnt
dec a ;reverse inc
dec a ;now decrement
@store_open_cnt
sta [vcr_ptr],y ;Y reg should point to vcr_open_cnt
@open_cnt_ok
@no_fcr
plp ;restore result of bit instruction above
bpl @no_vcr ;high bit tells whether VCR was allocated
@cancel_vcr
ldy #vcr_open_cnt
lda #$0000
sta [vcr_ptr],y ;must zero the open count to release it!
ldy #vcr_id
lda [vcr_ptr],y ;get ID of the VCR
jsl release_vcr ;this invokes sys_remove_vol call to this fst
@no_vcr
pla ;restore error code
sec
rts
copy_tbl ;defines which words get copied into the new fcr for a multiply-opened
;file. A 0 entry indicates that the word does NOT get copied.
;dc.w %0001100000100111
; |
; first word in fcr
; last word in fcr
; |
;dc.w %0011111100000000
copy equ $1
no_copy equ 0
; ***** NOTE ***** The presence of comments after the line continuation *****
; character (\) is NOT officially supported by the MAX
; assembler but works fine for version 1.1d9.
; If errors are generated for these lines by a later
; version of the assembler, remove all text between the
; **************** '\' and the carriage return. | *****
; |
; |
; bit position field |
; ------------ ----- |
dc.w 0 ++ \ �
no_copy << 0 ++ \ fcr_ref_num
no_copy << 1 ++ \ fcr_path_name
no_copy << 2 ++ \ fcr_path_name (h)
copy << 3 ++ \ fcr_fst_id
copy << 4 ++ \ fcr_vcr_id (h)
no_copy << 5 ++ \ fcr_level
no_copy << 6 ++ \ fcr_newline
no_copy << 7 ++ \ fcr_newline (h)
no_copy << 8 ++ \ fcr_newline_len
no_copy << 9 ++ \ fcr_mask
copy << 10 ++ \ fcr_access
no_copy << 11 ++ \ fcr_buf_vp
no_copy << 12 ++ \ fcr_buf_vp (h)
copy << 13 ++ \ fcr_eof
copy << 14 ++ \ fcr_eof (h)
copy << 15 ; fcr_aux
dc.w 0 ++ \
no_copy << 0 ++ \ fcr_mark
no_copy << 1 ++ \ fcr_mark (h)
copy << 2 ++ \ fcr_type
copy << 3 ++ \ fcr_dos_type
copy << 4 ++ \ fcr_blks_used
copy << 5 ++ \ fcr_blks_used (h)
copy << 6 ++ \ fcr_cat_sec
copy << 7 ++ \ fcr_fde_num
no_copy << 8 ; fcr_data_sec
; These words were derived from this table:
;@copy equ $1
;@no_copy equ 0
; dc.w @no_copy ;fcr_ref_num
; dc.l @no_copy ;fcr_path_name
; dc.w @copy ;fcr_fst_id
; dc.w @copy ;fcr_vcr_id
; dc.w @no_copy ;fcr_level
; dc.l @no_copy ;fcr_newline
; dc.w @no_copy ;fcr_newline_len
; dc.w @no_copy ;fcr_mask
; dc.w @copy ;fcr_access
; ;fst-specific:
; dc.l @no_copy ;fcr_buf_vp
; dc.l @copy ;fcr_eof
; dc.w @copy ;fcr_aux
; dc.l @no_copy ;fcr_mark
; dc.w @copy ;fcr_type
; dc.w @copy ;fcr_dos_type
; dc.l @copy ;fcr_blks_used
; dc.w @copy ;fcr_cat_sec
; dc.w @copy ;fcr_fde_num
; dc.w @no_copy ;fcr_data_sec
fcr_to_pblk ;table to specify what data to copy from the fcr to the user
;parameter block. each position corresponds to the same position
;in the user parameter block.
;each word entry contains:
; bit 15 = decrement pcount--high for the 1st word in each parameter
; bit 14 = copy inhibit--if high, nothing gets copied for this word position
@no_copy equ %0100000000000000
@dec equ %1000000000000000
; dc.w @no_copy ++ @dec ;pcount
dc.w @no_copy ;pcount <CAF> removed the @dec because pcount is not an actual parm.
dc.w fcr_ref_num ++ @dec ;ref_num
dc.w @no_copy ++ @dec ;pathname (low)
dc.w @no_copy ;pathname (high)
dc.w @no_copy ++ @dec ;request access
dc.w @no_copy ++ @dec ;resource number
dc.w fcr_access ++ @dec ;access
dc.w fcr_type ++ @dec ;file_type
dc.w fcr_aux ++ @dec ;aux_type (low)
dc.w @no_copy ;aux_type (high)
dc.w @no_copy ++ @dec ;storage_type
dc.w @no_copy ++ @dec ;create_date_time (0)
dc.w @no_copy ;create_date_time (1)
dc.w @no_copy ;create_date_time (2)
dc.w @no_copy ;create_date_time (3)
dc.w @no_copy ++ @dec ;mod_date_time (0)
dc.w @no_copy ;mod_date_time (1)
dc.w @no_copy ;mod_date_time (2)
dc.w @no_copy ;mod_date_time (3)
dc.w @no_copy ++ @dec ;option_list (low)
dc.w @no_copy ;option_list (high)
dc.w fcr_eof ++ @dec ;eof (low)
dc.w fcr_eof+2 ;eof (high)
dc.w fcr_blks_used ++ @dec ;blocks_used (low)
dc.w @no_copy+2 ;blocks_used (high)
dc.w @no_copy ++ @dec ;resource_eof (low)
;no entry resource_eof (high)
dc.w @no_copy ++ @dec ;resource_blocks (low)
;no entry resource_blocks (high)
*** added 7-Nov-92 DAL to fix run-off-the-end-of-the-table problem
dc.w @no_copy++@dec
*** end 7-Nov-92
copy_fcr_temp ds.w 1
copy_fcr_ctr ds.w 1
;vcr_fcr_flags ds.w 1 ;bit 15 = VCR allocated during this call
;bit 14 = FCR allocated during this call
;these flags are read if an error occurs during the OPEN call�
;�so that any allocated structures may be released on abort
ENDP
END