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
518 KiB
Plaintext

print push
print off
string asis
include 'common.equ.src'
;------------------------------------------
;
; Equates from procedure
;
;------------------------------------------
CallStringToText equ 1 ;conditional code for JudgeName, 14-Apr-91 DAL
AllowLCPeriods equ 0 ;conditional code allowing blanks in names, 7-Aug-91 DAL
;(0 = no blanks allowed, 12-Aug-91 GAB)
debug_on equ 00 ;00 = debug off, <>0 = debug on
word equ 02 ;two bytes for a word
blk_size equ 512 ;standard size of a prodos block
delimiter equ $3A ;pathname delimiter = ":"
min_send_cnt equ $21 ;If read < 33 bytes use local routine
vcr_main equ vcr_dev+2
vol_reserved equ $0000 ;8 bytes of reserved field from disk
vol_mod equ vol_reserved+2 ;volume mod date
vol_create equ vol_reserved+8 ;create date/time from header
vol_version equ vol_create+4 ;version of OS that created file
vol_min_version equ vol_version+1 ;minimum version of OS that can access
vol_access equ vol_min_version+1 ;access for a file
vol_entry_len equ vol_access+1 ;entry length of each entry
vol_entries equ vol_entry_len+1 ;number of entries per block
vol_file_count equ vol_entries+1 ;number of files in volume directory
vol_bitmap_addr equ vol_file_count+2 ;starting block of bitmap
vol_total_blks equ vol_bitmap_addr+2 ;total number of blocks on disk
num_bitmap_blks equ vol_total_blks+2 ;total number of bitmap blocks
end_bitmap equ num_bitmap_blks+2 ;1st byte that starts all zeros
avail_bitmap equ end_bitmap+2 ;1st bitmap block(rel) with free space
curr_bitmap equ avail_bitmap+2 ;current bitmap(rel) block in memory
bitmap_dirty equ curr_bitmap+2 ;0 = bitmap clean, <> 0 = bitmap dirty
free_blocks equ bitmap_dirty+2 ;Free blocks on volume,$FFFF = unknown
vol_damaged equ free_blocks+2 ;stops writing to bad disk if <> 00
dirty_file_cnt equ vol_damaged+2 ;number of files that are open for writing and are dirty
bitmap_start equ dirty_file_cnt+4+vcr_main
pro_vcr_size equ bitmap_start+$200
bitmap_mod equ $FFFF
bitmap_unknown equ $FFFF
vcr_entry_length equ $000F
fcr_res_num equ $0000 ;resource number of this file
fcr_storage equ fcr_res_num+2 ;real storage type on disk
fcr_entry_type equ fcr_storage+2 ;storage type from directory entry
fcr_file_id equ fcr_entry_type+2 ;used to ID duplicate files
fcr_file_type equ fcr_file_id+2 ;file type
fcr_key_blk equ fcr_file_Type+1 ;key block of the file
fcr_blks_used equ fcr_key_blk+2 ;number of blocks used
fcr_eof equ fcr_blks_used+2 ;current end of file
fcr_create equ fcr_eof+3 ;create date/time ProDOS
fcr_vers equ fcr_create+4 ;version of o/s that made file
fcr_min_vers equ fcr_vers+1 ;min version of o/s allowed
fcr_disk_acc equ fcr_min_vers+1 ;file access
fcr_aux_type equ fcr_disk_acc+1 ;aux type for this file
fcr_modified equ fcr_aux_type+2 ;modified date of file
fcr_header_ptr equ fcr_modified+4 ;key block of dir owner
fcr_status equ fcr_header_ptr+2 ;offset: current status of file
gde_entry_num equ fcr_status+2 ;current entry for Get_Dir_Entry
gde_offset equ gde_entry_num+2 ;current offset to entry
gde_curr_blk equ gde_offset+2 ;current directory block loaded
gde_tot_files equ gde_curr_blk+2 ;total files in directory
fcr_curr_mark equ gde_tot_files+2 ;current file mark
fcr_blks_alloc equ fcr_curr_mark+4 ;current number of blks alloc
fcr_curr_eof equ fcr_blks_alloc+2 ;current end of file
fcr_parent equ fcr_curr_eof+4 ;block number of directory block
fcr_entry_offs equ fcr_parent+2 ;index to entry within parent block
data_blk_num equ fcr_entry_offs+2 ;block number of current data block
index_blk_num equ data_blk_num+2 ;block number of current index block
master_blk_num equ index_blk_num+2 ;block number of master index block
clr_global_clean equ $7FFF ;clear the global clean bit
set_global_clean equ $8000 ;set the file clean in the FCR
set_access_res equ $4000 ;set the resource fork bit in the FCR
ClrAllButAccess equ $3FFF ;clear the resource bit from access word.
fcr_fst_ptr equ fcr_access+2
fcr_io_start equ master_blk_num+2+fcr_fst_ptr
fcr_data_size equ fcr_io_start+$200
fcr_index_size equ fcr_io_start+$400
fcr_master_size equ fcr_io_start+$600
my_pblk_ptr equ fst_start
my_vcr_ptr equ my_pblk_ptr+4
prodos_vcr_ptr equ my_vcr_ptr+4
my_fcr_ptr equ prodos_vcr_ptr+4
pro_fcr_ptr equ my_fcr_ptr+4
gbuf_ptr equ pro_fcr_ptr+4
temp_ptr equ gbuf_ptr+4
temp2_ptr equ temp_ptr+4
temp3_ptr equ temp2_ptr+4
temp4_ptr equ temp3_ptr+4
temp5_ptr equ temp4_ptr+4
math_temp equ temp5_ptr+4
data_ptr equ math_temp+4
index_ptr equ data_ptr+4
master_ptr equ index_ptr+4
bitmap equ master_ptr+4
users_buf_ptr equ bitmap+4
sparse_ptr equ users_buf_ptr+4
newline_ptr equ sparse_ptr+4
pro_id equ $0001 ;identifies prodos fst
prodos_version equ $0005 ;current version of the PRODOS.
path1_mask equ $4000 ;used to determine if path1 is avail.
path2_mask equ $0040 ;used to determine if path2 is avail.
carry equ $01 ;used to set the carry
overflow equ $40 ;used to set the overflow flag
in_cache equ $8002
user_cache_val equ $0001
class_mask equ $E000
disk_protect equ $0004 ;bit to see if disk is protected
max_base equ $0003
pcount1 equ 1
pcount2 equ 2
pcount3 equ 3
pcount4 equ 4
pcount5 equ 5
pcount6 equ 6
pcount7 equ 7
pcount8 equ 8
pcount9 equ 9
write_call equ $13
read_enabled equ $0001 ;1=Read Enabled
write_enabled equ $0002 ;1=Write Enabled
backup_enabled equ $0020 ;1=Needs to be backed up
rename_enabled equ $0040 ;1=Rename allowed
destroy_enabled equ $0080 ;1=Destroy is enabled
read_access equ $01
write_access equ $02
invis_bit equ $04
read_write_acc equ $03
seedling_type equ $0010 ;$0000000 < EOF < $0000201
sapling_type equ $0020 ;$0000200 < EOF < $0020001
tree_type equ $0030 ;$0020000 < EOF < $1000000
pascal_area equ $0040 ;Area set aside for Pascal
resource_type equ $0050 ;Special type for resource files
purge_dir equ $0060 ;directory that contains pugred files
subdir_type equ $00D0 ;Subdirectory
subdir_header equ $00E0 ;First entry in sudbirectory
volume_header equ $00F0 ;First entry in the volume directory
vol_dir_start equ $0002 ;start of the volume directory
bitmap_strt_blk equ $0006 ;start of the volume bitmap
entry_length equ $0027
entries_per_block equ $000D
parent_index equ $0027
par_entry_num equ $0029
res_header equ 08 ;number of bytes in a header
res_map_size equ 382 ;size of map for info call
purge_index equ $0019 ;index to flag indicating purge files.
purgable_index equ $0029 ;index to total blocks
purge_flag equ $00FE
purge_verify equ $0004
header_file_cnt equ $0025 ;number of files in a directory
backup_on equ $20
backup_off equ $DF
file_type_index equ $10 ;look at page 168 (ProDOS 8 manual)
key_blk_index equ $11 ;index into disk entry
blks_used_index equ $13
eof_index equ $15
create_index equ $18
version_index equ $1C
min_version equ $1D
access_index equ $1E
aux_type_index equ $1F
last_mod_index equ $21
header_ptr_index equ $25
no_root equ $8000 ;indicates root level name is invalid
no_dup equ $4000 ;indicates duplicates are not valid
root_only equ $2000 ;allow only the root level in pathname
vol_mod_offset equ $16 ;offset to the volume mod date field
vol_acc_offset equ $22 ;offset to the volume access field
dir_file_type equ $0F
tool_locator equ $E10000
misc_tools equ $0003
read_time_hex equ $000D ;read the clock via misc_tools
set_time_stamp equ $0001
get_time_stamp equ set_time_stamp+$8000
set_upper_case equ $0002
get_upper_case equ set_upper_case+$8000
debugger_cmnd equ $0002
debugger_status equ debugger_cmnd+$8000
file_dirty equ $8000 ;ora masks to active field
data_active equ $4000
index_active equ $2000
master_active equ $1000
file_clean equ $7FFF ;and masks to deactivate field
data_inactive equ $BFFB
index_inactive equ $DFFD
master_inactive equ $EFFE
dirEntry_dirty equ $0008
dirEntry_clean equ ÅdirEntry_dirty ;'Å' is the assembler's one's complement operator
data_dirty equ $0004
data_clean equ $FFFB
index_dirty equ $0002
index_clean equ $FFFD
master_dirty equ $0001
master_clean equ $FFFE
hfs_id equ $0006 ;HFS FST ID
ashare_id equ $000D ;AppleShare FST ID
finfo_type equ 1 ;HFS FInfo entry type
xfinfo_type equ 2 ;HFS xFInfo entry type
;------------------------------------------
;
; Equates from procedure data
;
;------------------------------------------
drvr_bytes equ $1A ;start at byte $1A and $1B
path_used equ $0100
vcr_used equ $0200
fcr_used equ $0400
io_used equ $0800
clr_io_used equ $F7FF
flush_1st equ $1000
damage equ $2000
max_class1 equ $0020
max_class2 equ $0040
max_class3 equ $0060
max_class4 equ $0080
max_class5 equ $00A0
max_class6 equ $00C0
max_class7 equ $00E0
max_pcnt1 equ 1+1
max_pcnt2 equ 2+1
max_pcnt3 equ 3+1
max_pcnt4 equ 4+1
max_pcnt5 equ 5+1
max_pcnt6 equ 6+1
max_pcnt7 equ 7+1
max_pcnt8 equ 8+1
max_pcnt9 equ 9+1
max_pcnt10 equ 10+1
max_pcnt11 equ 11+1
max_pcnt12 equ 12+1
max_pcnt13 equ 13+1
max_pcnt14 equ 14+1
max_pcnt15 equ 15+1
max_pcnt16 equ 16+1
max_pcnt17 equ 17+1
do_dirty equ $4000
;-----------------------------------------------
;
; Forward addresses and entries
;
;-----------------------------------------------
ENTRY abs_to_rel
ENTRY activate_vcr
ENTRY add_free_blk
ENTRY add_index
ENTRY appl_call
ENTRY bitmap_changed
ENTRY bitmap_corrupt
ENTRY build_path
ENTRY build_the_fcr
ENTRY build_vcr
ENTRY calc_curr_mark
ENTRY change_path
ENTRY check_base
ENTRY check_dup
ENTRY check_vcr_first
ENTRY chk_allocation
ENTRY chk_vol_syntax
ENTRY clear_backup
ENTRY clear_index
ENTRY close
ENTRY clr_fcr_status
ENTRY clr_to_boundary
ENTRY cmd_error
ENTRY cmd_tbl
ENTRY create
ENTRY damaged_message
IF debug_on<>0 THEN
ENTRY debugger
ENDIF
ENTRY dec_blk_count
ENTRY deferred_flush
ENTRY destroy
ENTRY dev_with_mount
ENTRY device_call
ENTRY dow_convert
ENTRY end_read_write
ENTRY entry_to_gstr
ENTRY eof_to_blks
ENTRY erase_disk
ENTRY fast_read
ENTRY fill_io_buf
ENTRY find_file
ENTRY find_resource
ENTRY find_volume
ENTRY flush
ENTRY flush_blocks
ENTRY flush_data_blk
ENTRY flush_file
ENTRY flush_index
ENTRY force_bitmap
ENTRY format_disk
ENTRY fst_specific
ENTRY get_bit_block
ENTRY get_data_num
ENTRY get_dev_num
ENTRY get_dir_entry
ENTRY get_eof
ENTRY get_file_info
ENTRY get_index_blk
ENTRY get_mark
ENTRY get_next_fcr
ENTRY get_time
ENTRY get_write_blk
ENTRY id_disk
ENTRY issue_mount
ENTRY judge_name
ENTRY kill_bitmap
ENTRY load_data
ENTRY load_index
ENTRY make_entry_num
ENTRY mount_volume
ENTRY move_dir_entry
ENTRY move_vol_entry
ENTRY num_seq_blks
ENTRY open
ENTRY range_error
ENTRY read
ENTRY read_block
ENTRY read_with_cache
ENTRY reset_vcr
ENTRY sapling_seed
ENTRY save_curr_mark
ENTRY send_partial
ENTRY send_sparse_blk
ENTRY send_time
ENTRY set_eof
ENTRY set_fcr_status
ENTRY set_file_info
ENTRY set_mark
ENTRY set_user_cache
ENTRY setup_bitmap_ptr
ENTRY setup_curr_eof
ENTRY setup_curr_mark
ENTRY setup_io_buf
ENTRY setup_io_ptrs
ENTRY setup_my_fcr
ENTRY setup_my_vcr
ENTRY setup_name
ENTRY setup_vol_mesg
ENTRY show_damage
ENTRY shutdown
ENTRY slug_ripple
ENTRY standard_req
ENTRY startup
ENTRY sub_free_blk
ENTRY sys_remove_vol
ENTRY sys_tbl
ENTRY system_call
ENTRY tree_sapling
ENTRY update_key_blk
ENTRY update_mark_eof
ENTRY vol_to_buffer
ENTRY volume
ENTRY write
ENTRY write_block
ENTRY Chk_Dirty_Flag
ENTRY dirty_flags
print pop
;======================================================================
;
;ProDOS File System Translator for GS/OS
;
;Created: Feb 25, 1987
;Modified: Mar 16, 1988
;Author: Rob Turner
;
;
;Copyright Apple Computer Inc. 1987-92 ALL RIGHTS RESERVED
;
;======================================================================
PRINT GEN
msb off
longa on
longi on
;equates for the read call.
pblk_12_ref equ $0000 ;index to the reference number
pblk_12_buf equ pblk_12_ref+2 ;index to user's buffer pointer
pblk_12_req equ pblk_12_buf+4 ;index to request count
pblk_12_tran equ pblk_12_req+4 ;index to transfer count
pblk_12_cache equ pblk_12_tran+4 ;index to user provided cache priority
max_read_pcnt equ $0005
;equates used by open
pblk_10_access equ $000A
pblk_10_ftype equ pblk_10_access+2
pblk_10_aux equ pblk_10_ftype+2
pblk_10_storage equ pblk_10_aux+4
pblk_10_eof equ pblk_10_storage+2
pblk_10_num_res equ pblk_10_eof+4
pblk_10_create equ pblk_10_num_res+2
pblk_10_mod equ pblk_10_create+8
pblk_10_blks equ pblk_10_mod+8
pblk_10_map equ pblk_10_blks+4
;equates used by set_mark
pblk_16_base equ $0002
pblk_16_disp equ pblk_16_base+2
;equates used by get_mark
pblk_17_pos equ $0002
;equates used by get_eof
pblk_19_eof equ $0002
;equates used by send_info
pblk_s_access equ $0000
pblk_s_ftype equ pblk_s_access+2
pblk_s_aux equ pblk_s_ftype+2
pblk_s_storage equ pblk_s_aux+4
pblk_s_create equ pblk_s_storage+2
pblk_s_mod equ pblk_s_create+8
pblk_s_opt equ pblk_s_mod+8
pblk_s_eof equ pblk_s_opt+4
pblk_s_blks equ pblk_s_eof+4
pblk_s_res_eof equ pblk_s_blks+4
pblk_s_res_blk equ pblk_s_res_eof+4
;equates used by volume
pblk_08_vol equ $0004
pblk_08_tot equ pblk_08_vol+4
pblk_08_free equ pblk_08_tot+4
pblk_08_id equ pblk_08_free+4
pblk_08_size equ pblk_08_id+2
pblk_08_purge equ pblk_08_size+2
;equates used by set_file_info
pblk_05_accs equ $0004
pblk_05_ftype equ pblk_05_accs+2
pblk_05_aux equ pblk_05_ftype+2
pblk_05_create equ pblk_05_aux+6
pblk_05_mod equ pblk_05_create+8
pblk_05_opt equ pblk_05_mod+8
;equates used by get_file_info
pblk_06_map equ $0028
;equates used by set_eof
pblk_18_odisp equ $0002
pblk_18_base equ $0002
pblk_18_disp equ pblk_18_base+2
;equates used by create
pblk_01_accs equ $0004
pblk_01_ftype equ pblk_01_accs+2
pblk_01_aux equ pblk_01_ftype+2
pblk_01_stype equ pblk_01_aux+4
pblk_01_eof1 equ pblk_01_stype+2
pblk_01_eof2 equ pblk_01_eof1+4
pblk_01_old_time equ $000E
;equates used by get dir entry
pblk_1c_extend equ $0002
pblk_1c_disp equ $0002
pblk_1c_name equ pblk_1c_disp+2
pblk_1c_entry equ pblk_1c_name+4
pblk_1c_ftype equ pblk_1c_entry+2
pblk_1c_eof equ pblk_1c_ftype+2
pblk_1c_blks equ pblk_1c_eof+4
pblk_1c_create equ pblk_1c_blks+4
pblk_1c_mod equ pblk_1c_create+8
pblk_1c_access equ pblk_1c_mod+8
pblk_1c_aux equ pblk_1c_access+2
pblk_1c_id equ pblk_1c_aux+4
pblk_1c_opt equ pblk_1c_id+2
pblk_1c_res_eof equ pblk_1c_opt+4
pblk_1c_res_blks equ pblk_1c_res_eof+4
gde_max_base equ $0002
bad_file_format equ $004a
;equates used by judge_name 1/22/91 CAE
pblk_07_ntype equ $0002
pblk_07_syntax equ pblk_07_ntype+2
pblk_07_max_len equ pblk_07_syntax+4
pblk_07_name equ pblk_07_max_len+2
pblk_07_nflags equ pblk_07_name+4
;equates used by change_path 5-Dec-91 MSD
pblk_04_src equ $0000 ; (long) source pathname
pblk_04_dest equ $0004 ; (long) destination pathname
pblk_04_flags equ $0008 ; (word) $8000 = assume source is duplicate volume
; Revisions have been moved to the file "pro.fst.revisions" in the ProDOS project
; by Monte Benaresh on 6/21/90
;======================================================================
;Below is the FST header definition used by GS/OS at boot time
;======================================================================
EXPORT fst_header
fst_header PROC
EXPORT code_start
code_start
DC.B 'FST ' ;Says I'm a FST
DC.L appl_call ;Process a normal application call
DC.L system_call ;Process a FST system call
DC.W Pro_ID ;My ID number (ProDOS)
Export fst_attr
fst_attr DC.W $3003 ;(Uppercase pathnames if enabled), formattable, strip high bit.
DC.W $0402 ;Version 4.02 final
DC.W 512 ;block size
DC.L $10000 ;Max Blocks (32 meg)
DC.L $0007 ;Min Blocks
DC.L $01000000 ;max file size
DC.L $0000 ;reserved
DC.B 6
DC.B 'ProDOS'
string pascal
IF debug_on<>0 THEN
DC.B 'ProDOS FST w/Debugger v04.02'
ELSE
DC.B 'ProDOS FST v04.02'
ENDIF
DC.W 0000 ;reserved field
dc.b 'ProDOS FST written by Rob Turner. V4.02'
string asis
ENDP
;======================================================================
;Below is the main entry point for ALL standard operating system calls.
;
;Input: X = Call number * 2
; Y = Class number * 2
; A = undefined
longa on
longi on
EXPORT appl_call
appl_call PROC
import max_call
import write_occurred
import write_dev_num
import cp_device_flag
import fake_name_str
import error_priority
phk ;setup our bank register
plb
rep #$30
cpx #max_call+1 ;check command number
bcs cmd_error
jmp (cmd_tbl-2,x) ;call the function
EXPORT cmd_error
cmd_error
lda #bad_system_call
EXPORT error_exit
error_exit
sec
EXPORT main_exit
main_exit
IMPORT flags
; If we're reporting an error, then check cp_device_flag to see if we were
; renaming a duplicate volume. If so, we built a vcr with a fake name. Since we
; got an error at some point in the process, we need to find the vcr with the fake
; name and release it if it still exists. 1/22/91 CAE
pha ;save the error code
php ;save error flag
bcc @cont ;branch if no error
lda cp_device_flag ;did we build a vcr with a fake name?
bpl @cont ;no - so no need to clean up
ldx #fake_name_str ;get pointer to fake name string
ldy #^fake_name_str
lda #0000 ;search by name
jsl find_vcr
bcs @cont ;didn't find the vcr so we're ok
jsl deref ;dereference vcr pointer
stx <temp_ptr
sty <temp_ptr+2
lda [temp_ptr] ;get vcr id
jsl release_vcr ;release the vcr
@cont
jsl unlock_mem ;unlock the used handles
plp
php
bcc no_damage
lda 2,s ;get access to the error code
and #$00FF ;clear the garbage from the high byte please
cmp #drvr_bad_block ;the disk is probably bad
beq mark_damaged ;mark the volume as damaged
cmp #damaged_bitmap
beq mark_damaged
cmp #dir_error
bne no_damage ;the disk is still ok
mark_damaged
lda prodos_vcr_ptr ;make sure there is a vcr
ora prodos_vcr_ptr+2
beq no_damage
ldy #vol_damaged ;mark the VCR please
lda [prodos_vcr_ptr],y
inc a ;see if we have already told the user about the damage
beq no_damage ;we have told the user so skip the message
lda #$FFFF
sta [prodos_vcr_ptr],y
lda 1,s ;tell the user about the problem
jsr show_damage
no_damage
;
;Now we need to set the Global Clean Flag if we need to
;
lda pro_fcr_ptr ;make sure there is a fcr
ora pro_fcr_ptr+2
beq @no_fcr ;don't worry about it
lda flags ;There is a FCR now make sure it was used during the call.
and #fcr_used
beq @no_fcr ;no FCR is used by the call
ldy #fcr_status ;get the status out of the File Control Record
lda [pro_fcr_ptr],y
;
;If my bit (15) is set then the file is dirty so we must clear the Global_Clean Bit.
;
and #file_dirty ;clear all but the file dirty bit 15
eor #set_global_clean
sta math_temp
ldy #fcr_access
lda [my_fcr_ptr],y
and #clr_global_clean
ora math_temp
sta [my_fcr_ptr],y
;
;Now we need to see if we should check if the file has become dirty during the call
;
lda chk_dirty_flag
beq @no_fcr ;This call does not require processing
;
;Now lets see if the file was dirty at the beginning of the call. If it was then just
;exit. Otherwise update the dirty_file count in the VCR
;
lda dirty_flags
bne @no_fcr ;the file was dirty at the beginning
ldy #fcr_status ;get the status out of the File Control Record
lda [pro_fcr_ptr],y
and #file_dirty+data_dirty+index_dirty+master_dirty
beq @no_fcr ;The file is still clean
;
;Now we must update the dirty file count
;
ldy #dirty_file_cnt ;get the original count
lda [prodos_vcr_ptr],y
inc a
sta [prodos_vcr_ptr],y
@no_fcr
lda write_occurred ;see if a volume was modified during the call
beq @no_write ;no write occurred
lda call_number ;the call that was made by application
pha
ldx write_dev_num ;push the device number
phx
phy ;we do not care about Y
lda #volume_change ;this is the event code
ldx #^volume_change
jsl post_os_event ;post the event please
@no_write
plp ;restore error flag
pla ;restore error code
ora error_priority ;put error priority in hi byte 3/5/91 CAE
IF debug_on<>0 THEN
phx ;save X and Y please
phy
pha
php
jsr debugger
plp
pla
ply
plx
ENDIF
jml sys_exit
ENDP
;======================================================================
;Below is the main entry point for ALL FST system calls
;
;Input: X = Call number * 2
; Y = Class number * 2
; A = undefined
EXPORT system_call
system_call PROC
import max_sys
phk ;setup our bank register
plb
cpx #max_sys+1 ;maximum system calls
bcs sys_err
jmp (sys_tbl-2,x) ;call system routine
sys_err ;
lda #bad_system_call
rtl ;return error to Jim and Mike
ENDP
IF debug_on<>0 THEN
include 'debugger'
ENDIF
;======================================================================
;Below are my dispatch tables for application and system calls
EXPORT dispatch_tables
dispatch_tables PROC
EXPORT cmd_tbl
cmd_tbl DC.W create ;create a file call
DC.W destroy ;delete a file
DC.W cmd_error
DC.W change_path ;rename or move a file or subdir
DC.W set_file_info ;alter directory info on a file
DC.W get_file_info ;return directory info on a file
DC.W judge_name ;judge a name
DC.W volume ;return information on a volume
DC.W cmd_error ;we don't handle 'Set_Prefix'
DC.W cmd_error ;we also don't handle 'Get_Prefix'
DC.W clear_backup ;clear the backup bit on a file
DC.W cmd_error ;RESERVED
DC.W cmd_error ;RESERVED
DC.W cmd_error ;RESERVED
DC.W cmd_error
DC.W open ;open a file or subdirectory
DC.W cmd_error ;we don't handle newline
DC.W read ;read 'n' bytes from a file
DC.W write ;write 'n' bytes to a file
DC.W close ;close a file or subdirectoy
DC.W flush ;flush a file(s)
DC.W set_mark ;set a file marker
DC.W get_mark ;return file marker
DC.W set_eof ;change EOF of a file
DC.W get_eof ;return EOF of a file
DC.W cmd_error ;system call
DC.W cmd_error ;system call
DC.W get_dir_entry ;return a entry from a directory
DC.W cmd_error ;RESERVED
DC.W cmd_error ;RESERVED
DC.W cmd_error ;RESERVED
DC.W get_dev_num ;Get_Dev_Num
DC.W cmd_error ;GS/OS handles 'Get_Last_Dev'
DC.W read_block ;read Block only in ProDOS fst
DC.W write_block ;write Block only in ProDOS fst
DC.W format_disk ;format media and build directory
DC.W erase_disk ;build directory structure
DC.W cmd_error ;reserved
DC.W cmd_error ;get name
DC.W cmd_error ;get boot vol
DC.W cmd_error ;quit
DC.W cmd_error ;get version
DC.W cmd_error ;get fst info
DC.W cmd_error ;device info
DC.W cmd_error ;device status
DC.W cmd_error ;device control
DC.W cmd_error ;device read
DC.W cmd_error ;device write
DC.W cmd_error ;alloc int
DC.W cmd_error ;dealloc int
DC.W fst_specific ;FST Specific
EXPORT end_tbl
end_tbl
EXPORT Max_call:EQU
Max_call equ end_tbl-cmd_tbl ;Last valid call I support
EXPORT sys_tbl
sys_tbl 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.
EXPORT max_sys:equ
max_sys equ *-sys_tbl
ENDP
;
; 22-Jan-92 MSD: Moved all non-zero items in the data to the start of
; the proc so that all of the zero bytes would be contiguous. This
; should help the FST sparsify itself (and reduce disk space).
;
;======================================================================
;below are virtual pointers returned by GS/OS. Init these to zero
;on startup
EXPORT data
data PROC
Export dummy_name
dummy_name dc.b 'cant.find.me' ;used to calculate the latest mod date
dc.w 00
EXPORT time_stamp_opt
time_stamp_opt DC.W 02 ;0 = files
; ;1 = files + subdirs
; ;2 = files + subdirs + subdirs to root
;
EXPORT dir_offset_tbl
dir_offset_tbl DC.W $0004 ;offsets to directory entries within
DC.W $002b ;a directory block
DC.W $0052
DC.W $0079
DC.W $00A0
DC.W $00C7
DC.W $00EE
DC.W $0115
DC.W $013C
DC.W $0163
DC.W $018A
DC.W $01B1
DC.W $01D8
export fake_name_str
fake_name_str DC.W 3 ;fake name string for VCR
DC.B $C
DC.B $A
DC.B $E
DC.B $00
export syntax_str
syntax_str DC.B end_text-start_text
start_text DC.B 'ProDOS names must begin with a letter. '
DC.B 'They may be up to 15 characters long and may contain '
DC.B 'letters, digits'
if AllowLCPeriods then ;added 7-Aug-91 DAL
DC.B ', spaces'
endif ;end 7-Aug-91 DAL
DC.B ' and periods.'
end_text
EXPORT format_header
format_header ;
DC.W $0000 ;backword link is nill
DC.W $0003 ;forward link to block three
EXPORT new_vol_name
new_vol_name DC.B 00 ;length plus storage type
DS.B 15 ;fifteen bytes for the name
DS.B 2 ;eight bytes reserved
Export new_vol_mod
new_vol_mod ds.b 4 ;four byte for the Mod time
ds.b 2 ;2 bytes for the case bits
EXPORT new_vol_date
new_vol_date DS.B 4 ;create date for the volume
DC.B prodos_version
DC.B 00 ;minimum version (back to SOS 1.0)
DC.B $C3 ;access
DC.B $27 ;entry length (39)
DC.B $0D ;entries per block (13)
DC.W $0000 ;file count is nill
DC.W $0006 ;bitmap pointer
EXPORT new_tot_blks
new_tot_blks DC.W $0000 ;total number of blocks on the volume
EXPORT control_list
control_list ;partion string
DC.W $0E ;length increased by 2 7/26/90, see below
DC.B 'Apple_PRODOS'
dc.b 0,0 ;terminator nulls added 7/26/90 for compatibility with
;Macintosh-side FSM, by Matt Gulick, Monte Benaresh
Export key_blk_changed
key_blk_changed dc.w 00 ;used by set EOF to indicate that the file changed structure
;======================================================================
;below is the startup tables for each call. The table contains the
;following data:
;
; 1) Max PCount for the call
; 2) Max Class for the call
; 3) Pathnames used by call
; 4) volume control record used
; 5) file control record used.
; 6) io buffer used (data_ptr,Index_ptr,master_ptr)
; 7) flush file before call
; 8) damaged disk protect
; 9) Check Dirty Flag.
;
; |9|8|7|6|5|4|3| 2 | 1 |
; _|_|_|_|_|_|_|_|_____|_________|
; | | | | | | | | | | | | | | | | |
; |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
; | | |
; 1 6 0 8 0 0
EXPORT start_tbl
start_tbl
DC.W max_pcnt7+max_class1+path_used+damage ;Create
DC.W max_pcnt1+max_class1+path_used+damage ;Destroy
DC.W 0000
DC.W max_pcnt3+max_class1+path_used+damage ;ChangePath
; set max pCount=3 (5-Dec-91 MSD)
DC.W max_pcnt12+max_class1+path_used+damage ;SetFileInfo
DC.W max_pcnt12+max_class1+path_used ;GetFileInfo
DC.W max_pcnt6+max_class1 ;JudgeName
DC.W max_pcnt6+max_class1 ;Volume
DC.W 0000
DC.W 0000
DC.W max_pcnt1+max_class1+path_used+damage ;ClearBackup
DC.W 0000
DC.W 0000
DC.W 0000
DC.W 0000
DC.W max_pcnt15+max_class1+path_used ;Open
DC.W 0000
DC.W max_pcnt5+max_class1+vcr_used+fcr_used+io_used ;Read
DC.W max_pcnt5+max_class1+vcr_used+fcr_used+io_used+damage+do_dirty ;Write
DC.W max_pcnt1+max_class1+vcr_used+fcr_used+io_used ;Close
DC.W max_pcnt2+max_class1+vcr_used+fcr_used+io_used+damage ;Flush
DC.W max_pcnt4+max_class1+vcr_used+fcr_used+io_used ;SetMark
DC.W max_pcnt2+max_class1+fcr_used ;GetMark
DC.W max_pcnt3+max_class1+vcr_used+fcr_used+flush_1st+io_used+damage+do_dirty ;SetEof
DC.W max_pcnt2+max_class1+fcr_used ;GetEof
DC.W 0000
DC.W 0000
DC.W max_pcnt17+max_class1+vcr_used+fcr_used+io_used ;GetDirEntry
DC.W 0000
DC.W 0000
DC.W 0000
DC.W max_pcnt2+max_class1+path_used ;GetDevNum
DC.W 0000
DC.W 0000 ;ReadBlock
DC.W 0000 ;WriteBlock
DC.W max_pcnt6+max_class1+path_used ;Format
DC.W max_pcnt6+max_class1+path_used ;Erase
EXPORT gbuf_addr
gbuf_addr DC.L 0 ;pointer to general I/O buffer
EXPORT virtual
virtual DC.L 0 ;pointer to virtual memory after
; ;a call to 'Get_Segment'
EXPORT virtual2
virtual2 DC.L 00
;======================================================================
;data storage used
EXPORT hours
hours DC.W 00
EXPORT minutes
minutes DC.W 00
EXPORT year
year DC.W 00
EXPORT month
month DC.W 00
EXPORT day
day DC.W 00
EXPORT day_of_week
day_of_week DC.W 00
EXPORT pro_time
pro_time DC.L 00
Export write_occurred
write_occurred dc.w 00 ;IF non-zero on exit then a write occurred
Export write_dev_num ;device number that was written too
write_dev_num dc.w 00
Export curr_mod_date
curr_mod_date dc.l 00
Export case_bits ;case bits for the last pathname
case_bits dc.w 00
Export case_bit_pattern
case_bit_pattern dc.w 00
EXPORT src_dev_num
src_dev_num DC.W 00 ;used by change path
EXPORT src_start_blk
src_start_blk DC.W 00 ;''
EXPORT src_dir_blk
src_dir_blk DC.W 00 ;''
EXPORT src_offset
src_offset DC.W 00
EXPORT slug_block
slug_block DC.W 00 ;for slug ripple 'to root time stamp'
EXPORT slug_offset
slug_offset DC.W 00
EXPORT close_flag
close_flag DC.W 00 ;used to determine if we should call
; ;slug_ripple from flush_file
;
EXPORT src_slug_blk
src_slug_blk DC.W 00 ;used by change_path
EXPORT src_slug_offset
src_slug_offset DC.W 00
EXPORT dest_slug_blk
dest_slug_blk DC.W 00
EXPORT dest_slug_offset
dest_slug_offset DC.W 00
EXPORT dest_dev_num
dest_dev_num DC.W 00 ;used by change path
EXPORT dest_start_blk
dest_start_blk DC.W 00 ;''
EXPORT dest_dir_blk
dest_dir_blk DC.W 00 ;''
EXPORT dest_offset
dest_offset DC.W 00
EXPORT vol_rename_flag
vol_rename_flag DC.W 00 ;''
EXPORT direct_page
direct_page DS.B $4C ;used when we must mount a volume
EXPORT my_direct
my_direct DS.B $78 ;''
EXPORT world_flag
world_flag DC.W 00 ;detect re-entrent code
EXPORT def_dir_page
def_dir_page DS.B $4C ;used when we flush deferred blocks
EXPORT def_my_direct
def_my_direct DS.B $78 ;''
EXPORT def_vol_name
def_vol_name DS.B 20
EXPORT alloc_chk_flag
alloc_chk_flag DC.W 00 ;used by write routine
EXPORT seq_write_start
seq_write_start DC.L 00 ;pointer used by write routine
EXPORT seq_write_cnt
seq_write_cnt DC.W 00 ;sequential block counter
EXPORT seq_write_blk
seq_write_blk DC.W 00 ;starting block for sequential write
EXPORT path_searched
path_searched DC.W 00 ;set by find file.
;
EXPORT last_blk
last_blk DC.W 0 ;used by find_file to verify
; ;directory structure
;
;
EXPORT search_flag
search_flag DC.W 00 ;flag used by process path
; ; bit 15 = volume name not allowed
; ; bit 14 = search for new entry
; ; bit 13 = volume only allowed
;
EXPORT dir_start_blk
dir_start_blk DC.W 00 ;starting block of a directory
EXPORT dir_last_blk
dir_last_blk DC.W 00 ;last block read by find file
EXPORT free_dir_blk
free_dir_blk DC.W 00 ;1st dir block with a free entry
EXPORT free_dir_offset
free_dir_offset DC.W 00 ;offset to free entry in dir block
EXPORT searching_free
searching_free DC.W 00 ;flag that is used by Find_File
EXPORT blks_used1
blks_used1 DC.W 00 ;blks needed for data fork. (create)
EXPORT blks_used2
blks_used2 DC.W 00 ;blks needed for res. fork. (create)
EXPORT blocks_needed
blocks_needed DC.W 00 ;total blocks needed for files. (create)
EXPORT data_storage
data_storage DC.W 00 ;storage type for data fork
EXPORT res_storage
res_storage DC.W 00 ;storage type for resource fork
EXPORT res_tot_blks
res_tot_blks DC.W 00 ;used by create
EXPORT res_key_blk
res_key_blk DC.W 00 ;''
EXPORT some_free
some_free DC.W 00 ;used by find_free_blks
EXPORT volume_name
volume_name DS.B 20 ;holds the volume name of an input str
EXPORT gstring
gstring DS.B 20 ;holds gstrings
EXPORT search_length
search_length DC.W 00 ;used by 'find_file' routine
EXPORT search_name
search_name DS.B 17
EXPORT fcr_wanted
fcr_wanted DC.W 00 ;used by check_dup routine
EXPORT key_block
key_block DC.W 00 ;used by check_dup routine
EXPORT find_this_entry
find_this_entry DC.W 00 ;used by get dir entry
EXPORT entry_sto_type
entry_sto_type DC.W 00
EXPORT storage_type
storage_type DC.W 00 ;used by open
EXPORT alloc_entry
alloc_entry DC.B 00 ;used to give entry name a length word
EXPORT one_entry
one_entry DS.B 40 ;holds one directory entry
EXPORT files_key_block
files_key_block DC.W 00 ;used by open
EXPORT hold_path_ptr
hold_path_ptr DC.W 00
EXPORT data_eof
data_eof DC.L 00 ;used by create
EXPORT res_eof
res_eof DC.L 00 ;used by create
EXPORT tree_key_blk
tree_key_blk DC.W 00 ;used by create
EXPORT tree_blks
tree_blks DC.W 00 ;used by create
EXPORT parent_blk
parent_blk DC.W 00 ;used by the open routine
EXPORT entry_offset
entry_offset DC.W 00 ;used by the open routine
EXPORT num_of_res
num_of_res DC.W 00 ;used by open
EXPORT newline_len
newline_len DC.W 00
EXPORT newline_mask
newline_mask DC.W 00
EXPORT newline_char
newline_char DC.W 00
EXPORT base
base DC.W 00 ;used with calls that have base param
EXPORT displacement
displacement DC.L 00 ;used with calls that have displacement
EXPORT purge_addr
purge_addr DC.W 00 ;block address of purgable files
EXPORT new_last
new_last DC.W 00 ;used to cut back eof (SET_EOF)
EXPORT bitmap_blks
bitmap_blks DC.W 00 ;used to calc_free_blks
EXPORT last_bitmap
last_bitmap DC.W 00 ;'' ''
EXPORT first_free
first_free DC.W 00 ;'' ''
EXPORT bits
bits DC.W 00 ;used to count bits in calc_free_blks
EXPORT alloc_count
alloc_count DC.W 00 ;used by find_free_blks
EXPORT alloc_start
alloc_start DC.W 00 ;''
EXPORT byte_offset
byte_offset DC.W 00 ;''
EXPORT mark_changed
mark_changed DC.W 00 ;used by fill_io_buf (setmark=EOF case)
EXPORT user_cache
user_cache DC.W 00 ;default cache value for file reads
EXPORT create_time
create_time DC.L 00 ;used by set_file_info
EXPORT mod_time
mod_time DC.L 00 ;used by set_file_info
EXPORT first_free_bm
first_free_bm DC.W 00
EXPORT index_blks
index_blks DC.W 00 ;number of indexes needed for tree files
EXPORT data_indexes
data_indexes DC.W 00 ;used by create
EXPORT res_indexes
res_indexes DC.W 00 ;used by create.
EXPORT entries_blk_num
entries_blk_num DC.W 00
EXPORT files_examined
files_examined DC.W 00
EXPORT files_in_dir
files_in_dir DC.W 00
EXPORT pcount
pcount DC.W 00
EXPORT resource_num
resource_num DC.W 00
EXPORT access
access DC.W 00
EXPORT and_mask
and_mask DC.W 00
EXPORT curr_mark
curr_mark DC.L 00
EXPORT curr_eof
curr_eof DC.L 00
EXPORT tran_cnt
tran_cnt DC.L 00
EXPORT user_req_cnt
user_req_cnt DC.L 00
EXPORT entries_checked
entries_checked DC.W 00
EXPORT chars_checked
chars_checked DC.W 00
EXPORT name_length
name_length DC.W 00
EXPORT flush_entry
flush_entry dc.w 00 ;allows us to flush only the I/O buffer
EXPORT Users_access
users_access dc.w 00 ;used during the open call
Export dirty_flags ;flags at the beginning of the call
dirty_flags dc.w 00
Export flush_flags
flush_flags dc.w 00 ;used by the flush routines
export chk_dirty_flag ;flag used to indicate that the call can cause dirty blocks
chk_dirty_flag dc.w 00
export dirty_cnt_changed
dirty_cnt_changed dc.w 00 ;indicates that we have changed the dirty_cnt once
export new_eof
new_eof dc.l 00 ;used by set EOF
export cp_device_flag
cp_device_flag DC.W 00 ;1 = ChangePath using device name, else 0
export cp_flags ;Added 5-Dec-91 MSD
cp_flags DC.W 00 ;$8000 = assume source is duplicate volume
export cp_to_child ; non-zero if ChangePath trying to move an item
cp_to_child dc.w 00 ; into its own child (i.e. :A:B to :A:B:C).
export hfs_flag
hfs_flag DC.W 00 ;1 = we have HFS Finder Info, else 0
export hfs_finfo
hfs_finfo DS.B 32 ;storage area for HFS Finder Info
export error_priority
error_priority DC.W 00 ;storage for error priority
;======================================================================
;Data used by Format and Erase disk calls
EXPORT format_ctrl
format_ctrl DC.W 00 ;length of control list
EXPORT status_list
status_list DC.W 00 ;status code
EXPORT return_blks
return_blks DC.L 00000 ;number of blocks on device
EXPORT entries_offset
entries_offset dc.w 00 ;offset to directory entry
export gde_temp
gde_temp dc.w 00 ; temp used by GetDirEntry
;driver save area
EXPORT drvr_save
drvr_save DS.B $1b ;save room for direct page
;equates used by 'setup_params'
EXPORT class
class DC.W 00
EXPORT max_pcount
max_pcount DC.W 00
EXPORT flags
flags DC.W 00
*** added 28-Feb-92 DAL
;
; Used by chk_disk_protect to save the first 512 bytes of GBUF
;
export chk_protect_gbuf
chk_protect_gbuf ds.b 512
*** end 28-Feb-92 DAL
ENDP
expand_record Record
expand_flag dc.w 00
expand_file dc.w 00 ;used to indicate we are extending a file
expand_storage dc.w 00 ;storage type of the data fork on extension
expand_key_blk dc.w 00
expand_blks_used dc.w 00
expand_eof dc.l 00
endr
;==========================================================================
; Prodos universal boot loader. This is the second stage boot
; for all apple manufactured Apple II disk drives.
; It is located at block zero (0) of a ProDOS formatted disk(ette).
; Support for Apple /// has been removed to save space.
;
;Rev History:
;
;Jan 12, 1988 By Rob Turner
;
; Removed Apple /// (SOS) support and converted source to APW
;
;Jun 02, 1988 By Rob Turner
;
; Changed the boot code to handle a sparse ProDOS file.
;
;==========================================================================
boot_code_start proc
endp
msb off
longa off
longi off
MACHINE M6502
EXPORT boot_code
boot_code PROC temporg $000800
dcmd equ $42 ;disk command (=1 for read)
unit equ $43 ;(16*slot)+(128*(drive-1))
buff equ $44 ;ram address
blok equ $46 ;disk address
dent equ $48 ;device call entry address.
idxl equ $4a ;pointer to low page of index block
idxh equ $4c ;pointer to high page of index block
idxp equ $4e ;index byte pointer.
iobuff equ $60
; the following are for disk ii only:
dbuf equ $26
slotz equ $2b
oddbits equ $3c
sector equ $3d
trktmp equ $40
track equ $41
prior equ $50
trkn equ $51
rtrycnt equ $52
curtrk equ $53
trkcnt equ $54
*
q6l equ $c08c
motoron equ $c089
motoroff equ $c088
phaseoff equ $c080
nbuf1 equ $300
dnib equ $2d6
* directory dependent stuff...
clrscrn equ $fc58
scrn equ $5b1 ;5ae
dostyp equ $ff
sosid equ $c00
entlen equ sosid+$23
kernel equ $2000
zzstart equ $09F2
rd1 equ $09F2
rd1a equ $09F7
mod1 equ $0008
chg1 equ $FFF4
rd2 equ $09FB
rd3 equ $0A05
mod2 equ $001E
chg2 equ $FFD7
mod3 equ $0024
rdhd0 equ $0A15
chg3 equ $FFD1
rdhd1 equ $0A17
rdhd2 equ $0A19
rdhd3 equ $0A1B
rdhd4 equ $0A23
mod4 equ $003F
chg4 equ $FFB6
mod5 equ $0045
chg5a equ $0A38
mod6 equ $0047
chg6 equ $FFB4
rddt1 equ $0A3A
rddt1a equ $0A3C
rddt2 equ $0A3E
rddt3 equ $0A4E
rddt4 equ $0A50
rdchk equ $0A5F
mod7 equ $0076
chg7 equ $FFAC
nxttwo equ $0A6B
twobit equ $0A6D
zzzend equ $0A7F
chg5 equ $004B
goseek equ $0A83
;==========================================================================
;Main Entry Point For Booting Apple II Computer.
;==========================================================================
xboot DC.B $01 ;(prodos boot id)
entry sec ;(apple iii enters xboot 'ora $38')
bcs entry1 ;branch if not apple iii native mode.
jmp booterr ;Apple /// Boot.
entry1 sei ;disable interupts so we can boot on the IIGS with debugger
stx unit ;save unit number.
cmp #$03 ;for disk ii.
php ;save result, it may be irrelevent.
txa ;find out if disk ii.
and #$70 ;strip drive # if any.
lsr a
lsr a ;get slot address.
lsr a
lsr a
ora #$c0
sta dent+1
ldy #$ff ;look at last byte.
sty dent
plp ;restore carry (if disk ii & sect 0&2 read carry set).
iny ;make y=0
lda (dent),y ;get device entry addr.
bne ndsk2 ;branch if not disk ii (16 sector).
bcs isdsk2 ;branch if it is disk ii, but block 0 read.
lda #3 ;make rom read only sector 2
sta xboot ;to complete block 0
inc sector ;(was = 1)
lda dent+1 ;do rts to re-enter rom.
pha
lda #$5b
pha
rts ;go read sector 2 into $900.
isdsk2 sta trktmp ;make sure previous track =0
sta dent ;and dent points at beginning of slot
ldy #$5e ;move code from card to ram
mvboot lda (dent),y
sta zzstart-$5e,y
iny
cpy #$eb ;have we moved enough?
bne mvboot
ldx #6 ;now modify code to handle errors.
modboot ldy mods,x
lda chgs,x
sta zzstart,y
lda endcode,x
sta zzzend,x
dex
bpl modboot
lda #>d2io ;reset device entry
sta dent+1 ; to point at disk ii routines.
lda #<d2io ;get low addr (must be <$80)
ndsk2 ldy #0 ;make sure y=0 again.
cmp #$f9
bcs bterr1 ;branch if not bootable device.
sta dent ;save low adr of device call entry.
sty iobuff
sty idxl
sty idxh ;(y=0)
sty idxp
sty blok+1
iny
sty dcmd ;set read command.
iny
sty blok ; to read directory blocks
lda #$c ; 2-5 at $c00
sta iobuff+1
sta idxl+1
rddir jsr goread ;call read block routine.
bcs bterr2 ;give up on error.
inc iobuff+1
inc iobuff+1
inc blok
lda blok ;have all directory blocks been read?
cmp #6
bcc rddir ;loop if not.
lda sosid ;is it a prodos (sos) directory?
ora sosid+1
bterr1 bne bterr2 ;booterr ;branch if not.
lda #4 ;begin look-up with first entry past header.
bne nxdent1 ;branch always
nxdent lda idxl
nxdent1 clc
adc entlen ;bump to next directory entry.
tay ;save in y for now.
bcc nxdent2 ;branch if not a page cross.
inc idxl+1
lda idxl+1 ;check for new block.
lsr a ;if even then new block.
bcs nxdent2
cmp #$a ;have all file names been compared?
beq nopro ;branch if no pro.kernel.
ldy #4 ;else, begin at block beginning.
nxdent2 sty idxl ;note: this method treats garbage at
lda sysname ; the end of the dir block as an entry.
and #$f ;get target name length.
tay
lookpro lda (idxl),y ;look for matching name.
cmp sysname,y ;last to first method.
bne nxdent ;branch if no match.
dey ;else check all characters.
bpl lookpro ;including length/storage type.
ldy #$16 ;convert eof to blks.
lda (idxl),y
lsr a ;convert to blocks
adc blk_count
sta blk_count
; ldy #$10 ;get file type & index block addr.
; lda (idxl),y
; cmp #dostyp ;is it a system file?
; bne booterr
; iny
ldy #$11 ;starting block of the file
lda (idxl),y
sta blok
iny
lda (idxl),y
sta blok+1
lda #0 ;now set up to read kernel.
sta idxl
ldy #$1e ;read index block at $1e00 and
sty idxl+1 ; kernel at $2000
sty iobuff+1
iny
sty idxh+1
rdkernl jsr goread ;read index block.
bterr2 bcs booterr
inc iobuff+1 ;setup pointer for the next block please
inc iobuff+1
@sparse_return
ldy idxp ;get index pointer
inc idxp ;bump for next time.
lda (idxl),y
sta blok
lda (idxh),y ;high disk addr.
sta blok+1
ora (idxl),y ;if both=0 then done. (This was changed to handle sparse)
bne not_sparse
ldx #$01
lda #$00 ;clear the block please
tay
@1 sta (ioBuff),y
iny
bne @1 ;clear the low page
inc ioBuff+1 ;bump to the next page
nop
nop ;keep things aligned please
dex
bpl @1 ;clear the high page
dec blk_count
beq go_kernel
bne @sparse_return
not_sparse
dec blk_count ;are we done yet
bne rdkernl ;branch if more to read.
go_kernel
cli ;enable interupts before calling ProDOS
jmp kernel ;go execute kernel code.
nopro equ *
booterr equ *
jmp quitmes
blk_count dc.b 02
msb off
sysname DC.B $26
DC.B 'PRODOS'
goread lda iobuff
sta buff
lda iobuff+1
sta buff+1
jmp (dent)
*
mods DC.B mod1,mod2,mod3,mod4
DC.B mod5,mod6,mod7
*
chgs DC.B chg1**$FF,chg2**$FF,chg3**$FF,chg4**$FF
DC.B chg5**$FF,chg6**$FF,chg7**$FF
*
endcode equ *
ldx slotz
clc
rts
jmp seek
*
quitmes jsr clrscrn ;clear video.
ldy #meslen ;print message centered on screen.
prmess lda errmess,y
sta scrn,y
dey
bpl prmess
hang jmp hang
*
msb on
meslen equ 20
errmess DC.B 'UNABLE TO LOAD PRODOS'
********************************************************************************
* This code MUST be aligned to $16D ($96D Absolute) relative address. Otherwise
* disk][s will not work. This is a major pain in the butt.
********************************************************************************
setphase lda curtrk ;get current track
clrphase and #3 ;mask for 1 of 4 phases
rol a ;double for phaseon/off index
ora slotz
tax
lda phaseoff,x ;turn on/off one phase
lda #$2c
**************************
* *
* mswait subroutine *
* *
**************************
mswait ldx #$11
msw1 dex ;delay 86 usec.
bne msw1
sbc #$1 ;done 'n' intervals?
bne mswait ;(a-reg counts)
ldx slotz ;restore x-reg
rts
*
*
d2io lda blok ;figure out track & sector.
and #7 ;strip track for now.
cmp #4
and #3
php
asl a
plp
rol a ;now we have the first sector of block.
sta sector
lda blok+1 ;get high block #
lsr a ;shift hi addr to carry.
lda blok ;now figure track #
ror a
lsr a
lsr a
sta track
asl a
sta trkn
lda buff+1
sta dbuf+1
ldx slotz
lda motoron,x
jsr rdsector ;go read sector.
inc dbuf+1 ;bump address
inc sector
inc sector ;and sector #
bcs quitrd ;branch if error.
jsr rdsector
quitrd ldy motoroff,x
erretrn rts ;return error status in carry.
*
rdsector equ * ;do seek then read sector.
*
seek lda trktmp ;get track we're on.
asl a
sta curtrk
lda #$0
sta trkcnt ;halftrack count.
seek2 lda curtrk ;save curtrk for
sta prior ;delayed turnoff.
sec
sbc trkn ;delta-tracks.
beq seekend ;br if curtrk=destination
bcs out ;(move out, not in)
inc curtrk ;incr current track (in).
bcc skin ;(always taken)
out dec curtrk ;decr current track (out).
skin sec
step2 jsr setphase
lda prior
clc ;for phaseoff
jsr clrphase ;de-energize previous phase.
bne seek2 ;(always taken)
seekend equ *
*
rdsect1 ldy #$7f ;allow 127 mistakes.
sty rtrycnt
php
*
tryread plp ;fix stack.
rdhead sec ;anticipate error.
dec rtrycnt ;if = 0 then give up!
beq erretrn ;branch if can't fine/read sector.
clc ;indicate reading header.
rddata php ;carry set if reading sector.
rd0 dey ;every time y=0 decrement find count.
beq tryread
MACHINE M65816 ;start using 65816 code again
msb off
longa on
longi on
export boot_size:equ ;export the equate for Max
boot_size equ *-xboot
ENDP
;======================================================================
;Pack_Time: This routine will read the time from the GS Clock and
; convert it to packed ProDOS disk format
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..0
; b = k
;
;Output: A = undefined
; X = low word of time
; Y = High word of time
;
; time is also stored at Pro_Time and Pro_Time+2
;
;Format of time:
;
; |<-------- X Register --------->|<-------- Y Register --------->|
; | | |
; |<--high Byte-->|<--low Byte-->|<--high Byte-->|<--low Byte-->|
; _______________________________________________________________
; | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
; | year | month | day |0 0 0| hour |0 0| minute |
; |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
;
;
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: all register
; get_time (Subroutine that reads time)
; hours,minutes (data words)
; year,month,day
;
longa on
longi on
EXPORT pack_time
pack_time PROC
stz pro_time
stz pro_time+2
jsr get_time ;read the time from tool set
;We call this entry point if the time parameters are setup and need
;only to be convert to the ProDOS format.
EXPORT convert_time
convert_time
lda day
sta pro_time ;save the day in prodos format
lda month
asl a
asl a
asl a
asl a
asl a ;shift to correct position
ora pro_time ;month is now happy
sta pro_time
lda year
; 27-Jan-92 MSD: Years outside the range 1940..2039 will be converted
; to "unknown" (i.e. all zeroes).
cmp #40 ; less than 1940?
blt @bad_year
cmp #140 ; 2040 or greater?
bge @bad_year
cmp #100 ; 2000..2039?
blt @got_year ; no, so leave as is
sbc #100 ; convert 2000..2039 to 0..39
@got_year
; End 27-Jan-92 MSD
xba
asl a ;shift year to upper 7 bits
ora pro_time
sta pro_time
tax ;return in X register too
lda minutes
sta pro_time+2
lda hours
xba
ora pro_time+2
sta pro_time+2
tay ;return in Y register
clc ; 27-Jan-92 MSD: valid date/time
rts
@bad_year ; year was bad, set date/time
ldx #0 ; to "unknown" (all zeroes).
txy
stx pro_time
sty pro_time+2
sec ; 27-Jan-92 MSD: invalid date/time
rts
ENDP
;======================================================================
;unpack_time: This routine will unpack a prodos time into a GS/OS time.
;
;Input: A = undefined
;
;Format of time:
;
; |<-------- X Register --------->|<-------- Y Register --------->|
; | | |
; |<--high Byte-->|<--low Byte-->|<--high Byte-->|<--low Byte-->|
; _______________________________________________________________
; | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
; | year | month | day |x x x| hour |x x| minute |
; |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
;
; P = nvmxdizc
; ..000..0
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
; hours (Times are all hex format)
; minutes
; year
; month
; day
; day_of_week
;
;Uses: all register
; dow_convert (Subroutine)
;
longa on
longi on
EXPORT unpack_time
unpack_time PROC
stx pro_time
sty pro_time+2
;Init time to zero (unknown)
jsr zero_date
txa
ora pro_time+2
beq end_unpack
lda pro_time
and #$001F ;isolate day
sta day
cmp #32 ;valid days are 01 to 31
bcs zero_date ;sorry bud you get unknown
tay ;save for DOW convert
txa ;get year/month/day again (X is still valid here)
lsr a
lsr a
lsr a
lsr a
lsr a
and #$000F ;isolate month
sta month
cmp #13 ;is the month 13 or >?
bcs zero_date
tax
lda pro_time
xba
lsr a
and #$007F ;isolate the year
cmp #40 ;year = 40-99?
bcs @store_it ;yes, must be 1940-1999
adc #100 ;else must be 2000-2039, adjust
@store_it
sta year
jsr dow_convert ;go ahead and calculate the day of week
sta day_of_week
lda pro_time+2
and #$003F ;isolate minutes
sta minutes
cmp #60
bcs zero_date
lda pro_time+2
xba
and #$001F ;isolate hours
sta hours
cmp #24
bcc end_unpack
zero_date ;indicate that the date is unknown
stz hours
stz minutes
stz year
stz month
stz day
stz day_of_week
end_unpack rts
ENDP
;======================================================================
;DOW_Convert:This routine convert Month,Day,Year into day of week
;
;Input: A = Year
; X = Month
; Y = Day
; P = saved
; b = k
;
;Output: A = day of week
; X = undefined
; Y = undefined
; P = restored
; b = k
;
;Uses: all register
; local temps
;
longa off
longi off
EXPORT dow_convert
dow_convert PROC
php
sep #$30
phy
tay
;Figure the number of leap years since 1900
lsr a
lsr a
sta dow_temp
;If it's a leap year and it's Jan or Feb subtract 1 from the total
tya ;get the year back
and #%00000011
bne stim015
cpx #03
bcs stim015
dey
;add the number of leap years elapsed, the year, a constant for the
;month, and the day
stim015
clc
tya ;get the year again
adc dow_temp ;# of leap years since 1900
adc wkmon-1,x
adc 1,s
ply ;clean the stack
;Now mod the result by seven, but exit in the range [1..7]
sec
stim016 ;
sbc #07
cmp #08
bcs stim016
plp ;restore mode
rts
dow_temp DC.B 0
wkmon DC.B $08,$0B,$0B,$07,$09,$0C,$07,$0A,$0D,$08,$0B,$0D
ENDP
;======================================================================
;get_time: This routine will read the time from the GS Clock.
;
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..0
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; hours,minutes
; year,month,day ;all are setup with the time
; day_of_week
;
;uses: Misc tools ;(Gets hex time)
;
;======================================================================
longa on
longi on
EXPORT get_time
get_time PROC
lda #0000 ;setup default time to zero
pha ;minutes,seconds result
pha ;year,hour results
pha ;month,day results
pha ;weekday,null results
ldx #(read_time_hex*$100)+misc_tools
jsl tool_locator
longa off
sep #$20
pla ;disregard seconds
pla
sta minutes
pla
sta hours
pla
sta year
pla
bcs no_inc1
inc a
no_inc1 sta day ;convert 0 to 30 -> 1 to 31
pla
bcs no_inc2
inc a
no_inc2 sta month ;convert 0 to 11 -> 1 to 12
pla ;null field
pla
sta day_of_week
longa on
rep #$30
rts
ENDP
;============================================================================
;
;Calc_Bit: This routine will convert a 16 bit block number into
; the correct index into the bitmap block, the correct pattern
; to indicate which bit it is, and which relative block in the
; bitmap contains the bit
;
;Input: X = Block number to convert
; m = 0
; x = 0
; b = k
;
;Output: A = Bit Pattern (Which bit that needs to be changed)
; Y = Byte offset into bitmap block that contains bit
; X = Relative Block number of the bitmap that contains the bit
; Carry Clr
; All other flags undefined
;
;Uses: Local Table
; All registers
;
EXPORT calc_bit
calc_bit PROC
txa ;X=Block number
and #$0007 ;isolate bit index
tay
lda bit_table,y ;get the bit pattern
and #$00FF ;table is a byte table
pha
txa ;now isolate byte index
and #$0FF8
lsr a
lsr a
lsr a
tay ;Y=Offset into bitmap block
jsr abs_to_rel ;absolute to relative block number
pla ;A=Bit pattern
rts
EXPORT abs_to_rel
abs_to_rel
clc
txa ;now isolate relative block number
and #$F000
rol a
rol a
rol a
rol a
rol a
tax ;X=Relative block number in bitmap
rts
bit_table DC.B $80,$40,$20,$10,$08,$04,$02,$01
ENDP
;============================================================================
;Alloc_block This routine will allocate a block in the bitmap.
; If the block is not in memory then it will load it.
;
;Input: X = Block number that you want to allocate
; m = 0
; x = 0
; b = k
; prodos_vcr_ptr
;
;Output: carry clr = no error
; carry set = could not allocate
; A = error code if carry set
; 01 = User would not mount the volume
; all flags undefined
;
;Uses: All Registers
; Calc_bit (Subroutine)
; Get_Bit_block (Subroutine)
; bitmap_changed (Subroutine)
;
EXPORT alloc_block
alloc_block PROC
jsr calc_bit ;calculate bit(A),byte(Y),block(X)
pha
phy
jsr get_bit_block ;load the correct block if needed
ply
pla
tax ;save mask
bcs done ;could not load block from disk
and [bitmap],y ;is the block already allocated?
beq bitmap_corrupt ;the bitmap is in bad shape, sorry.
txa ;restore the AND mask
eor #$FFFF ;we need to flip the patterns
and [Bitmap],y ;clear the correct bit
sta [bitmap],y
jsr bitmap_changed ;update bitmap flags
jsr sub_free_blk ;adjust free block count
done rts
EXPORT bitmap_corrupt
bitmap_corrupt ;we get here if the bitmap is corrupt
lda #damaged_bitmap
sec
rts ;return the error
EXPORT sub_free_blk
sub_free_blk
ldy #free_blocks
lda [prodos_vcr_ptr],y
inc a
beq who_knows
dec a ;$FFFF = major problem
dec a
sta [prodos_vcr_ptr],y
who_knows
clc
rts
ENDP
;============================================================================
;DeAlloc_block This routine will deallocate a block in the bitmap.
; If the bitmap block is not in memory then it will load it.
; The routine will also update the free block count if needed
;
;Input: X = Block number that you want to deallocate
; m = 0
; x = 0
; b = k
; prodos_vcr_ptr (must be setup)
; my_vcr_ptr (must be setup)
;
;Output: carry clr = no error
; carry set = could not allocate
; A = error code if carry set
; 01 = User would not mount the volume
; all flags undefined
;
;Uses: All Registers
; Calc_bit (Subroutine)
; Get_Bit_block (Subroutine)
;
EXPORT dealloc_block
dealloc_block PROC
jsr calc_bit ;calculate bit(A),byte(Y),block(X)
phx
pha
phy
jsr get_bit_block ;load the correct block if needed
ply
pla
plx
bcs done ;could not load block from disk
pha ;save the mask please
and [bitmap],y ;is the block already deallocated?
beq no_prob ;the block is currently allocated
pla ;fix the stack
bra bitmap_corrupt ;return the error please
no_prob ;
pla ;restore the mask please
ora [bitmap],y ;set the correct bit
sta [bitmap],y
;We have just deallocated some blocks so lets make sure the first bitmap block
;that contains available space is correct for the current VCR
ldy #avail_bitmap
txa ;relative block number
cmp [prodos_vcr_ptr],y
;
; 2/3/90 Mensch, Hauser,Benaresh
;
; Rob says that avail_bitmap should *ALWAYS* be equal to the lowest bitmap
; block that has space in it. So, the branch that was a BEQ should really be
; a BCS (BGE) this way, so it will work right.
;
bge go_changed ;the relative block num is the same
;If we freed up a block in a previously full bitmap then lets update the VCR
sta [prodos_vcr_ptr],y
go_changed
jsr add_free_blk
EXPORT bitmap_changed
bitmap_changed
clc
;==========================================================================
;This routine will mark the current bitmap block dirty. NOTE: This routine
;must not change the state of the carry.
ldy #bitmap_dirty
lda #bitmap_mod ;bitmap modified
sta [prodos_vcr_ptr],y
done rts
;==========================================================================
;This routine will add one to the free block count for a volume. If the
;number of free blocks is unknown then the routine will just exit.
EXPORT add_free_blk
add_free_blk
ldy #free_blocks
lda [prodos_vcr_ptr],y
inc a ;$FFFF = free block count unknown
beq end_free ;free block count is not known
sta [prodos_vcr_ptr],y
end_free
rts
ENDP
;==========================================================================
;get_bit_block: This routine will load in the correct bitmap block from
; disk. If the correct block is already loaded then the
; routine will just exit.
;
;Input: A = undefined
; X = relative block number to load
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; prodos_vcr_ptr (must be setup correctly)
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; bitmap (points to bitmap buffer)
; drvr_buf_ptr (same as bitmap)
;
;Uses: All registers
; device_call
; drvr_call_num
;
longa on
longi on
EXPORT get_bit_block
get_bit_block PROC
phx ;save the block number
jsr setup_bitmap_ptr
stx drvr_buf_ptr
stx bitmap
sty drvr_buf_ptr+2
sty bitmap+2
pla ;retore relative block number
sta math_temp
ldy #curr_bitmap
cmp [prodos_vcr_ptr],y
beq good_exit
pha
;force dirty bitmap block back to disk if needed.
jsr force_bitmap ;force the bitmap to disk
bcc flushed
plx
rts
flushed
;If we get here we have to calculate the right block to read into memory
pla
clc
ldy #vol_bitmap_addr
adc [prodos_vcr_ptr],y
sta drvr_blk_num
jsr read_with_cache
bcc read_ok
jsr kill_bitmap
sec
rts ;return the error
read_ok
;Now we need to update the current bitmap
ldy #curr_bitmap
lda math_temp
sta [prodos_vcr_ptr],y
good_exit
clc
rts
ENDP
;==========================================================================
;calc_free_blks This routine will calculate the number of blocks that are
; free on a volume.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; prodos_vcr_ptr (must be setup correctly)
; drvr_dev_num (must be setup up)
;
;Output: A = error code if carry set otherwise total blocks free
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; device_call
; I/O buffer
;
longa on
longi on
EXPORT calc_free_blks
calc_free_blks PROC
;before we start going to disk lets see if we already know the number of
;free blocks. IF free_blocks = $FFFF then free blocks are unknown.
ldy #free_blocks
lda [prodos_vcr_ptr],y
inc a
beq go_figure ;we need to calculate it
dec a ;A = total blocks
clc
rts
go_figure
jsr force_bitmap ;flush the bitmap if needed
;setup the buffer pointer to the general I/O buffer provided by GS/OS shell
stz first_free_bm ;indicate that we do not know free cnt
;First lets see if there is already a bitmap buffer allocated. If so
;lets use it as our I/O buffer.
jsr setup_bitmap_ptr
stx drvr_buf_ptr
sty drvr_buf_ptr+2
;read 512 bytes from the disk.
jsr standard_req
;go do a run on the bitmap calculating the number of blocks free
stz math_temp ;counts the free blocks
ldy #vol_bitmap_addr
lda [prodos_vcr_ptr],y
sta drvr_blk_num ;starting block number of bitmap
ldy #num_bitmap_blks
lda [prodos_vcr_ptr],y
sta bitmap_blks ;number of blocks in the bitmap
ldy #end_bitmap ;first byte that should be all zeros
lda [prodos_vcr_ptr],y
sta last_bitmap
ldy #avail_bitmap
lda [prodos_vcr_ptr],y
inc a
beq block_loop
dec a
tay ;save start
clc
adc drvr_blk_num ;adjust the starting block number
sta drvr_blk_num
phy
sec
lda bitmap_blks ;number of blocks to scan
sbc 1,s ;subtract start block number
sta bitmap_blks
pla
block_loop
jsr read_with_cache ;read in a block
bcc read_ok
jsr kill_bitmap
sec
rts ;we should not get any errors
bad_bitmap lda #damaged_bitmap
sec
rts
read_ok
;update the current bitmap in memory field.
sec
ldy #vol_bitmap_addr
lda drvr_blk_num
sbc [prodos_vcr_ptr],y
ldy #curr_bitmap
sta [prodos_vcr_ptr],y
;if this is the last block of the bitmap then verify ending with zeros.
dec bitmap_blks
bne not_last
ldy last_bitmap ;starting location that must be zero
beq not_last ;don't bother
zero_loop
cpy #$01FF ;are we near the end of the block?
bcs setup_index ;If we are this close the end
lda [drvr_buf_ptr],y
bne bad_bitmap
iny
iny
bra zero_loop
setup_index
ldy last_bitmap
tya
lsr a ;is it an even value?
bcc word_loop ;Yes, so fall into loop
iny ;make it even if it is not
cpy #$01FE ;is it now greater then one block
bcc word_loop ;no so fall into the loop
not_last
ldy #$01FE ;start scanning the block
word_loop
lda [drvr_buf_ptr],y
bne count_bits
return_loop dey
dey
bpl word_loop ;keep scanning the block please
lda bitmap_blks ;see if we are done
beq calc_done ;we are done
inc drvr_blk_num ;read next block please
bra block_loop
;Check to see if we already know the first bitmap block that contains
;free blocks. IF we do then just count the blocks free
count_bits
sta bits ;save bitmap word
lda first_free_bm
bne cont_count
;We now know the first bitmap block that contains available blocks, so lets
;save it please.
phy
sec
ldy #vol_bitmap_addr
lda drvr_blk_num
sta first_free_bm
sbc [prodos_vcr_ptr],y
ldy #avail_bitmap
sta [prodos_vcr_ptr],y
ply
cont_count
ldx #16 ;cycle thru word 16 times
lda bits ;get the bit count back
inc a ;special case $FFFF to speed calc
beq add_16
bit_loop
dex
bmi return_loop ;we have scanned the word
lsr bits
bcc bit_loop
inc math_temp ;advance the count please
bra bit_loop
add_16
clc
lda math_temp
adc #$0010 ;$FFFF = 16 blocks free
sta math_temp
bra return_loop
;When we get here we have counted all the blocks that are marked as free.
;Now we need to see if there are any blocks marked as purgable.
calc_done
lda math_temp ;number of blocks free
;Now lets update the VCR to indicate that we know the number of free blocks
save_and_exit
ldy #free_blocks
sta [prodos_vcr_ptr],y ;total free count
jsr set_user_cache ;restore cache ('A' and 'P' preserved)
clc
rts
EXPORT kill_bitmap
kill_bitmap
pha
lda #bitmap_unknown ;flag bitmap as unknown
ldy #curr_bitmap
sta [prodos_vcr_ptr],y
pla
rts
ENDP
;==========================================================================
;find_free_blks This routine will scan the bitmap looking for a sequence
; of blocks that are marked as free. The routine will always
; try to allocate the blocks in one large group. If a group
; cannot be found to satisfy the request count then this
; routine will allocate the first available block.
; At least one block MUST be free for allocation!!!!!!!!!!
;
;Input: A = number of blocks to allocate. ( 'n' blocks)
; X = starting block number for search. (might be minus 7 blks)
; prodos_vcr_ptr (must be setup)
; ^avail_bitmap (must be valid)
;
;output: A = error or number of blocks allocated. (1 or 'n')
; X = starting block number of allocated blocks
; P = nvmxdizc
; ..000..1 = no blocks allocated (error)
; 0 = allocated
;
;uses: calc_bits
; math_temp
;
longa on
longi on
EXPORT find_free_blks
find_free_blks PROC
sta alloc_count ;number of blocks to allocate
stx alloc_start ;starting block number for search
stx first_free
jsr calc_bit ;absolute blk to relative blk.
;
; calc_bit results:
;
;A = bit mask $80 $40 $20 $10 $08 $04 $02 $01
;Y = byte offset in the bitmap buffer
;X = relative block number that contains the block number passed as input
;
sty byte_offset ;starting byte in block
xba
align_loop ; ;set blk num to byte boundary
asl a ;check bit position of user block num
bcs chk_avail ;ok here
dec first_free
bra align_loop ;check next bit
chk_avail
lda byte_offset ;are we word aligned?
lsr a
bcc aligned ;yes we are OK
dec byte_offset ;now set starting block to word boundary
sec
lda first_free
sbc #$0008
sta first_free ;everything should be word aligned now.
aligned
txa
ldy #avail_bitmap ;see if start is >= first free
cmp [prodos_vcr_ptr],y
bcs start_ok ;start is >= first free
lda [prodos_vcr_ptr],y
tax
ror a
ror a
ror a
ror a
ror a ;calc starting block number
sta first_free ;ex. rel blk #1 = blk #4096 or $1000
txa
stz byte_offset
start_ok
stz last_blk ;starting block for caller
stz math_temp ;reset the free block counter
tax ;now load in the bitmap block
next_blk
lda math_temp
pha ;save temp count
jsr get_bit_block
bcc got_block
ply ;fix the stack
rts ;return error please
got_block
pla ;reset temp count
sta math_temp
stz some_free ;indicates if ANY blocks are free!
ldy byte_offset ;start looking here for 1st free block
cpy #$01FF ;are we near then end?
bcc next_word
ldy #$01FE
next_word
ldx #16 ;scan all 16 bits
lda [bitmap],y ;get a bitmap word (16 blocks)
bne check_16 ;sorry no bits here.
stz math_temp ;reset the counter please.
stz last_blk
bra no_change
check_16
xba ;put bits in the right order
sta bits
sta some_free ;there are free blocks in this dir.
inc a ;check 16 please
bne scan_loop
;
;special case of all sixteen blocks allocated
;
clc
lda math_temp
adc #16 ;easy case
sta math_temp
lda last_blk ;do we have the starting blk yet?
bne chk_req_cnt ;we already have start block
lda first_free
sta last_blk
chk_req_cnt
lda math_temp ;see if we are done
cmp alloc_count
bcs req_filled ;request filled. We are done!!!
no_change
clc
lda first_free
adc #16 ;bump block number
sta first_free
bra bump_index ;go get the next word
scan_loop
lda math_temp ;see if we are done
cmp alloc_count
bcs req_filled ;request filled. We are done!!!
asl bits ;see if this block is available
bcc reset_count ;a used block
inc math_temp ;add one to block count
lda last_blk
bne next_bit
lda first_free
sta last_blk ;update start block
bra next_bit
reset_count
stz math_temp ;reset the counter
stz last_blk ;reset start block
next_bit ;
inc first_free ;bump the block number
dex
bne scan_loop
lda math_temp ;see if we are done
cmp alloc_count
bcs req_filled ;request filled. We are done!!!
bump_index
iny
iny
cpy #$01FF ;are we done?
bcc next_word ;not yet
ldy #curr_bitmap ;current loaded bitmap block
lda [prodos_vcr_ptr],y
inc a ;convert 0 to 'n' to 1 to 'n'+1
ldy #num_bitmap_blks
cmp [prodos_vcr_ptr],y
bcs find_one ;no more bitmap blocks to search!
jsr bump_1st_free ;bump to next block if needed.
stz byte_offset ;reset byte offset
ldx first_free
jsr abs_to_rel ;calc relative block number
jmp next_blk ;load in the next block
find_one ; ;allocate one block only
lda #$0001
ldx #$0000 ;start at block 0
jsr find_free_blks
rts
;now we need to update the bitmap blocks!
req_filled
ldx last_blk
ldy alloc_count
dey ;convert 1 to 'n' -> 0 to 'n-1'
alloc_loop ;
phy ;number of blocks wanted
phx ;block number to allocate
jsr alloc_block ;mark the block as used!
plx
ply
bcs bad_news
inx ;allocate the next block
dey
bpl alloc_loop
lda alloc_count ;user request satisfied
ldx last_blk ;starting block for allocation
clc ;no error
bad_news
rts
bump_1st_free
lda some_free ;is this block empty?
bne bad_news ;no...
ldy #avail_bitmap ;bump avail to next block if needed
lda [prodos_vcr_ptr],y
ldy #curr_bitmap
cmp [prodos_vcr_ptr],y
bne bad_news
ldy #avail_bitmap
lda [prodos_vcr_ptr],y
inc a
sta [prodos_vcr_ptr],y
rts
ENDP
;======================================================================
;free_vcr: This routine will free all memory associated with a
; volume control record.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; my_vcr_ptr (Points to main VCR)
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Uses: All registers
; release_seg (system routine to release a vcr)
;
;======================================================================
longa on
longi on
EXPORT free_vcr
free_vcr PROC
lda [my_vcr_ptr] ;get the volume ID
jsl release_vcr ;and call the systems nuke vcr routine
rts
ENDP
;======================================================================
;Remove_fcr: This routine will remove a FCR
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; my_fcr_ptr (must be setup)
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Uses: All registers
;
;======================================================================
longa on
longi on
EXPORT remove_fcr
remove_fcr PROC
ldy #dirty_file_cnt ;check if bitmap needs to be flushed
lda [prodos_vcr_ptr],y
dec a
bne @did_flush ;we do not need to flush the bitmap
jsr force_bitmap ;force a dirty block to disk
bcs @error ;we have to be able to flush
@did_flush
ldy #vcr_open_cnt ;subtract one from the open count
lda [my_vcr_ptr],y
dec a
bmi die_die_die ;the system is very sick
sta [my_vcr_ptr],y ;Update the open count in the VCR
lda [my_fcr_ptr] ;reference number to remove
jsl release_fcr ;nuke the puppy
clc ;we are very happy now
@error
rts
die_die_die
lda #'SK'
jml sys_death
ENDP
;======================================================================
;setup_params This routine is called at the beginning of each system
; call. It will setup parameters and verify certain fields
; of the os call. If a VCR is used and the media is swapped
; out of the system then the system mount routine will be called
;
;Input: A = undefined
; X = two times the call number
; Y = two times the class
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = class of call/2 (MUST be last instruction before RTS!!)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; pcount (contains pcount for call. 0=class0)
; my_pblk_ptr (points past pcount to first param)
; my_vcr_ptr (if available)
; prodos_vcr_ptr
; my_fcr_ptr (if available)
; pro_fcr_ptr
;
;Uses: All registers
; math_temp
; pcount
;
;What the table looks like and what fields we verify.
;
; 1) Max PCount for the call
; 2) Max Class for the call
; 3) Pathnames used by call
; 4) volume control record used
; 5) file control record used.
; 6) io buffer used.
; 7) flush file to disk
; 8) damaged disk protect
;
; |8|7|6|5|4|3| 2 | 1 |
; ___|_|_|_|_|_|_|_____|_________|
; | | | | | | | | | | | | | | | | |
; |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
; | | |
; 1 6 0 8 0 0
;
longa on
longi on
EXPORT setup_params
setup_params PROC
;First we load the flags from the start table and save them for later
;use.
lda gbuf_addr ;setup the general buffer pointer
sta gbuf_ptr
lda gbuf_addr+2
sta gbuf_ptr+2
stz drvr_vol_id ;zero the volume ID(for Mike). Allow each call to set it up
stz search_flag ;indicate normal find file
stz prodos_vcr_ptr
stz prodos_vcr_ptr+2
stz my_vcr_ptr
stz my_vcr_ptr+2
stz pro_fcr_ptr
stz pro_fcr_ptr+2
stz case_bits ;indicate that the case is all upper.
stz slug_block ;indicate no slug_ripple
stz close_flag ;indicate that 'flush_file' will not
; ;call slug_ripple
stz expand_record.expand_file
stz expand_record.expand_flag
stz chk_dirty_flag ;do not check the VCR to see if the file is dirty
stz dirty_flags
stz dirty_cnt_changed
stz write_occurred ;If non-zero a write occurred to the disk.
stz cp_device_flag ;indicate normal ChangePath 1/22/91 CAE
stz cp_flags ;assume not a duplicate volume 5-Dec-91 MSD
stz error_priority ;error priority is 0 3/05/91 CAE
lda start_tbl-2,x ;get the flags for the call
sta flags
and #$001F
sta max_pcount
;Go ahead and setup the default parameters.
stz pcount
;Now verify the max class of the call.
;
;To do this we divide class by two and compare to the calls max class
tya
lsr a
sta class
beq class0 ;this is a OLD style call
lda flags
and #$00E0 ;clear other flags please
asl a
asl a
asl a
xba
cmp class
bcs good_class
lda #invalid_class
bra error_out
good_class
;Now we need to verify the max pcount for the new class calls
lda [param_blk_ptr]
cmp max_pcount
bcc good_pcount
lda #invalid_pcount
;Carry has to be set before calling error_out!!
error_out
jmp main_exit
good_pcount
;We get here if the pcount is good. We also get here with a 'BCC'
;so if this changes we must add a CLC to the following 'ADD'
sta pcount
lda param_blk_ptr ;CARRY CLEAR on entry!!!!
adc #$0002
sta my_pblk_ptr
lda param_blk_ptr+2
adc #$0000
sta my_pblk_ptr+2
bra cont01
class0
lda param_blk_ptr
sta my_pblk_ptr
lda param_blk_ptr+2
sta my_pblk_ptr+2
cont01
ldx #pro_id ;setup FST number for our FST.
stx drvr_fst_num
jsl lock_mem ;make sure data blocks do not move
lda dev_num ;move the device number to driver
sta drvr_dev_num ;direct page area
;when we get here we know that both class and pcount are valid.
;now we need to check if a VCR or FCR is used by the call.
lda flags
and #vcr_used
beq check_fcr ;no VCR is used by the call
;setup my pointer to the VCR and deref VP to my local data.
ldx vcr_ptr
ldy vcr_ptr+2
jsl deref
stx my_vcr_ptr
sty my_vcr_ptr+2
jsr setup_my_vcr
stx prodos_vcr_ptr
sty prodos_vcr_ptr+2
lda flags ;do we need to check damage?
and #damage
beq vol_ok ;this must be a read only call.
ldy #vol_damaged ;is this volume damaged??
lda [prodos_vcr_ptr],y
inc a ;Have I already showed the error message
bne vol_ok ;the disk is not damaged
sec
lda #invalid_access ;get the error code please
jmp main_exit
vol_ok ;
ldy #vcr_status ;see if the volume is swapped out
lda [my_vcr_ptr],y
and #vcr_swapped
beq check_fcr ;the volume is online.
lda call_number ;see if this is a close call.
and #$DFFF ;get rid of the class please
cmp #$0014 ;Compare class zero close call number
bne @do_mount ;we must do a mount message
ldy #bitmap_dirty ;See if the Bitmap is dirty
lda [prodos_vcr_ptr],y
bne @do_mount ;we need the disk bad!!!
ldy #dirty_file_cnt ;see if there open files on disk that are dirty?
lda [prodos_vcr_ptr],y
beq check_fcr ;no open dirty files so skip mount.
@do_mount
jsr mount_volume ;force the user to put our disk online
check_fcr
lda flags
and #fcr_used
beq check_path ;no FCR is used by the call
;setup my pointer to the FCR and deref VP to my local data.
ldx fcr_ptr
ldy fcr_ptr+2
jsl deref
stx my_fcr_ptr
sty my_fcr_ptr+2
jsr setup_my_fcr ;build pointer to local data
stx pro_fcr_ptr
sty pro_fcr_ptr+2
lda flags ;do we need to flush the file?
and #flush_1st
beq check_path ;no flush needed
ldy #fcr_status ;see if the file is dirty
lda [pro_fcr_ptr],y
and #file_dirty
beq check_path ;file is clean!
jsr setup_io_ptrs
jsr flush_file ;force dirty blocks to disk
tax
lda flags ;clear IO needed bit
and #clr_io_used
sta flags
txa
bcc check_path
jmp main_exit
;Moved the check_path code into a seperate routine and we now
;only check for bad spans after we have located the target vol
;and determined that it is indeed a ProDOS volume.
; 10/10/91 JEV
check_path
; lda flags
; and #path_used
; beq check_io_buf ;no pathname is used
;See if there are any pathnames available
; lda path_flag
; and #path1_mask
; beq check_path2
; lda span1
; beq bad_span ;added 11/12/90 CAE
; cmp #$0010
; bcc check_path2
;bad_span sec ;added 11/12/90 CAE
;backup lda #bad_path_syntax
; brl error_out
;check_path2 ;
;We don't check span2 here if we are doing a change path call
;we need to do some preprocessing first so that error priority
;gets setup properly for the change path call.. added 8/21/91 JEV
; lda call_number ;see if this is a changepath call.
; and #$DFFF ;get rid of the class please
; cmp #$0004 ;Compare with changepath call number
; beq check_io_buf ;just continue
; lda path_flag
; and #path2_mask
; beq check_io_buf
; lda span2
; beq bad_span ;added 11/12/90 CAE
; cmp #$0010
; bcs backup
check_io_buf ;
lda flags
and #io_used
beq end_buf_setup
;now we need to setup real pointers to the I/O buffer. There is only
;one I/O buffer for a file. However, the I/O buffer varies in it's size
;and structure.
jsr setup_io_ptrs
end_buf_setup
lda flags ;see if we need to check for file dirty
and #do_dirty
beq @exit
sta chk_dirty_flag ; 00 = do not check
ldy #fcr_status ;get the status out of the File Control Record
lda [pro_fcr_ptr],y
and #file_dirty+data_dirty+index_dirty+master_dirty
sta dirty_flags ;save the flags
@exit
lda class
rts
ENDP
;======================================================================
;check_spans This routine is called when a pathname is used and we
; we need to verify that the spans are valid. If they
; are incorrect then we bail out.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = error code
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
;
EXPORT check_spans
check_spans PROC
lda path_flag
and #path1_mask
beq check_path2
lda span1
beq bad_span
cmp #$0010
bcc check_path2
bad_span sec
backup lda #bad_path_syntax
jmp main_exit
check_path2 ;
lda path_flag
and #path2_mask
beq @spans_ok
lda span2
beq bad_span
cmp #$0010
bcs backup
@spans_ok
rts
ENDP
;======================================================================
;process_path This routine is called when a pathname needs to be
; processed and located on the disk. If the pathname is
; not a prodos name then we bail out.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = error code
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; drvr_buf_ptr (points to the file entry)
; entry_offset (offset to entry within I/O buffer)
; parent_blk (entries parent block address)
;
;Uses: All registers
; math_temp
; pcount
;
longa on
longi on
EXPORT process_path
process_path PROC
lda #in_cache ;put directories in the cache
sta drvr_cache
lda #path1_mask ;and mask for path_flag
ldx path1_ptr
ldy path1_ptr+2
jsr build_path
bcc @chk_root_only
brl dev_or_vol ;No path just a device # or vol. name
@chk_root_only
lda search_flag ;do we allow a pathname?
and #root_only
beq @path_ok
lda #bad_path_syntax
sec
rts ;return error to caller
@path_ok
;If we fall thru then we know that there is a pathname to find.
;We also know that there is either a volume name or device.
stx hold_path_ptr
sty hold_path_ptr+2
lda dev_num ;There is a device number
beq cont01 ;Find Volume Name
sta drvr_dev_num
;next call removed 2/26/90 by Monte Benaresh
;leaving the call in doesn't let the user swap a new volumeÉ
;Éinto a device and specify a device name in the path
;jsr check_vcr_first ;see if we have a vcr for it
;bcc find_path ;we do?
;Look on a specific device
go_id_disk
lda drvr_dev_num
jsr id_disk ;go and find out if it is our disk
;this also allocates a VCR if needed
bcc find_path ;I own the disk.
jmp main_exit ;return error to GS/OS
cont01 ;
jsr vol_to_buffer
jsr find_volume ;locate the volume
bcc find_path
jmp main_exit
find_path lda #$8000 ;indicate that we found the volume 3/5/91 CAE
sta error_priority ;and it's a ProDOS volume 3/5/91 CAE
;So it's one of our volumes, now check to see if we are using paths, and
;the spans are ok. 10/10/91 JEV
lda flags
and #path_used
beq check_damage ;no pathname is used
jsr check_spans ;so check them
check_damage lda flags ;is this a call to check the disk
and #damage
beq vol_good
jsr setup_my_vcr ;setup the pointer first
stx prodos_vcr_ptr
sty prodos_vcr_ptr+2
ldy #vol_damaged ;see if the volume is damaged
lda [prodos_vcr_ptr],y
beq vol_good
lda #dir_error ;write protected
jmp damaged_message
vol_good ;
lda drvr_dev_num
ldx hold_path_ptr
ldy hold_path_ptr+2
jsr find_file ;try to locate the file
bcc save_parent
cmp #drvr_disk_sw ;was the media switched?
beq go_id_disk
bit search_flag ;is 'file not found' ok for this call?
bvc not_ok ;no, tell caller about the error
cmp #file_not_found
bne not_ok ;this error cannot be handled!
sec
rts ;tell caller that an error occured.
not_ok ;
sec
jmp main_exit
save_parent ;
sta entry_offset ;offset to the entry within the blk
lda drvr_blk_num ;block number of parent
sta parent_blk
cont02 stx drvr_buf_ptr ;save pointer to name
sty drvr_buf_ptr+2
process_exit ;
jsr set_user_cache
rts
;If we get here we know that there is no pathname to process.
;We also know if there is no device number then 'volume_name' contains
;a volume name that I am suppose to open.
dev_or_vol ;
lda dev_num
beq use_vol_name ;we must have a volume name.
jsr id_disk ;go and find out if it is our disk
bcs to_main_exit
;Before we check to see if the volume name is legal we must see if the
;volume is online. The reason we do this is to be compatible with P16.
;
;Example:
; Create '/VOLUME' ;If '/VOLUME' is online then
; ;the error returned is 'BadPath'
; Create '/VOLUME' ;If '/VOLUME' is offline then
; ;the error returned is 'VolNotFound'
;
its_mine lda #$8000 ;indicate that we found the volume 3/5/91 CAE
sta error_priority ;and it's a ProDOS volume 3/5/91 CAE
check_root bit search_flag ;is a root level valid?
bpl continue1 ;no problem with root level.
lda #bad_path_syntax
sec
to_main_exit ;
jmp main_exit ;return error to M.A. and J.J.
continue1 ;
lda [drvr_buf_ptr] ;points to volume name
ora #volume_header ;add back in the storage type
sta [drvr_buf_ptr]
lda #vol_dir_start
sta parent_blk
lda #$0004
sta entry_offset
bra process_exit
use_vol_name ;
jsr find_volume ;locate the volume
bcc its_mine
jmp main_exit
EXPORT set_user_cache
set_user_cache ;
pha ;save entry offset
lda user_cache ;set cache to user setting
sta drvr_cache
pla
rts
ENDP
;======================================================================
;send_info This routine is called when we need to return the
; informational parameters from a directory entry.
; This routine is called by 'OPEN' and 'GET_FILE_INFO'
;WARNING:
; This routine assumes parameters are in a certain order.
; I did this so I could use that same routine across several
; calls!
;
;Input: A = number of entries to send
; X = low word pointer to start of parameters
; Y = high word pointer to start of parameters
; P = nvmxdizc
; ..000...
; b = k
;
; one_entry (contians the info to send)
; drvr_buf_ptr (points to resource header block)
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Uses: All registers
; math_temp
; temp_ptr
; one_entry
; time_routines
;
longa on
longi on
EXPORT send_info
send_info PROC
stx temp_ptr
sty temp_ptr+2
;Save the number of entries the user wants.
sta math_temp
tax
bne continue
rts
;First we return the access as it is stored on disk.
continue ;
lda call_number ;is this a class one call?
and #$2000
beq class0
lda one_entry ;is this a volume entry?
and #$00F0 ;isolate the storage type
cmp #volume_header
bne not_vol
stz one_entry+aux_type_index
not_vol ;
cmp #subdir_type ;is it a subdir or volume?
bcc class0 ;no
stz one_entry+blks_used_index
class0 ;
ldy #pblk_s_access
lda one_entry+access_index
and #$00E7
sta [temp_ptr],y
dec math_temp
bne send_two
end_send1
clc
rts
;If we are here then we know the user wants the file type as well
send_two ;
ldy #pblk_s_ftype
lda one_entry+file_type_index
and #$00FF
sta [temp_ptr],y
dec math_temp
beq end_send1
;Now the user wants the aux type!
ldy #pblk_s_aux
lda one_entry+aux_type_index
sta [temp_ptr],y
iny
iny
lda #$0000
sta [temp_ptr],y
dec math_temp
beq end_send1
;Also send the storage type of the file
ldy #pblk_s_storage
lda one_entry
and #$00F0
lsr a
lsr a
lsr a
lsr a
sta [temp_ptr],y
dec math_temp
beq end_send1
;Now convert the create date and send it to.
send_five ;
ldx one_entry+create_index
ldy one_entry+create_index+2
jsr unpack_time
ldy #pblk_s_create
jsr send_time
dec math_temp
beq end_send1
;Now convert the modification data and time
ldx one_entry+last_mod_index
ldy one_entry+last_mod_index+2
jsr unpack_time
ldy #pblk_s_mod
jsr send_time
dec math_temp
beq @go_to_end
; We now store the ProDOS FST ID in the 5th byte of the option list buffer
ldy #pblk_s_opt ;index to option list pointer
lda [temp_ptr],y ;get the address of the option list buffer
tax
iny
iny
lda [temp_ptr],y
tay
bne @save_fst_id
txa
beq @skip_save
@save_fst_id
pei temp_ptr ;save the working temp_ptr since I am going to use it
pei temp_ptr+2 ;to handle the option list pointer
stx temp_ptr
sty temp_ptr+2
;
;Now I need to verify the option list length. I need at least 6 bytes.
;
lda [temp_ptr] ;get the length of the option list
cmp #$0004 ;is the minimum count OK
bcc opt_range_err ;the user doesn't even have the minimum size>>>>
tax ;save the count for data area check
lda #$0002 ;length of the ProDOS FST ID.
tay
sta [temp_ptr],y ;save the number of bytes stored
txa
cmp #$0006
bcc opt_size_error
iny
iny
lda #pro_id
sta [temp_ptr],y
;new code added 2/13/91 CAE
txa
cmp #6+32 ;is buffer big enough to hold HFS Finder Info?
bcc @no_info ;no
lda one_entry ;yes - so check storage type
and #$00F0
cmp #resource_type ;is it a resource file?
bne @no_info ;no - so no HFS info
jsr get_hfs_info ;yes - so get the HFS info if we have it
@no_info
;end of new code
pla
sta temp_ptr+2 ;restore
pla
sta temp_ptr
@skip_save
dec math_temp ;Option List is included in PCount
@go_to_end beq end_send
;Give the EOF as well. If the storage type is a resource file then I will
;get the EOF info from the resource block instead of the directory entry
ldy #pblk_s_storage
lda one_entry
and #$00F0
cmp #resource_type
beq send_res_eof
ldy #pblk_s_eof ;send the EOF for the file
lda one_entry+eof_index+2
and #$00FF
tax
lda one_entry+eof_index
jsr store_longword
dec math_temp
beq end_send
ldy #pblk_s_blks ;send the number of blocks used
lda one_entry+blks_used_index
ldx #$0000
jsr store_longword
dec math_temp
beq end_send
ldy #pblk_s_res_eof ;send the resource eof
backup ;
lda #$0000
tax
jsr store_longword
dec math_temp
beq end_send
ldy #pblk_s_res_blk ;send the number of blocks used by res
bra backup ;please send a zero Mr. ProDOS.
end_send clc
rts
opt_range_err
pla
sta temp_ptr+2 ;restore
pla
sta temp_ptr
lda #parm_range_err
sec
rts
opt_size_error
pla
sta temp_ptr+2 ;restore
pla
sta temp_ptr
lda #buff_too_small
sec
rts
;Send the EOF and blocks used from the header block.
send_res_eof ;
ldy #$0000 ;go get the info please from fork 1
jsr get_res_info ;A/X = EOF, Y = Blocks Used
phy ;save the blocks used field
ldy #pblk_s_eof ;send the EOF for the file
jsr store_longword
ldy #pblk_s_blks ;send the Blocks used
pla ;restore the Blocks used field
dec math_temp ;now see if they want blocks used.
beq end_res_send ;not today kind sir.
ldx #$0000
jsr store_longword
dec math_temp ;are we done
beq end_res_send
ldy #$0100 ;go get the info please from fork 2
jsr get_res_info ;A/X = EOF, Y = Blocks Used
phy
ldy #pblk_s_res_eof ;send the EOF for the file
jsr store_longword
ldy #pblk_s_res_blk ;send the Blocks used
pla ;restore the Blocks used field
dec math_temp ;are we done
beq end_res_send
ldx #$0000 ;send the blocks used by the res. fork
jsr store_longword
end_res_send ;
rts
;==========================================================================
;Grab the information from the resource block. first fork starts at
;offset $0000 in the header block. The second fork starts at offset $0100
;in the resource header block.
;
;format: storage_type (1 byte)
; key_block (2 bytes)
; blocks_used (2 bytes)
; eof (3 bytes)
get_res_info ;
lda [drvr_buf_ptr],y
and #$0003 ;check the storage type
beq file_dead ;this file is DOA
cmp #(tree_type/$10)+1
bcs file_dead
iny ;point to Key Block
iny
iny ;point to Blocks used
lda [drvr_buf_ptr],y
pha ;save blocks used for later
iny
iny ;point to EOF
lda [drvr_buf_ptr],y
pha ;save EOF low
iny
iny
lda [drvr_buf_ptr],y
and #$00FF ;clear any garbage
tax ;save EOF high
pla ;A = EOF Low
ply ;Y = Blocks used
rts ;A/X = EOF (A = Low) Y = Blocks Used
file_dead ;
sec ;sorry bad news
lda #bad_store_type
jmp main_exit ;no escape now!
;==========================================================================
;Get the HFS Finder Info if it has been stored in the resource block.
;The Finder Info is stored immediately after the mini-entry for the data fork,
;so it begins at offset $0008 in the block.
;
;format: entry size (1 byte)
; entry type (1 byte)
; HFS Finder Info (16 bytes)
; entry size (1 byte)
; entry type (1 byte)
; HFS Finder Info (16 bytes)
;
; entry_size = 18
; entry_type = 1 for first 16 bytes of Finder Info (FInfo)
; 2 for second 16 bytes of Finder Info (xFInfo)
;
;Note - the ProDOS FSM which runs on the Mac, has the ability to store only
; the first 16 bytes of Finder Info or only the second 16 bytes. So, we
; can't assume that both entries exist, or that they exist in a specific
; order.
;==========================================================================
entry get_hfs_info
get_hfs_info ;added 2/13/91 CAE
stz hfs_flag ;indicate that we have no HFS info
ldx #30
@zero_loop stz hfs_finfo,x ;zero out storage area for HFS info
dex ;since we may only find 1 entry
dex
bpl @zero_loop
ldy #8
@loop lda [drvr_buf_ptr],y ;get entry size from resource block
and #$00ff ;only want 1 byte
cmp #18 ;is it the right size for HFS info?
bne @done ;no - so we're done
iny
lda [drvr_buf_ptr],y ;yes - so get the entry type
and #$00ff ;only want 1 byte
cmp #finfo_type ;is it the first 16 bytes of HFS info?
bne @cont ;no
ldx #0 ;yes - so init index into storage area
jsr get_finfo ;and get the info
bra @loop ;loop
@cont cmp #xfinfo_type ;is it the second 16 bytes of HFS info?
bne @done ;no - so we're done
ldx #16 ;yes - so init index into storage area
jsr get_finfo ;and get the info
bra @loop ;loop
@done lda hfs_flag ;did we find any HFS info?
beq @exit ;no - so nothing to copy
ldx #0 ;init offsets
ldy #6
@copy_loop lda hfs_finfo,x ;copy the HFS Finder Info into the
sta [temp_ptr],y ;option list buffer
iny
iny
inx
inx
cpx #32
bcc @copy_loop
ldy #2
lda #2+32
sta [temp_ptr],y ;adjust the # of bytes stored
@exit rts ;return
get_finfo iny ;adjust offset
lda #8 ;we're going to copy 8 words
@loop pha ;save counter
lda [drvr_buf_ptr],y ;copy HFS Finder Info
sta hfs_finfo,x ;into storage area
iny ;adjust offsets
iny
inx
inx
pla ;retrieve counter
dec a
bne @loop ;loop until done
inc hfs_flag ;indicate that we found some HFS info
rts ;and return
;==========================================================================
store_longword ;
sta [temp_ptr],y ;store low order 16 bits of EOF
iny
iny
txa ;now store the upper 16 bits
and #$00FF
sta [temp_ptr],y
rts
;======================================================================
;This routine will store the time into the user's parameters block
EXPORT send_time
send_time ;
lda minutes
xba ;swap minutes and seconds
sta [temp_ptr],y
iny
iny
lda hours
sta [temp_ptr],y
iny
lda year
sta [temp_ptr],y
iny
lda day
beq zero_day ;the day is unknown
dec a ;convert to misc. tools hex format
zero_day ;
sta [temp_ptr],y
iny
lda month
beq zero_month
dec a ;convert to misc. tools hex format
zero_month ;
sta [temp_ptr],y
iny
lda day_of_week
xba ;swap day of week and null byte
sta [temp_ptr],y
rts
ENDP
;======================================================================
;chk_protected: This routine will see if a volume is write protected.
;
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; drvr_dev_num ;must be setup.
;
;Output: A = status Word ;see if driver ERS
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
;
; b = k
;
;
;Uses: All registers
;
longa on
longi on
EXPORT chk_protected
chk_protected PROC
jsr save_drvr
lda #drvr_status ;issue a status call to the device
sta drvr_call_num
lda #^status_list ;setup a pointer to the status list
sta drvr_slist_ptr+2
lda #status_list
sta drvr_slist_ptr
lda #$0006 ;a six byte request count
sta drvr_req_cnt
stz drvr_req_cnt+2
lda #blk_size
sta drvr_blk_size
stz drvr_stat_code ;issue a 'driver status' call.
jsr device_call ;blast off!!
bcs status_error ;An error?
lda status_list ;get the return status
status_error php
pha
jsr restore_drvr
pla
plp
rts
save_drvr ldx #drvr_bytes ;number of bytes to save
save_it lda drvr_dev_num,x
sta drvr_save,x
dex
bpl save_it
rts
restore_drvr ldx #drvr_bytes
restore_it lda drvr_save,x
sta drvr_dev_num,x
dex
bpl restore_it
rts
EXPORT chk_disk_protect
chk_disk_protect
stz chk_prot_swap ; haven't got disk switched yet (20-Dec-91 MSD)
lda my_vcr_ptr ;see if we have a VCR
ora my_vcr_ptr+2
beq do_chk ;go ahead and do the check
ldy #vcr_status ;do I need to status the disk?
lda [my_vcr_ptr],y
and #vcr_wr_enable
beq do_chk ;yes we need to check the media
rts ;just return to the caller
do_chk ;
jsr chk_protected ;is the disk protected?
bcs big_problem
tax
and #$0010 ;is the disk offline?
beq not_in_drive ;there is no disk!!!
txa
and #$0001 ;disk switched??
bne not_in_drive ;disk has been switched!
txa
and #disk_protect
bne is_protected
lda my_vcr_ptr ;see if we have a VCR
ora my_vcr_ptr+2
beq no_change ;go ahead and set the vcr status flag
ldy #vcr_status ;set the 'No Status' bit
lda [my_vcr_ptr],y
ora #vcr_wr_enable ;no need to do the status next time
sta [my_vcr_ptr],y
no_change txa ;restore original status word
rts
is_protected sec
lda #drvr_wr_prot ;return write protect error to caller
big_problem jmp main_exit
not_in_drive
;
; Begin 20-Dec-91 MSD
;
; We got a disk switched status, so try to find the volume (ask the user if necessary).
; This will force a new VCR to be built, and my_vcr_ptr will point to it.
;
lda chk_prot_swap ; already switched once?
bne do_disk_sw ; yes, so return disk switched error
inc chk_prot_swap ; found one disk swithced status
*** added 28-Feb-92 DAL and GAB -- Save GBUF and important direct page stuff
jsr save_drvr
pei <gbuf_ptr+2
pei <gbuf_ptr
pea chk_protect_gbuf>>16
pea chk_protect_gbuf
pea 0
pea $0200 ; move 512 bytes
pea move_sinc_dinc
jsl move_info
*** end 28-Feb-92, part one
jsr mount_volume ; find the volume and build a VCR for it
*** added 28-Feb-92 DAL and GAB -- Restore GBUF and important direct-page stuff
php
pha
jsr restore_drvr
pea chk_protect_gbuf>>16
pea chk_protect_gbuf
pei <gbuf_ptr+2
pei <gbuf_ptr
pea 0
pea $0200 ; move 512 bytes
pea move_sinc_dinc
jsl move_info
pla
plp
*** end 28-Feb-92, part two
bcs big_problem ; better not be an error (we already found the path)
bra do_chk ; try the status again
do_disk_sw
sec
lda #drvr_disk_sw ; give up, 2 consecutive disk switched statuses
bra big_problem
;
; End 20-Dec-91 MSD
;
; The following code was commented out because we should really retry the status call.
;
; ldy #vcr_dev ;update the VCR status please
; lda [my_vcr_ptr],y
; cmp drvr_dev_num ;is this the same device?
; bne just_exit ;no
; ldy #vcr_status ;mark volume swapped
; lda [my_vcr_ptr],y
; ora #vcr_swapped ;disk has been removed from drive
; sta [my_vcr_ptr],y
;just_exit ;
; txa ;original status word
; rts
chk_prot_swap dc.w 0 ; flag for disk switched (20-Dec-91 MSD)
ENDP
;======================================================================
;Mount_volume: This routine is called when we need to force the user
; To give us our disk back.
;
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; my_vcr_ptr (points to needed VCR)
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; b = k
;
;
;Uses: All registers
; find_volume
;
longa on
longi on
EXPORT mount_volume
mount_volume PROC
jsr save_the_world ;save alot of data.
jsr setup_vol_mesg ;move the VCR name to local buffer
try_again jsr find_volume ;go get the volume
bcc volume_online ;go and update everything
jsr issue_mount ;issue a mount request
bcc try_again ;user said volume is now online
jsr restore_world ;restore everything please
rts
volume_online lda drvr_dev_num ;where the volume was found
jsr restore_world ;A is saved
sta drvr_dev_num
ldy #vcr_dev ;update with the new device number
sta [my_vcr_ptr],y
ldy #vcr_status ;indicate that the volume is online
lda [my_vcr_ptr],y
and #vcr_swapped_in
sta [my_vcr_ptr],y
clc
rts ;volume is back on-line
save_the_world php
phy
phx
pha
lda world_flag ;see if data already saved
bne to_sys_death ;debug
inc world_flag
ldx #$004A ;save the direct page data
save_direct lda drvr_dev_num,x
sta direct_page,x
dex
bpl save_direct
ldx #$0052 ;save my direct page
save_my_direct lda fst_start,x
sta my_direct,x
dex
bpl save_my_direct
bra save_rest_exit
restore_world php
phy
phx
pha
ldx #$004A ;save the direct page data
restore_direct lda direct_page,x
sta drvr_dev_num,x
dex
bpl restore_direct
ldx #$0052 ;save my direct page
restore_dp lda my_direct,x
sta fst_start,x
dex
bpl restore_dp
dec world_flag
save_rest_exit pla
plx
ply
plp
rts
to_sys_death jsl sys_death
ENDP
;======================================================================
;build_path: This procedure will parse off the volume name out of a
; pathname if needed.
;
;Input: A = AND mask used on path_flag ($BFFF or $FFBF)
; X = low word pointer to pathname
; Y = high word pointer to pathname
; P = nvmxdizc
; ..000...
; b = k
;
; path_flag ;GS/OS parameter (See common equates)
; dev_num ;GS/OS parameter
;
;Output: A = undefined
; X = low word pointer to pathname
; Y = high word pointer to pathname
; P = nvmxdizc
; ..000..1 = no pathname (device # or vol name only)
; 0 = X and Y contain a pathname pointer
; b = k
;
; volume_name ;if volume name is available
; ;e.g. no device number is given
;
;Uses: All registers
; temp_ptr
; path_flag
; dev_num
; and_mask
;
longa on
longi on
EXPORT build_path
build_path PROC
sta and_mask ;setup by caller
;now adjust the pointer to point past the length word
inx
bne do_again
iny
do_again inx
bne cont
iny
cont
stx temp_ptr
sty temp_ptr+2 ;pointer to pathname
lda path_flag ;see if there is a pathname to process
and and_mask
bne proc_path ;there is a pathname so handle it
sec
rts ;device number the only thing available
proc_path ;
lda dev_num ;see if there is a device
beq no_device ;no device so isolate volume name
;tell caller device # and pathname
done
jsr set_case_bits ;save the case bits as needed please and convert to upper case
clc
rts
no_device ;
jsr vol_to_buffer ;out: A = terminator Y = length
pha ;save termination reason
iny
iny
tya ;length of name +1
ldy temp_ptr+2
clc
adc temp_ptr
tax
bcc do_end
iny ;adjust high word
do_end
pla ;restore terminator
bne done ;tell caller volume name and pathname!
pha
jsr set_case_bits ;set the case of the last path please
pla
sec ;tell caller volume name only!
rts
EXPORT vol_to_buffer
vol_to_buffer ;'build_path' has setup temp_ptr
ldy #$0001 ;to point past '/' in volume name
vol_name_loop
lda [temp_ptr],y
and #$00FF
beq loop_end ;we have hit the last character
cmp #delimiter ;see if it is a seperator
beq loop_end
if AllowLCPeriods then ;added 7-Aug-91 DAL
cmp #$0020
bne @not_blank
lda #$002E ;make a blank into a period
@not_blank
endif ;end 7-Aug-91 DAL
cmp #$0061 ;check if we need to convert to upper case. "a"
bcc @no_upper
cmp #$007B ;check "z"+1
bcs @no_upper
and #$00DF ;convert to upper case
@no_upper
sta volume_name+1,y ;store name in buffer
iny
cpy #$0011 ;is length > 15 characters? -- 1/12/92 AEN
bcc vol_name_loop ;no, keep copying
lda #bad_path_syntax ;else we've got a bad pathname
sec
jmp main_exit
loop_end
tax ;save the terminating reason
lda #$0000 ;terminate the string with a null byte
sta volume_name+1,y
dey ;adjust string length
sty volume_name
txa
rts
;
;This routine will save the case bits for the last piece of the pathname and
;will convert the input string to all uppercase.
;
Export set_case_bits
set_case_bits
pha
phx
phy
ldy #$0000 ;start at the first character please
@outter_loop
lda #$4000 ;arm the case bit mask please
sta case_bit_pattern
stz case_bits
@inner_loop
lda [temp_ptr],y ;get the user's input character please
and #$00FF ;now check delimiters
beq @end_of_string
cmp #delimiter
beq @next_part ;do the next part of the path please
if AllowLCPeriods then ;added 7-Aug-91 DAL
cmp #' '
bne @not_a_blank
lda #'.'
bra @was_a_blank
@not_a_blank
endif ;end 7-Aug-91 DAL
cmp #$0061 ;check if we need to convert to upper case. "a"
bcc @no_upper
cmp #$007B ;check "z"+1
bcs @no_upper
and #$00DF ;convert to upper case
@was_a_blank tax ;save the character please
lda case_bit_pattern
ora case_bits
sta case_bits ;save the case bit for this character
txa
@no_upper
sep #$20 ;8 Bit store
sta [temp_ptr],y ;save the character back please
rep #$20 ;back to 16 bit mode
lsr case_bit_pattern
iny ;move to the next position please
bra @inner_loop
@next_part
iny ;advance to the next part of the pathname
bra @outter_loop
@end_of_string
ply
plx
pla
rts
ENDP
;==========================================================================
;Move_vol_name: This routine will move the volume name pointed to by
; drvr_buf_ptr into the buffer pointed to by the address
; in the X and Y registers
;
;Input: A = undefined
; X = low word pointer to the destination buffer
; Y = high word pointer to the destination buffer
; P = nvmxdizc
; ..000...
; b = k
;
; drvr_buf_ptr ;points to the volume header in blk two
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: All registers
; temp_ptr
;
longa on
longi on
EXPORT move_vol_name
move_vol_name PROC
stx temp_ptr
sty temp_ptr+2
lda [drvr_buf_ptr]
and #$000F ;isolate name length
sta [temp_ptr] ;save the length in word format
tay
clc
lda temp_ptr
adc #$0002
sta temp_ptr
bcc ar1
inc temp_ptr+2
ar1 ;
longa off
sep #$20
move_loop ;
lda [drvr_buf_ptr],y
sta [temp_ptr],y
dey
bne move_loop
longa on
rep #$20
rts
ENDP
;==========================================================================
;set_default_buf: sets up the default buffer pointer
;
;
;Enter: jsr
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: A register
;
longa on
longi on
EXPORT set_default_buf
set_default_buf PROC
lda gbuf_addr ;setup the general I/O buffer
sta drvr_buf_ptr
lda gbuf_addr+2
sta drvr_buf_ptr+2
rts
EXPORT standard_req
standard_req ;
lda #blk_size
sta drvr_req_cnt
stz drvr_req_cnt+2
rts
ENDP
;======================================================================
;setup_my_vcr: this routine sets up a pointer to the prodos specific
; volume control record.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; my_vcr_ptr (Points to main VCR)
;
;Output: A = undefined
; X = low word of prodos vcr
; Y = high word of prodos vcr
; P = nvmxdizc
; ..000...
; b = k
;
;
;Uses: All registers
; my_vcr_ptr
;
longa on
longi on
EXPORT setup_my_vcr
setup_my_vcr PROC
clc
lda my_vcr_ptr
adc #vcr_dev+2 ;start of local data for vcr
tax
lda my_vcr_ptr+2
adc #$0000
tay
rts
ENDP
;======================================================================
;setup_my_fcr: this routine sets up a pointer to the prodos specific
; file control record data.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; my_fcr_ptr (Points to main FCR)
;
;Output: A = undefined
; X = low word of prodos fcr
; Y = high word of prodos fcr
; P = nvmxdizc
; ..000...
; b = k
;
;
;Uses: All registers
; my_fcr_ptr
;
longa on
longi on
EXPORT setup_my_fcr
setup_my_fcr PROC
clc
lda my_fcr_ptr
adc #fcr_access+2 ;start of local data for fcr
tax
lda my_fcr_ptr+2
adc #$0000
tay
rts
ENDP
;======================================================================
;setup_io_ptrs this routine sets up pointers to the active data buffers
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; my_fcr_ptr (Points to main FCR)
; pro_fcr_ptr (points to specific data)
;
;Output: A = undefined
; X = low word of prodos vcr
; Y = high word of prodos vcr
; P = nvmxdizc
; ..000...
; b = k
;
;
;Uses: All registers
; my_fcr_ptr
; math_temp
;
longa on
longi on
EXPORT setup_io_ptrs
setup_io_ptrs PROC
stz data_ptr ;init the pointers
stz index_ptr
stz master_ptr
stz data_ptr+2
stz index_ptr+2
stz master_ptr+2
lda #fcr_io_start
sta math_temp ;save the start of the I/O buffer
ldy #fcr_status ;see if master index is active
lda [pro_fcr_ptr],y
and #master_active
beq no_master ;there is no master index block
clc
lda my_fcr_ptr
adc math_temp
sta master_ptr
lda my_fcr_ptr+2
adc #$0000
sta master_ptr+2
jsr next_block ;bump the pointer to the next block
no_master ;
ldy #fcr_status ;see if there is an index block
lda [pro_fcr_ptr],y
and #index_active
beq no_index ;there is no index block
clc
lda my_fcr_ptr
adc math_temp
sta index_ptr
lda my_fcr_ptr+2
adc #$0000
sta index_ptr+2
jsr next_block ;bump the pointer to the next block
no_index ;
ldy #fcr_status ;see if there is a data block
lda [pro_fcr_ptr],y
and #data_active
beq end_io_setup ;there is no data block
clc
lda my_fcr_ptr
adc math_temp
sta data_ptr
lda my_fcr_ptr+2
adc #$0000
sta data_ptr+2
end_io_setup ;
rts
next_block ;
clc
lda math_temp
adc #blk_size
sta math_temp
rts
ENDP
;======================================================================
;setup_bitmap_ptr: This routine will return a pointer to the bitmap
; buffer and setup 'bitmap' to point to the bitmap buffer
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; my_vcr_ptr (Points to main VCR)
;
;Output: A = undefined
; X = low word of bitmap
; Y = high word of bitmap
; P = nvmxdizc
; ..000...
; b = k
;
; bitmap (bitmap pointer is setup)
;
;Uses: All registers
; my_vcr_ptr
;
longa on
longi on
EXPORT setup_bitmap_ptr
setup_bitmap_ptr PROC
clc
lda my_vcr_ptr
adc #bitmap_start ;starting location of bitmap
sta bitmap
tax
lda my_vcr_ptr+2
adc #$0000
sta bitmap+2
tay
rts
ENDP
;======================================================================
;clear_gbuf_low This routine will zero the lower half of the general
; buffer.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; gbuf_ptr
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Uses: All registers
; gbuf_ptr
;
longa on
longi on
EXPORT clear_gbuf_low
clear_gbuf_low PROC
lda #$0000
bra do_clear
EXPORT fill_gbuf_low
fill_gbuf_low ;
lda #$FFFF
do_clear ;
ldy #$01FE
clear ;
sta [gbuf_ptr],y
dey
dey
bpl clear
rts
ENDP
;======================================================================
;clear_gbuf_hi. This routine will zero the upper half of the general
; buffer.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; gbuf_ptr
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Uses: All registers
; gbuf_ptr
; temp3_ptr
;
longa on
longi on
EXPORT clear_gbuf_high
clear_gbuf_high PROC
clc
lda gbuf_ptr
adc #$0200
sta temp3_ptr
lda gbuf_ptr+2
adc #$0000
sta temp3_ptr+2
ldy #$01FE
lda #$0000
clear ;
sta [temp3_ptr],y
dey
dey
bpl clear
rts
ENDP
;======================================================================
;read_write_setup:
; this routine sets up common parameters between read/write
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; pcount ;for cache check
; my_pblk_ptr ;must be setup
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; user_buf_ptr
; user_req_cnt
; drvr_cache
; tran_cnt ;set to zero
;
;Uses: All registers
;
longa on
longi on
EXPORT read_write_setup
read_write_setup PROC
;Start code by turning off disk caching and setting up other parameters
lda user_cache
sta drvr_cache
stz tran_cnt
stz tran_cnt+2
lda pcount ;is cache priority avail?
cmp #$0005
bne no_cache
;If we get here then the user has provided a cache priority for the file.
;So we must verify that it is a legal value for caching.
get_priority ;
ldy #pblk_12_cache
lda [my_pblk_ptr],y
beq no_cache ;same as mine
cmp #user_cache_val
beq good_cache
bra param_err
good_cache ;
sta drvr_cache
;Setup a pointer to the user's buffer.
no_cache ;
ldy #pblk_12_buf
lda [my_pblk_ptr],y
sta users_buf_ptr
iny
iny
lda [my_pblk_ptr],y
and #$00FF
sta users_buf_ptr+2
;Go ahead and zero the user's transfer count.
ldy #pblk_12_tran ;zero the transfer count
lda #$0000
sta [my_pblk_ptr],y
iny
iny
sta [my_pblk_ptr],y
;Now lets get the user's request count.
ldy #pblk_12_req
lda [my_pblk_ptr],y
sta user_req_cnt
iny
iny
lda [my_pblk_ptr],y
sta user_req_cnt+2
tax
and #$FF00 ;is high byte zero?
beq good_cnt
param_err ;
sec
lda #parm_range_err
bra to_scm
;Handle the easy case of a request count of zero first.
good_cnt ;
txa
ora user_req_cnt
bne not_zero
clc
to_scm ;
jmp main_exit
not_zero ;
rts
ENDP
EXPORT rw_adjust
rw_adjust PROC ;read/write adjust
;==========================================================================
;adjust the buffer pointer by the number of bytes read
;
;IMPORTANT NOTES:
; drvr_req_cnt is always =< $0001FE00
; users_buf_ptr is always < $00FFFFFF
;
clc
lda users_buf_ptr
adc drvr_req_cnt
sta users_buf_ptr
lda users_buf_ptr+2
adc drvr_req_cnt+2
sta users_buf_ptr+2
;==========================================================================
;adjust the total number of bytes read from file
;
;IMPORTANT NOTES:
; tran_cnt is always =< $00FFFFFF
; drvr_req_cnt is always =< $0001FE00
;
;Carry is clear from the above addition.
lda tran_cnt
adc drvr_req_cnt
sta tran_cnt
lda tran_cnt+2
adc drvr_req_cnt+2
sta tran_cnt+2
;==========================================================================
;adjust the current file marker
;
;IMPORTANT NOTES:
; curr_mark is always =< $00FFFFFF
; drvr_req_cnt is always =< $0001FE00
;
lda curr_mark
adc drvr_req_cnt
sta curr_mark
lda curr_mark+2
adc drvr_req_cnt+2
sta curr_mark+2
;adjust the number of bytes remaining to send
sec
lda user_req_cnt
sbc drvr_req_cnt
sta user_req_cnt
lda user_req_cnt+2
sbc drvr_req_cnt+2
sta user_req_cnt+2
rts
ENDP
;==========================================================================
;entry_to_offset This routine will convert the data from a directory
; header to an offset.
;
;Input: A = entry_number
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = entry_offset
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;
;Uses: All registers
longa on
longi on
EXPORT entry_to_offset
entry_to_offset PROC
and #$00FF
dec a
cmp #$000D ;is it out of range?
bcs error
asl a
tay
lda dir_offset_tbl,y
clc
error ;
rts
ENDP
;======================================================================
;Issue Mount:calls the Mount_Message Routine with a pointer to the name
; stored in Volume_name.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; volume_name ;must be setup
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Mount_Message
;
EXPORT issue_mount
issue_mount PROC
lda volume_name
xba
sta volume_name
pea volume_name>>16
pea volume_name+1 ;push a pointer to the volume name
lda #$0001 ;1 = manditory mount!
jsl mount_message
tax ;get the result code
lda volume_name
xba
sta volume_name
txa ;restore result code 0 = ok 1 = cancel
bne cancel_mount
clc ;user selected OK
rts
cancel_mount ;
sec ;return volume not found error to user
lda #vol_not_found
rts
ENDP
;======================================================================
;Damaged_message: This routine will tell the user that the disk cannot
; be written too. The routine will also jmp to the
; main exit routine.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
;
EXPORT damaged_message
damaged_message PROC
pha
jsr show_damage
jsl unlock_mem ;release any memory
sec
pla ;get the error code please
jml sys_exit
EXPORT show_damage
show_damage ;
jsr setup_vol_mesg ;move the name from the VCR to buffer
lda volume_name ;make Gstring a Pstring
xba
sta volume_name
pea $0003 ;message number three
pea volume_name>>16
pea volume_name+1 ;push a pointer to the volume name.
pea $0000
pea $0000 ;push dummy space.
jsl report_error
lda volume_name ;make Pstring a Gstring
xba
sta volume_name
rts
EXPORT setup_vol_mesg
setup_vol_mesg ;
ldy #vcr_name ;index to Name VP
lda [my_vcr_ptr],y
tax
iny
iny
lda [my_vcr_ptr],y
tay
jsl deref ;make a real pointer to vol name
stx temp_ptr
sty temp_ptr+2
lda [temp_ptr] ;get the length of the string
tay
iny
move_loop ;
lda [temp_ptr],y
sta volume_name,y
dey
bpl move_loop
rts
ENDP
;======================================================================
;read_with_mount:
; This routine with make a device read call and if an
; error occurs (other then the I/O error) the mount
; volume routine will be called.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; all parameters for the driver must be setup.
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; device_call
EXPORT read_with_mount
read_with_mount PROC
lda #drvr_read
bra do_call
EXPORT write_with_mount
write_with_mount
lda #drvr_write
do_call
sta drvr_call_num
jsr dev_with_mount
rts
ENDP
;======================================================================
;read_with_cache:
;write_with_cache:
; This routine with make a device read/write call and
; store the data in the system cache. error occurs
; (other then the I/O error) the mount volume routine
; will be called.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; all parameters for the driver must be setup.
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; device_call
EXPORT read_with_cache
read_with_cache PROC
lda #drvr_read
bra continue
EXPORT write_with_cache
write_with_cache ;
lda #drvr_write
continue ;
sta drvr_call_num
lda [my_vcr_ptr] ;setup the Volume ID field
sta drvr_vol_id
lda drvr_cache ;save current cache setting
pha
lda #in_cache ;store this block in the cache
sta drvr_cache
jsr dev_with_mount
php ;save processor status 'A' = error code
tax ;save any error code
lda 2,s ;restore old cache value
sta drvr_cache
txa
plp ;restore carry bit
plx ;fix the stack
rts
ENDP
;======================================================================
;dev_with_mount This routine with make a device call and if an
; error occurs (other then the I/O error) the mount
; volume routine will be called.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; all parameters for the driver must be setup.
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; device_call
EXPORT dev_with_mount
dev_with_mount PROC
jsr device_call
bcs check_err
rts
check_err
cmp #drvr_disk_sw ;is this a disk switch error?
beq do_find ;go and find the volume please
cmp #drvr_off_line ;is the volume off-line?
beq do_find
sec
rts ;This is some real error from the user
do_find
jsr mount_volume
bcc dev_with_mount ;re-issue the original call please.
rts ;return the error please
ENDP
;======================================================================
;Device_call:calls the dispatcher and trys to read or write. If the
; call returns a disk switched error then the routine will
; re-read the disk again.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; all parameters for the driver must be setup except for
; drvr_blk_size and drvr_fst_num, these are setup by the
; call. (ONLY if the call is a READ or WRITE)
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
;
EXPORT device_call
device_call PROC
lda drvr_call_num
cmp #drvr_status
bcs to_device ;just call the device
cmp #drvr_write
beq is_write
lda drvr_cache
and #$7FFF ;clear the deferred bit if not a write
sta drvr_cache
bra do_it
is_write
sta write_occurred ;indicate that a write occured during this call
lda drvr_dev_num ;save the device number that I have written too.
sta write_dev_num
do_it
lda #blk_size
sta drvr_blk_size
stz drvr_blk_num+2
to_device
jsl dev_dispatcher ;call the read routine
rts
ENDP
;======================================================================
;flush_bitmap: This routine forces a dirty bitmap to be written to
; disk. The bitmap will be written to disk only if the
; file counter is equal to zero.
;
;force_bitmap: same as flush_bitmap except the bitmap block is ALWAYS
; flushed.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; prodos_vcr_ptr
; my_vcr_ptr
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; drvr_buf_ptr
; dev_with_mount
;
longa on
longi on
EXPORT flush_bitmap
flush_bitmap PROC
ldy #dirty_file_cnt ;see if there open files on disk?
lda [prodos_vcr_ptr],y
beq force_bitmap ;no open files so do flush.
clc
rts ;just exit.
EXPORT force_bitmap
force_bitmap ;This entry point force block to disk
ldy #bitmap_dirty
lda [prodos_vcr_ptr],y
beq no_change
;Get the device number first
ldy #vcr_dev
lda [my_vcr_ptr],y
sta drvr_dev_num
;We need to update the disk since the bitmap is corrupt
;First we calculate the block number of the block to write
ldy #vol_bitmap_addr
lda [prodos_vcr_ptr],y
ldy #curr_bitmap
clc
adc [prodos_vcr_ptr],y
sta drvr_blk_num
stz drvr_blk_num+2
;Get the address of where the bitmap block is located.
;setup drvr_buf_ptr to point to the bitmap buffer
jsr setup_bitmap_ptr
stx drvr_buf_ptr
sty drvr_buf_ptr+2
;setup the call to do a write
;Force the request count to 512 bytes
lda #blk_size
sta drvr_req_cnt
stz drvr_req_cnt+2
;Call the device.
jsr write_with_cache
bcc clear_dirty
rts
clear_dirty ;
ldy #bitmap_dirty
lda #$0000
sta [prodos_vcr_ptr],y
ldy #curr_bitmap ;see if this is first free block.
lda [prodos_vcr_ptr],y
ldy #avail_bitmap
cmp [prodos_vcr_ptr],y
bne no_change
inc a ;see if this is the last bitmap blk.
ldy #num_bitmap_blks
cmp [prodos_vcr_ptr],y
bcs no_change ;this is the last bitmap block
ldy #$01FE ;we must check first free
chk_first_free ;
lda [drvr_buf_ptr],y
bne no_change
dey
dey
bpl chk_first_free ;check the entire block please.
ldy #avail_bitmap ;bump avail to next block please
lda [prodos_vcr_ptr],y
inc a
sta [prodos_vcr_ptr],y
no_change ;
clc
rts
ENDP
;==========================================================================
;flush_file: This routine will write any modified blocks to disk.
;
;Input: A = dev_number
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; pro_fcr_ptr (points to data to write)
; my_vcr_ptr (points to main VCR)
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: All registers
longa on
longi on
EXPORT flush_file
flush_file PROC
stz flush_entry ;indicate that we should flush everything
bra cont_flush
Entry do_fast_flush ;entry point used by the fast version of the flush call
do_fast_flush
lda #1 ;set flag
bra stuffit
EXPORT flush_io_Buffer
flush_io_buffer
lda #$FFFF ;do not flush the directory entry
stuffit
sta flush_entry
cont_flush
jsr standard_req
;First lets call flush bitmap which will update the bitmap for us
jsr flush_bitmap
bcs error_return ;return the error directly
;No we need to flush the file itself. We only flush blocks that need to
;be flushed. We will always update the file entry.
flushed ;
ldy #fcr_status ;get the status bits
lda [pro_fcr_ptr],y
sta flush_flags
bit #file_dirty ;see if there is anything to update
bne do_update
clc
error_return
rts ;file does not need to be updated
;First lets see if we need to update the master index block.
do_update
bit #master_dirty
beq check_index
ldy #master_blk_num ;get the block number
lda [pro_fcr_ptr],y
sta drvr_blk_num
beq clear_master ;master block is sparse so do not update disk
ldx master_ptr
ldy master_ptr+2
jsr flush_block
bcs error_return ;return the error code
clear_master ;
lda #master_clean
jsr clr_fcr_status ;mark master block as clean
;Update the index block if needed
check_index ;
lda flush_flags
bit #index_dirty ;is the index block dirty
beq check_data
jsr flush_index
bcs error_return
;Update the data block if needed.
check_data ;
lda flush_flags
bit #data_dirty ;is the data block dirty
beq update_entry
jsr flush_data_blk
bcs error_return
update_entry
lda flush_entry ;do we update the directory entry?
beq @do_entry ;yup, update the whole thing
bmi @just_data ;no, just flush the data blocks
; else we're doing a fast flush call here. flush dirty bitmap and
; dirty directory ('dirty' meaning that anything besides mod date
; and backup-bit have been changed).
jsr force_bitmap ;force the bitmap update
lda flush_flags ;is the directory dirty?
bit #dirEntry_dirty
bne @do_entry ;yes, so just go ahead and do the full flush
@just_data
clc
rts
@do_entry
lda #file_clean
jsr clr_fcr_status ;mark master block as clean
;
;Now lets update the VCR count to indicate that the file is clean
;
lda dirty_cnt_changed
bne @changed_once ;have we adjusted the number already?
inc dirty_cnt_changed
ldy #dirty_file_cnt
lda [prodos_vcr_ptr],y
beq @major_error
dec a
sta [prodos_vcr_ptr],y
jsr flush_bitmap ;now go and update the bitmap
bcs go_cannot_update
@major_error
@changed_once
jsr set_default_buf ;use the system buffer
jsr standard_req
jsr update_mod_time ;make sure we use the correct time
jsr get_parent ;load the I/O buffer with directory blk
go_cannot_update
bcs to_cannot_update
jsr verify_storage ;verify storage type
;
;Change for final release 8/16/88
;
;
; First we update the File Control Record to indicate the correct backup state
;
ldy #fcr_disk_acc ;update fcr along with dir entry
lda [pro_fcr_ptr],y
ora #backup_on
sta [pro_fcr_ptr],y
;
;Now we update the directory entry to indicate the correct backup state
;
ldy #access_index ;index to the access byte in a dir entry
lda [temp_ptr],y
ora #backup_on ;set the backup bit before update of disk
sta [temp_ptr],y
bcs to_cannot_update
;If the file is a resource file then only update the modification time.
;Otherwise update the entire directory entry.
ldy #fcr_file_id
lda [pro_fcr_ptr],y
ldy #fcr_key_blk
cmp [pro_fcr_ptr],y
beq entry_ok ;file is not a resource file
jsr update_resource ;update the resource header
bcs to_cannot_update ;cannot write info to disk!
jsr set_default_buf ;restore buffer pointer
ldy #fcr_modified ;index into FCR
lda [pro_fcr_ptr],y
ldy #last_mod_index ;index into directory block
sta [temp_ptr],y ;update the directory entry
ldy #fcr_modified+2
lda [pro_fcr_ptr],y
ldy #last_mod_index+2
sta [temp_ptr],y
bra update_disk ;now update the directory
;Move the entire directory info then write the block back to disk.
entry_ok ;
ldy #fcr_storage
lda [temp_ptr]
and #$FF0F ;clear current storage type
ora [pro_fcr_ptr],y ;adjust the storage type
sta [temp_ptr]
ldx #fcr_eof
ldy #fcr_curr_eof ;move the current EOF to disk EOF
lda [pro_fcr_ptr],y
phy
txy
sta [pro_fcr_ptr],y
inx
ply
iny
lda [pro_fcr_ptr],y
txy
sta [pro_fcr_ptr],y
ldy #fcr_header_ptr ;start at the end
ldx #header_ptr_index
blast_loop ;
lda [pro_fcr_ptr],y
phy
txy
sta [temp_ptr],y
dex
ply
dey
cpy #fcr_file_type
bne blast_loop
update_disk
jsr write_with_cache
to_cannot_update
bcs cannot_update
lda #dirEntry_clean
jsr clr_fcr_status ;mark directory entry as clean
lda close_flag ;should we update the directories?
ora time_stamp_opt ;check user's option for time stamp too
ora flush_flags ;and which type of flush we're doing
beq dont_bother ;no directory updates please
ldy #header_ptr_index
lda [temp_ptr],y
sta drvr_blk_num
cmp #vol_dir_start
beq dont_bother
jsr read_with_cache
bcs cannot_update
ldy #parent_index ;get the parent block number
lda [drvr_buf_ptr],y
sta slug_block ;save the block number
ldy #par_entry_num
lda [drvr_buf_ptr],y
jsr entry_to_offset
sta slug_offset
jsr slug_ripple ;do the slug ripple
rts
dont_bother ;
clc
cannot_update ;
rts
EXPORT flush_index
flush_index ;
ldy #index_blk_num ;get the block number
lda [pro_fcr_ptr],y
sta drvr_blk_num
beq clear_index ;the block is sparse so don't write it!
ldx index_ptr
ldy index_ptr+2
jsr flush_block
bcc clear_index
rts ;return the error code
clear_index ;
lda #index_clean
jsr clr_fcr_status ;mark master block as clean
rts
EXPORT flush_data_blk
flush_data_blk ;
ldy #data_blk_num ;get the block number
lda [pro_fcr_ptr],y
sta drvr_blk_num
beq clear_data ;the block is sparse so don't write it to disk
ldx data_ptr
ldy data_ptr+2
jsr flush_block2
bcc clear_data
rts ;return the error code
clear_data ;
lda #data_clean
jsr clr_fcr_status ;mark master block as clean
clc
rts
;If we get here then we are updating a resource file. So we need to update
;the resource header block with the following information.
;
; Storage Type
; key_block
; blocks used
; eof
;
update_resource ;
lda drvr_blk_num ;save the block number please
pha
jsr standard_req
jsr set_default_buf
inc drvr_buf_ptr+1
inc drvr_buf_ptr+1 ;use the high block of the buffer!!!
ldy #fcr_file_id ;index to header block
lda [pro_fcr_ptr],y
sta drvr_blk_num ;read in the block please
jsr read_with_cache
bcs end_res_update
ldy #fcr_res_num ;which resource are we updating?
lda [pro_fcr_ptr],y
xba ;make an index $0000 or $0100
tax ;point to start of header
ldy #fcr_storage ;stoarge type of file
lda [pro_fcr_ptr],y
lsr a ;convert $10,$20,$30 -> $1,$2,$3
lsr a
lsr a
lsr a
txy ;index to storage type
sta [drvr_buf_ptr],y
inx ;advance to key block
ldy #fcr_key_blk
lda [pro_fcr_ptr],y
txy
sta [drvr_buf_ptr],y
inx
inx ;advance to blocks used
ldy #fcr_blks_used
lda [pro_fcr_ptr],y
txy
sta [drvr_buf_ptr],y
inx
inx ;advance to EOF
ldy #fcr_curr_eof
lda [pro_fcr_ptr],y
txy
sta [drvr_buf_ptr],y
inx
ldy #fcr_curr_eof+1
lda [pro_fcr_ptr],y
txy
sta [drvr_buf_ptr],y
jsr write_with_cache ;Update the disk
bcs end_res_update
clc
ldy #$0003 ;index to the blocks used data
lda [drvr_buf_ptr],y
ldy #$0103 ;index to blocks used resource
adc [drvr_buf_ptr],y
adc #$0001 ;add in the header block
ldy #blks_used_index ;index into directory block
sta [temp_ptr],y ;update the directory entry
clc
end_res_update
plx ;restore the block number
stx drvr_blk_num
rts
;==========================================================================
;This routine reads the GS clock and stores the time in the modification
;field of the current FCR.
update_mod_time ;
jsr pack_time ;time for mod date (X=low Y=high)
phy ;save high word
ldy #fcr_modified
txa
sta [pro_fcr_ptr],y
iny
iny
pla
sta [pro_fcr_ptr],y ;update high word
rts
;==========================================================================
;This routine will read in the parent block for a FCR and update the
;modification date.
;
;input: pro_fcr_ptr ;MUST be setup
;
;output: carry = error
; temp_ptr ;points to the file entry
get_parent ;
ldy #fcr_parent
lda [pro_fcr_ptr],y ;get the address of the directory blk
sta drvr_blk_num
jsr read_with_cache ;get the directory block please
bcc block_loaded
rts
block_loaded ;
ldy #fcr_entry_offs ;offset to directory entry
lda [pro_fcr_ptr],y
adc drvr_buf_ptr
sta temp_ptr
lda #$0000
adc drvr_buf_ptr+2
sta temp_ptr+2
clc
rts
;==========================================================================
;This routine verifies that the entry pointed to by 'temp_ptr' is a valid
;ProDOS entry. It verifies this by looking at the storage type.
verify_storage ;
lda [temp_ptr] ;get stoarge/length byte
and #$00F0 ;verify the storage type please
cmp #resource_type
beq good_entry
cmp #seedling_type
beq good_entry
cmp #sapling_type
beq good_entry
cmp #tree_type
beq good_entry
cmp #subdir_type
beq good_entry
lda #dir_error ;the directory must be damaged
sec
rts
good_entry clc
rts
;Force the block to disk
flush_block ;
stx drvr_buf_ptr
sty drvr_buf_ptr+2
jsr write_with_cache
rts
flush_block2 ;
stx drvr_buf_ptr
sty drvr_buf_ptr+2
jsr write_with_mount
rts
EXPORT clr_fcr_status
clr_fcr_status ;
ldy #fcr_status
and [pro_fcr_ptr],y
sta [pro_fcr_ptr],y
rts
ENDP
;==========================================================================
;slug_ripple: This routine will time stamp the parent directories for ALL
; operations that require writing to the directory. This
; routine is SLOW and is very stupid on our type of system.
; It is put here for compatability only.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; drvr_buf_ptr (points to the directory entry)
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: All registers
; device_dispatcher
;
longa on
longi on
EXPORT slug_ripple
slug_ripple PROC
lda time_stamp_opt ;are we time stamping at all?
bne do_stamp
clc
rts
do_stamp
lda drvr_blk_num
pha
lda drvr_buf_ptr
pha
lda drvr_buf_ptr+2
pha
jsr pack_time ;get the time please
clc
lda gbuf_addr
adc #blk_size
sta drvr_buf_ptr
lda gbuf_addr+2
adc #$0000
sta drvr_buf_ptr+2
lda slug_block ;setup the block
bne @around
brl do_volume_mod ;no slug block, so time stamp the volume please
@around sta drvr_blk_num
slug_loop
jsr read_with_cache ;read in the block please
bcs end_slug_err
ldy slug_offset ;offset to the entry
lda [drvr_buf_ptr],y
and #$00F0 ;check the storage type
cmp #subdir_type
bne bad_slug_dir ;sorry bud
tya
clc
adc #last_mod_index
tay ;index to the last mod field
lda pro_time ;Get the time we are going to stamp with
sta [drvr_buf_ptr],y
lda pro_time+2
iny
iny
sta [drvr_buf_ptr],y
;new code added 3/15/91 CAE
ldy slug_offset ;offset to the entry
tya
clc
adc #access_index
tay ;index to the access field
lda [drvr_buf_ptr],y
ora #backup_on ;set the backup bit
sta [drvr_buf_ptr],y
;end of new code
jsr write_with_cache
bcs end_slug_err ;a major error
lda time_stamp_opt ;are we walking to root?
dec a
beq end_slug ;not today.
clc
lda slug_offset ;offset to the entry
adc #header_ptr_index ;point to the header pointer please
tay
lda [drvr_buf_ptr],y
cmp #vol_dir_start ;is this the root?
beq do_volume_mod ;set the volume modification date
cmp drvr_blk_num ;is the block already loaded?
beq block_loaded ;you bet-cha
sta drvr_blk_num ;next block to read
jsr read_with_cache
bcs end_slug_err ;sorry bud major error
block_loaded
ldy #parent_index
lda [drvr_buf_ptr],y
sta drvr_blk_num ;new block number
iny
iny ;get the entry number
lda [drvr_buf_ptr],y
jsr entry_to_offset
sta slug_offset ;the new offset
bra slug_loop ;ripple to the top.
bad_slug_dir
lda #dir_error
bra end_slug_err
end_slug
clc
end_slug_err
plx
stx drvr_buf_ptr+2
plx
stx drvr_buf_ptr
plx
stx drvr_blk_num
rts
;
;Now lets go and time stamp the volume please
;
do_volume_mod
lda #vol_dir_start ;Setup the block number for the Root
sta drvr_blk_num
jsr read_with_cache ;read in the block please
bcs end_slug_err ;I hate it with this happens
ldy #vol_mod_offset ;offset to the Mod time field for the volume
lda pro_time
sta [drvr_buf_ptr],y
iny
iny
lda pro_time+2
sta [drvr_buf_ptr],y ;store the time
;new code added 3/15/91 CAE
ldy #vol_acc_offset ;offset to the access field
lda [drvr_buf_ptr],y
ora #backup_on ;set the backup bit on disk
sta [drvr_buf_ptr],y
;end of new code
ldy #vol_mod ;update the VCR as well as the disk
lda pro_time
sta [prodos_vcr_ptr],y
iny
iny
lda pro_time+2
sta [prodos_vcr_ptr],y
;new code added 4/4/91 CAE
ldy #vol_access ;offset to the access field
lda [prodos_vcr_ptr],y
ora #backup_on ;set the backup bit in the VCR
sta [prodos_vcr_ptr],y ;so that the disk and VCR match
;end of new code
jsr write_with_cache
bcs end_slug_err ;a major error
bra end_slug ;no error, all is wonderful
ENDP
;==========================================================================
;extend_subdir This routine will extend a directory if needed.
;
;input: free_dir_blk ;dir blk with a empty entry.
; dir_start_blk ;starting block of the directory
; dir_last_blk ;last block of the directory
;
;output: free_dir_blk ;dir blk with a empty entry.
; free_dir_offset ;offset to directory entry within block
; dir_start_blk ;starting block of the directory
; dir_last_blk ;last block of the directory
;
longa on
longi on
EXPORT extend_subdir
extend_subdir PROC
with expand_record
bit expand_file ;are we adding a directory entry?
bmi good_exit ;if we are expanding a file we donot need to extend the
;directory
lda free_dir_blk ;is there a free entry for this file?
bne good_exit ;do not extend the directory
lda dir_start_blk
cmp #vol_dir_start ;is this the root directory?
bne not_root
lda #vol_dir_full ;return an error please
sec
rts
not_root ;
lda #$0001 ;allocate one block for directory.
ldx dir_last_blk ;start looking here for next block
jsr find_Free_blks ;go and get one
bcs error_out ;error out
stx drvr_blk_num ;this is the new directory block
stx free_dir_blk ;new block number
lda #$0004 ;index to first free entry
sta free_dir_offset
jsr set_default_buf ;use the genaral buffer
jsr standard_req ;512 byte write
jsr clear_dir_buf ;clear the 512 byte general buffer
jsr append_last_blk
bcs error_out ;major problem
jsr update_forw_lnk ;update forward link to new block
bcs error_out
jsr update_parent ;update parents blocks used count
bcs error_out
good_exit ;
clc
error_out ;
rts ;extend complete
;==========================================================================
;Update_parent: This routine will update the blocks used and last mod date
; of the parent entry for the directory.
update_parent ;
lda dir_start_blk
sta drvr_blk_num ;key block for directory
jsr read_with_cache
bcs exit_update
ldy #parent_index
lda [gbuf_ptr],y ;get the parent block number
sta drvr_blk_num
ldy #par_entry_num
lda [gbuf_ptr],y
and #$00FF ;get the number
dec a
cmp #$000D ;number of entries
bcs bad_dir_bud
asl a ;times 2 for a word table
tay
lda dir_offset_tbl,y
sta entry_offset ;save offset to dir entry
jsr read_with_cache ;read in the parent block
bcs exit_update
ldy entry_offset
lda [gbuf_ptr],y
and #$00F0 ;is this a subdir entry?
cmp #subdir_type
bne bad_dir_bud
tyx
clc
tya
adc #$0013 ;index to blocks used for file
tay
lda [gbuf_ptr],y
inc a
sta [gbuf_ptr],y
iny
iny ;point to EOF
clc
lda [gbuf_ptr],y
adc #blk_size
sta [gbuf_ptr],y ;add 512 to the EOF field
bcc good_eof
iny
iny ;point to the third byte of EOF
longa off
sep #$20 ;short A
lda [gbuf_ptr],y
inc a
sta [gbuf_ptr],y
longa on
rep #$20
good_eof ;
clc
txa
adc #$0021 ;index to mod time
pha
jsr pack_time
ply ;index
lda pro_time
sta [gbuf_ptr],y ;update mod time
iny
iny
lda pro_time+2
sta [gbuf_ptr],y
jsr write_with_cache
exit_update ;
rts
bad_dir_bud ;
lda #dir_error ;bad directory block
sec
rts
;==========================================================================
;update_forw.. This routine will link the new directory block just
; allocated to the current last block of the directory.
update_forw_lnk ;
lda dir_last_blk
sta drvr_blk_num ;now read in the directory block.
jsr read_with_cache
bcs forw_exit ;major problems. sorry about droppings!
ldy #$0002 ;index to next pointer
lda [gbuf_ptr],y
bne bad_dir_bud
lda free_dir_blk ;forward link directory block.
sta [gbuf_ptr],y
jsr write_with_cache
forw_exit rts
;==========================================================================
;append_last.. This routine will link the new block to the previous
; block in the directory.
append_last_blk ;
lda dir_last_blk ;setup the backward link
sta [gbuf_ptr]
jsr write_with_cache
rts
clear_dir_buf ;
ldy #$01FE ;clear the general work buffer
lda #$0000
clear_dir ;
sta [gbuf_ptr],y
dey
dey
bpl clear_dir ;every body happy now!
rts
ENDP
;==========================================================================
;Chk_swapped: This routine will determine if the volume has been swapped
; If the volume has been swapped then we will request the
; user to put the volume back online.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; my_vcr_ptr ;pointer to vcr
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; temp_ptr
; volume_name
; find_volume
;
longa on
longi on
EXPORT chk_swapped
chk_swapped PROC
;Now we have all of our data setup for the read. Now we need to see if
;the volume has been swapped out. If it has then we must go and find
;it again.
ldy #vcr_status
lda [my_vcr_ptr],y
and #vcr_swapped ;disk logged out?
beq disk_back
;Little Bo-Peep-Show has lost her disk and doesn't know where to find it!
;Therefore we will go and find it for her.
ldy #vcr_name
lda [my_vcr_ptr],y
tax
iny
iny
lda [my_vcr_ptr],y
tay
jsl deref
stx temp_ptr
sty temp_ptr+2
;Now we have a pointer to the name but we must move it to 'volume_name'
lda [temp_ptr]
and #$000F
tay
dey
move_name ;
lda [temp_ptr],y
sta volume_name,y
dey
bpl move_name
;Now that we have moved the name to volume_name buffer we need to call
;'find_volume' and let it look for it.
jsr mount_volume
rts
disk_back ;
clc
rts
ENDP
;======================================================================
;ID.Disk: This file contains the code that will id the media
; to determine if the media is in ProDOS/SOS format.
; If the disk is a ProDOS disk and there is no VCR then
; a new VCR will be generated for that disk. If there is
; a VCR then it is marked active.
;
;Input: A = device number
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; drvr_buf_ptr ;points to storage/vol name field
; ;with storage type removed
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
;
;Diagram of pointers and memory segments
;
; (I/O Buffer)
; ___________
; | | <--- pointed to by 'drvr_buf_ptr'
; | | <--- 'drvr_buf_ptr' later points to volume name
; |___________| |
; |____ 'temp3_ptr' points to data AFTER volume name area
;
;
; (vcr)
; ___________ <-- pointed to by 'my_vcr_ptr'
; | | ___________________
; | vol name -|------> |___________________| <-- pointed to by 'Temp2_Ptr'
; | |
; | |
; | |
; | | <-- pointed to by 'prodos_vcr_ptr'
; |___________|
;
longa on
longi on
EXPORT id_disk
id_disk PROC
sta drvr_dev_num ;driver number
lda #drvr_read
sta drvr_call_num
lda #vol_dir_start ;starting block of directory
sta drvr_blk_num
stz drvr_blk_num+2
jsr standard_req
ldx gbuf_addr ;setup pointer to general I/O buffer
ldy gbuf_addr+2
stx drvr_buf_ptr
sty drvr_buf_ptr+2
try_again jsr device_call ;make a system call to drive
bcc id_no_error
; ;error code in X already but it may
; ;change in the future. currently
; ;all code is cast in jello
and #$00FF ;clear the dispatcher error code
beq id_no_error ;Dispatcher returning disk switched
cmp #drvr_disk_sw ;was the disk switched?
beq try_again
sec ;swapped out by device call routine
rts ;if the disk was switched
;======================================================================
;ID method used to determine disk is ProDOS format:
;
;(1) read in block two (First directory block)
;(2) backward link must be zero (first word)
;(3) storage type must be $F (high nibble: byte $04)
;(4) name length must be > $0 (low nibble: byte $04)
;(5) name must be valid syntax
;(6) minimum version must be $00 (byte $21)
;(7) entry length must be $27 (SOS/ProDOS size)
;(8) entries per block must be $0D
;
;If the above information causes problems with other disks then I will
;read in the second block of the directory and verify that the link
;fields are correct. This may cause a problem if there is no second
;block in the directory. If this is the case then I will check the
;bitmap allocation for correctness. If this fails then forget it.
;
;======================================================================
id_no_error lda [drvr_buf_ptr] ;load backward link
bne bad_format
ldy #$0021 ;offset to minimum version
lda [drvr_buf_ptr],y
and #$00FF
bne bad_format
;Now that we have checked both the backward link and version we now
;need to adjust the pointer to point to the storage/name length field
;within the block.
clc
lda drvr_buf_ptr
adc #$0004
sta drvr_buf_ptr
bcc ar
inc drvr_buf_ptr+2
ar ;
lda [drvr_buf_ptr] ;load storage/name length
tay
and #$00F0
cmp #$00F0 ;is it a volume directory type?
bne bad_format
tya
and #$000F ;isolate file name length
beq bad_format ;must be $01 to $0F
tay
;
;Now we need to verify that the name is a valid ProDOS filename
;
loop
lda [drvr_buf_ptr],y
and #$00FF
cmp #'A'
bcc numeric
cmp #'Z'+1
bcs bad_format
dey
bne loop
;If we get here then we need to check both the entry length and
;entries per block. If everything is OK then we will call a routine
;that will build a new vcr or mark the vcr as active
its_mine ;
ldy #$0023-4 ;index to entry length
lda [drvr_buf_ptr],y
cmp #$0D27 ;both entry length and entry per blk
bne bad_format
jsr build_vcr ;build a volume control record
rts ;note: error set be build_vcr
numeric ;
cmp #'0'
bcc period
cmp #'9'+1
bcs bad_format
continue dey
bne loop
bad_format ;
lda #unknown_vol ;I don't know what this disk is
sec
rts
period cmp #'.'
bne bad_format
bra continue
ENDP
;======================================================================
;Build_VCR: This routine will add a new VCR if one is currently not
; allocated, if the VCR is allocated then it will be
; marked as active.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; drvr_buf_ptr (points to volume header)
; cp_device_flag = 1 if doing a ChangePath call and source is device #
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;
;Uses: All registers
; find_vcr (system subroutine)
; alloc_vcr (system subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
;
longa on
longi on
EXPORT build_vcr
build_vcr PROC
jsr move_vol_entry ;move the entry to the work area
jsr entry_to_gstr
;
; If bit 15 of cp_flag is set, assume the source disk is a duplicate volume
; and fall into the code that handles duplicate volumes with open files.
; Since cp_device_flag is already set, it will cause a fake VCR to be allocated.
; 5-Dec-91 MSD
;
bit cp_flags ; should we assume the disk is a duplicate?
bpl @not_dup ; no, so do the usual
brl is_open ; yes, pretend duplicate volume with open files
@not_dup
; End of 5-Dec-91 MSD change.
;Call system routine to see if it can find a vcr with the same volume
;name. If the accum contains a zero then the routine will will
;use x and y as a pointer to a volume name. If the accum contains
;a non zero value then the routine will try to find a vcr with
;the same volume id.
ldx #gstring ;pointer to the volume name
ldy #^gstring
lda #0000 ;0000=search for vcr by name
jsl find_vcr
bcs no_vcr ;this name is not in any VCR
jsl deref ;convert virtual to real
stx my_vcr_ptr
sty my_vcr_ptr+2 ;setup pointer to VCR
ldy #vcr_fst_id
lda [my_vcr_ptr],y
cmp #pro_id ;is this my vcr
bne check_active ;if not, can we kick it out?
jsr setup_my_vcr ;make a pointer to my vcr stuff
stx prodos_vcr_ptr
sty prodos_vcr_ptr+2
bit drvr_fst_num ;are we looking at name only?
bmi name_only ;yes.
ldy #entry_length-18
compare_loop lda one_entry+16,y ;we add 16 so we pass the name field
cmp [prodos_vcr_ptr],y
bne check_active
dey
bpl compare_loop
name_only ;
ldy #vcr_dev ;update the last known device
lda drvr_dev_num
sta [my_vcr_ptr],y
jsr activate_vcr ;mark the vcr as swapped in
jsr reset_vcr ;reset blocks free etc.
clc
rts
sorry_dup ;
lda #dup_volume ;duplicate volume online
sec
rts
;If we get here it means that the volume name already exist in vcr.
;So I need to see if there are any open files on the volume. If there
;are no open files then I can kick the current vcr out of memory and
;build a new one. If there are open files then I will return a
;duplicate volume error. Note: I check to make sure that the volume
;header has not changed. This protects me from marking a volume as
;being swapped in and writing to it blindly if it has the same name.
; Note - If we get a duplicate volume error and cp_device_flag = 1 then ignore
; the error since we are doing a ChangePath call with a device # as the source.
; 1/22/91 CAE
check_active ;
; ldy #vcr_open_cnt
; lda [my_vcr_ptr],y
; bne sorry_dup ;files open so don't log in new volume
; jsr free_vcr ;free vcr pointed to by 'my_vcr_ptr'
ldy #vcr_open_cnt
lda [my_vcr_ptr],y ;any open files?
beq not_open ;no
is_open ;Added 5-Dec-91 MSD
lda cp_device_flag ;are we doing a special ChangePath call?
beq sorry_dup ;no - so report a duplicate volume error
ora #$8000 ;yes - so indicate we got a dup vol error
sta cp_device_flag
lda <drvr_dev_num ; 5-Feb-92 MSD: Force other VCR's for this
jsl swap_out ; 5-Feb-92 MSD: to be swapped out.
bra no_vcr ;and continue
not_open jsr free_vcr ;free vcr pointed to by 'my_vcr_ptr'
;below begins the grunt work. we now have to allocate a new vcr and
;setup all are fields.
; Note - If cp_device_flag = $8001 then we need to use a fake name for the VCR
; since we got a duplicate volume error. 1/22/91 CAE
no_vcr ldx #gstring ;pointer to the name
ldy #^gstring
lda cp_device_flag ;do we need to use fake name for vcr?
bpl @cont ;no
ldx #fake_name_str ;yes - so point to fake name string
ldy #^fake_name_str
@cont lda #pro_vcr_size ;number of bytes to allocate
jsl alloc_vcr
bcc alloc_ok
rts ;exit with the error
alloc_ok ; ;now build my data structures
jsl deref ;convert virtual to real address
stx my_vcr_ptr
sty my_vcr_ptr+2
jsr setup_my_vcr ;setup pointer to my data
stx prodos_vcr_ptr
sty prodos_vcr_ptr+2
ldy #vcr_fst_id ;indicate that this vcr is owned by me.
lda #pro_id
sta [my_vcr_ptr],y
ldy #vcr_dev ;convert to INY,INY
lda drvr_dev_num
sta [my_vcr_ptr],y
ldy #vcr_status
lda #0000 ;zero the status word
sta [my_vcr_ptr],y
ldy #entry_length-16
entry_loop lda one_entry+16,y
sta [prodos_vcr_ptr],y
dey
bpl entry_loop ;move the entire directory entry
;warning: if the 'BPL' instruction above changes, then the accum will
; have to be loaded with $FFFF directly.
tya ;now init free blocks to $FFFF. This
ldy #free_blocks ;indicates that free blks is unknown
sta [prodos_vcr_ptr],y
ldy #avail_bitmap
sta [prodos_vcr_ptr],y
ldy #curr_bitmap ;indicate we don't know about these
sta [prodos_vcr_ptr],y
;Now we need to calculate the number of bitmap blocks that are on
;the volume. We also calculate how many bytes in the last block must
;by zero. If these bytes are not zero then the bitmap is damaged
stz math_temp ;setup for even multiple of 4K
ldy #vol_total_blks ;convert total blocks to number of
; ;bitmap blocks on volume
lda [prodos_vcr_ptr],y
tax ;save total block count
and #$0FFF
beq calc_bitmap
inc math_temp
calc_bitmap ;
txa
and #$F000 ;clear unwanted garbage
rol a
rol a
rol a
rol a
rol a
adc math_temp ;add in for odd block size
ldy #num_bitmap_blks
sta [prodos_vcr_ptr],y
even_bit ;
txa
and #$0007
beq found_even_bit ;this is the block number we want
inx ;add one to the block count
bra even_bit ;keep trying to find byte boundary
found_even_bit ;
jsr calc_bit ;'Y' will equal byte index into blk
tya
ldy #end_bitmap ;first byte in the bitmap that must be
sta [prodos_vcr_ptr],y
; ;set to zero in the last blk of bitmap
clc
rts
ENDP
;======================================================================
;activate_vcr: This routine will mark the vcr as being logged in
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; my_vcr_ptr (Points to main VCR)
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
; (the VCR is logged in)
;
;Uses: All registers
; my_vcr_ptr
;
longa on
longi on
EXPORT activate_vcr
activate_vcr PROC
ldy #vcr_status
lda [my_vcr_ptr],y
and #vcr_swapped_in
sta [my_vcr_ptr],y
rts
ENDP
;======================================================================
;Enrty_to_gstr This routine moves the name from the alloc_entry to
; gstring buffer. It will also append a null.
;
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; alloc_entry (contains the volume name)
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; gstring (contains gstring)
;
;Uses: All registers
;
longa on
longi on
EXPORT entry_to_gstr
entry_to_gstr PROC
ldy alloc_entry ;the length of the string
iny
iny ;point to null field
lda #$0000
sta gstring,y ;store the null
dey
php ;save current mode
longa off
sep #$20
loop ;
lda alloc_entry,y
sta gstring,y
dey
bpl loop
plp
rts
ENDP
;======================================================================
;Find_volume: This routine will locate a volume using the ID routine.
;
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; volume_name (contains the volume name to find)
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;
;Uses: All registers
; find_vcr (system subroutine)
; id_disk (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
; volume_name
; drvr_buf_ptr
longa on
longi on
EXPORT find_volume
find_volume PROC
lda #$0001 ;start with device number 1
sta search_device
stz skip_dev
jsr chk_vol_syntax
ldx #volume_name ;try to find the volume in a vcr
ldy #^volume_name
lda #$0000
jsl find_vcr
bcs check_drive
;check to see if this vcr is mine
jsl deref ;convert to real pointer
stx my_vcr_ptr
sty my_vcr_ptr+2
ldy #vcr_fst_id
lda [my_vcr_ptr],y
cmp #pro_id
beq get_device
lda #unknown_vol ;tell caller its another fsts
sec
rts ;return
;setup the device number to check first
get_device
ldy #vcr_dev
lda [my_vcr_ptr],y
sta search_device
sta skip_dev ;indicate we are starting in the middle
bra do_id
check_drive
lda search_device
cmp skip_dev ;do I need to skip this one?
beq next_dev
do_id jsr id_disk
bcc compare_vol
cmp #invalid_dev_num ;have we looked at all the devices?
beq vol_not_avail
cmp #dup_volume ;is the volume a duplicate?
beq abort_search
bra check_search ;go ahead and try another drive.
vol_not_avail
lda #vol_not_found
abort_search
sec
rts ;sorry not today
next_dev
inc search_device
bra check_drive
compare_vol
lda [drvr_buf_ptr] ;get the length
and #$000F
cmp volume_name ;sixteen bit name length
bne check_search ;Cannot be the same
tay ;save the length
php
longa off
sep #$20 ;short accum for string compare
comp_loop
if AllowLCPeriods then ;added 7-Aug-91 DAL
lda volume_name+1,y
cmp #' '
bne @not_blank
lda #'.'
@not_blank cmp [drvr_buf_ptr],y
else ;original code
lda [drvr_buf_ptr],y
cmp volume_name+1,y
endif ;end 7-Aug-91 DAL
bne not_same
dey
bne comp_loop
longa on ;keep the assembler in line
plp
clc
rts ;this is it.
not_same ;
plp
check_search ;
lda skip_dev
cmp search_device ;are we looking at a default drive?
bne next_dev ;no we are not.
stz search_device ;start the search from the beginning
bra next_dev
EXPORT chk_vol_syntax
chk_vol_syntax ;check the syntax of the volume name
longa off
longi off
sep #$30
ldy volume_name ;get the length word
;check the length first 10/10/91 JEV
cpy #$10
bcc vol_syntax_loop
bra syntax_death
vol_syntax_loop ;
lda volume_name+1,y
cpy #$0001 ;is this the first character
bne not_first
cmp #'a'
bcc not_lower
cmp #'z'+1
bcs syntax_death
and #$DF
not_lower ;
cmp #'A'
bcc syntax_death
cmp #'Z'+1
bcc good_syntax
syntax_death ;
longa on
longi on
rep #$30
lda #bad_path_syntax
sec
jmp main_exit
good_syntax ;
dey
bne vol_syntax_loop
rep #$30
rts
longa off
longi off
not_first ;
cmp #'a'
bcc not_lower2
cmp #'z'+1
bcs syntax_death
and #$DF
not_lower2 ;
cmp #'A'
bcc check_num
cmp #'Z'+1
bcs syntax_death
bra good_syntax
check_num ;
cmp #'0'
bcc check_period
cmp #'9'+1
bcs syntax_death
bra good_syntax
check_period ;
if AllowLCPeriods then ;added 7-Aug-91 DAL
cmp #' '
bne @not_blank
lda #'.'
sta volume_name+1,y
bra good_syntax
@not_blank
endif ;end 7-Aug-91 DAL
cmp #'.'
bne syntax_death
bra good_syntax
;local data for find volume
search_device DC.W 00
skip_dev DC.W 00
ENDP
;======================================================================
;Find_File: This file contains the code that will locate a file on
; disk.
;
;revisions:
; apr 09 1987 ;added forward/backward link verify
; apr 09 1987 ;changed routine to read a block
; ;at a time instead of 1K
; apr 10 1987 ;added additional check for path
; ;searching.
; apr 27 1987 ;changed code to handle 'C' strings
; may 02 1987 ;added memory based tree routines
; ;so all accessed directories will
; ;be added into a b-tree.
; sep 14 1987 ;added support for searching for
; ;blank entries in the directory
;
;Input: A = device number
; X = low word: pointer to name
; Y = high word: pointer to name
; P = nvmxdizc
; ..000...
; b = k
;
; drvr_vol_id
; my_vcr_ptr
; prodos_vcr_ptr
; search_flag ;IF bit 14 = 1 then search for blank
; ;blank entry in directory, as well
; ;as checking for duplicate file error.
;
;Format of pathname:
; 1) volume name is NOT part of the pathname
; 2) pathname begins with a length byte
; 3) each part of the pathname is seperated with a colon char.
; 4) pathname MUST end with a NULL char.
;
;Output: A = error code or offset to entry within the I/O buffer
; X = pointer to name (low word)
; Y = pointer to name (high word)
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; dir_start_blk ;starting block of last dir searched
; free_dir_blk ;first block with a blank entry
; free_dir_offset ;offset to first free entry within blk
; dir_last_blk ;block number that contains found file.
; or drvr_blk_num ;or the last block of the directory.
; path_searched ;if + then entire path NOT searched
; ;if - then the entire path was searched
; slug_block ;block number of the entry
; slug_offset ;offset to the entry.
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 1K bytes)
; temp_ptr
; gbuf_ptr
; path_level
;
longa on
longi on
EXPORT find_file
find_file PROC
sta drvr_dev_num ;device number to search on
stx temp_ptr ;pointer to the name
sty temp_ptr+2
lda gbuf_addr
sta gbuf_ptr
sta drvr_buf_ptr
lda gbuf_addr+2
sta gbuf_ptr+2
sta drvr_buf_ptr+2
stz curr_mod_date ;zero the latest mod date field
stz curr_mod_date+2
lda #vol_dir_start ;default starting block
sta dir_start_blk ;starting block for current directory
sta dir_last_blk ;last block read by find file
sta drvr_blk_num
stz drvr_blk_num+2
vcr_not_alloc
jsr standard_req
lda #drvr_read ;setup for reading
sta drvr_call_num
stz chars_checked
stz path_searched ;indicates if entire path searched
jsr setup_name ;setup first part to look for
stz entries_blk_num ;block that contains the entry
outter_loop ;
stz last_blk ;used to verify directory structure
stz free_dir_blk ;indicate no free dir entry
stz free_dir_offset
stz searching_free ;we are looking for free entries
search_loop ;
jsr read_with_cache ;issue the read command
bcs error_leave ;some error has occured
jsr verify_dir ;verify forward/backward links
bcs error_leave ;get out of here, links disagree
stz entries_checked ;reset file counter
ldy #0004 ;pass the link pointers
jsr check_block ;see if name is in the block
bcc found
bit searching_free ;are we looking for a free entry?
bpl check_active ;no we are looking for the file.
lda free_dir_blk ;Have we found a free entry yet?
beq load_next ;No, so load the next block
bit path_searched ;are we looking for the last name?
bpl load_next ;No.
bra no_more_blks ;yes, so return data
check_active ;
lda files_in_dir ;subtract off active entries checked
sbc files_examined
bcc dir_damaged ;the directory count does not match
beq not_found ;we have looked at all the files.
load_next ;
ldy #0002 ;check next link field
lda [gbuf_ptr],y ;get the forward link
beq no_more_blks ;cannot check any further
sta drvr_blk_num ;read this block next
sta dir_last_blk ;save last block read
bra search_loop
not_found ;
bit search_flag ;are we suppose to find a free entry?
bvc no_more_blks ;no we are not
lda free_dir_blk ;do we already have a free entry?
bne no_more_blks ;yes we do!
dec searching_free
bra load_next ;continue to look for a blank entry.
no_more_blks ;
lda path_searched ;was the entire path searched?
bne file_err ;yes the entire path was searched
lda #path_not_found ;a subdirectory was not found
sec
rts
file_err ;
lda #file_not_found ;you guessed it, no file today
sec ;sorry no file by that name
error_leave rts
;we get here if the total number of active files is greater then the
;total number of files in the directory
dir_damaged ;
lda #dir_error ;this is a bad directory
sec
rts
;If we get here then we have found the name we were looking for.
;Now we need to see if there is anymore pathname to process. If there
;is nothing left to process then we exit all happy. If there is more
;to process then we make sure that the entry we just found is a
;subdirectory. If it is not then we exit with file not found.
found ;
lda drvr_blk_num ;block number that contains entry
sta entries_blk_num ;block number that contains the entry
sta dir_last_blk ;last block read
sty entries_offset ;offset to the entry
tyx
ldy chars_checked
dey
lda [temp_ptr],y ;see if we are done
and #$00FF
beq find_success ;yes we have succeeded
txy
lda [gbuf_ptr],y ;see if type is directory
and #$00F0
cmp #$00D0 ;it is a directory
bne not_found ;can not go any further
lda drvr_blk_num ;save the entries block number for
sta slug_block ;slug ripple routine.
sty slug_offset ;offset to file entry.
tya
clc
adc #$0011 ;make index to key block pointer
tay
lda [gbuf_ptr],y
sta drvr_blk_num
sta dir_start_blk ;starting block for next directory
sta dir_last_blk ;save last block read
jsr setup_name ;setup next partial to find
jmp outter_loop
;Below is a happy place to be. This means that we have found the
;file that we were commissioned to find. So we will setup a pointer
;to the name and return to the caller. Isnt' that special.
find_success txa ;we have located the name
pha ;save the offset
clc
adc gbuf_ptr ;adjust buffer pointer
tax
lda #0000
adc gbuf_ptr+2
tay
pla ;offset (0 to 512)
clc
rts ;X,Y contains a pointer to the name
;==========================================================================
;verify dir: This routine will verify that the forward and backward
; links of the subdirectory agree with each other. If
; they do not then we will return a dir_error. The
; routine also makes sure that the parent pointer,
; entry length and entries per block are also correct.
;
;input: last_blk ;contains previous dir block
; entries_blk_num ;parents block number
; drvr_blk_num ;contains current block number
;
;output: A = error if carry set otherwise no error
verify_dir
lda last_blk ;the previous directory block
beq verify_parent ;this is a special case since
;no previous block has been read
cmp [gbuf_ptr]
beq dir_ok ;this is the way it should be
bad_dir ;
lda #dir_error ;this is a bad directory
sec
rts
dir_ok
lda drvr_blk_num ;setup last block for next time
sta last_blk
clc
rts
verify_parent ;
lda entries_blk_num ;skip check if zero
beq pass_parent ;don't bother checking parent
ldy #$0027 ;index to parent pointer
cmp [gbuf_ptr],y ;compare to childs parent
bne bad_dir ;parent/child don't match
pass_parent ldy #$0023 ;index to entry length/entries per blk
lda [gbuf_ptr],y
cmp #$0D27
bne bad_directory ;this is something I don't know about
ldy #$0025 ;index to total files in directory
lda [gbuf_ptr],y
sta files_in_dir
stz files_examined ;reset files examined
bra dir_ok
bad_directory
lda #version_error ;I don't know about this disk
sec
rts
;==========================================================================
;The following routine will look at all the entries within a directory
;block. The entry is skipped if it is a volume header, subdirectory
;header or an inactive entry. If an entry is found that is active then
;it is checked against the name we are looking for. If the name is
;found then we exit indicating that we have found the file. We
;continue to look for the name until we have looked at all the entries
;or the file is found.
check_block lda entries_checked
cmp #entries_per_block
beq check_done
inc a
sta entries_checked
lda [gbuf_ptr],y ;get the storage type
tax
and #$00F0 ;check for directories
cmp #$00F0 ;is this the volume Entry????
bne @chK_sub ;no it is not
jsr save_vol_mod ;save the volume modification if possible
bra next_entry
@chk_sub
cmp #$00E0
bcs next_entry ;skip headers
txa
and #$000F ;isolate name length
beq chk_need_free ;the entry is not active.
inc files_examined ;advance the number of active files
jsr save_latest_mod ;save the lastest mod date
jsr check_name
bcs next_entry
rts ;we have found it
chk_need_free ;
lda free_dir_blk ;block that contains a free entry
bne next_entry
lda drvr_blk_num
sta free_dir_blk ;save directory block number
sty free_dir_offset ;save offset to free entry
next_entry
tya
clc
adc #entry_length ;adjust pointer to next entry
tay
bra check_block
check_done sec ;sorry entry not found
rts
save_vol_mod
phy ;save directory entry index
phx
pha
ldy #vol_mod_offset ;get the mod date from the volume header
lda [gbuf_ptr],y
sta curr_mod_date
iny
iny
lda [gbuf_ptr],y
sta curr_mod_date+2
pla
plx
ply
rts
;Save_lastest_mod will save the lastest mod date that we find. The reason
;we do this is to allow the mod date to be returned for the volume header.
save_latest_mod
phy ;save directory entry index
phx
pha
tya
clc
adc #last_mod_index
tay
lda [gbuf_ptr],y
tax
cmp curr_mod_date ;see if the date is greater
bcc @not_greater
beq @check_time
iny
iny
lda [gbuf_ptr],y ;get the minutes etc.
sta curr_mod_date+2
stx curr_mod_date ;save the new latest date
bra @not_greater ;just exit
@check_time
iny
iny
lda [gbuf_ptr],y ;get the minutes etc.
and #$1F3F ;clear unused bits
cmp curr_mod_date+2
bcc @not_greater
sta curr_mod_date+2
stx curr_mod_date ;save the new latest date
@not_greater
pla
plx
ply
rts
;==========================================================================
;Check name will examine the name to see if it is the one that
;we are looking for. If it is not then we return with carry set
;otherwise we return with carry clear.
check_name
cmp search_length
bne noway
sta name_length
phy ;save y during search
iny
ldx #0000
longa off
sep #$20
name_loop
lda [gbuf_ptr],y
cmp search_name,x
bne not_it
inx
iny
dec name_length
bne name_loop
rep #$30
ply
clc
rts
longa on
not_it
rep #$30
ply
noway sec
rts
;==========================================================================
;The following routine when called, will isolate the next name to
;locate. It does this by looking for the delimiter(delimiter = ':')
;The last delimiter is a null character.
EXPORT setup_name
setup_name
phy
ldy chars_checked ;index into source name
lda [temp_ptr],y ;are we done yet
and #$00FF
bne more_names
dec path_searched ;the entire path has been searched
ply
sec ;indicate we are thru
rts
more_names
longa off
longi on
sep #$20 ;short accumulator please
ldx #$0000
looper
lda [temp_ptr],y
beq end_path ;end of name and end of string
cmp #delimiter
beq end_name ;we found a delimiter
sta search_name,x
cpx #$0000 ;is this the first character?
bne not_first
cmp #'a'
bcc not_lower1
cmp #'z'+1
bcs syntax_prob
and #$DF
not_lower1
cmp #'A'
bcc syntax_prob
cmp #'Z'+1
bcc good_syntax
syntax_prob
longa on
longi on
rep #$20
lda #bad_path_syntax
sec
jmp main_exit ;return to Jim with the error
longa off
longi on
not_first
cmp #'a'
bcc not_lower2
cmp #'z'+1
bcs syntax_prob
and #$DF
not_lower2
cmp #'A'
bcc check_num
cmp #'Z'+1
bcs syntax_prob
good_syntax
inx
iny
bra looper
check_num
cmp #'0'
bcc check_period
cmp #'9'+1
bcs syntax_prob
bra good_syntax
check_period
if AllowLCPeriods then ;added 7-Aug-91 DAL
cmp #' '
beq good_syntax
endif ;end 7-Aug-91 DAL
cmp #'.'
bne syntax_prob
bra good_syntax
end_path
dec path_searched+1 ;the entire path has been searched
end_name
iny
sty chars_checked
stx search_length
longa on
longi on
rep #$30
ply
clc
rts
ENDP
;======================================================================
;init the ProDOS fst
longa on
longi on
EXPORT startup
startup PROC
jsl get_sys_gbuf ;get the systems general buffer
good ;
stx gbuf_addr
sty gbuf_addr+2 ;save pointer to general work buffer
clc
rtl
ENDP
EXPORT shutdown
shutdown PROC
clc
rtl
ENDP
;======================================================================
;Sys_Remove_vol This file contains code that is called by the system
; to remove a volume control record (VCR).
;
;Input: A = undefined
; X = (call number * 2)
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..0 = no error
; b = k
;
;Uses: All registers
;
longa on
longi on
EXPORT sys_remove_vol
sys_remove_vol PROC
clc
rtl
ENDP
;======================================================================
;deferred_flush This file contains code that flush any deferred blocks
; blocks to disk. This routine is called when the stop
; session call is issued, and when swapout is called.
; Since the routine can be called from swapout it must
; save most of the direct page and local buffers.
;
;Input: A = undefined
; X = (call number * 2)
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..0 = no error
; b = k
;
;Uses: All registers
;
;======================================================================
longa on
longi on
EXPORT deferred_flush
deferred_flush PROC
;
;Save the world. We do this in case we are called by SwapOut.
;
jsr save_def ;save the deferred envirnoment
jsr standard_req ;write 512 bytes
lda #blk_size
sta drvr_blk_size
ldx vcr_ptr
ldy vcr_ptr+2
stx my_vcr_ptr
sty my_vcr_ptr+2
jsr setup_my_vcr
stx prodos_vcr_ptr
sty prodos_vcr_ptr+2
;
;Setup ID_Disk routine to ID by volume name ONLY!! This is important
;since block two is deferred and we are reading block two from the disk.
;Therefore the two blocks will problably not be the same.
;
lda #$8001 ;set the high bit of FST_NUM
sta drvr_fst_num ;to force device access
;
;Try to locate the volume we are trying to write too. We do this by
;calling 'find_Volume'. More then likely the volume is still in the
;same drive.
;
jsr mount_volume ;go and locate the volume please
bcs bad_bad_news ;we our up the creek!
;
;After finding the volume I will call the 'cache_flsh_def' routine. This
;routine will write all the deferred blocks to disk.
;
lda [my_vcr_ptr] ;setup the VOL ID
sta drvr_vol_id
jsl cache_flsh_def
bcc exit_deferred
;
;Now that we are here we must restore the world and return back to scm
;via an RTL instruction.
;
bad_bad_news ;
exit_deferred ;
jsr restore_def
clc
rtl
save_def ;
php
phy
phx
pha
ldx #$004A ;save the direct page data
save_direct ;
lda drvr_dev_num,x
sta def_dir_page,x
dex
bpl save_direct
ldx #$0052 ;save my direct page
save_my_direct ;
lda fst_start,x
sta def_my_direct,x
dex
bpl save_my_direct
ldx #19 ;save the volume name buffers
save_def_vol ;
lda volume_name,x
sta def_vol_name,x
dex
bpl save_def_vol
bra save_rest_exit
;======================================================================
restore_def ;
php
phy
phx
pha
ldx #$004A ;restore the direct page data
restore_direct ;
lda def_dir_page,x
sta drvr_dev_num,x
dex
bpl restore_direct
ldx #$0052 ;restore my direct page
restore_dp ;
lda def_my_direct,x
sta fst_start,x
dex
bpl restore_dp
ldx #19 ;restore the volume name buffers
restore_def_vol ;
lda def_vol_name,x
sta volume_name,x
dex
bpl restore_def_vol
save_rest_exit ;
pla
plx
ply
plp
rts
ENDP
;======================================================================
;Create: This file will create a new fresh file on disk.
;
;Input: A = undefined
; X = (call number * 2)
; Y = (class number * 2)
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set (exit via system exit routine)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
; find_file (subroutine)
;
;Call Format:
;
;Class 0 Pathname Pointer (4 Bytes)
; Access (2 Bytes)
; File Type (2 Bytes)
; Aux Type (4 Bytes)
; Storgae_type (2 Bytes)
; Create Date/Time (4 Bytes: ProDOS Format)
;
;Class 1 PCount (2 Bytes)
; Pathname Pointer (4 Bytes)
; Access (2 Bytes: E3 if not supplied)
; File Type (2 Bytes: 00 if not supplied)
; Aux Type (4 Bytes: 00 if not supplied)
; Storage Type (2 Bytes: 01 if not supplied)
; Size Data Fork (4 Bytes: 00 if not supplied)
; Size Resource Fork (4 Bytes: 00 if not supplied)
;
longa on
longi on
EXPORT create
create PROC
with expand_record
;Setup the defaults for the create call.
stz data_eof
stz data_eof+2
stz res_eof
stz res_eof+2
jsr setup_params
php
jsr do_create_path
jsr default_entry ;Build a Entry with All defaults!
plp
bne class1 ;this is a new wonder version
lda #$0005 ;set PCount for common setup
sta pcount
jsr create_common
ldy #pblk_01_old_time
lda [my_pblk_ptr],y ;load the old format time
tax
iny
iny
lda [my_pblk_ptr],y
bne users_date ;user has supplied the data
txy
beq both_classes ;us the GS system clock for the time
users_date ;
and #$1F3F ;force the date to correct format.
beq clock_time ;use the GS clock time please
sta one_entry+create_index+2
clock_time ;
txa ;is the date field set?
beq both_classes ;use GS clock Date
stx one_entry+create_index
bra both_classes
class1
jsr create_common
both_classes
bit expand_flag ;are we expanding a current file?
bpl @no_expansion
lda one_entry+last_mod_index
pha
lda one_entry+last_mod_index+2
pha
lda storage_type
pha
lda one_entry
pha
jsr move_dir_entry ;copy the entry from the disk
pla
sta one_entry
pla
sta storage_type
pla
sta one_entry+last_mod_index+2
pla
sta one_entry+last_mod_index
@no_expansion
jsr eof_to_blks ;convert bytes to blocks
;
;Blocks needed := Data_fork_blks + Res_fork_blks + subdir_blks + storage
;
chk_dir ;
lda #$0000 ;assume zero
bit expand_file ;are we adding a directory entry?
bmi no_extend
ldx free_dir_blk ;is there room for a dir entry?
bne no_extend ;yes, so don't bump blks needed
inc a ;add one for directory block needed
no_extend
clc
adc blks_used1 ;blocks for data fork
clc
adc blks_used2 ;blocks for resource fork
sta blocks_needed ;save total blocks needed for file
bcs @no_room
lda storage_type ;if the file is an extended file then add
cmp #resource_type ;one block for the extended file header
bne @not_extended
inc blocks_needed ;bump the blocks needed
@not_extended
jsr calc_free_blks ;see if we have enough room for the file
bcs to_exit
bit expand_flag ;are we expanding a current file?
bpl @chk_sum ;no, so check the sum of the blocks needed
;
;We now know that we are extending a file, so we must check the blocks free against the
;number of blocks needed to add the resource fork. The blocks needed for the resource
;fork is equal to blks_used2 + 1 (for resource header blk). To compensate for the extra
;block needed, we will reduce the number of free blocks by 1, then compare against
;blks_used2 (blocks needed for resource fork).
;But first, we need to check if the number of free blocks is 0!! CAE 11-6-90
;
cmp #0 ;is free blocks = 0?
beq @no_room ;yes - so can't extend the file
dec a ;free blocks := free blocks -1; (compensate for header)
cmp blks_used2
bcs it_fits
bra @no_room ;sorry you cannot extend the file.
@chk_sum
cmp blocks_needed ;do we have enough?
bcs it_fits ;yes we do.
@no_room
lda #volume_full ;no room on volume
to_exit
sec
jmp main_exit
it_fits
jsr build_file ;go and make the file
bcs create_err
jsr force_bitmap ;make sure the bitmap is up to date
bcs create_err
jsr slug_ripple ;time stamp previous
create_err
jmp main_exit
;===============END OF THE CREATE CALL
;==========================================================================
;Build_file This routine will allocate all needed blocks and build
; the file. It will also handle directory extending etc.
;
;input: free_dir_blk ;dir blk with a empty entry.
; dir_start_blk ;starting block of the directory
; dir_last_blk ;last block of the directory
; used_blks1 ;blocks needed for data fork
; used_blks2 ;blocks needed for resource fork
; stoarge_type ;type of file to be created
; data_storage ;storage type of data fork
; res_storage ;storage type of resource fork
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
build_file ;
jsr extend_subdir ;extend the directory IF needed.
bcc chk_storage
rts
chk_storage ;
lda storage_type ;see what we are creating
cmp #subdir_type
bne chk_next1
jsr create_subdir ;make a subdirectory
bcs build_failed
bra write_dir_entry
chk_next1 ;
cmp #resource_type
bne chk_next2
jsr create_resource ;make a resource file
bcs build_failed
bra write_dir_entry
chk_next2 ;
cmp #tree_type
bne chk_next3
jsr create_tree ;make a tree file
bcs build_failed
bra write_dir_entry
chk_next3 ;
cmp #sapling_type
bne do_seedling
jsr create_sapling ;make a sapling file
bcs build_failed
bra write_dir_entry
build_failed ;
rts
do_seedling jsr create_seedling
bcs build_failed
write_dir_entry
jsr set_default_buf ;get ready to write the dir entry.
jsr standard_req
lda free_dir_blk ;where to store dir entry
sta drvr_blk_num
jsr read_with_cache ;read in the directory block
bcs build_failed ;this is real bad news! bitmap killed.
lda dir_start_blk
sta one_entry+header_ptr_index
;
;now let's save the case state of the file please
;
lda case_bits ;case bits for this file
beq @its_all_upper
bit fst_attr ;If the user has set all upper then skip case code
bmi @its_all_upper
ora #$8000 ;set the case active bit.
sta case_bits
sta one_entry+version_index
@its_all_upper
ldx #$0001
ldy free_dir_offset
lda one_entry
sta [gbuf_ptr],y
iny
move_it ;
lda one_entry,x ;move in the directory entry.
sta [gbuf_ptr],y
iny
iny
inx
inx
cpx #entry_length
bcc move_it
lda drvr_blk_num ;is this 'dir_start_blk'
cmp dir_start_blk ;If yes then do not write twice
beq write_once
jsr write_with_cache
bcs build_failed ;sorry, your disk is bad now
bit expand_flag ;are we expanding the file?
bmi chk_pcount ;don't change the number of files in the directory
lda dir_start_blk ;now update file count
sta drvr_blk_num
jsr read_with_cache
bcs build_failed
write_once
bit expand_flag ;are we expanding the file? minus = yes
bmi dont_update_vcr ;don't change the number of files in the directory
ldy #header_file_cnt
lda [gbuf_ptr],y ;load current file count
inc a ;bump it by one
sta [gbuf_ptr],y
lda drvr_blk_num ;see if this is the volume header
cmp #vol_dir_start
bne dont_update_vcr
ldy #vol_file_count
lda [prodos_vcr_ptr],y
inc a ;bump file count in vcr also
sta [prodos_vcr_ptr],y
dont_update_vcr ;
jsr write_with_cache
bcs no_store_type
chk_pcount lda pcount
cmp #$0005 ;can we return the storage type
bcs do_store
rts
do_store ;
ldy #pblk_01_stype ;set the user's storage type
lda storage_type
lsr a
lsr a
lsr a
lsr a
sta [my_pblk_ptr],y
clc
no_store_type ;
rts ;exit with error / no error
;==========================================================================
;create_seed.. This routine will create a seedling file on disk.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = error code if carry set (exit via system exit routine)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; one_entry ;ready to be written to disk
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
create_seedling ;
lda #$0001 ;allocate one block please
sta one_entry+blks_used_index
; ;setup in default entry
ldx #$0000 ;start from the beginning.
jsr find_free_blks ;go and get it
bcs end_seed ;sorry.
stx one_entry+key_blk_index
stx drvr_blk_num
jsr set_default_buf
jsr standard_req ;write 512 bytes please
jsr clear_gbuf_low ;we must write zeros for seedling
jsr write_with_cache
end_seed rts
;==========================================================================
;create_sapl.. This routine will create a sapling file one disk.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = error code if carry set (exit via system exit routine)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; one_entry ;ready to be written to disk
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
create_sapling ;
jsr setup_index_ptr ;setup pointer for add index
jsr clear_gbuf_high ;clear high 512 bytes of general buffer
lda one_entry ;adjust the storage type
and #$FF0F ;clear old stoarge type
ora #sapling_type
sta one_entry
lda blks_used1 ;number of blocks needed for file
sta one_entry+blks_used_index
ldx #$0000 ;start from the beginning
jsr find_free_blks ;see if I have enough room?
bcs end_sapling
stx one_entry+key_blk_index
inx
stx math_temp
cmp blks_used1 ;did we get all the blocks needed?
beq big_sapling
stz first_free ;start from the beginning
ldy #$0000 ;starting index
ldx blks_used1
dex ;blocks to allocate
sapling_loop ;
jsr get_one_blk ;allocate another block
bcs end_sapling ;major problems
lda first_free ;block number
jsr add_index
iny
dex
beq write_sap_index
bra sapling_loop
end_sapling ;
rts
big_sapling ;
ldy #$0000 ;starting index value
ldx blks_used1 ;number of blocks allocated
dex ;minus one for index block
build_loop ;
lda math_temp ;block number
jsr add_index ;add block to the index
iny ;move to the next index
dex
beq write_sap_index ;write the block to disk
inc math_temp ;bump to the next block please
bra build_loop
write_sap_index ;
lda temp4_ptr ;pointer to index block
sta drvr_buf_ptr
lda temp4_ptr+2
sta drvr_buf_ptr+2
jsr standard_req ;512 byte transfer
lda one_entry+key_blk_index
sta drvr_blk_num ;write the index
jsr write_with_cache
rts
;==========================================================================
;create_tree.. This routine will create a tree file one disk.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = error code if carry set (exit via system exit routine)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; one_entry ;ready to be written to disk
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
create_tree ;
sec ;allocate a 512 byte buffer please
lda #blk_size
jsl alloc_seg
bcs end_tree ;sorry out of memory!!
stx virtual
sty virtual+2 ;save the VP for release seg
jsl deref ;convert to a real pointer
stx temp2_ptr
sty temp2_ptr+2 ;save a pointer to buffer
lda #$0001 ;allocate one block for master index
ldx #$0000 ;start from the beginning
jsr find_free_blks
bcs end_tree
stx tree_key_blk ;save key block of tree file.
lda blks_used1
sta key_block ;save total blocks please
sec
lda blks_used1 ;remove overhead blocks from count
sbc data_indexes ;number of index blocks needed
dec a ;subtract one for master index block
sta tree_blks
ldy #$0000 ;starting index for master block
tree_loop ;
ldx #256 ;number of blocks to allocate
lda tree_blks
cmp #256 ;max blocks+1 in an index block
bcs use_max
tax ;use the remaining blocks
use_max ;
inx ;add one for index block itself
stx blks_used1 ;this is the amount to allocate
phy
jsr create_sapling ;build a sapling index
ply
bcs end_tree ;major problems
jsr add_master_index
iny ;advance to next index please
sec
lda tree_blks ;number of blocks left
sbc #256
sta tree_blks
beq write_tree
bcs tree_loop ;do another index block
write_tree ;
lda one_entry
and #$FF0F ;change storage to tree type
ora #tree_type
sta one_entry
lda key_block
sta one_entry+blks_used_index
lda temp2_ptr ;setup pointer for write
sta drvr_buf_ptr
lda temp2_ptr+2
sta drvr_buf_ptr+2
lda tree_key_blk
sta drvr_blk_num
sta one_entry+key_blk_index
jsr write_with_cache
end_tree
php ;save error flag
pha ;save error if any
ldx virtual
ldy virtual+2
jsl release_seg ;give up the temp buffer
pla
plp
rts ;return any error
add_master_index
lda temp2_ptr ;setup pointer to master index block
sta temp4_ptr
lda temp2_ptr+2
sta temp4_ptr+2
lda one_entry+key_blk_index
jsr add_index
rts
;==========================================================================
;create_res... This routine will create a resource file.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = error code if carry set (exit via system exit routine)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; one_entry ;ready to be written to disk
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
; 512 bytes additional mem
create_resource ;
lda #prodos_version+(prodos_version*$100)
sta one_entry+version_index
clc
lda blks_used1
adc blks_used2
inc a ;add one for resource header
sta res_tot_blks
sec ;allocate a 512 byte buffer please
lda #blk_size
jsl alloc_seg
bcs @to_end ;sorry out of memory!!
stx virtual2
sty virtual2+2 ;save the VP for release seg
jsl deref ;convert to a real pointer
stx temp5_ptr ;real pointer to buffer
sty temp5_ptr+2
lda #$0001 ;allocate one block for resource header
ldx #$0000 ;start from the beginning
jsr find_free_blks
@to_end bcs end_resource
stx res_key_blk ;save key block of resource file.
lda data_storage ;go and create the correct data fork
jsr make_fork
bcs end_resource ;major problems
ldy #$0000
lda data_storage
jsr save_fork_info
;
;Below is a hack. Since the create call was written to add in a directory entry not update
;an entry, we have to full the update directory routine. To do this I copy in the directory
;block and entry offset into "free_dir_blk" and "free_dir_offset" This will make the
;directory update routine work. This is the way we make code do more then it was meant to do.
;
bit expand_file ;are we extending the file?
bpl @no_extend
lda dir_last_blk ;block number of the directory
sta free_dir_blk
lda entries_offset ;offset to the entry within the dir block
sta free_dir_offset
@no_extend
stz expand_file ;we are now thru with the data fork so reset the expand flag
stz one_entry+eof_index
stz one_entry+eof_index+1
lda blks_used2 ;setup for resource fork create
sta blks_used1
lda res_indexes
sta data_indexes ;used if we are creating a tree file.
lda res_storage ;go and create the correct res fork
jsr make_fork
bcs end_resource ;major problems
ldy #$0100
lda res_storage
jsr save_fork_info
write_res
lda one_entry
and #$FF0F ;change storage to resource type
ora #resource_type
sta one_entry
lda res_tot_blks
sta one_entry+blks_used_index
lda temp5_ptr ;setup pointer for write
sta drvr_buf_ptr
lda temp5_ptr+2
sta drvr_buf_ptr+2
lda res_key_blk
sta drvr_blk_num
sta one_entry+key_blk_index
lda #blk_size
sta one_entry+eof_index
jsr write_with_cache
end_resource
pha
php
ldx virtual2 ;give back the 512 byte temp buffer
ldy virtual2+2
jsl release_seg
plp
pla
rts
make_fork ;A = storage type
bit expand_file ;are we expanding an existing file?
bpl @not_expanding
;
;If we are expanding an existing file I will setup the entry parameters as if I
;had just created the file. This way we will just bypass the create call.
;
lda expand_storage ;move the real storage type
sta data_storage
lda expand_key_blk ;move the real key block of the file
sta one_entry+key_blk_index
lda expand_blks_used
sta one_entry+blks_used_index
lda expand_eof
sta one_entry+eof_index
lda expand_eof+1
sta one_entry+eof_index+1
clc
rts
@not_expanding
cmp #seedling_type
bne res_sap
jsr create_seedling
rts
res_sap ;
cmp #sapling_type
bne res_tree
jsr create_sapling
rts
res_tree ;
jsr create_tree
rts
save_fork_info ;A = storage, Y = index into header
lsr a ;temp5_ptr = pointer to header
lsr a
lsr a
lsr a
sta [temp5_ptr],y
iny
lda one_entry+key_blk_index
sta [temp5_ptr],y
iny
iny
lda one_entry+blks_used_index
sta [temp5_ptr],y
iny
iny
lda one_entry+eof_index
sta [temp5_ptr],y
iny
lda one_entry+eof_index+1
sta [temp5_ptr],y
rts
;==========================================================================
;create_subdir This routine will create a new subdirectory on disk
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = error code if carry set (exit via system exit routine)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; one_entry ;ready to be written to disk
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
;
create_subdir ;
jsr setup_index_ptr ;setup temp4_ptr to point to gbuf+$200
jsr clear_gbuf_high ;clear high 512 bytes of general buffer
lda #$000F ;force file type to subdirectory
sta one_entry+file_type_index
lda blks_used1 ;number of blocks needed for subdir.
sta one_entry+blks_used_index
tax
asl a ;convert blocks to EOF
sta one_entry+eof_index+1
txa
ldx #$0000 ;start from the beginning
jsr find_free_blks ;see if I have enough room?
bcs end_subdir ;major problem my friend
stx one_entry+key_blk_index
stx parent_blk
inx
stx math_temp
pha ;save number of blocks allocated
jsr make_sub_header ;build the header block to disk
pla
cmp blks_used1 ;did we get all the blocks needed?
beq seq_subdir ;yes we can make the directory seq
dec blks_used1
non_seq_loop ;
lda #$0001 ;allocate one block
ldx #$0000 ;start from the beginning
jsr find_free_blks
bcs end_subdir
stx math_temp ;the new block.
txa
ldx parent_blk ;block that needs to be written
jsr update_forward
bcs end_subdir
lda one_entry+key_blk_index
cmp parent_blk
bne no_clr
jsr clear_gbuf_high ;clear high 512 bytes of general buffer
no_clr ;
lda parent_blk ;update backward link please
sta [temp4_ptr]
lda math_temp ;this is the new block to write
sta parent_blk
dec blks_used1 ;see if we are done
bne non_seq_loop
lda #$0000
ldx math_temp
jsr update_forward
bcc end_subdir
rts
good_subdir
clc
end_subdir
php ;save error flag
pha
lda one_entry
and #$FF0F ;change storage to subdirectory
ora #subdir_type
sta one_entry
pla ;restore possible error code
plp ;restore error flag
rts
seq_subdir ;
lda #$0000 ;default forward link
ldy blks_used1
dey ;is there another block?
beq last_dir_blk
lda math_temp ;forward link
last_dir_blk ;
ldx one_entry+key_blk_index ;block to write to disk
jsr update_forward ;write the block to disk
bcs end_subdir ;bad news
jsr clear_gbuf_high ;clear high 512 bytes of general buffer
seq_sub_loop ;
dec blks_used1 ;see if we are done
beq good_subdir
lda drvr_blk_num ;this is the backward link now
sta [temp4_ptr]
lda #$0000
ldy blks_used1 ;is there more blocks to write?
dey
beq write_last ;no this is the last one
lda math_temp
inc a ;make a forward link
write_last ;
ldx math_temp ;block number to write
inc math_temp ;bump to the next block
jsr update_forward
bcc seq_sub_loop
bra end_subdir
;==========================================================================
;update_forward This routine will update the forward link of a directory
; block then write the block to disk.
;
;input: A = forward link
; X = block number to write
;
;output: A = error code if carry set
;
update_forward ;
ldy #$0002 ;forward link
sta [temp4_ptr],y
stx drvr_blk_num ;save block number to write
lda temp4_ptr ;setup pointer to block.
sta drvr_buf_ptr
lda temp4_ptr+2
sta drvr_buf_ptr+2
jsr write_with_cache
rts ;update the disk!
;==========================================================================
;This routine will create the header block for the subdirectory.
;
;input: X = header block +1
; temp4_ptr = pointer to work buffer
make_sub_header ;
dex ;block number to write
stx drvr_blk_num
clc
lda temp4_ptr
sta drvr_buf_ptr
adc #$0004
sta temp_ptr
lda temp4_ptr+2 ;setup pointer for the write
sta drvr_buf_ptr+2
adc #$0000
sta temp_ptr+2 ;setup pointer to header
ldy #entry_length-1
move_header ;
lda one_entry,y
sta [temp_ptr],y
dey
dey
bpl move_header ;move the header into first block
ldy #access_index
lda [temp_ptr],y
and #$FF00
ora #$00C3
sta [temp_ptr],y
lda [temp_ptr]
and #$FF0F
ora #subdir_header
sta [temp_ptr]
ldy #$001F ;index to entry len and entries per blk
lda #$0D27 ;13 entries per blk and 39 byte entries
sta [temp_ptr],y
iny
iny ;point to file count
lda #$0000
sta [temp_ptr],y ;no files yet
iny
iny
lda free_dir_blk ;parent pointer
sta [temp_ptr],y
iny
iny
jsr make_entry_num
bcc go_on_please
jmp main_exit
go_on_please
sta [temp_ptr],y
ldy #$0010 ;clear reserved area
lda #$0000
clear_reserved
sta [temp_ptr],y
iny
iny
cpy #$0018
bcc clear_reserved
ldy #$0010 ;setup for no 'HUSTON'
lda #$0076
sta [temp_ptr],y
rts
EXPORT make_entry_num
make_entry_num
ldx #(12*2) ;index into offset table
lda free_dir_offset
offset_loop cmp dir_offset_tbl,x
beq found_offset
dex
dex
bpl offset_loop
lda #dir_error
sec
rts
found_offset
txa
lsr a ;divide by two
inc a ;add one to convert from zero based.
ora #$2700 ;add in parent entry length
clc
rts
;==========================================================================
;setup_index... This routine will setup temp4_ptr to point to the second
; half of the general work buffer supplied by GS/OS.
;==========================================================================
setup_index_ptr
clc
lda gbuf_ptr ;set temp4_ptr to point to second
adc #$0200 ;have of the general work buffer.
sta temp4_ptr
lda gbuf_ptr+2
adc #$0000
sta temp4_ptr+2
rts
;==========================================================================
;get_one_blk This routine will allocate one block from disk
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; gbuf_ptr
;
;Output: A = restored or error code if carry set.
; X = restored
; Y = restored
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; first_free ;block number allocated
;
;Uses: find_free_blks
;
;==========================================================================
get_one_blk
pha
phx
phy
lda #$0001 ;allocate one block
ldx first_free ;start at the beginning
jsr find_free_blks
bcs exit_with_err
stx first_free
bra exit_one_blk
exit_with_err
sta 5,s ;store the error code on the stack!
exit_one_blk
ply
plx
pla
rts
;==========================================================================
;add_index This routine will add in a block to an index buffer.
;
;Input: A = block number
; X = undefined
; Y = index into buffer
; P = nvmxdizc
; ..000...
; b = k
;
; temp4_ptr
;
;Output: A = restored
; X = restored
; Y = restored
; P = nvmxdizc
; ..000...
; b = k
;
;
;Uses: temp4_ptr
;
;==========================================================================
EXPORT add_index
add_index
phx
phy
pha
tya
ora #$0100 ;index to second half of buffer
tax
lda 1,s
longa off
sep #$20
sta [temp4_ptr],y ;store the low byte please
txy ;index to second half of buffer
xba ;high word of the block number
sta [temp4_ptr],y
longa on
rep #$20
pla ;restore A
ply ;restore Y
plx ;restore X
rts ;exit
;==========================================================================
;eof_to_blks This routine will convert the size of the file into the
; number of blocks needed to store the file on disk. The
; routine does NOT account for directory extension that is
; handled elsewhere, it does however account for storage
; type.
;
;Input: data_eof ;size of data fork
; res_eof ;size of resource fork
;
;output: blks_used1 ;blocks needed for data fork/subdir
; blks_used2 ;blocks needed for resource fork
;
;==========================================================================
EXPORT eof_to_blks
eof_to_blks
lda expand_file ;are we expanding a file or building a new one?
bpl @its_new
lda expand_blks_used
sta blks_used1 ;set the number of blocks needed for current file
bra do_res ;now do the resource fork
@its_new
lda data_eof+1 ;convert bytes to blocks
lsr a
sta blks_used1 ;blocks needed by data fork
beq bump_blks1 ;see if we need to round up
lda data_eof ;see if size is < 512
and #$01FF
beq do_res ;no need to bump
bump_blks1 ;
inc blks_used1
do_res
lda storage_type ;do we need resources?
cmp #resource_type
bne end_eof_conv ;not a resource file
lda res_eof+1 ;convert bytes to blocks
lsr a
sta blks_used2 ;blocks needed by data fork
beq bump_blks2 ;see if we need to round up
lda res_eof ;see if size is < 512
and #$01FF
beq end_eof_conv ;no need to bump resource size
bump_blks2 ;
inc blks_used2 ;add for resource file
end_eof_conv
lda expand_file ;are we expanding a file or building a new one?
bmi @skip_adjust ;since the blocks used is already the total skip adjust
lda blks_used1 ;add any storage overhead for data fork
jsr adjust_storage
sta blks_used1
sty data_storage ;storage type for data fork
stx data_indexes ;number of index blocks
@skip_adjust
lda blks_used2 ;add any storage overhead for res. fork
jsr adjust_storage
sta blks_used2
sty res_storage ;storage type for resource fork
stx res_indexes ;number if inedx blocks
rts
;==========================================================================
;do_create_path This routine will setup the flags needed to process the
; input pathname. we only return from the subroutine if
; everything with the pathname is OK.
;
;input: none
;
;output: seach_name is still valid from find file.
;
;==========================================================================
do_create_path
lda #no_root+no_dup ;root level or duplciate is not valid.
sta search_flag
jsr process_path
bcs no_dup_problem ;there is no duplicate file
;
;If the file already is on the disk we need to see if we are adding the resource
;fork. If we are adding the resource fork we will pull out the important information
;from the current file entry (data fork) and save if so we can use it to build the
;data fork of are new extended file
;
ldy #pblk_01_stype ;see if we are adding a resource fork to the file
lda [my_pblk_ptr],y ;get the input storage type
cmp #(resource_type/$10)+$8000
bne @not_an_extend
sta expand_file ;set the flag indicating we are extending the file
sta expand_flag ;expand_flag lives thru the entire call, whereas expand_file
;only lives thru the create data fork portion of the call.
;
;now lets setup the important information:
; storage type
; key block
; blks_used
; eof
;
;Remember drvr_buf_ptr points to the prodos directory entry
;
lda [drvr_buf_ptr] ;get the storage type please
and #$00F0 ;clear the garbage
cmp #resource_type ;see if the resource fork is already here.
beq @no_extend
cmp #tree_type+1 ;file must be a seedling sapling or tree
bcs @storage_err ;we cannot extend this file
sta expand_storage
sta data_storage ;this will be a 10,20 or 30 (seedling, sapling or tree)
ldy #key_blk_index
lda [drvr_buf_ptr],y
sta expand_key_blk ;save the key block for the data fork
ldy #blks_used_index
lda [drvr_buf_ptr],y
sta expand_blks_used
ldy #eof_index
lda [drvr_buf_ptr],y
sta expand_eof
iny
lda [drvr_buf_ptr],y
sta expand_eof+1
bra no_dup_problem
@no_extend
lda #resource_exist ;the resource already exist bud.
sec
bra real_problem
@storage_err
lda #res_add_err ;the resource cannot be added to this type file
sec
bra real_problem
@not_an_extend
sec
lda #dup_pathname
real_problem
sec
jmp main_exit
no_dup_problem
bit path_searched ;was the entire path searched?
bpl real_problem ;no, only a portion.
jsr chk_disk_protect ;is the volume write protected?
rts ;If protected, routine will not return
;==========================================================================
;adjust_storage This routine will add in the overhead of the storage type
; to the blocks used for a file.
;
;input: A = blocks
;
;output: A = blocks + overhead
; Y = storage type needed for size
; X = number of index blocks needed (for tree only)
;
;uses: math_temp
;
;Overhead:
; subdirectories: no overhead
; seedling: no overhead
; sapling: 1 block of overhead
; tree: ((blks div 256) + (blks mod 256)) + 1
;==========================================================================
adjust_storage
sta math_temp ;number of file blocks
stz index_blks ;no tree index blocks. (default)
ldy #subdir_type
ldx storage_type
cpx #subdir_type
beq end_overhead ;no overhead for directories
ldy #seedling_type ;this is a seedling type
cmp #$0002
bcc end_overhead ;no overhead
cmp #$0101 ;check high end of sapling
bcs data_tree ;this is a tree file
inc math_temp ;add one for sapling overhead
ldy #sapling_type
bra end_overhead
data_tree
lda math_temp+1 ;see how many sapling blocks we need
and #$00FF
sta index_blks ;number of index blocks needed
clc
adc math_temp
tax
lda math_temp
and #$00FF
beq no_extra
inc index_blks ;add one to number of indexes needed
inx
stx math_temp ;adjust for $xx01 thru $xxFF
no_extra
inx
stx math_temp ;add extra block for tree header.
ldy #tree_type
end_overhead
lda math_temp
ldx index_blks ;index blocks for the tree file.
rts
;==========================================================================
;Default_entry: This routine will fill 'one_entry' with the default
;values for a file.
;
; storage type = seedling
; File Type = unknown ($00)
; Blks Used = $0001
; EOF = $000000
; Version = $05
; Minimum Version = $00
; access = $E3
; create date/time = Current Time
; mod date/time = Current Time
; aux type = $0000
;
;NOTE: The store operations happen in a certain order so as to not overwrite
; information already stored.
;==========================================================================
default_entry
ldy #$0026 ;zero the entry
lda #$0000
clr_name
sta one_entry,y
dey
dey
bpl clr_name
lda #seedling_type ;set the default storage type
sta storage_type
stz one_entry+file_type_index
lda #$0001
sta one_entry+blks_used_index
stz one_entry+eof_index
stz one_entry+eof_index+1
jsr pack_time ;read the GS Clock and set the current time
stx one_entry+create_index
sty one_entry+create_index+2
stx one_entry+last_mod_index
sty one_entry+last_mod_index+2
lda #prodos_version
sta one_entry+version_index
lda #$00E3
sta one_entry+access_index
stz one_entry+aux_type_index
rts
;==========================================================================
;Create_Common: This routine will setup and verify parameters supplied
; by the caller. It will also move the file name into
; one_entry.
;
;input: my_pblk_ptr ;must point to pathname field
;
;output: returns to caller or jmps to sys_exit with error
;
; Access Byte:
; _______________________________
; | | | | | | | | |
; | 1 | 1 | 1 | | | 1 | 1 | 1 |
; |___|___|___|___|___|___|___|___|
; ^ ^ ^ ^ ^ ^
; | | | | | |
; | | | Invisible | Read-Enabled
; | | | Write-Enabled
; | | Backup-Enabled
; | Rename-Enabled
; Destroy-Enabled
;
;==========================================================================
create_common ;
ldy search_length ;length of name to create
longa off
sep #$20
name_loop lda search_name-1,y ;move name to directory entry
sta one_entry,y
dey
bne name_loop
lda search_length
ora storage_type ;add in the storage type
sta one_entry ;store storage/length byte
longa on
rep #$20
lda pcount ;see if we are done
dec a
bne cont01
rts
cont01
sta math_temp
ldy #pblk_01_accs ;check the access
lda [my_pblk_ptr],y
tax ;save access so we can verify range
and #$FF18 ;Destroy,Rename,Backup,Invisible,Write,Read
beq good_access ;no problem
not_in_range
sec
lda #parm_range_err
jmp main_exit ;no return from here
good_access ;WARNING zero is stored in half of aux.
txa
ora #backup_enabled ;force the backup bit please.
sta one_entry+access_index
dec math_temp ;see if any more fields?
beq to_end_common
ldy #pblk_01_ftype ;get the user's file type
lda [my_pblk_ptr],y
xba
bne not_in_range
xba ;put file type in low byte please
sep #$20 ;08 bit accum
sta one_entry+file_type_index
rep #$20 ;16 bit accum
cmp #dir_file_type ;set storage to SubDir incase PCount = 4
bne skip_storage ;do not setup the storage type
lda #subdir_type
sta storage_type
skip_storage
dec math_temp ;are we done?
beq end_common
ldy #pblk_01_aux+2 ;get the user's aux type
lda [my_pblk_ptr],y
bne not_in_range ;sorry bud
dey
dey
lda [my_pblk_ptr],y
sta one_entry+aux_type_index
dec math_temp ;are we done?
to_end_common
beq end_common ;yes
ldy #pblk_01_stype ;get the user's storage type
lda [my_pblk_ptr],y
and #$7FFF ;clear the expand file bit please
beq standard_file ;seedling is the default
xba
bne not_in_range ;parameter not valid
xba
cmp #(tree_type/$10)+1
bcc standard_file ;seedling is the default
cmp #(resource_type/$10)
beq force_resource
cmp #(subdir_type/$10)
bne not_in_range
lda #subdir_type
sta storage_type ;we are making a directory
bra next_parm
force_resource
lda #resource_type
sta storage_type
bra next_parm
standard_file
lda #seedling_type
sta storage_type
next_parm
lda storage_type ;if storage is subdirectory then force filetype
cmp #subdir_type
bne @not_subdir
lda #dir_file_type ;set file type to subdirectory
sep #$20
sta one_entry+file_type_index
rep #$20 ;16 bit accum
@not_subdir
dec math_temp ;are we done?
beq end_common
ldy #pblk_01_eof1+2 ;data fork EOF
jsr create_eof
stx data_eof
sty data_eof+2
dec math_temp ;is there a resource eof.
beq end_common
lda storage_type ;is this a resource file?
cmp #resource_type
bne end_common ;don't bother checking
ldy #pblk_01_eof2+2
jsr create_eof
stx res_eof
sty res_eof+2
end_common ;
rts
;==========================================================================
;Create_Eof: This routine returns the EOF supplied by the user
;
;Input: my_pblk_ptr (must be setup)
; Y register (index into parameter block)
;
;Output: X register = low word of EOF
; Y register = high word of EOF
; storage_type adjusted for correct eof
;
;==========================================================================
create_eof ;
lda [my_pblk_ptr],y
xba
bne not1_in_range ;I cannot make a file this large
xba
pha
dey
dey
lda [my_pblk_ptr],y ;get the low word
tax ;save low word of eof
ply ;save high word of eof
lda storage_type ;adjust storage type as needed
cmp #resource_type
beq end_create_eof
cmp #subdir_type
beq entry_to_bytes
tya ;see what type of file is needed
lsr a
beq not_tree ;this cannot be a tree file
cmp #$0002 ;still might be a sapling
bcs is_tree
txa ;if low word is zero then its a sapl.
beq make_sap
is_tree
lda #tree_type
sta storage_type
rts
not_tree
tya ;See if EOF greater then 512
bne make_sap ;This file must be a sapling file!
txa
cmp #$0201 ;is the eof < 512
bcc make_seedling
make_sap ;
lda #sapling_type
sta storage_type
rts
make_seedling ;
lda #seedling_type
sta storage_type
end_create_eof ;
rts ;exit to caller
not1_in_range ;
sec
lda #parm_range_err
jmp main_exit
entry_to_bytes ; ;convert file entries to bytes
tya ;check high byte of entries wanted
bne not1_in_range
cpx #1664 ;no more then 1663 entries for subdirs.
bcs not1_in_range
txa ;convert entries to bytes
pha
;
; Convert number of entries to bytes needed.
;
; Bytes := ((entries * 3) * 13 + ((entries / 13) + 5))
;
clc
asl a ;times 2
adc 1,s ;times 3
sta 1,s ;math_temp
asl a ;times 2
asl a ;times 4
adc 1,s ;times 5
adc 1,s ;times 6
asl a ;times 12
adc 1,s ;times 13
adc #13 ;add 13 for the header field needed
pha ;save partial
sub_loop ;
txa ;restore number if entries left
sec
sbc #13 ;subtract 13 entries
beq sub_done ;we are done
bcc sub_done
tax
clc
lda 1,s ;get the partial
adc #$0005 ;add in block overhead for each block
sta 1,s
bra sub_loop
sub_done plx ;save bytes in X register
pla ;clean up stack
rts
ENDP
;==========================================================================
;Destroy: This routine will delete a file from the disk.
;
;Created: may 28, 1987
;Modified: jun 04, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; find_file
;
;Call Format:
;
;Class 0 Pathname Pointer (4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Pathname Pointer (4 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT destroy
destroy PROC
;==========================================================================
;Go and setup the defaul parameters for the call.
;==========================================================================
jsr setup_params
;==========================================================================
;Process the file name and locate it.
;==========================================================================
jsr process_path
jsr chk_disk_protect
;==========================================================================
;Now that we have located the file we need to see if its OK to destroy it.
;
;We can delete any file except the volume header, or a file that is destroy
;disabled.
;==========================================================================
lda [drvr_buf_ptr]
and #$00F0
sta math_temp ;save the storage type for later
cmp #volume_header
bne check_access
wrong_access
lda #invalid_access
destroy_error
sec
destroy_exit
jmp main_exit
check_access
jsr move_dir_entry
ldy #access_index
lda [drvr_buf_ptr],y
and #destroy_enabled
beq wrong_access ;file is marked as 'destory disabled'
;==========================================================================
;make sure the file that they are deleting is NOT open
;==========================================================================
ldy #key_blk_index ;see if the file is open
lda [drvr_buf_ptr],y
sta files_key_block
stz fcr_wanted ;reset for search
look_loop
jsr get_next_fcr
bcs not_open
ldy #fcr_vol_id
lda [my_fcr_ptr],y ;get the Volume ID so we can make sure the file is on
cmp [my_vcr_ptr] ;the same volume.
bne look_loop ;File is on another volume
ldy #fcr_file_id
lda [pro_fcr_ptr],y ;see if this is the same file
cmp files_key_block
bne look_loop
lda #file_busy
bra destroy_error ;cannot delete an open file
not_open
ldy files_key_block
lda math_temp ;storage TYPE!!!
cmp #subdir_type
bne check_tree
;==========================================================================
;It's a directory so go ahead and try to delete it. We can only delete
;it if there are no files in the directory. Y=key block of the file
;==========================================================================
jsr kill_dir ;Y=key block
bra update_dir
check_tree ;
cmp #tree_type
bne check_sapling
;==========================================================================
;Go ahead and remove the tree file if you can. Y=Key block of the file
;==========================================================================
jsr kill_tree ;Y=Key block of the file
bra update_dir ;update the directory now
check_sapling ;
cmp #sapling_type
bne check_seedling
;==========================================================================
;it's a sapling file so remove it. Y=key block of the file
;==========================================================================
jsr kill_sapling
bra update_dir
check_seedling ;
cmp #seedling_type
bne check_res ;it better be a resource file
;==========================================================================
;This is a simple file to delete. Y=data block that needs to be deleted
;==========================================================================
jsr kill_seedling
bra update_dir
check_res
cmp #resource_type
bne wrong_type
;==========================================================================
;This is the hardest of the files to delete, since it is made up of many
;files. Y=key block of the file
;==========================================================================
jsr kill_resource
bra update_dir
wrong_type
lda #bad_store_type ;I should NEVER get this error!
brl destroy_error
update_dir
php
pha ;save the error code and flag
jsr flush_bitmap ;write out any left over bitmap changes
bcs piggy_back
;==========================================================================
;at this point we have removed all the blocks of the file. We now need
;to update the directory entry itself. The following parameters better
;be setup correctly or death and destruction will occur.
;
; entries_blk_num (block number of the entry to delete)
; entry_offset (offset to entry within the block)
; dir_start_blk (header block for this directory)
;
;==========================================================================
;==========================================================================
;Setup the buffer pointer and device parameters
;==========================================================================
jsr set_default_buf
;==========================================================================
;We want to load in the block that contains the files entry.
;==========================================================================
lda entries_blk_num
sta drvr_blk_num
jsr read_with_cache
bcs piggy_back
;==========================================================================
;We have loaded the block, now lets zap the entry to indicate that it's
;dead.
;==========================================================================
ldy entry_offset
lda [drvr_buf_ptr],y
and #$FF00 ;clear storage type and name length
sta [drvr_buf_ptr],y
;==========================================================================
;before we write the block back to disk lets see if it is the key block
;This way we save an extra write to disk
;==========================================================================
lda [drvr_buf_ptr] ;check the previous link
beq update_header ;don't write block twice
;==========================================================================
;write the entries block back to disk.
;==========================================================================
jsr write_with_cache
bcc fix_header
piggy_back brl destroy_exit ;Major problems.
fix_header ;
;==========================================================================
;Now lets go and update the header of this directory.
;==========================================================================
lda dir_start_blk
sta drvr_blk_num
jsr read_with_cache
bcs piggy_back
update_header
lda [drvr_buf_ptr]
bne dir_damaged
ldy #$0023
lda [drvr_buf_ptr],y
cmp #$0D27
bne dir_damaged
ldy #header_file_cnt
lda [drvr_buf_ptr],y
dec a
bmi dir_damaged
sta [drvr_buf_ptr],y
ldx drvr_blk_num ;see if this is the root directory
cpx #vol_dir_start
bne not_root
pha
jsr setup_my_vcr
stx prodos_vcr_ptr
sty prodos_vcr_ptr+2
pla
ldy #vol_file_count ;update the file count in VCR also
sta [prodos_vcr_ptr],y
;==========================================================================
;write the header block back to disk.
;==========================================================================
not_root ;
jsr write_with_cache
bcs piggy_back
;==========================================================================
;Now we need to ripple back. This routine will time stamp all previous
;subdirectory files with the current time.
;==========================================================================
do_ripple ;
jsr slug_ripple
pla ;restore any error code
plp ;restore error flag from delete
bra piggy_back
dir_damaged ;
lda #dir_error
brl destroy_error
;==========================================================================
;==========================================================================
;===== END of MAIN CODE FOR DESTROY ======================================
;==========================================================================
;==========================================================================
kill_dir ;
;==========================================================================
;This routine is called when we are deleting a directory. Before we delete
;the directory we must make sure that there are NO files in the directory.
;
;input: Y = key block address of the directory
;==========================================================================
sty drvr_blk_num
jsr standard_req ;read of 512 bytes
jsr set_default_buf ;use GS/OS system work buffer
lda drvr_buf_ptr
sta temp_ptr
lda drvr_buf_ptr+2
sta temp_ptr+2
jsr read_with_cache
bcs dir_err_exit
lda [drvr_buf_ptr] ;the previous link better be zero
bne dir_damaged
ldy #$0023 ;verify that this is a directory
lda [drvr_buf_ptr],y
cmp #$0D27
bne dir_damaged
ldy #header_file_cnt
lda [drvr_buf_ptr],y
beq remove_dir ;it's ok to delete the directory
brl wrong_access
remove_dir ;
lda [drvr_buf_ptr]
beq first_block
cmp data_ptr
bne dir_damaged
first_block ;
ldx drvr_blk_num
stx data_ptr
dir_loop ;
jsr dealloc_block ;remove the block please
bcs dir_err_exit
lda temp_ptr
sta drvr_buf_ptr
lda temp_ptr+2
sta drvr_buf_ptr+2
;==========================================================================
;Verify that the backward link is correct, and if it is then delete the blk
;==========================================================================
ldy #$0002 ;walk the forward links
lda [drvr_buf_ptr],y
sta drvr_blk_num
beq dir_done ;the forward link is nill so we are done
jsr read_with_cache
bcc remove_dir
dir_err_exit ;
rts
dir_done
clc
rts
kill_resource
sty drvr_blk_num
sty data_ptr ;save key block of the file
jsr standard_req
;==========================================================================
;Allocate a one block buffer
;==========================================================================
clc
lda #blk_size
jsl alloc_seg ;allocate a one block buffer
bcs res_err_exit
stx virtual
stx displacement
sty virtual+2 ;save the virtual pointer please
sty displacement+2
jsl deref ;convert this to a real address
stx drvr_buf_ptr ;save the address of the buffer
stx temp2_ptr
sty drvr_buf_ptr+2
sty temp2_ptr+2
jsr read_with_cache ;read in the resource header block
bcs res_err_exit
lda [temp2_ptr] ;get the storage type
and #$00FF
tax ;save storage type
ldy #$0001
lda [temp2_ptr],y
beq res_bad
tay ;key block of the resource
jsr zap_branch ;delete the resource
bcs res_err_exit
ldy #$0100
lda [temp2_ptr],y ;get the storage type
and #$00FF
tax ;save storage type
iny
lda [temp2_ptr],y
beq res_bad
tay ;key block of the resource
jsr zap_branch ;delete the resource
bcs res_err_exit
res_done
ldx displacement ;release memory used by the call
ldy displacement+2
jsl release_seg ;release the one block of head space
ldy data_ptr
jmp kill_seedling ;remove the last block now
;==========================================================================
res_bad
lda #out_of_range ;resource block is screwed up
res_err_exit
jsr free_header_buf
sec
rts
;===========================================================================
;this routine will delete a resource branch.
;X = storage type
;Y = key block
;output : carry clear = no error
;
zap_branch
dex
bne not_seedling
jsr kill_seedling
rts
not_seedling
dex
bne not_sapling
jsr kill_sapling
rts
not_sapling dex
bne @res_error ;file screwed up
jsr kill_tree
rts
@res_error
sec
rts
;==========================================================================
free_header_buf ;
pha ;save the error code
ldx displacement
ldy displacement+2
jsl release_seg ;release the one block of head space
pla
rts
;==========================================================================
;The following code will delete a tree file. It will also flip the index
;block and write it back out to disk.
;
;input: Y = key block of the file
;
;==========================================================================
kill_tree ;
sty drvr_blk_num
sty master_ptr
clc
lda gbuf_addr
adc #blk_size
sta drvr_buf_ptr
sta temp3_ptr
lda gbuf_addr+2
adc #$0000
sta drvr_buf_ptr+2
sta temp3_ptr+2
jsr standard_req
jsr read_with_cache
bcs master_error
lda #$0100 ;start at the last entry
pha
master_loop ;
lda temp3_ptr ;restore pointer to I/O buffer
sta temp_ptr
lda temp3_ptr+2
sta temp_ptr+2
inner_master ;
pla ;get the current index
dec a
bmi master_done
pha
tax
ora #$0100
tay
jsr get_and_flip ;get and flip the index block number
tay
beq inner_master
jsr kill_sapling
bcs master_pop
bra master_loop
master_done ;
ldx temp_ptr
ldy temp_ptr+2
lda master_ptr
jsr write_flipped ;write master index block flipped
bcs master_error
ldy master_ptr
bra kill_seedling
master_error ;
rts
master_pop ;
pla
sec
rts
;==========================================================================
;The following code will delete a sapling file. It will also flip the index
;block and write it back out to disk.
;
;input: Y = key block of the file
;
;==========================================================================
kill_sapling ;
;==========================================================================
;Now we need to read in the index block and deallocate all allocated blocks
;==========================================================================
sty drvr_blk_num
sty index_ptr ;save for later delete
jsr set_default_buf
lda drvr_buf_ptr
sta temp_ptr
lda drvr_buf_ptr+2
sta temp_ptr+2
jsr standard_req
jsr read_with_cache
bcs index_error
lda #$0100 ;start at the last entry
pha
index_loop ;
pla ;get the current index
dec a
bmi index_done
pha
tax
ora #$0100
tay
jsr get_and_flip ;get the block number from index buffer
tax
beq index_loop
jsr dealloc_block ;free the block up please
bcc index_loop
plx
rts
index_done ;
ldx temp_ptr
ldy temp_ptr+2
lda index_ptr
jsr write_flipped
bcc kill_index
index_error rts
kill_index ;
ldy index_ptr ;now release the last block please
;==========================================================================
;This routine will free up a seedling files block.
;
;input: Y = key block of the file.
;
;==========================================================================
kill_seedling ;
tyx
jsr dealloc_block
rts ;return error
;==========================================================================
;this routine will write a flipped index block to disk
;
;input:
; X = low word of buffer pointer
; Y = high word ''''
; A = block number
;
;Output:
; A = error if carry set
;
;==========================================================================
write_flipped ;
stx drvr_buf_ptr
sty drvr_buf_ptr+2
sta drvr_blk_num
jsr write_with_cache
rts
;==========================================================================
;This routine will flip the block number within an index block.
;
;input:
; X = Index into the lower half of the index block
; Y = Index into the higher half of the index block
; temp_ptr = pointer to the index block
;
;OutPut:
; A = Block number from index block
;
;==========================================================================
get_and_flip ;
longa off
sep #$20
lda [temp_ptr],y ;grab the high block number
xba ;put high block number in upper byte
phy ;save high byte index
txy
lda [temp_ptr],y ;get the low block number
xba ;restore high block number
sta [temp_ptr],y ;store high block number in low half
xba ;restore low block number
ply ;get high byte index
sta [temp_ptr],y ;store low block number in high half
longa on
rep #$20
rts
ENDP
;======================================================================
;Chnage_Path This file will RENAME or MOVE a file to a new directory
;
;Created: Nov 04, 1987
;Modified: Nov 04, 1987
;Author: Rob Turner
;
;Enter: From GS/OS jmp table
;
;Input: A = undefined
; X = (call number * 2)
; Y = (class number * 2)
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set (exit via system exit routine)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
; find_file (subroutine)
;
;Call Format:
;
;Class 0 Source Pathname Pointer (4 Bytes)
; Destination Path Pointer(4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Source Pathname Pointer (4 Bytes)
; Destination Path Pointer(4 Bytes)
; Flags (2 Bytes)
;
;======================================================================
longa on
longi on
EXPORT change_path
change_path PROC
jsr setup_params
; If the source is only a device # then set cp_device_flag to 1.
; This is a special case so that the Finder can rename a volume whose current name
; is a duplicate of another volume which has open files. 1/22/91 CAE
lda <path_flag ;get pathname flags
and #path1_mask ;is pathname 1 defined?
bne @not_dev ;yes
lda <dev_num ;do we have a device #?
beq @not_dev ;no
inc cp_device_flag ;yes - so indicate source was a device #
; If pCount was 3 or more, get the cp_flags from the parameter block. 5-Dec-91 MSD
lda pcount
cmp #3 ; did it include cp_flags?
blt @not_dev ; no, so leave cp_flags set to 0.
ldy #pblk_04_flags
lda [my_pblk_ptr],y ; get flags from param block
sta cp_flags
; End 5-Dec-91 MSD
@not_dev
jsr process_path ;see if the source file exist also sets up priority bit
;and now checks the spans. 10/10/91 JEV
jsr valid_change
bcs end_change
lda slug_block ;save for slug ripple routine
sta src_slug_blk
lda slug_offset
sta src_slug_offset
jsr chk_disk_protect
jsr save_src_parms
jsr move_dir_entry ;move entry into one_entry
jsr chk_open ;see if the file is currently open
bcs end_change
jsr process_dest ;find the destination name
bcs end_change
; 27-Jan-92 MSD: We let valid_change see if the source path is a subset of the
; destination path (i.e. we're trying to move an item into its own child). If
; that was the case, cp_to_child will be non-zero
lda cp_to_child ; were we moving into our own child?
beq @not_subset ; no, so do the move
lda #bad_path_syntax ; source path was a subset of destination path
sec
bra end_change
@not_subset
jsr save_dest_parms
jsr rename_or_move
end_change ;
pha ;save the error code
php
jsr flush_bitmap ;make sure the bitmap is OK!
plp ;restore error flag
pla ;restore error code if any
jmp main_exit
;======================================================================
chk_open
stz fcr_wanted ;start from the beginning
lda one_entry+key_blk_index
sta key_block
open_loop
jsr get_next_fcr
bcs end_chk_open
ldy #fcr_vol_id
lda [my_fcr_ptr],y ;get the Volume ID so we can make sure the file is on
cmp [my_vcr_ptr] ;the same volume.
bne open_loop ;File is on another volume
ldy #fcr_file_id
lda [pro_fcr_ptr],y
cmp key_block
bne open_loop
lda #file_busy
sec
rts
end_chk_open ; ;the file is not open
clc
rts
;======================================================================
rename_or_move ; ;what will we do?
lda src_dev_num
cmp dest_dev_num
bne bad_device ;cannot rename on different devices
lda src_start_blk
cmp dest_start_blk ;is the file in the same directory?
beq do_rename ;this can only be a rename
;
;IF here we are doing a change_path
;
jsr move_file ;do a change_path please
rts
;
;IF here we are doing a simple rename
;
do_rename
jsr rename_file
rts
bad_device
brl bad_path
;======================================================================
move_file
; First, we need to check the file/dir that we're moving to see if it's
; 'rename enabled'. If it isn't, then we need to make sure we're just
; doing a move and not a 'move and rename'.
; Added 3/15/91 CAE
jsr standard_req ;read in 512 bytes
jsr set_default_buf
lda src_dev_num
sta drvr_dev_num
lda src_dir_blk ;block that contains the entry
sta drvr_blk_num
jsr read_with_cache
bcs to_end_move_file
clc
lda src_offset
adc #access_index
tay
lda [drvr_buf_ptr],y
and #rename_enabled ;can we rename the file?
bne @ok ;branch if rename is OK
ldy src_offset ;rename is not allowed so
lda [drvr_buf_ptr],y ;get file's name length
and #$000f ;strip off storage nibble and hi byte
cmp search_length ;same as dest name length?
beq @cont ;yes - so compare the names
@uh_oh rep #$20 ;ensure 16 bit mode
sec
lda #invalid_access ;report an error
rts
@cont tax ;calculate offset to end of dest name
dex ;and put in X
clc
adc src_offset ;calculate offset to end of file's name
tay ;in the dir entry and put in Y
longa off
sep #$20 ;begin 8 bit mode
@loop lda [drvr_buf_ptr],y ;compare file's name
cmp search_name,x ;to dest name
bne @uh_oh ;branch if mis-match
dey
dex
bpl @loop ;loop until done
longa on
rep #$20 ;begin 16 bit mode
@ok
; End of new code.
ldx free_dir_blk ;do we need to extend the directory?
bne no_extend
jsr calc_free_blks ;do we have a free block
bcs to_end_move_file ;NO we have an error
tax
bne not_full
brl full_disk
not_full
jsr extend_subdir ;grow the subdirectory
bcs end_move_file
lda free_dir_offset ;offset to directory entry
sta dest_offset
lda free_dir_blk ;block that contains entry
sta dest_dir_blk
no_extend
jsr standard_req ;512 bytes
jsr set_default_buf ;use the temp buffer
lda src_dir_blk ;move and delete the old entry
sta drvr_blk_num
jsr read_with_cache
bcs end_move_file
jsr delete_entry ;move entry and delete it
jsr write_with_cache
to_end_move_file
bcs end_move_file
lda src_start_blk
sta drvr_blk_num
jsr read_with_cache
bcs end_move_file
jsr dec_file_cnt
jsr write_with_cache
bcs end_move_file
jsr do_src_slug
lda dest_start_blk ;new header pointer
sta one_entry+header_ptr_index
lda dest_dir_blk ;block
sta drvr_blk_num
jsr read_with_cache
bcs end_move_file
jsr move_old_entry
ldy dest_offset
jsr move_new_name ;rename the entry
jsr write_with_cache
bcs end_move_file
lda dest_start_blk ;now update the file count
sta drvr_blk_num
jsr read_with_cache
bcs end_move_file
jsr inc_file_cnt
jsr write_with_cache
bcs end_move_file
jsr do_dest_slug
lda storage_type
and #$00F0 ;see if this is a subdirectory
cmp #subdir_type
bne move_done
jsr fix_subdir
rts
move_done
clc
end_move_file
rts
full_disk
lda #volume_full
sec
rts
do_src_slug
lda src_slug_blk
sta slug_block
lda src_slug_offset
sta slug_offset
bra do_slug
do_dest_slug
lda dest_slug_blk
sta slug_block
lda dest_slug_offset
sta slug_offset
do_slug
jsr slug_ripple
rts
fix_subdir
lda one_entry+key_blk_index
sta drvr_blk_num
jsr read_with_cache ;read in the key block
bcs end_fix_subdir
lda dest_dir_blk ;block number where entry is
ldy #parent_index
sta [drvr_buf_ptr],y
lda #subdir_header
sta storage_type
ldy #$0004 ;rename the subdirectory as well
jsr move_new_name
jsr make_entry_num
bcs end_fix_subdir
ldy #parent_index+2
sta [drvr_buf_ptr],y
jsr write_with_cache
end_fix_subdir
rts
dec_file_cnt
ldy #header_file_cnt
lda [drvr_buf_ptr],y
dec a ;update the file count for directory
bmi end_dec
sta [drvr_buf_ptr],y
jsr chk_vol_cnt ;pass the new count in the accum
end_dec
rts
inc_file_cnt
ldy #header_file_cnt
lda [drvr_buf_ptr],y
inc a ;update the file count for directory
sta [drvr_buf_ptr],y
jsr chk_vol_cnt ;pass the new count in the accum
rts
;
;The new count is in the accumulator
;
chk_vol_cnt
ldy drvr_blk_num ;our we updating the Root Level of the disk?
cpy #vol_dir_start
bne @not_vol
;
;Now we need to update the VCR
;
ldy #vol_file_count
sta [prodos_vcr_ptr],y
@not_vol
rts
delete_entry ;
ldx #$0000
ldy src_offset
delete_loop ; ;move the entry to one_entry
lda [drvr_buf_ptr],y
sta one_entry,x
iny
inx
cpx #entry_length
bne delete_loop
ldy src_offset
lda [drvr_buf_ptr],y
and #$FF00 ;clear storage/length field
sta [drvr_buf_ptr],y
rts
move_old_entry ;
ldx #$0000
ldy dest_offset
longa off
sep #$20
change_loop ;
lda one_entry,x
sta [drvr_buf_ptr],y
iny
inx
cpx #entry_length
bne change_loop
longa on
rep #$20
rts
;======================================================================
rename_file ;
jsr standard_req ;read in 512 bytes
jsr set_default_buf
lda src_dev_num
sta drvr_dev_num
lda src_dir_blk ;block that contains the entry
sta drvr_blk_num
jsr read_with_cache
bcs end_rename
clc
lda src_offset
adc #access_index
tay
lda [drvr_buf_ptr],y
and #rename_enabled ;can we rename the file?
bne @10 ;rename is OK
sec
lda #invalid_access ;sorry bud
rts ;return with the error please
@10
clc
lda src_offset ;get the key block
adc #key_blk_index
tay
lda [drvr_buf_ptr],y
sta one_entry+key_blk_index
ldy src_offset ;offset the old name
lda [drvr_buf_ptr],y
and #$00F0 ;isolate the storage type
sta storage_type
cmp #volume_header
beq rename_vol
cmp #subdir_type
beq type_ok
cmp #seedling_type
beq type_ok
cmp #sapling_type
beq type_ok
cmp #tree_type
beq type_ok
cmp #resource_type
bne bad_type
type_ok ;
ldy src_offset
jsr move_new_name
jsr write_with_cache
bcs end_rename
lda storage_type
and #$00F0 ;clear the garbage
cmp #subdir_type
bne end_rename_clr
lda src_dir_blk
sta dest_dir_blk
lda src_offset
sta free_dir_offset
jsr fix_subdir
bcs end_rename
end_rename_clr
jsr slug_ripple ;ripple the time please
end_rename
rts
bad_type
lda #bad_store_type
sec
rts
;Rename a volume.
;
;Note - if cp_device_flag is $8001 then we're renaming a duplicate volume. In this
; case, don't call chk_boot_vol since the duplicate volume could have the same
; name as the boot volume and we don't want to change the boot prefix.
; 3/26/91 CAE
rename_vol
pei my_vcr_ptr
pei my_vcr_ptr+2
jsr Check_dup_dest ;see if the destination name is an active VCR
ply
sty my_vcr_ptr+2
ply
sty my_vcr_ptr
bcc @not_active
rts
@not_active
ldy volume_name ;terminate the new name with a null
lda #$0000
sta volume_name+2,y
; jsr chk_boot_vol ;moved after call to setup_name 3/26/91 CAE
lda #volume_name ;move the name to search_name and
sta temp_ptr ;do syntax checking on the name
lda #^volume_name
sta temp_ptr+2
lda #$0002 ;starting index for check
sta chars_checked
jsr setup_name
;following 2 lines added 3/26/91 CAE
lda cp_device_flag ;are we renaming a duplicate volume?
bmi @skip_chk ;yes - so don't change the boot prefix
jsr chk_boot_vol ;change boot prefix if renaming boot volume
@skip_chk
lda [my_vcr_ptr] ;get the VCR ID
ldx #volume_name ;pointer to the new name
ldy #^volume_name
jsl rename_vcr
@go_type_ok
clc
brl type_ok
;======================================================================
;We need to make sure that the destination name is not an allocated
;VCR with open files. If we find a VCR with open files then we should
;remove it or return duplicate volume error.
;======================================================================
Check_dup_dest
lda [my_vcr_ptr] ;get the VCR ID of the source
pha
ldx #volume_name ;pointer to the volume name
ldy #^volume_name
lda #0000 ;0000=search for vcr by name
jsl find_vcr
bcc @handle_dup
@no_dup_error
pla ;just fix the stack
clc
rts ;no problem
@handle_dup
jsl deref ;convert virtual to real
stx my_vcr_ptr
sty my_vcr_ptr+2 ;setup pointer to VCR
lda [my_vcr_ptr] ;get the VCR ID of the destination.
cmp 1,s ;is source = dest?
beq @no_dup_error
pla ;we must fix the stack
ldy #vcr_open_cnt
lda [my_vcr_ptr],y
bne @sorry_dup ;files open so don't log in new volume
jsr free_vcr ;free vcr pointed to by 'my_vcr_ptr'
clc
rts ;we are happy again!
@sorry_dup ;
lda #dup_volume ;duplicate volume online
sec
rts
;======================================================================
move_new_name
phy
ldx #14
lda #$0000
clear_old
sta [drvr_buf_ptr],y
iny
dex
bpl clear_old
ply
phy
ldx #$0000 ;index into new name
longa off
sep #$20 ;8 bit mode
lda storage_type
ora search_length
sta [drvr_buf_ptr],y
iny
name_store ;
lda search_name,x ;first ascii character
sta [drvr_buf_ptr],y
iny
inx
cpx search_length
bne name_store ;move the entire name
longa on
rep #$20
pla ;get index to the storage type
clc
adc #version_index
tay
lda storage_type
cmp #volume_header
beq @skip_case_save
@do_both lda case_bits ;set the case bits please
beq @do_save ;the case is all upper
bit fst_attr ;user does not want case info
bmi @do_save
ora #$8000 ;set the case on bit.
@do_save
sta [drvr_buf_ptr],y
rts
@skip_case_save
ldy #create_index+4-2;save the case bits please
jsr @do_both
ldy #vol_create-2
sta [prodos_vcr_ptr],y
rts
;======================================================================
chk_boot_vol ;change the boot prefix if needed
ldy src_offset ;original name of volume
lda [drvr_buf_ptr],y
and #$000F ;clear storage type etc.
inc a ;add one for leading ':'
sta def_vol_name ;length word
lda #'::' ;leading ':'
sta def_vol_name+2
iny ;point to the first character
ldx #$0000
boot_loop ;
lda [drvr_buf_ptr],y
sta def_vol_name+3,x
iny
inx
cpx #16 ;move the max number of characters
bne boot_loop
jsl get_boot_pfx ;get the pointer to the current volume
stx temp_ptr
sty temp_ptr+2
lda [temp_ptr] ;verify the length
cmp def_vol_name
bne not_same
tay ;use as index
iny ;get the last character please
longa off
sep #$20
boot_loop2
lda [temp_ptr],y
cmp #'a'
bcc @no_convert ;The character cannot be lower case
cmp #'z'+1
bcs @no_convert
and #$DF ;convert char to upper case for compare
@no_convert
cmp def_vol_name,y
bne not_same
dey
bpl boot_loop2 ;check the entire volume name
longa on
rep #$20
lda volume_name ;get the length word of the new name
tay
inc a ;add one for leading ':'
sta def_vol_name
lda #'::'
sta def_vol_name+2
new_boot ;
lda volume_name+1,y
sta def_vol_name+2,y
dey
bne new_boot
ldx #def_vol_name ;pointer to the new name
ldy #^def_vol_name
jsl set_boot_pfx
not_same
longa on
rep #$20
rts
;======================================================================
save_src_parms ;
lda drvr_dev_num ;save source info
sta src_dev_num
lda dir_start_blk ;starting block of directory
sta src_start_blk
lda entry_offset ;offset to directory entry
sta src_offset
lda parent_blk ;block that contains entry
sta src_dir_blk
rts
save_dest_parms ;
lda drvr_dev_num ;save source info
sta dest_dev_num
lda dir_start_blk ;starting block of directory
sta dest_start_blk
lda free_dir_offset ;offset to directory entry
sta dest_offset
lda free_dir_blk ;block that contains entry
sta dest_dir_blk
rts
;======================================================================
process_dest ;
lda path1_ptr ;save pathname one pointer
sta curr_mark
lda path1_ptr+2
sta curr_mark+2 ;temp save area!!!!!!!!!!!!!!!
lda dev1_num
sta curr_eof
lda path_flag
sta curr_eof+2 ;temp storage
lda path2_ptr ;make path two appear as path one
sta path1_ptr
lda path2_ptr+2
sta path1_ptr+2
lda dev2_num
sta dev_num
lda path_flag
and #path2_mask
xba
sta path_flag
;
;IF vol_rename_flag = minus then we are renaming the volume
;
bit vol_rename_flag
bpl normal_rename
lda #path1_mask
ldx path1_ptr
ldy path1_ptr+2
jsr build_path
lda src_dev_num
sta drvr_dev_num
lda src_start_blk
sta dir_start_blk
lda src_dir_blk
sta free_dir_blk
lda src_offset
sta free_dir_offset
jsr restore_path1
clc
rts
normal_rename ;
lda #no_dup ;duplicate not allowed
sta search_flag
jsr process_path ;find the destination
ldx slug_block ;save for slug_ripple routine
stx dest_slug_blk
ldx slug_offset
stx dest_slug_offset
bcs no_dup_problem
lda drvr_dev_num ;did the user enter the same name?
cmp src_dev_num
bne dup_prob ;duplicate filename
lda dir_start_blk
cmp src_start_blk
bne dup_prob
lda parent_blk ;block that contains entry
cmp src_dir_blk
bne dup_prob
lda entry_offset ;offset to directory entry
cmp src_offset
bne dup_prob
jsr restore_path1
clc
rts ;renaming the same file
dup_prob ;
jsr restore_path1
lda #dup_pathname
real_problem ;
sec
rts
no_dup_problem ;
pha
jsr restore_path1
pla
bit path_searched ;was the entire path searched?
bpl real_problem
clc
rts
restore_path1 ; ;restore path1 stuff
lda curr_mark
sta path1_ptr
lda curr_mark+2
sta path1_ptr+2
lda curr_eof
sta dev_num
lda curr_eof+2
sta path_flag
rts
;======================================================================
;Possible combinations of pathnames
;
;(1) .DEV1 --> /FOO (Rename)
;(2) /FOO1 --> /FOO (Rename)
;(3) /FOO1/PATHNAME --> /FOO1/NEWNAME (Rename)
;(4) /FOO1/SUB/FILE --> /FOO1/FILE (Change path)
;(5) .DEV1/FILE --> /FOO/NEW.NAME (Rename)
;(6) .DEV1/FILE --> /FOO/SUB/FILE (Change path)
;(7) /FOO/FILE --> .DEV1/SUB/NEW (Change path)
;(8) /FOO/FILE --> .DEV1/NEW (rename)
;
;Invalid Pathnames:
;
;(1) /FOO --> .DEV1
;(2) /FOO --> /FOO/FILE
;(3) /FOO/SUB/ --> /FOO/SUB/SUB1/SUB
;(4) /FOO/FILE --> /FOO
;(5) /FOO/FILE --> .DEV1
;
;======================================================================
valid_change
stz cp_to_child ; assume source is not a subset of dest
stz vol_rename_flag
lda path_flag
and #path2_mask ;there has to be a second pathname
beq to_bad_path
jsr setup_path1 ;Path1_ptr --> temp_ptr
jsr setup_path2 ;Path2_ptr --> temp2_ptr
;
;IF (Path1 = device_name) THEN
; (Path2 must be a volume name only)
;
;
lda path_flag ;is path1 a device name only?
and #path1_mask
bne not_device ;not just a device name
lda dev2_num
bne to_bad_path ;path2 cannot contain a device name
ldx temp2_ptr
ldy temp2_ptr+2
jsr scan_pass_vol ;pass the volume name IF possible
bcc to_bad_path ;there is more then just a volume name
;
;We are doing the following: '.DEV1' to '/FOO'
;
bra volume_rename
to_bad_path ;
brl bad_path
;
; IF (Path1 = volume_only) THEN
; (Path2 must be volume only)
;
not_device ;
lda dev1_num ;IF dev <> 00 THEN not volume only
bne chk_same_dev
ldx temp_ptr ;pointer to path1
ldy temp_ptr+2
jsr scan_pass_vol
bcc not_vol_only ;path1 <> volume only
;
; IF (path1 - volume) and ((path2 = device) or (path2 = dev/vol)) THEN
; bad_path_syntax
;
lda path_flag
and #path2_mask
beq to_bad_path
lda dev2_num ;is there a device name also?
bne a_bad_path
ldx temp2_ptr
ldy temp2_ptr+2
jsr scan_pass_vol ;pass the volume name IF possible
bcc to_bad_path ;path2 <> volume only
;
;We are doing the following: '/FOO1' to '/FOO'
;
volume_rename ;
dec vol_rename_flag
clc
rts
;
;IF (Path1 <> volume only) THEN
; (Path2 MUST be a pathname)
;
chk_same_dev ;
lda dev2_num ;is the rename on the same device?
beq not_vol_only
cmp dev1_num
a_bad_path bne to_bad_path
not_vol_only ;
lda path_flag
and #path2_mask ;is there a pathname?
if AllowLCPeriods then ;added 7-Aug-91 DAL
beq to_bad_path
else ;original code
beq bad_path ;must be device name only
endif ;end 7-Aug-91 DAL
lda dev2_num ;IF (dev = true) and (path = true) THEN
bne path2_is_path ; (path2 = pathname)
ldx temp2_ptr
ldy temp2_ptr+2
jsr scan_pass_vol
bcs bad_path ;Path2 = volume name only!
stx temp2_ptr
sty temp2_ptr+2
;
;IF (path2 = path2-volume_name) and (path1 = volume_name) THEN
; Path1 := Path1-volume_name
;
lda dev1_num ;does path1 contain a volume name?
bne do_scan ;no it does not
ldx temp_ptr
ldy temp_ptr+2
jsr scan_pass_vol ;scan past the volume name
stx temp_ptr
sty temp_ptr+2
bra do_scan
;
; When Here Path1 and Path2 are both pathnames!
;
; IF (Path1 = dev) and (path2 = vol) THEN
; path2 := path2-volume name
;
path2_is_path ;
lda dev1_num ;does path1 contain a device name?
beq path1_volume ;NO
lda dev2_num ;does path2 contain a device name?
bne do_scan ;path1 and path2 contains device names
ldx temp2_ptr
ldy temp2_ptr+2 ;scan pass the volume name
jsr scan_pass_vol
stx temp2_ptr
sty temp2_ptr+2
bra do_scan ;everything is OK so far
;
; IF (path1 = volume) and (path2 = dev) THEN
; path1 := path1-volume_name
path1_volume ;
lda dev2_num ;does path2 contain a
beq do_scan ;Path1 and path2 contain volume names
;
; IF (path2 = dev) and (path1 = vol) THEN
; path1 := path1-volume_name
;
scan_path1 ;
ldx temp_ptr ;path1
ldy temp_ptr+2
jsr scan_pass_vol ;pass the volume name
stx temp_ptr
sty temp_ptr+2
;The following code was moved into the changepath code so
;that ProDOS conforms to the way AppleShare and HFS do a
;changepath. The check to see if the path1 is a subset
;of path2 should not be done until after we check to see
;if the path2 allready exsists. 10/3/91 JEV
;
; IF (path1 is a subset of path2) THEN
; set cp_to_child
;The following code has been changed to do a case-insensitive
;compare in order to fix a bug which was allowing illegal moves
;if the case of the pathnames was different. 1/22/91 CAE
do_scan ;
ldy #$0000 ;start scan at beginning
longa off
sep #$20
subset_loop ;
; lda [temp_ptr],y ;source file
; cmp [temp2_ptr],y ;destination
; bne more_chk
; iny
; bra subset_loop
lda [temp_ptr],y ;get source file char
if AllowLCPeriods then ;added 7-Aug-91 DAL
cmp #' '
bne @not_blank1
lda #'.'
@not_blank1
endif ;end 7-Aug-91 DAL
cmp #'a' ;upper case?
bcc @its_upper1 ;yes
and #$DF ;no - so convert to upper case
@its_upper1 pha ;save char
lda [temp2_ptr],y ;get dest file char
if AllowLCPeriods then ;added 7-Aug-91 DAL
cmp #' '
bne @not_blank2
lda #'.'
@not_blank2
endif ;end 7-Aug-91 DAL
cmp #'a' ;upper case?
bcc @its_upper2 ;yes
and #$DF ;no - so convert to upper case
@its_upper2 cmp 1,s ;same as source file char?
bne @no_match ;nope
pla ;yes - so clean up stack
iny ;adjust index
bra subset_loop ;and loop
@no_match pla ;clean up stack
more_chk ;
lda [temp2_ptr],y ;this cannot be a 'delimiter'
cmp #delimiter
longa on ;go ahead and fix the mode
rep #$20
bne paths_ok
lda [temp_ptr],y ;is this the end of the source?
and #$00FF
bne paths_ok ;not the end of source name
inc cp_to_child ; source was subset of dest! 27-Jan-92 MSD
paths_ok clc
rts
bad_path
lda #bad_path_syntax
sec
jmp main_exit
;======================================================================
scan_pass_vol ;
stx temp3_ptr ;pointer to the string
sty temp3_ptr+2
ldy #$0001
longa off
sep #$20
scanner ;
lda [temp3_ptr],y
beq scan_failed ;sorry volume name only
cmp #delimiter
beq scan_passed
iny
bra scanner
scan_passed ;
; 27-Jan-92 MSD: This routine was returning a pointer to the character
; before the pathname separator. It should have been returning a pointer
; to the character after the separator. Removed the DEY and changed a
; CLC to a SEC before adding (so it will add 1 to point past separator).
;
; dey
longa on
rep #$20 ;back to 16 bit mode
sec ; 27-Jan-92 MSD (was CLC)
tya
adc temp3_ptr
tax
lda #$0000
adc temp3_ptr+2
tay
clc ;not just a volume name
rts
scan_failed ;
longa on
rep #$20
; ldx temp3_ptr
ldy temp3_ptr+2 ;restore X and Y
sec
rts
;======================================================================
setup_path2 ;
ldx path2_ptr
ldy path2_ptr+2
inx
bne do_again
iny
do_again inx
bne cont
iny
cont ;
stx temp2_ptr ;pointer to pathname two
sty temp2_ptr+2
rts
;======================================================================
setup_path1 ;
ldx path1_ptr
ldy path1_ptr+2
inx
bne do2_again
iny
do2_again inx
bne cont2
iny
cont2 ;
stx temp_ptr ;pointer to pathname one
sty temp_ptr+2
rts
ENDP
;==========================================================================
;Set_file_info: This routine will alter information about a file on disk.
;
;Created: may 26, 1987
;Modified: jan 06, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; find_file
;
;Call Format:
;
;Class 0 Pathname Pointer (4 Bytes)
; Access (2 Bytes: Disk Format)
; File Type (2 Bytes)
; Aux Type (4 Bytes)
; Null Field (2 Bytes)
; Create Date/Time (4 Bytes: ProDOS Format)
; Mod Date/Time (4 Bytes: ProDOS Format)
;
;Class 1 PCount (2 Bytes)
; Pathname Pointer (4 Bytes)
; Access (2 Bytes: Disk Format)
; File Type (2 Bytes)
; Aux Type (4 Bytes)
; Null Field (2 Bytes)
; Create Date/Time (8 Bytes: Misc. Tools Format)
; Mod Date/Time (8 Bytes: Misc. Tools Format)
; Option List (4 Bytes)
; Null Field (4 Bytes)
; Null Field (4 Bytes)
; Null Field (4 Bytes)
; Null Field (4 Bytes)
;==========================================================================
longa on
longi on
EXPORT set_file_info
set_file_info PROC
;==========================================================================
;Indicate that we don't have any HFS Finder Info.
;==========================================================================
stz hfs_flag
;==========================================================================
;Reset all time fields to zero. This forces me to calculate the time if
;needed. However, if the create time is set to zero then the create date
;and time fields will be left unchanged.
;==========================================================================
stz mod_time
stz mod_time+2
stz create_time
stz create_time+2
;==========================================================================
;Go and setup the default parameters for the call.
;==========================================================================
jsr setup_params
php ;save class state
;==========================================================================
;Process the file name and locate it.
;==========================================================================
jsr process_path
jsr chk_disk_protect
;==========================================================================
;See if this is a volume header. If it is then return 'bad_path_syntax'
;==========================================================================
lda [drvr_buf_ptr]
and #$00F0
cmp #volume_header
bne ok_to_change
lda #bad_path_syntax
set_info_err ;
sec
set_info_exit ;
jmp main_exit
;==========================================================================
;Move the directory entry into one entry please
;==========================================================================
ok_to_change ;
jsr move_dir_entry
lda alloc_entry ;get the length word
ora storage_type ;add in the storage type
ora one_entry ;add in the 1st char of name
sta one_entry
plp ;restore class flag.
beq class0
;==========================================================================
;When we get here we know that this is a class one call.
;So now we start processing the parameters supplied.
;==========================================================================
dec pcount
dec pcount
jsr get_access ;setup the access field
dec pcount
bmi to_no_time_change
;==========================================================================
;go and grab the file type that the user has provided.
;If the storage type is a directory then we must nake sure the file
;type field is $0F, which indicates that the file is a directory.
;==========================================================================
jsr get_ftype
jsr set_dir_type
dec pcount
bmi to_no_time_change
;==========================================================================
;The user's has also provided the aux type.
;==========================================================================
jsr get_aux_type
;==========================================================================
;Skip all the null fields
;==========================================================================
dec pcount
dec pcount
bpl cont
to_no_time_change
brl no_time_change
cont
;==========================================================================
;user's has also provided the create data. Isn't that special!
;==========================================================================
ldy #pblk_05_create
jsr get_pblk_time
bcc @good1 ; 27-Jan-92 MSD:
ldy #$8000 ; invalid, but non-zero
@good1
stx create_time
sty create_time+2
dec pcount
bmi do_create_only
;==========================================================================
;Holly Cow Batman the user's has even given us the modification date!
;==========================================================================
ldy #pblk_05_mod
jsr get_pblk_time
bcc @good2 ; 27-Jan-92 MSD:
ldy #$8000 ; invalid, but non-zero
@good2
stx mod_time
sty mod_time+2
;new code added 2/13/91 CAE
dec pcount
bmi set_the_info
;==========================================================================
;An option list was also provided
;==========================================================================
jsr get_option_list
;end of new code
bra set_the_info
class0
;==========================================================================
;The old style call has to provide everything, So go ahead and get it.
;==========================================================================
jsr get_access
jsr get_ftype
jsr set_dir_type
jsr get_aux_type
;==========================================================================
;get the create date and time
;==========================================================================
ldy #$000E ;index to the create time
lda [my_pblk_ptr],y
sta create_time
iny
iny
lda [my_pblk_ptr],y
sta create_time+2
;==========================================================================
;Get the modification date and time.
;==========================================================================
iny
iny
lda [my_pblk_ptr],y
sta mod_time
iny
iny
lda [my_pblk_ptr],y
sta mod_time+2
;==========================================================================
;We get to 'set_the_info' from both the class1 and class0 style calls.
;==========================================================================
set_the_info
jsr pack_time ;read the system clock please
;X & Y contain the date and time
lda mod_time ;did the user supply the date and time?
ora mod_time+2 ;(i.e. is the parameter != 0?)
beq @use_current ;yes, use the current date and time
ldx mod_time ;else use the user supplied values
lda mod_time+2
and #$1F3F ;make sure parameters are in range
tay
@use_current
stx one_entry+last_mod_index
sty one_entry+last_mod_index+2
do_create_only
lda create_time ;did the user supply a creation date & time?
ora create_time+2
beq no_time_change ;no, just leave it alone
lda create_time ;else copy user's values to entry
sta one_entry+create_index
lda create_time+2
and #$1F3F ;make sure parameters are in range
sta one_entry+create_index+2
no_time_change
ldy #entry_length-1
put_back ;
lda one_entry,y
sta [drvr_buf_ptr],y
dey
dey
bpl put_back
lda gbuf_addr
sta drvr_buf_ptr
lda gbuf_addr+2
sta drvr_buf_ptr+2
jsr standard_req
jsr write_with_cache
bcs all_done
jsr fix_the_fcr ;update the File Control Record if needed
jsr slug_ripple
bcs all_done
;new code code added 2/13/91 CAE
lda hfs_flag ;do we have any HFS info?
beq all_done ;no - so we're done
jsr store_hfs_info ;yes - so store it in the resource header
;end of new code
all_done jmp set_info_exit
;==========================================================================
;=============== END OF THE MAIN CODE FOR SET_FILE_INFO ===================
;==========================================================================
;==========================================================================
;Below is the subroutines used by SET_FILE_INFO.
;==========================================================================
;==========================================================================
;Now we see if the file is already open. We do this by calling
;get_next_fcr. If this routine finds a ProDOS FCR then we see if the key
;block is the same. If it is then we also update the FCR along with the
;disk
;==========================================================================
fix_the_fcr
stz fcr_wanted ;reset counter for Get_Next_FCR
look_loop ;
jsr get_next_fcr ;go grab a FCR
bcs no_fcr
ldy #fcr_vol_id
lda [my_fcr_ptr],y ;get the Volume ID so we can make sure the file is on
cmp [my_vcr_ptr] ;the same volume.
bne look_loop ;File is on another volume
ldy #fcr_file_id
lda [pro_fcr_ptr],y
cmp one_entry+key_blk_index
bne look_loop
;==========================================================================
;Save a modified Aux type please
;==========================================================================
ldy #fcr_aux_type ;update the aux type please
lda one_entry+aux_type_index
sta [pro_fcr_ptr],y
;==========================================================================
;Save file type please
;==========================================================================
ldy #fcr_file_type
lda one_entry+file_type_index
sep #$20 ;a short store
sta [pro_fcr_ptr],y
rep #$20
;==========================================================================
;Save create time please
;==========================================================================
ldy #fcr_create ;update the create time
lda one_entry+create_index
sta [pro_fcr_ptr],y
iny
iny
lda one_entry+create_index+2
sta [pro_fcr_ptr],y
no_fcr rts
;==========================================================================
;Store the user's access field in the
;==========================================================================
get_access
;
ldy #pblk_05_accs
lda [my_pblk_ptr],y
tax
and #$FF18 ;clear the reserved bits
bne bad_bits ;sorry bud you are out of range
longa off
sep #$20
txa
ora #backup_on ;force the backup bit on
sta one_entry+access_index
longa on
rep #$20
rts
bad_bits
lda #parm_range_err
brl set_info_err
;==========================================================================
;get_aux_type and store it
;==========================================================================
get_aux_type
ldy #pblk_05_aux+2
lda [my_pblk_ptr],y
bne range_problem
dey
dey
lda [my_pblk_ptr],y
sta one_entry+aux_type_index
rts
range_problem
lda #parm_range_err
brl set_info_err
;==========================================================================
;Get the file type
;==========================================================================
get_ftype
ldy #pblk_05_ftype
lda [my_pblk_ptr],y
tax
and #$FF00
bne range_problem
longa off
sep #$20
txa
sta one_entry+file_type_index
longa on
rep #$20
rts
;==========================================================================
;This routine is used to convert the extended date and time fields into
;ProDOS data and time fields
;==========================================================================
get_pblk_time
lda [my_pblk_ptr],y ;Get the second field so I can range check it
and #$00FF ;clear the minutes field
cmp #60
bcs range_problem
iny ;get the minutes field
lda [my_pblk_ptr],y
tax
and #$00FF
cmp #60 ;minutes are 00 to 59
bcs range_problem
sta minutes ;save the minutes field
txa
xba
and #$00FF
cmp #24 ;hours are 00 to 23
bcs range_problem
sta hours ;save the hours field
iny
iny
lda [my_pblk_ptr],y
tax
and #$00FF
sta year ;save the year as well
txa
xba
and #$00FF
cmp #31 ;days are 00 to 30
bcs range_problem
sta day ;don't forget the day now.
iny
iny
lda [my_pblk_ptr],y
and #$00FF
sta month
cmp #12 ;month = 0 to 11
bcs range_problem
iny ;get the null/day of week field
lda [my_pblk_ptr],y
xba ;put null in the high byte for easy compare
cmp #$0008 ;max value is 7 for day of week
bcs range_problem
lda hours
ora minutes
ora year
ora day
ora month
beq use_clock
inc day
inc month ;comvert to ProDOS format
jsr convert_time ;convert the time to ProDOS format
rts
use_clock ;
tax ;return 0000 so we use the current time
txy
clc ; 27-Jan-92 MSD: date was valid
rts
;==========================================================================
;This routine will change the file type to $0F if the storage type of the
;file is a subdirectory
;==========================================================================
set_dir_type ;
lda storage_type ;if storage is a directory only allow
cmp #subdir_type ;file type of $0F please
bne norm_file
longa off ;short mode for a second please
sep #$20
lda #$000F ;File type for directories
sta one_entry+file_type_index
longa on
rep #$20 ;back to 16 bit mode
norm_file ;
rts
;==========================================================================
;Get the option list info.
;
;The option list is structured as follows -
;
; word - buffer size
; word - data size
; word - FST ID
; additional data (FST-specific)
;==========================================================================
get_option_list ;added 2/13/91 CAE
; If the file is not a resource file then skip the option list since there's
; no place to store the info anyway.
lda storage_type ;get storage type
cmp #resource_type ;is it a resource file?
bne @done ;no - so skip option list
; Store ptr to option list buffer on direct page. Exit if ptr = 0.
ldy #pblk_05_opt
lda [my_pblk_ptr],y ;get option list ptr
sta <temp_ptr ;and store
iny
iny
lda [my_pblk_ptr],y
and #$00ff ;clear hi byte
sta <temp_ptr+2
ora <temp_ptr ;is ptr 0?
beq @done ;yes - so done
; Is the buffer big enough to contain what we expect (ie. HFS Finder Info)?
lda [temp_ptr] ;get buffer size
cmp #6+32 ;is it big enough?
bcc @done ;no - so done
; The buffer is big enough so now check the data size to see if the data is
; really there. We expect a minimum of 34 bytes of data.
; NOTE - We need to check both the buffer size and the data size because in
; previous releases of GS/OS, the data size field was undefined.
ldy #2 ;get offset to data size field
lda [temp_ptr],y ;get data size
cmp #34 ;enough data?
bcc @done ;no - so done
; Check the FST ID. If it's not ProDOS, HFS or AppleShare then exit.
ldy #4 ;get offset to fst id field
lda [temp_ptr],y ;get the fst id
cmp #pro_id ;is it prodos?
beq @id_ok ;yes
cmp #hfs_id ;is it hfs?
beq @id_ok ;yes
cmp #ashare_id ;is it appleshare?
bne @done ;no - so done
@id_ok
; Copy the HFS Finder Info from the option list buffer into a storage area.
ldx #0 ;init offset into finfo storage
ldy #6 ;init offset into option list buffer
@finfo_loop lda [temp_ptr],y ;get word of finder info
sta hfs_finfo,x ;and save it in storage area
iny ;adjust offsets
iny
inx
inx
cpx #32
bcc @finfo_loop ;loop until done
inc hfs_flag ;indicate that we have HFS info
@done rts ;and return
;==========================================================================
;Store the HFS Finder Info in the resource header.
;The Finder Info is stored immediately after the mini-entry for the data fork,
;so it begins at offset $0008 in the block.
;
;format: entry size (1 byte)
; entry type (1 byte)
; HFS Finder Info (16 bytes)
; entry size (1 byte)
; entry type (1 byte)
; HFS Finder Info (16 bytes)
;
; entry_size = 18
; entry_type = 1 for first 16 bytes of Finder Info (FInfo)
; 2 for second 16 bytes of Finder Info (xFInfo)
;
;==========================================================================
store_hfs_info ;added 2/13/91 CAE
jsr set_default_buf
jsr standard_req
lda one_entry+key_blk_index
sta drvr_blk_num
stz drvr_blk_num+2
jsr read_with_cache ;get the resource header
bcs @error
lda #finfo_type ;get entry type
xba ;move it into the hi byte
ora #18 ;put entry size in lo byte
ldy #8
sta [drvr_buf_ptr],y ;and store in first entry
lda #xfinfo_type ;get entry type
xba ;move it into the hi byte
ora #18 ;put entry size in lo byte
ldy #8+18
sta [drvr_buf_ptr],y ;and store in second entry
ldx #0
ldy #10
@loop lda hfs_finfo,x ;copy the HFS Finder Info into
sta [drvr_buf_ptr],y ;the resource header
iny
iny
inx
inx
cpx #32 ;at end of second entry?
beq @loop_done ;yes
cpx #16 ;at end of first entry?
bne @loop ;no - so loop
iny ;yes - so skip over entry size
iny ;and entry type of second entry
bra @loop ;loop
@loop_done
jsr write_with_cache ;write out the header
@error rts ;and return
ENDP
;==========================================================================
;Get_file_info: This routine will return information about a file on disk.
; If the file is open and has been modified then the EOF
; and Blocks used fields come from the FCR. I do not
; return the current time for the modification time, since
; This would require a call to the SLOW clock routines.
;
;
;Created: May 23, 1987
;Modified: Aug 26, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; find_file
;
;Call Format:
;
;Class 0 Pathname Pointer (4 Bytes)
; Access (2 Bytes: Disk Format)
; File Type (2 Bytes)
; Aux Type (4 Bytes)
; Storage Type (2 Bytes)
; Create Date/Time (4 Bytes: ProDOS Format)
; Mod Date/Time (4 Bytes: ProDOS Format)
; Blocks Used (4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Pathname Pointer (4 Bytes)
; Access (2 Bytes: Disk format)
; File Type (2 Bytes)
; Aux Type (4 Bytes)
; Storage Type (2 Bytes)
; Create Date/Time (8 Bytes: Misc. Tools Format)
; Mod Date/Time (8 Bytes: Misc. Tools Format)
; Option List Pointer (4 Bytes)
; EOF Data Fork (4 Bytes)
; Blocks Used Data Fork (4 Bytes)
; EOF Resource Fork (4 Bytes)
; Blocks Used Res. Fork (4 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT get_file_info
get_file_info PROC
;==========================================================================
;Go and setup the defaul parameters for the call.
;==========================================================================
jsr setup_params
;==========================================================================
;Process the file name and locate it.
;==========================================================================
jsr process_path
;==========================================================================
;Move the directory entry into one entry please
;==========================================================================
jsr move_dir_entry ;move the entry to one_entry
lda one_entry ;add in the storage type
ora storage_type
sta one_entry
;==========================================================================
;If the file is a resource file then get the extended key block.
;(This code used to be below 'loop_done'. Moved here 3/13/91 CAE)
;==========================================================================
lda storage_type
cmp #resource_type
bne @cont
lda gbuf_addr
sta drvr_buf_ptr
lda gbuf_addr+2
sta drvr_buf_ptr+2
lda one_entry+key_blk_index
sta drvr_blk_num
stz drvr_blk_num+2
jsr read_with_cache
bcc @cont
brl get_exit
@cont
;==========================================================================
;Now we see if the file is already open. We do this by calling
;get_next_fcr. If this routine finds a ProDOS FCR then we see if the key
;block is the same. If it is then we get the EOF and Number of blocks
;used from the FCR instead of from disk.
;(Modified to handle resource files correctly 3/13/91 CAE)
;==========================================================================
stz fcr_wanted ;reset counter for Get_Next_FCR
look_loop
jsr get_next_fcr ;go grab a FCR
bcs loop_done
ldy #fcr_vol_id
lda [my_fcr_ptr],y ;get the Volume ID so we can make sure the file is on
cmp [my_vcr_ptr] ;the same volume.
bne look_loop ;File is on another volume
ldy #fcr_file_id
lda [pro_fcr_ptr],y
cmp one_entry+key_blk_index
bne look_loop
;Check the status bit to see if the file info is different from the info on disk.
;If not, then loop.
ldy #fcr_status
lda [pro_fcr_ptr],y
bpl look_loop
;Get the current EOF and current number of blocks allocated by the file.
ldy #fcr_curr_eof
lda [pro_fcr_ptr],y
sta one_entry+eof_index
iny
lda [pro_fcr_ptr],y
sta one_entry+eof_index+1
ldy #fcr_blks_used
lda [pro_fcr_ptr],y
sta one_entry+blks_used_index
;Check to see if we're dealing with a resource file.
;If so, copy the EOF and blocks_used info into the extended key block so send_info will
;use it.
lda storage_type
cmp #resource_type ;are we dealing with a resource file?
bne loop_done ;no - so we're done
lda [pro_fcr_ptr] ;get resource number
xba ;convert $0000 or $0001 into
tay ;$0000 or $0100
iny
iny
iny
lda one_entry+blks_used_index
sta [drvr_buf_ptr],y
iny
iny
lda one_entry+eof_index
sta [drvr_buf_ptr],y
iny
lda one_entry+eof_index+1
sta [drvr_buf_ptr],y
bra look_loop ;loop until done
loop_done
;==========================================================================
;Check to see if we're dealing with a volume header.
;==========================================================================
lda storage_type
cmp #volume_header
bne go_send_info
jsr calc_last_mod ;we need to calculate the last mod for the volume
jsr calc_free_blks ;A = free blocks
bcc have_free
jmp main_exit ;sorry there is an error
have_free
sta one_entry+blks_used_index
ldy #vol_total_blks
lda [prodos_vcr_ptr],y
sta one_entry+aux_type_index
sec
sbc one_entry+blks_used_index
sta one_entry+blks_used_index
go_send_info
lda pcount
beq old_style_info
clc
lda my_pblk_ptr
adc #$0004
tax
lda my_pblk_ptr+2
adc #$0000
tay
lda pcount
dec a
jsr send_info
bcs get_exit
clean_exit
clc
get_exit
jmp main_exit
old_style_info
;==========================================================================
;Just return the old style info for class 0 calls
;firts field is ACCESS
;==========================================================================
ldy #$0004
lda one_entry+access_index
and #$00E7
sta [my_pblk_ptr],y
;==========================================================================
;now send the FILE TYPE
;==========================================================================
iny
iny
lda one_entry+file_type_index
and #$00FF
sta [my_pblk_ptr],y
;==========================================================================
;go ahead and give them the AUX TYPE
;==========================================================================
iny
iny
lda one_entry+aux_type_index
sta [my_pblk_ptr],y
iny
iny
lda #$0000
sta [my_pblk_ptr],y
;==========================================================================
;through in the storage type for good measure
;==========================================================================
iny
iny
lda one_entry
and #$00F0
lsr a
lsr a
lsr a
lsr a
sta [my_pblk_ptr],y
;==========================================================================
;how about the CREATE DATE and TIME
;==========================================================================
iny
iny
lda one_entry+create_index
sta [my_pblk_ptr],y
iny
iny
lda one_entry+create_index+2
sta [my_pblk_ptr],y
;==========================================================================
;Send the last MODIFICATION DATE and TIME
;==========================================================================
iny
iny
lda one_entry+last_mod_index
sta [my_pblk_ptr],y
iny
iny
lda one_entry+last_mod_index+2
sta [my_pblk_ptr],y
;==========================================================================
;and lastly, the number of blocks used.
;==========================================================================
iny
iny
lda one_entry+blks_used_index
sta [my_pblk_ptr],y
iny
iny
lda #$0000
sta [my_pblk_ptr],y
bra clean_exit
;==========================================================================
;Calculate the modification date for the volume. We do this since the mod
;date is not stored on the disk. This should not be bad since the blocks
;are stored in the cache.
;
;Enter: jsr
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1= error
; b = k
;
; one_entry+last_mod_index ;is setup with the date
;
;==========================================================================
; Export calc_mod_date
calc_last_mod
lda drvr_dev_num ;pass in the device number
ldx #dummy_name ; and a pointer to the name
ldy #^dummy_name
jsr find_file ;this will fail but we do not care
lda curr_mod_date
tax
ora curr_mod_date+2
beq @same_time ;there are no files on the disk!
stx one_entry+last_mod_index
lda curr_mod_date+2
sta one_entry+last_mod_index+2
rts
@same_time
lda one_entry+create_index
sta one_entry+last_mod_index
lda one_entry+create_index+2
sta one_entry+last_mod_index+2
rts
ENDP
;==========================================================================
;Judge_name: Returns information about ProDOS filename syntax.
; Modifies name (if supplied) to conform to ProDOS syntax.
;
;Note: This is a class 1 call only.
;
;Created: Jan 22, 1991
;Author: Cheryl Ewy
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; <temp_ptr
; <temp2_ptr
; <temp3_ptr
;
;Call Format:
;
;Class 1 PCount (2 Bytes)
; File System ID (2 Bytes)
; Name Type (2 Bytes)
; Syntax (4 Bytes)
; Max Len (2 Bytes)
; Name (4 Bytes)
; Name Flags (2 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT judge_name
judge_name PROC
name_flags equ temp2_ptr ;local equates
buf_size equ temp2_ptr+2
temp_val equ temp3_ptr
nul_flag equ temp3_ptr+2
max_len equ 15 ;maximum name length
; Setup the default parameters for the call.
jsr setup_params
; Init flags word to 0.
stz <name_flags
; Make sure the name_type input parameter is valid.
ldy #pblk_07_ntype
lda [my_pblk_ptr],y ;get name_type input param
cmp #4 ;is it < 4?
bcs bad_param ;no - so report error
; Minimum pcount is 3 so we'll always return a ptr to the syntax string.
lda #syntax_str ;get lo word of ptr to string
ldy #pblk_07_syntax
sta [my_pblk_ptr],y ;store it in output param
iny
iny
lda #^syntax_str ;get hi word of ptr to string
sta [my_pblk_ptr],y ;store it in output param
; Adjust the pcount to reflect the number of remaining parameters to deal with.
dec pcount ;done with file_sys_id param
dec pcount ;done with name_type param
dec pcount ;done with syntax param
; Return the maximum name length if requested.
dec pcount
bmi done
lda #max_len ;get maximum name length
ldy #pblk_07_max_len
sta [my_pblk_ptr],y ;store it in the max_len output param
; Get the ptr to the name buffer if supplied. The name buffer is a class 1 output buffer.
; If the ptr is 0 then there's no name to judge.
dec pcount
bmi done
ldy #pblk_07_name
lda [my_pblk_ptr],y ;get lo word of ptr to name buffer
sta <temp_ptr ;and store
iny
iny
lda [my_pblk_ptr],y ;get hi word of ptr to name buffer
and #$00ff ;mask off hi byte
sta <temp_ptr+2 ;and store
ora <temp_ptr ;is the ptr 0?
beq do_flags ;yes - so there's no name to judge
; Check the name buffer size to make sure the buffer is large enough to contain
; the maximum size name. If not, return an error.
lda [temp_ptr] ;get buffer size
cmp #max_len+4 ;is the buffer big enough?
bcc buf_err ;no - so return an error
sta <buf_size ;yes - so save buffer size
; Adjust <temp_ptr so that it points to the length word of the name string instead of
; to the buffer size word.
clc
lda <temp_ptr ;adjust ptr to point to the name length
adc #2
sta <temp_ptr
lda <temp_ptr+2
adc #0
sta <temp_ptr+2
; Judge the name.
jsr judge_it
; Return the name_flags parameter if requested.
do_flags dec pcount
bmi done
lda <name_flags ;get name_flags
ldy #pblk_07_nflags
sta [my_pblk_ptr],y ;store it in the output param
; Indicate no error and return.
done clc
jmp main_exit
; Return a parameter range error.
bad_param lda #parm_range_err
sec
jmp main_exit
; Return a buffer too small error.
buf_err lda #buff_too_small
sec
jmp main_exit
;--------------------------------------------------------------------------
judge_it
; If the name length = 0 then set the name string to "A", indicate that there
; was a syntax error, then return.
lda [temp_ptr] ;is the name length = 0?
bne @got_name ;no - so we have a name
@nameIsNull lda #1
sta [temp_ptr] ;set name length to 1
ldy #2
lda #'A'
sta [temp_ptr],y ;set name string to "A"
lda #$2000 ;set bit 13 of name_flags to indicate
tsb <name_flags ;that there was a syntax error
rts ;and return
@got_name
; We have a name to judge.
*** added 14-Apr-91 DAL -- call StringToText in the Misc Tools
if CallStringToText=1 then
pha ;space for StringToText result flags
pha ;space for StringToText result length
pea $6000 ;flags: force "English", allow expansion
ldx temp_ptr+2 ;push [temp_ptr]+2 -- input text
lda temp_ptr
clc
adc #2
bcc @ok
inx
@ok phx
pha
lda [temp_ptr]
pha ;push input string length
ldx temp_ptr+2 ;push [temp_ptr]-2 -- result buffer
lda temp_ptr
sec
sbc #2
bcs @ok2
dex
@ok2 phx
pha
ldx #$3b03
jsl $e10000
; _StringToText
pla ;printable length (ignore)
pla ;result flags
bpl @noChange
lda #$8000 ;set bit 15 = illegal characters
tsb <name_flags
@noChange
endif
*** end of 14-Apr-91 DAL
; Set bit 14 of name_flags if the name is too long.
; Note - we have to do this now because the name length may change.
lda [temp_ptr] ;get name length
*** added 29-Apr-91 DAL
beq @nameIsNull
*** end of 29-Apr-91 DAL
cmp #max_len+1 ;is it too long?
bcc @len_ok ;no
lda #$4000 ;yes - so set bit 14 of name_flags
tsb <name_flags ;to indicate name was too long
@len_ok
; Convert all illegal characters to NULs. They'll be converted to periods later.
lda [temp_ptr] ;get length of pathname
inc a ;adjust for extra length byte
tay
ldx #0 ;init to 0
longa off ;set 8 bit A
sep #$20
loop1 lda [temp_ptr],y ;get byte
cmp #'a'
bcc @check_upper
cmp #'z'+1
bcs @bad_char
bra @ok ;it's a lower-case letter
@check_upper cmp #'A'
bcc @check_num
cmp #'Z'+1
bcs @bad_char
bra @ok ;it's an upper-case letter
@check_num cmp #'0'
bcc @check_period
cmp #'9'+1
bcs @bad_char
bra @ok ;it's a number
@check_period cmp #'.'
beq @ok ;it's a period
if AllowLCPeriods then ;added 7-Aug-91 DAL
cmp #' ' ;it's a blank
beq @ok
endif ;end 7-Aug-91 DAL
@bad_char lda #$00 ;it's an illegal character so
sta [temp_ptr],y ;replace char with a NUL
ldx #$8000 ;indicate illegal character found
@ok dey ;decrement offset
cpy #1
bne loop1 ;loop until done
longa on ;set 16 bit A
rep #$20
txa ;set bit 15 if an illegal
tsb <name_flags ;character was found
; If the name doesn't begin with a letter, then indicate that there was a syntax error.
; Note - we have to check this now because if the first character is illegal, it will
; be removed by the next section of code.
ldy #2
lda [temp_ptr],y ;get first 2 chars
and #$00ff ;only want first char
cmp #'A' ;is it a letter?
bcs @syntax_ok ;yes - so we're ok
lda #$2000 ;set bit 13 to indicate that
tsb <name_flags ;there was a syntax error
@syntax_ok
; Replace runs of 2 or more NULs with a single period.
; All leading and trailing NULs will be removed.
; Note - if no illegal characters were found, then skip this section.
lda <name_flags ;were there any illegal characters?
bpl skip_it ;no - so skip this section
lda [temp_ptr] ;get name length
inc a ;add 2 to account for the length bytes
inc a
sta <temp_val ;and save
longa off
sep #$20 ;set 8 bit A
ldx #2 ;init offsets
phx
stx <nul_flag ;init flag to indicate we've seen a NUL
loop2 ply ;get offset
cpy <temp_val ;are we done?
bcs loop2_done ;yes
lda [temp_ptr],y ;no - so read next character
iny ;adjust offset
phy ;and save
cmp #$00 ;is the char a NUL?
bne @not_nul ;no - so branch
ldy <nul_flag ;was the last character a NUL?
bne loop2 ;yes - so loop
inc <nul_flag ;no - so indicate we've seen a NUL
lda #'.' ;convert the NUL into a period
bra @store_char ;and store the period
@not_nul stz <nul_flag ;indicate we've seen a non-NUL
@store_char txy
sta [temp_ptr],y ;store the character
tyx
inx ;adjust offset
bra loop2 ;and loop
loop2_done lda <nul_flag ;was the last char read a NUL?
beq @ok ;no
cpx #2 ;yes - was it stored?
beq @ok ;no
dex ;yes - so don't count it
@ok
longa on
rep #$20 ;set 16 bit A
txa ;determine new name length
dec a
dec a
sta [temp_ptr] ;and store
skip_it
; If the name doesn't begin with a letter, insert an 'A' as the first character.
; If the buffer has no extra space, then lose the last character.
; Note - we need to check for a zero-length name in case all chars were illegal.
lda [temp_ptr] ;get name length
bne @not_zero ;branch if it's > 0
inc a ;name length is 0 so
sta [temp_ptr] ;set length to 1
bra stuff_a ;and insert the 'A'
@not_zero
ldy #2
lda [temp_ptr],y ;get first 2 chars
and #$00ff ;only want first char
cmp #'A' ;is it a letter?
bcs char1_ok ;yes - so we're ok
lda [temp_ptr] ;get name length
tay ;and save
clc
adc #4
cmp <buf_size ;does the buffer have any unused space?
bcs @lose_last ;no - so lose last char
iny ;yes - so save last char
tya
sta [temp_ptr] ;add 1 to name length
@lose_last
loop3 lda [temp_ptr],y ;move all chars over by 1
xba
sta [temp_ptr],y
dey
cpy #1
bne loop3
longa off
stuff_a sep #$20 ;set 8 bit A
ldy #2
lda #'A'
sta [temp_ptr],y ;stuff an 'A' in as first char
longa on
rep #$20 ;set 16 bit A
char1_ok
; If the name is too long then keep the first 7 chars, insert '..', keep the last 6 chars.
lda [temp_ptr] ;get name length
cmp #max_len+1 ;is it too long?
bcc length_ok ;no
ldy #9 ;get offset to 8th char
lda #'..'
sta [temp_ptr],y ;insert the '..'
lda [temp_ptr] ;get length of name
sec
sbc #4 ;subtract 4 to get offset
pha ;to last part of name to keep
longa off ;set 8 bit A
sep #$20
ldx #11 ;set offset after the '..' (10th char)
loop4 ply
lda [temp_ptr],y ;move the last 6 characters
iny ;so they are after the '..'
phy
txy
sta [temp_ptr],y
inx
cpx #17 ;are we done?
bne loop4 ;no - so loop
ply ;clean up stack
longa on ;set 16 bit A
rep #$20
lda #max_len ;adjust the name length
sta [temp_ptr]
length_ok
; Done with name.
rts
ENDP
;==========================================================================
;Volume: This routine will return the volume information on a disk
;
;Note: This routine will now store the volume name in the user's
; buffer if a "duplicate volume" error is being returned.
; 1/22/91 CAE
;
;Created: may 24, 1987
;Modified: may 24, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; fill_io_buf
;
;Call Format:
;
;Class 0 Device Name Pointer (4 Bytes)
; Volume Name Pointer (4 Bytes)
; Total Blocks (4 Bytes)
; Free Blocks (4 Bytes)
; File System ID (2 Bytes)
;
;Class 1 PCount (2 Bytes)
; Device Name Pointer (4 Bytes)
; Volume Name Pointer (4 Bytes)
; Total Blocks (4 Bytes)
; Free Blocks (4 Bytes)
; File System ID (2 Bytes)
; Block Size (2 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT volume
volume PROC
;==========================================================================
;Call setup routine. This routine will verify input parameters like
; call class, max pcount and span. It will also setup
; any pointers that are used.
;
;The routine loads the class of the call as the last instruction before RTS
;==========================================================================
jsr setup_params
;==========================================================================
;Now lets go and and ID that puppy. The device number to ID is on ZP
;==========================================================================
lda #in_cache ;force block two into the cache
sta drvr_cache
stz dup_vol_flag ;init flag word to 0
lda dev_num
jsr id_disk ;go see if it belongs to me
jsr set_user_cache ;set cache back to default
bcc its_my_puppy
cmp #dup_volume ;got an error - is it 'duplicate volume'?
sec ;make sure carry is still set
bne vol_exit ;no - so exit with error
sta dup_vol_flag ;yes - so store error code in flag word
bra its_my_puppy ;and continue
vol_exit jmp main_exit
dup_vol_flag DS.B 2 ;non-zero if we've got a duplicate volume
;==========================================================================
;move the name into the user's buffer.
;==========================================================================
its_my_puppy
ldy #pblk_08_vol
lda [my_pblk_ptr],y
sta temp_ptr
tax
iny
iny
lda [my_pblk_ptr],y
sta temp_ptr+2
stz case_bits ;all upper case please
lda fst_attr ;if the high bit is set skip Upper/Lower stuff
bmi @no_case_change
ldy #create_index-2 ;save the case bits please
lda [drvr_buf_ptr],y
bpl @no_case_change
asl a ;remove the case ON/OFF bit
sta case_bits
@no_case_change
lda pcount
bne class1
;==========================================================================
;before we send the name we have to add in the damn leading slash.
;==========================================================================
lda [drvr_buf_ptr]
and #$000F
sta math_temp ;save the length of the name
inc a
sta [temp_ptr] ;length of the name
inc temp_ptr
bne class0_name
inc temp_ptr+2
class0_name
ldy #$0001 ;start at the beginning
longa off
sep #$20 ;8 bit moves please
lda #'/'
sta [temp_ptr] ;store the leading slash
class0_loop ;
lda [drvr_buf_ptr],y
rep #$20
rol case_bits ;see if this char is lower case
sep #$20
bcc @its_upper
if AllowLCPeriods then ;added 7-Aug-91 DAL
cmp #'.'
bne @not_lc_period
lda #0 ;let it ORA our 0 into a $20 (blank!)
@not_lc_period
endif ;end 7-Aug-91 DAL
ora #$20 ;set the lower case bit please
@its_upper
sta [temp_ptr],y
cpy math_temp
beq @were_done
iny
bra class0_loop
@were_done
longa on
rep #$20
;If dup_vol_flag is non-zero then we don't want to send the rest of the output
;parameters so just exit with the "duplicate volume" error.
lda dup_vol_flag ;check the flag
beq @no_error ;it's 0 so continue
sec ;we've got a duplicate volume so set carry
brl vol_exit ;and exit with the error
@no_error
;==========================================================================
;Since this is a class zero call lets set the pcount to three so all the
;parameters will be returned to the caller.
;==========================================================================
lda #$0003 ;setup pcount
sta pcount
bra do_bitmap
class1
;==========================================================================
;If we get here we need to verify that the buffer is large enought to hold
;the volume name.
;
;diagram
; ______________
; | Buffer size | W
; |--------------|
; | Name Length | W
; |--------------|
; | Leading '/' | B
; |--------------|
; | THE |
; | REST OF | 'n'
; | THE NAME |
; |______________|
;
;==========================================================================
lda [drvr_buf_ptr]
and #$000F
tax
inc a
sta math_temp ;save the size of the name + slash.
sec
lda [temp_ptr]
sbc #$0004 ;minus overhead
bmi too_small ;get out of here!
cmp math_temp
beq size_ok
bcs size_ok
ldy #$0002
lda math_temp
sta [temp_ptr],y ;store the length of the name
too_small ;
sec
lda #buff_too_small ;return the error
back_to_exit brl vol_exit
size_ok ;
;==========================================================================
;now move the name to the user's buffer.
;==========================================================================
ldy #$0002 ;index to the length result
lda math_temp
sta [temp_ptr],y
iny
iny
lda #delimiter+(delimiter*$100)
sta [temp_ptr],y
;==========================================================================
;adjust the pointer to point to the beginning of the name buffer.
;==========================================================================
phx
tya
clc
adc temp_ptr
sta temp_ptr
lda #$0000
adc temp_ptr+2
sta temp_ptr+2
ply
sty math_temp
ldy #$0001
longa off
sep #$20
class1_loop
lda [drvr_buf_ptr],y
rep #$20
rol case_bits ;see if this char is lower case
sep #$20
bcc @its_upper
if AllowLCPeriods then ;added 7-Aug-91 DAL
cmp #'.'
bne @not_lc_period
lda #0 ;let it ORA our 0 into a $20 (blank)
@not_lc_period
endif ;end 7-Aug-91 DAL
ora #$20 ;set the lower case bit please
@its_upper
sta [temp_ptr],y
cpy math_temp ;are we done?
beq @were_done
iny
bra class1_loop
@were_done
longa on
rep #$20
;If dup_vol_flag is non-zero then we don't want to send the rest of the output
;parameters so just exit with the "duplicate volume" error.
lda dup_vol_flag ;check the flag
beq @no_error ;it's 0 so continue
sec ;we've got a duplicate volume so set carry
brl vol_exit ;and exit with the error
@no_error
;==========================================================================
;adjust the pcount to reflect the number of parameters that the user still
;wants.
;==========================================================================
dec pcount
dec pcount
do_bitmap ;
;==========================================================================
;now lets see if the caller wants the total blocks on the volume
;==========================================================================
dec pcount
bpl send_tot_blks
end_volume clc
backup2 brl vol_exit
send_tot_blks ;
ldy #vol_total_blks
lda [prodos_vcr_ptr],y
ldy #pblk_08_tot
sta [my_pblk_Ptr],y
iny
iny
lda #$0000
sta [my_pblk_ptr],y
;==========================================================================
;see if the caller wants the number of free blocks. We call calc_free_blks
;to calculate the number of free blocks. The routine returns the TOTAL
;number of blocks available in the 'A' register.
;==========================================================================
dec pcount
bmi end_volume
jsr calc_free_blks
bcs backup2 ;some major error occured
ldy #pblk_08_free
sta [my_pblk_ptr],y
iny
iny
lda #$0000
sta [my_pblk_ptr],y
;==========================================================================
;do they want the file system ID?
;==========================================================================
dec pcount
bmi end_volume ;no they do not!
lda #pro_id
ldy #pblk_08_id
sta [my_pblk_ptr],y
;==========================================================================
;do they want to know my block size?
;==========================================================================
dec pcount
bmi end_volume ;no they do not!
lda #blk_size
ldy #pblk_08_size
sta [my_pblk_ptr],y
bra end_volume
ENDP
;==========================================================================
;Clear_backup: This routine will clear the backup bit for a file.
;
;Created: may 27, 1987
;Modified: may 27, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; find_file
;
;Call Format:
;
;Class 0 Pathname pointer (4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Pathname pointer (4 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT clear_backup
clear_backup PROC
;==========================================================================
;Setup the general parameters.
;==========================================================================
jsr setup_params
;==========================================================================
;Process the file name and locate it. On exit 'drvr_buf_ptr' points to the
;file entry that we are suppose to act on.
;==========================================================================
jsr process_path
bcc good
clear_exit
jmp main_exit
good
jsr chk_disk_protect
ldy #access_index
longa off
sep #$20
;==========================================================================
;Clear the backup bit for the file/directory
;==========================================================================
lda [drvr_buf_ptr],y
and #backup_off ;clear the backup bit
sta [drvr_buf_ptr],y
; If we're dealing with a volume directory then we need to clear the backup bit
; in the VCR also so that the disk and VCR match. 4/5/91 CAE
lda [drvr_buf_ptr] ;get storage type
and #$F0 ;only want hi nibble
cmp #volume_header ;is this a volume directory?
bne @not_vol ;no - so branch
ldy #vol_access
lda [prodos_vcr_ptr],y
and #backup_off ;clear the backup bit in the vcr
sta [prodos_vcr_ptr],y
@not_vol
longa on
rep #$20
;==========================================================================
;Reset the I/O buffer pointer to the beginning of the buffer.
;The request count and all that stuff is still correct from the find file
;==========================================================================
lda gbuf_addr
sta drvr_buf_ptr
lda gbuf_addr+2
sta drvr_buf_ptr+2
jsr write_with_cache
bra clear_exit
ENDP
;======================================================================
;Open: This file contains code that is used to open a file on
; disk.
;
;Created: Apr 13, 1987
;Modified: Apr 16, 1988
;Author: Rob Turner
;
;Enter: From GS/OS jmp table
;
;Input: A = undefined
; X = (call number * 2)
; Y = (class number * 2)
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set (exit via system exit routine)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
; find_file (subroutine)
;
;Call Format:
;
;Class 0 Reference Number (2 Bytes)
; Pathname Pointer (4 Bytes)
; Reserved (4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Reference Number (2 Bytes)
; Pathname Pointer (4 Bytes)
; Request Access (2 Bytes: New Format)
; Resource Number (2 Bytes)
; (Returned Info)
; Access (2 Bytes)
; File Type (2 Bytes)
; Aux Type (4 Bytes)
; Storage Type (2 Bytes)
; Create Date/Time (8 Bytes: Misc. Tools Format)
; Mod Date/Time (8 Bytes: Misc. Tools Format)
; Option List Pointer (4 Bytes)
; EOF Data Fork (4 Bytes)
; Blocks Used Data Fork (4 Bytes)
; EOF Resource Fork (4 Bytes)
; Blocks Used Res. Fork (4 Bytes)
;
;======================================================================
longa on
longi on
EXPORT open
open PROC
;==========================================================================
;Setup the defaults for the open call.
;The defaults are set to OPEN resource number $0000(main data) and open file
;as permitted.
;==========================================================================
stz resource_num ;set resource to main data fork
stz access ;set users access to 'as permitted'
stz users_access ;set the users access please.
jsr setup_params
beq class0
jsr get_access ;get the user's access byte if avail
bcs class0 ;branch if no access provided
jsr get_res_num ;get the user's resource num if avail
class0 ;
;==========================================================================
;Now go and find the file we need. If the disk is not ProDOS then we
;never return from the call. It will automatically exit thru 'SYS_EXIT'
;==========================================================================
jsr process_path
;==========================================================================
;We get here when we have found the file and are ready to open it.
;This section of code expects 'drvr_buf_ptr' to point to the file name to open
;==========================================================================
open_the_file ;
jsr move_dir_entry ;put the disk entry in a common place
jsr verify_storage ;make sure I can deal with the file
bcs open_exit ;sorry bud
jsr check_dup ;see if this is a duplicate open
bcs open_exit ;yes this is a duplicate open
jsr build_the_fcr ;build the file control record
bcs open_exit
;==========================================================================
;Now that we have setup the entrire FCR we need to send back any info
;that the user requested.
;==========================================================================
ldy #$0000 ;default = Class zero type call
lda call_number ;determine class
and #class_mask
beq class0_call
ldy #$0002 ;index to ref field for new class calls
;==========================================================================
;Return the file reference number to the caller.
;==========================================================================
class0_call ;
lda [my_fcr_ptr]
sta [param_blk_ptr],y
jsr send_open_parms
bcc open_exit
pha
jsr remove_fcr ;remove this puppy
pla
sec
open_exit
jmp main_exit
;==========================================================================
;==========================================================================
;====== MAIN END OF OPEN CALL ============================================
;==========================================================================
;==========================================================================
;==========================================================================
;no see how many paremters the user would like with the open call.
;
;Enter: jsr
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;==========================================================================
send_open_parms ;
lda pcount
cmp #pcount5 ;see if there is more then 4 params
bcc end_send ;no additional parameters needed
sbc #pcount5-1
pha
;==========================================================================
;If we fall thru to here then we know the user wants to receive additional
;information with the open. So we start sending the number of field
;the user wants. The routine that sends the info expects the following:
;
; A = number of fields to send
; X = low word address of where to start storing the info
; Y = high word address ...
; one_entry = the entry from the disk
; drvr_buf_ptr = ptr to resource header block
;==========================================================================
clc ;make a pointer to data start
lda my_pblk_ptr
adc #$000A
tax
lda my_pblk_ptr+2
adc #$0000
tay
lda gbuf_addr ;set up pointer to resource header
sta drvr_buf_ptr
lda gbuf_addr+2
sta drvr_buf_ptr+2
lda one_entry
and #$FF0F ;clear garbage
ora entry_sto_type ;add in entry type from dir entry
sta one_entry
pla ;recall number of params wanted
jsr send_info
end_send
rts
;=============================================================================
;Now we check the storage type. The only storage types that I know about are:
;
;(1) seedling ($0010)
;(2) sapling ($0020)
;(3) tree ($0030)
;(4) pascal area (Access not allowed to pascal area)
;(5) resource ($0050)
;(6) purge_dir (access not allowed to purged directory)
;(7) subdir ($00D0)
;(8) subdir H (access not allowed to header entry)
;(9) vol header ($00F0)
;
;Enter: jsr
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;=============================================================================
verify_storage ;
lda storage_type
sta entry_sto_type
cmp #resource_type
beq setup_access
ldx resource_num ;make sure the resource number is zero
bne bad_resource
cmp #tree_type
beq setup_access
cmp #sapling_type
beq setup_access
cmp #seedling_type
beq setup_access
cmp #subdir_type
beq dir_access
cmp #volume_header
bne bad_storage
;=============================================================================
;If the file is a directory then we must set the access to read only
;=============================================================================
dir_access ;
lda access ;make sure user wants read only
beq force_read_only ;He wants as permitted, so force read
cmp #write_access
bcs bad_access
force_read_only ;
lda #read_access
sta access
clc
rts
;==========================================================================
;Now we need to check the access. If the access is 'as_permitted' then we
;must convert the disk access to one of our types (read only, read write, etc)
;==========================================================================
setup_access ;
lda access
bne verify_access
;==========================================================================
;Load the disk based access byte
;==========================================================================
lda one_entry+access_index
and #read_access+write_access
beq bad_access ;read/write disabled?
ldx #write_access
lsr a
bcc set_access ;force write only access
ldx #read_access
lsr a
bcc set_access ;force read only access
ldx #read_access+write_access
set_access ;
stx access ;access now set from disk
exit_access ;
clc
rts
;==========================================================================
;make sure the user's access agrees with the disk based access.
;==========================================================================
verify_access ;
ldx one_entry+access_index
lsr a
bcc no_read ;user does not want read access
tay ;temp save
jsr chk_read
beq bad_access ;sorry bud
tya
no_read ;
lsr a ;does the user want write access?
bcc exit_access ;no
jsr chk_write
beq bad_access ;write access not allowed
clc
rts
chk_read ;
txa ;access byte from the disk
and #$0001 ;isolate read bit
rts
chk_write ;
txa ;access byte from disk
and #$0002
rts
bad_resource ;
lda #res_not_found
sec
rts
bad_access ;
lda #invalid_access
sec
rts
bad_storage lda #bad_store_type
sec
rts
;==========================================================================
;see if the user provided resource number.
;
;Enter: jsr
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;==========================================================================
get_res_num ;
lda pcount
cmp #$0004
bcc end_res ;no resource number provided
ldy #$0008
lda [my_pblk_ptr],y
beq end_res ;default resource number so exit
sta resource_num ;save the resource. must be zero
dec a
bne not_in_range ;not even close.
end_res ;
rts
;==========================================================================
;get_access This routine will load the user's access byte if available
;
;
;Enter: jsr
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = no more parameters
; 0 = more paramters may follow
; b = k
;
;==========================================================================
get_access
lda pcount
cmp #$0003
bcc no_params ;there is no parameters
ldy #$0006
lda [my_pblk_ptr],y
sta users_access ;save the user's access please.
beq end_access ;same as default access so exit
;==========================================================================
;access:
; 00 = open as permitted ;default value for open call
; 01 = open for reading only
; 02 = open for writing only
; 03 = open for reading and writing
;==========================================================================
sta access ;save user's choice
cmp #read_write_acc+1
bcc end_access
not_in_range ;
lda #parm_range_err ;I don't know what to do with this
jmp error_exit ;sorry bud
no_params ;
sec
rts
end_access ;
clc
rts
;==========================================================================
;Calculate the size of the root directory. This is a pain but we cannot
;assume that the root directory will be four blocks long. So we might as
;well put the directory blocks in the cache while we are at it
;
;Enter: jsr
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = no more parameters
; 0 = more paramters may follow
; b = k
;==========================================================================
EXPORT vol_file_size
vol_file_size
stz one_entry+eof_index+1
lda gbuf_addr
sta drvr_buf_ptr
lda gbuf_addr+2
sta drvr_buf_ptr+2
stz math_temp ;used to count the number of blocks
stz drvr_blk_num+2
lda #vol_dir_start
block_loop
sta drvr_blk_num
jsr read_with_cache
bcc cont_blk
pha ;save the error code
jsr remove_fcr
pla
sec
jmp main_exit
cont_blk
inc math_temp
ldy #$0002
lda [drvr_buf_ptr],y
bne block_loop
end_blk_cnt ;
lda math_temp
sta one_entry+blks_used_index
xba
asl a ;times two for EOF
sta one_entry+eof_index
jsr set_user_cache
rts
ENDP
;==========================================================================
;This routine will build the FCR.
;
;Enter: jsr
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;==========================================================================
EXPORT build_the_fcr
build_the_fcr PROC
;==========================================================================
;allocation size:
;
;directories 2 blocks
;read/write files 3 blocks
;read seedling 1 block
;read sapling 2 block
;read tree 3 blocks
;
;==========================================================================
ldy #data_active+index_active
lda #fcr_index_size ;two block I/O buffer for directories
ldx storage_type
cpx #subdir_type
bcs alloc_the_fcr
lda access ;see if the file is open for read only
cmp #read_access
bne norm_io_buf ;allocate a large I/O buffer
ldy #data_active
lda #fcr_data_size
cpx #seedling_type ;one block buffer for seedling files
beq alloc_the_fcr
ldy #data_active+index_active
lda #fcr_index_size
cpx #sapling_type ;two blocks for a sapling file
beq alloc_the_fcr
norm_io_buf ;
ldy #data_active+index_active+master_active
lda #fcr_master_size ;allocate three blocks for I/O buffer
;==========================================================================
;when we get here A = number of bytes wanted
;==========================================================================
alloc_the_fcr ;
phy
pha
; jsr entry_to_gstr ;convert to a gstring for mike
;
ldy #vcr_name ;pass in the name of the Volume please
lda [my_vcr_ptr],y ;get the VP please
tax
iny
iny
lda [my_vcr_ptr],y
tay ;High word of the VP
jsl deref ;convert the VP to a real pointer
;return X&Y pointer to the name
pei temp_ptr ;save the pointer I am going to use
pei temp_ptr+2
stx temp_ptr ;save the address of the string
sty temp_ptr+2
lda [temp_ptr] ;get the length word please
tay ;save the count
inc a ;add one for the :
sta gstring ;save the count
lda #'::'
sta gstring+2 ;save the seperator
inc temp_ptr
bne @01
inc temp_ptr+2
@01
lda [temp_ptr],y ;get a char please
sta gstring+2,y
dey
bne @01
pla
sta temp_ptr+2 ;restore the pointer I used
pla
sta temp_ptr
ldx #gstring ;pointer to the file name or Volume Name to Open
ldy #^gstring ;high word
pla ;restore I/O buffer size
sec ;better clear the buffer
jsl alloc_fcr ;build a new fcr for file
bcc success
ply
rts ;return with the error
success ;
jsl deref ;convert VP to RP
stx my_fcr_ptr
sty my_fcr_ptr+2
jsr setup_my_fcr ;build a pointer to local data
stx pro_fcr_ptr
sty pro_fcr_ptr+2 ;save pointer to my structure
ldy #vcr_open_cnt ;advance the open count please by one
lda [my_vcr_ptr],y
inc a
sta [my_vcr_ptr],y
ldy #fcr_status
lda #$0000 ;zero the status word
sta [pro_fcr_ptr],y
pla ;restore the I/O buffer active bits
jsr set_fcr_status
lda access ;save the user's access
ldy #fcr_access
ora #set_global_clean
ldx resource_num ;does the file contain a resource fork?
beq @store_access
ora #set_access_res ;set the access resource bit please
@store_access
sta [my_fcr_ptr],y
ldy #fcr_vol_id
lda [my_vcr_ptr] ;get the volume ID from the VCR
sta [my_fcr_ptr],y ;and save it in the new FCR
ldy #fcr_fst_id
lda #pro_id ;save the ownership id word
sta [my_fcr_ptr],y
;==========================================================================
;Now start saving data into my local data segment
;==========================================================================
lda resource_num ;save the resource number of the file
sta [pro_fcr_ptr]
ldy #fcr_entry_type
lda entry_sto_type ;save the storage type from dir entry
sta [pro_fcr_ptr],y
ldy #fcr_storage
lda storage_type ;save the storage type
sta [pro_fcr_ptr],y
cmp #volume_header ;if file is a volume then calc EOF
bne no_adjust2
jsr vol_file_size ;calculate the EOF for the root vol
no_adjust2 ;
ldy #fcr_curr_eof ;save the current EOF
lda one_entry+eof_index
sta [pro_fcr_ptr],y
iny
lda one_entry+eof_index+1
sta [pro_fcr_ptr],y
ldy #fcr_blks_used ;save the number of blocks used
lda one_entry+blks_used_index
sta [pro_fcr_ptr],y
ldy #fcr_parent
lda parent_blk ;save address of parent block number
sta [pro_fcr_ptr],y
ldy #fcr_entry_offs
lda entry_offset ;save offset to entry within parent blk
sta [pro_fcr_ptr],y
ldy #fcr_file_type ;move the directory entry into the FCR
entry_loop lda one_entry-fcr_file_type+$10,y
sta [pro_fcr_ptr],y
iny
cpy #fcr_header_ptr+1
bne entry_loop
jsr setup_io_ptrs ;build pointers to data structures
jsr setup_io_buf
bcc no_prob
pha
jsr remove_fcr ;remove this puppy
pla
sec
no_prob ;
rts
ENDP
;======================================================================
;check_dup: This routine will check for duplicate file entries.
; To do this I look to see if the starting block of
; the FCR is equal to the starting block of the file I am
; trying to open.
;
;Created: may 04, 1987
;Modified: may 06, 1987
;Author: Rob Turner
;
;
;Enter: from open call
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; one_entry (39 byte entry from disk)
; access (requested access for open)
;
;Output: A = error code if carry set. (duplicate error)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; General I/O Buffer (1K buffer, I read 512 bytes)
; find_fcr (system routine to find a fcr
; my_fcr_ptr
; pro_fcr_ptr
; access
; resource_num
;
;======================================================================
longa on
longi on
EXPORT check_dup
check_dup PROC
stz fcr_wanted ;reset for get_next_fcr
lda one_entry+key_blk_index
sta key_block
next_loop ;
jsr get_next_fcr
bcs done
ldy #fcr_vol_id
lda [my_fcr_ptr],y ;get the Volume ID so we can make sure the file is on
cmp [my_vcr_ptr] ;the same volume.
bne next_loop ;File is on another volume
ldy #fcr_file_id ;index to key block of this file
lda [pro_fcr_ptr],y
cmp key_block ;is this the same block?
bne next_loop ;no problem with this file
;==========================================================================
;Now we need to see if the resource numbers are different.
;==========================================================================
lda [pro_fcr_ptr]
cmp resource_num
bne next_loop ;not the same resource
ldy #fcr_vol_id ;see if the file is on another volume
lda [my_fcr_ptr],y
cmp [my_vcr_ptr]
bne next_loop
ldy #fcr_access ;see if this file was open for writing
lda [my_fcr_ptr],y
and #ClrAllButAccess
cmp #write_access
bcs file_is_busy ;sorry this file is busy right now
;==========================================================================
;If we get here we know that the current file is the same and that it is
;open for read only. Therefore we must check the requested access of the
;file that we are currently opening. If the user wants read only access
;then there is no problem.
;==========================================================================
lda users_access ;did the user want "open_as_permitted"
beq open_as_perm ;user request open as permitted.
cmp #write_access
bcs file_is_busy
rts ;return carry clear indicating no dup
file_is_busy
lda #file_busy
sec
rts ;error out to caller
open_as_perm
; ldy #fcr_disk_acc
; lda [pro_fcr_ptr],y ;check disk assigned access.
; and #write_enabled ;is the file write enabled?
; bne file_is_busy ;yes it is, so die
lda #read_access
sta access ;change the user's access to Read Only.
done clc ;indicate no problem
rts
ENDP
;======================================================================
;get_next_fcr: This routine will load find the next FCR that belongs
; to ProDOS.
;
;Created: may 23, 1987
;Modified: may 23, 1987
;Author: Rob Turner
;
;
;Enter: from open call
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; fcr_wanted (counter for FCR, reset by caller!)
;
;Output: A = undefiend
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error (end of FCRs)
; 0 = no error
; b = k
;
; pro_fcr_ptr
; my_fcr_ptr (points to the found FCR)
;
;Uses: All registers
; system_routines
;
;======================================================================
longa on
longi on
EXPORT get_next_fcr
get_next_fcr PROC
loop
lda fcr_wanted
inc a
sta fcr_wanted
jsl get_fcr ;see if this is a fcr
bcs done
jsl deref ;convert VP to RP
stx my_fcr_Ptr
sty my_fcr_ptr+2
ldy #fcr_fst_id ;see if this one is mine
lda [my_fcr_ptr],y
cmp #pro_id
bne loop ;not mine so who cares
jsr setup_my_fcr
stx pro_fcr_ptr
sty pro_fcr_ptr+2
clc
done
rts
ENDP
;======================================================================
;move_dir_entry This routine will move the 39 byte entry pointed to by
; drvr_buf_ptr to one_entry. If the entry pointed to
; is a volume header then I will simulate a normal 39
; byte entry.
;
;Created: may 05, 1987
;Modified: may 05, 1987
;Author: Rob Turner
;
;
;Enter: from open call
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; drvr_buf_ptr (points to the 39 byte entry from disk)
; storage_type (original storage type of entry)
;
;Output: A = undefiend
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; one_entry (contains a 39 byte entry)
;
;Uses: All registers
; General I/O Buffer (1K buffer, I read 512 bytes)
; drvr_buf_ptr
;
;======================================================================
longa on
longi on
EXPORT move_dir_entry
move_dir_entry PROC
lda [drvr_buf_ptr] ;get the storage type
and #$00F0
sta storage_type
cmp #volume_header
beq simulate_entry ;simulate a reg. file entry
EXPORT move_vol_entry
move_vol_entry ;
start_move ldy #entry_length-1 ;just move a normal file entry
move_loop lda [drvr_buf_ptr],y
sta one_entry,y
dey
dey
bpl move_loop
lda alloc_entry
and #$0F00 ;clear all but the storage type
xba
sta alloc_entry ;convert length byte to word
rts
simulate_entry ;
jsr start_move ;move the basic entry
;==========================================================================
;below we setup the volume directory as a subdirectory. To do this
;we setup the following fields:
;==========================================================================
; File_type := $0F (directory File)
; Key_pointer := vol_dir_start (starting block for directory = 2)
; Blocks_used := (Calculated from disk)
; EOF := (Calculated from disk)
; AUX Type := $0000 (zero the aux field)
; Last_mod := $00000000 (don't have this info)
; Header Ptr := $0002 (Starting block of owning directory)
;==========================================================================
;==========================================================================
;Now setup the file type as a subdirectory entry
;==========================================================================
lda #dir_file_type
sta one_entry+file_type_index
;==========================================================================
;Now setup Key block pointer and header_ptr
;==========================================================================
lda #vol_dir_start
sta one_entry+key_blk_index
sta one_entry+header_ptr_index
;==========================================================================
;Zero the aux type and mod_date
;==========================================================================
stz one_entry+aux_type_index
stz one_entry+last_mod_index
stz one_entry+2+last_mod_index
rts
ENDP
;==========================================================================
;setup_io_buf This routine will fill the I/O buffer with the correct
; data
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; one_entry (39 byte entry from disk)
; FCR (setup)
;
;Output: A = error code if carry set.
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
;
;==========================================================================
EXPORT setup_io_buf
setup_io_buf PROC
ldy #fcr_key_blk ;key block of the resource
lda [pro_fcr_ptr],y
sta drvr_blk_num
ldy #fcr_file_id
sta [pro_fcr_ptr],y ;save file id for duplicate checks
ldy #fcr_entry_type
lda [pro_fcr_ptr],y ;see if this is a resource file
cmp #resource_type
bne not_resource ;must be a standard file
jsr find_resource
bcc standard_file
nuke_it ;
rts
not_resource ;
cmp #subdir_type
bcc standard_file
lda #in_cache
sta drvr_cache
ldy #fcr_key_blk ;key block of the file
lda [pro_fcr_ptr],y
sta drvr_blk_num
ldx data_ptr
ldy data_ptr+2
jsr load_io_blk ;store one block into the I/O buffer
bcs nuke_it
ldy #data_blk_num
lda drvr_blk_num
sta [pro_fcr_ptr],y
clc
rts
standard_file ;
ldy #fcr_key_blk
lda [pro_fcr_ptr],y
sta drvr_blk_num
lda storage_type
cmp #tree_type
bne chk_sapling
ldx master_ptr
ldy master_ptr+2
jsr load_io_blk
bcc master_loaded
rts
master_loaded ;
ldy #master_blk_num
lda drvr_blk_num ;save the block num of the master block
sta [pro_fcr_ptr],y
jsr get_first_index ;load the address of the first index blk
sta drvr_blk_num
bra load_index
chk_sapling ;
cmp #sapling_type
bne do_seedling
load_index ;
ldx index_ptr
ldy index_ptr+2
jsr load_io_blk ;load in the index block
bcc cont_index
rts
cont_index ;
ldy #index_blk_num
lda drvr_blk_num ;save the block num of the index block
sta [pro_fcr_ptr],y
jsr get_first_index ;load the address of the first index blk
sta drvr_blk_num
do_seedling ;
ldx data_ptr
ldy data_ptr+2
jsr load_io_blk ;load in the seedling block
bcc cont_data
rts
cont_data ;
ldy #data_blk_num
lda drvr_blk_num
sta [pro_fcr_ptr],y
clc
rts ;return to user in happy camper mode
;==========================================================================
;this routine will read into the buffer pointed to by X and Y the block
;number countained in drvr_blk_num.
;==========================================================================
load_io_blk
stx drvr_buf_ptr
sty drvr_buf_ptr+2 ;save the pointer to the buffer
jsr read_with_cache
rts
;==========================================================================
;This routine will load into the accumulator the first block address from
;an index block.
;==========================================================================
get_first_index
longa off
sep #$20
ldy #blk_size/2
lda [drvr_buf_ptr],y
xba
lda [drvr_buf_ptr]
longa on
rep #$20
rts
ENDP
;==========================================================================
;set_fcr_status This routine will set bits in the FCR pointed to by
; 'pro_fcr_ptr'
;
;Input: A = ORA Mask
;
;OutPut: Operation complete
;
;Uses: A and Y registers
;==========================================================================
EXPORT set_fcr_status
set_fcr_status PROC
ldy #fcr_status
ora [pro_fcr_ptr],y
sta [pro_fcr_ptr],y
rts
ENDP
;==========================================================================
;find_resource: This routine will read in the key block of a resource file
; and locate the resource fork. If the resource is available
; then this routine will replace the following fields in the fcr:
;
; 1) fst_key_blk := Key block of the resource
; 2) blocks used := blocks used by the resource
; 3) fst_eof := eof of resource
; 4) storage_type := storage type of the resource
;
;Input: drvr_blk_num = key block of resource file
;
;Output: Carry Set if error otherwise clear
;
;==========================================================================
EXPORT find_resource
find_resource PROC
;==========================================================================
;Setup the system buffer as our temp work area
;==========================================================================
lda gbuf_addr ;address of systems work buffer
sta drvr_buf_ptr
lda gbuf_addr+2
sta drvr_buf_ptr+2
lda #in_cache ;go ahead and store this block in cache
sta drvr_cache
jsr device_call
jsr set_user_cache ;restore user's cache value
bcc got_res_header
rts ;return error to caller
got_res_header ;
;==========================================================================
;open the correct fork.
;==========================================================================
lda resource_num ;calculate the resource
xba ;convert $0000 or $0001 into
tay ;$0000 or $0100
;==========================================================================
;Now we load the storage type, key block and eof
;==========================================================================
lda [drvr_buf_ptr],y
and #$0003 ;clear resource bits
beq bad_resource
cmp #(tree_type/$10)+1
bcc get_key_blk ;storage type ok
bad_resource ;
lda #res_not_found ;the file is damaged
sec
rts
get_key_blk ;
asl a ;convert $1,$2,$3 to $10,$20,$30
asl a
asl a
asl a
sta storage_type
iny ;now get the key block
lda [drvr_buf_ptr],y
beq bad_resource
sta files_key_block
iny
iny
;==========================================================================
;Now get the total blocks used field
;==========================================================================
lda [drvr_buf_ptr],y
sta one_entry+blks_used_index
tyx
ldy #fcr_blks_used
sta [pro_fcr_ptr],y
ldy #fcr_blks_alloc
sta [pro_fcr_ptr],y
txy
iny
iny
;==========================================================================
;Now load the new EOF for the resource. (EOF = three bytes)
;==========================================================================
lda [drvr_buf_ptr],y
sta one_entry+eof_index
tax ;low order 16 bits of resource
iny
lda [drvr_buf_ptr],y
sta one_entry+eof_index+1
;==========================================================================
;Now save the new EOF for this resource.
;==========================================================================
ldy #fcr_eof+1
sta [pro_fcr_ptr],y
ldy #fcr_curr_eof+1
sta [pro_fcr_ptr],y
txa ;store low order 16 bits
ldy #fcr_eof
sta [pro_fcr_ptr],y
ldy #fcr_curr_eof
sta [pro_fcr_ptr],y
ldy #fcr_storage
lda storage_type
sta [pro_fcr_ptr],y
ldy #fcr_key_blk
lda files_key_block
sta [pro_fcr_ptr],y ;save the key block as well
clc
rts ;exit all happy now!!!
ENDP
;======================================================================
;check_vcr_first: This routine trys to find a vcr from the device number
; in 'A'. If a vcr is found the no error is reported.
; otherwise the error is returned.
;
;Created: may 08, 1987
;Modified: may 08, 1987
;Author: Rob Turner
;
;
;Enter: from open call
;
;Input: A = device number to find
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; dev_num ;device number we are looking for
;
;Output: A = error code if carry set.
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; find_vcr
; math_temp
; my_vcr_ptr
;
;======================================================================
longa on
longi on
EXPORT check_vcr_first
check_vcr_first PROC
stz math_temp
search_loop ;
lda math_temp
inc a
sta math_temp
jsl get_vcr
bcc good
rts
;==========================================================================
;If we find a vcr then make sure that I own it.
;==========================================================================
good ;
jsl deref
stx my_vcr_ptr
sty my_vcr_ptr+2
ldy #vcr_fst_id
lda [my_vcr_ptr],y
cmp #pro_id
bne search_loop
ldy #vcr_dev ;is this the correct device
lda [my_vcr_ptr],y
cmp dev_num
bne search_loop
ldy #vcr_status
lda [my_vcr_ptr],y
and #vcr_swapped
bne do_mount
clc ;this is it.
rts
do_mount ;
jsr mount_volume
rts
ENDP
;==========================================================================
;Read: This file contains code used to read data from a file.
; The types of files supported are:
;
; 1) seedling
; 2) sapling
; 3) tree
; 4) directory
;
;Created: May 16, 1987
;Modified: Apr 16, 1988
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
; and lots of other stuff to.
;
;Call Format:
;
;Class 0 Reference Number (2 Bytes)
; Pointer to Data Buffer (4 Bytes)
; Request Count (4 Bytes)
; Transfer Count (4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Reference Number (2 Bytes)
; Pointer to Data Buffer (4 Bytes)
; Request Count (4 Bytes)
; Transfer Count (4 Bytes)
; Cache Priority (2 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT read
read PROC
lda #drvr_read ;setup for device read
sta drvr_call_num
;==========================================================================
;sets up parameters from GS/OS call block, and verifies class and max pcnt
;
;setup params returns the class in the Accumulator. It also does the
;load as the last instruction before the 'RTS'
;==========================================================================
jsr setup_params
jsr read_write_setup
;==========================================================================
;Go and get the newline length from the FCR and save it on zero page.
;==========================================================================
stz newline_len ;disable newline mode please
;==========================================================================
;We need to get the mask as well as the newline list pointer
;==========================================================================
ldy #fcr_mask
lda [my_fcr_ptr],y
sta newline_mask
beq no_newline
ldy #fcr_newline_len
lda [my_fcr_ptr],y
sta newline_len
beq no_newline ;ok I like this read
;==========================================================================
;Since newline is enabled we must dereference the newline VP and save it
;==========================================================================
ldy #fcr_newline
lda [my_fcr_ptr],y
tax
iny
iny
lda [my_fcr_ptr],y
tay
jsl deref
stx newline_ptr
sty newline_ptr+2
;==========================================================================
;When we finally get here we are thru with setting up the newline list
;==========================================================================
no_newline ;
ldy #vcr_dev
lda [my_vcr_ptr],y
sta drvr_dev_num
;==========================================================================
;Just to be complete I guess I should check if the file is read enabled.
;==========================================================================
ldy #fcr_access ;check the user's access privs.
lda [my_fcr_ptr],y
and #ClrAllButAccess
cmp #write_access ;does the file have write only access?
bne i_can_read ;read is allowed
lda #invalid_access
error_out ;
sec
jmp main_exit
I_can_read ;
;==========================================================================
;Now we need to move some variables from the FCR to zero page.
;We do this just to make it easy to get to the variables.
;
;First lets move the current file marker and EOF to direct page
;==========================================================================
jsr setup_curr_mark
jsr setup_curr_eof
;==========================================================================
;Now lets see if we are at the end of the file.
;IF curr_mark = EOF THEN return EOF error
;==========================================================================
lda curr_mark+2
cmp curr_eof+2
bcc not_eof
lda curr_mark
cmp curr_eof
bne not_eof
lda #end_of_file ;sorry no more bytes to read
bra error_out
not_eof ;
;==========================================================================
;OK go and get the storage type for the file
;We use this field to determine how to read the file.
;==========================================================================
ldy #fcr_storage
lda [pro_fcr_ptr],y
sta storage_type
jsr chk_swapped ;mount the volume if needed
bcc disk_online
jmp main_exit
disk_online ;
;==========================================================================
;when we get here we have found the lost volume. So we must reset our
;flags and change our device number, in both the VCR and FCR
;==========================================================================
lda drvr_dev_num ;new device number
ldy #vcr_dev
sta [my_vcr_ptr],y ;update VCR
ldy #vcr_status
lda [my_vcr_ptr],y
and #vcr_swapped_in ;set 'disk in' flag
sta [my_vcr_ptr],y
;==========================================================================
;Now the disk is ready to be talked to
;==========================================================================
disk_in ;
;==========================================================================
;Now we need to compare req_cnt to EOF.
;
;IF (req_count + curr_mark) > EOF THEN
; req_cnt := (EOF-curr_mark)
;
;NOTE: carry must be clear when we get here.
;==========================================================================
lda curr_mark
adc user_req_cnt
sta math_temp
lda curr_mark+2
adc user_req_cnt+2
cmp curr_eof+2
bcc not_greater ;no problem with request count
bne too_large
lda math_temp
cmp curr_eof ;check lower 16 bits
bcc not_greater
beq not_greater
too_large ;
;==========================================================================
;If the request is too large then we need to adjust the request count.
;==========================================================================
sec
lda curr_eof
sbc curr_mark
sta user_req_cnt
lda curr_eof+2
sbc curr_mark+2
sta user_req_cnt+2
not_greater ;
;==========================================================================
;Lets stop here and take a brief look at the current file marker.
;
;Diagram: (A) (B) (C)
;
; | | $00 to $7F | $00 to $FF | $000 to $1FF |
; | | | | |
; | MUST BE ZERO |<- master ->|<- index ->|<- Byte index ->|
; |_______________|_____________|_______________|_________________|
; | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
; |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
; | | | | |
; 3 2 2 4 1 6 0 8 0 0
;
; (A) = Index into the master index block:
; This value goes upto 127. This means that the total
; number of index blocks that a file can contain is
; 127.
;
; (B) = Index into the index block:
; This value goes upto 255. This means that there
; can be upto 255 data blocks per index block.
;
; (C) = index into the data block:
; This value goes upto 512. This means that there
; can be upto 512 bytes of data in the data block.
;
;==========================================================================
;==========================================================================
;see if newline is enabled. If it is not then call the fast routines.
;otherwise fall into snell mode.
;==========================================================================
lda newline_len
beq fast_read ;runs like a bat out of hell!
dec a ;see if this is a single char newline
bne fast_read
lda [newline_ptr]
and #$00FF ;clear garbage byte
sta newline_char
EXPORT fast_read
fast_read ;
;==========================================================================
;If we get here we know that newline is disabled so we do not have to check
;each character that we read. This means that multiple blocks can be moved
;into the user's buffer directly without having to go into the I/O buffer.
;Note however that the last block is moved into the I/O buffer.
;
;before we can start sending multiply blocks we must empty the current
;data block that is in our buffer. If there are enough bytes to satisfy
;the read request we just send the bytes and exit.
;==========================================================================
single_block ;
jsr send_partial
bcc fast_loop
clc
brl end_read_write ;all done with read.
;==========================================================================
;First lets calculate how many bytes are left in this block.
;==========================================================================
fast_loop ;
lda user_req_cnt+2
bne do_seq_blks
lda user_req_cnt
cmp #blk_size+1
bcs do_seq_blks
jmp send_last ;we have less then 512 bytes left
do_seq_blks ;
;==========================================================================
;We know that the user still has more then 512 bytes to go before the read
;request is satisfied. So lets go and see if there are any sequential
;blocks that we can read. If there are no sequential blocks then we must
;get the block address our selves and issue a read of one block. This
;of course makes mister happy face very sad since he will have to double
;buffer.
;==========================================================================
lda storage_type
cmp #subdir_type
bcs single_block
jsr num_seq_blks ;see if there are any sequential blks
beq single_block
seq_avail ;
;==========================================================================
;when we get here we know that there are some sequential blocks that can
;be read. But we must see if the number of sequential blocks is less then
;the number of blocks wanted.
;==========================================================================
stx drvr_blk_num ;save block number for read
inc a
sta math_temp ;number of sequential blocks avail.
stz drvr_req_cnt
stz drvr_req_cnt+2
stz drvr_blk_num+2
lda user_req_cnt+1 ;first convert bytes wanted to blocks
lsr a
cmp math_temp
bcc use_users_size
lda math_temp ;use ALL sequential blocks.
use_users_size ;
asl a ;convert block count to bytes
sta drvr_req_cnt+1
;==========================================================================
;Now setup the buffer pointer to point to the user's buffer.
;==========================================================================
lda users_buf_ptr
sta drvr_buf_ptr
lda users_buf_ptr+2
sta drvr_buf_ptr+2
jsr read_with_mount
bcc cont001 ;good.
jmp end_read_write
cont001 ;
jsr rw_adjust ;adjust the following:
; ;(1) user_buf_ptr
; ;(2) tran_cnt
; ;(3) curr_mark
; ;(4) user_req_cnt
jmp fast_loop
;==========================================================================
;
;==========================================================================
send_last ;
jsr send_partial
clc
;==========================================================================
;===== END of READ call. We save the processor status and error code =====
;===== We then send back the number of bytes that =====
;===== were read successfully. Then we restore any=====
;===== error condition and exit thru sys_exit. =====
;==========================================================================
EXPORT end_read_write
end_read_write ; ;common exit for read and write
php
pha
ldy #pblk_12_tran
lda tran_cnt
sta [my_pblk_ptr],y
iny
iny
lda tran_cnt+2
sta [my_pblk_ptr],y
;==========================================================================
;Save the current file marker after the read call completes.
;==========================================================================
jsr save_curr_mark
pla
plp
jmp main_exit
;==========================================================================
;bump_mark This routine will adjust the following fields:
;
; 1) users_buf_ptr := users_buf_ptr + bytes_sent
; 2) curr_mark := curr_mark + bytes_sent
; 3) user_req_cnt := user_req_cnt - bytes_sent
; 4) tran_cnt := tran_cnt+bytes_sent
;input:
; A = number of bytes to bump fields by.
;
;Uses: math_temp
;==========================================================================
EXPORT bump_mark
bump_mark ;
sta math_temp
clc
lda users_buf_ptr
adc math_temp
sta users_buf_ptr
bcc do_mark
inc users_buf_ptr+2
clc
do_mark ;
;==========================================================================
;IMPORTANT NOTES:
;
; users_buf_ptr is always =< $00FFFFFF
;
;WARNING! --> CARRY must be clear when we get here!
;==========================================================================
lda curr_mark
adc math_temp
sta curr_mark
bcc do_tran
inc curr_mark+2
clc
do_tran ;
;==========================================================================
;IMPORTANT NOTES:
;
; curr_mark+math_temp is always less the $00FFFFFF
;
;WARNING! --> CARRY must be clear when we get here!
;==========================================================================
lda tran_cnt
adc math_temp
sta tran_cnt
bcc do_req_cnt
inc tran_cnt+2
do_req_cnt ;
sec
lda user_req_cnt
sbc math_temp
sta user_req_cnt
bcs end_bump
dec user_req_cnt+2
end_bump rts
;==========================================================================
;Set_users_buf This routine moves the users buffer pointer into
; drvr_buf_ptr. This allows block read from a device driver
; to go directly to the users buffer.
;==========================================================================
set_users_buf ;
lda users_buf_ptr ;bypass my I/O buffer
sta drvr_buf_ptr
lda users_buf_ptr+2
sta drvr_buf_ptr+2
rts
ENDP
;==========================================================================
;send_partial This routine will send data from the current file marker to
; the end of the data buffer.
;
;Input: A = undefined
; X = indefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; curr_mark (must be correct)
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = data transfer complete
; 0 = no error
; b = k
;
;Uses: All registers
; move_block
;==========================================================================
EXPORT send_partial
send_partial PROC
;==========================================================================
;first make sure the I/O buffer is filled to the brim.
;==========================================================================
jsr fill_io_buf
bcc filled_up ;buffer is now ok
jmp end_read_write
;==========================================================================
;lets see how many bytes are left in the currently loaded data block.
;==========================================================================
filled_up ;
lda curr_mark
and #$01FF
sta math_temp
sec
lda #blk_size ;Bytes_left := buf_size-curr_position
sbc math_temp
sta math_temp ;number of bytes left in buffer.
;==========================================================================
;now lets calculate how many bytes we need to send to the user
;==========================================================================
ldx user_req_cnt+2
bne send_all_bytes ;he wants as many as I can give
cmp user_req_cnt
beq send_all_bytes
bcc send_all_bytes
lda user_req_cnt ;the user wants less then I have
send_all_bytes ;
sta math_temp
tay
;==========================================================================
;If newline is enabled then we have to do processing on each character
;so we avoid the block move routine.
;==========================================================================
lda newline_len
bne check_newline
;==========================================================================
;If we get here then newline is disabled so use the block move instruction
;==========================================================================
clc
lda curr_mark ;adjust the starting position
and #$01FF
adc data_ptr
tax ;save low address of buf pointer
lda #$0000
adc data_ptr+2
cpy #min_send_cnt ;minimum bytes needed to use blk move
bcc send_small
pha ;push high word of buffer pointer
phx ;push low word of buffer pointer
pei users_buf_ptr+2 ;destination address
pei users_buf_ptr
pea $0000 ;high word of count
phy ;number of bytes to send
pea move_sinc_dinc
jsl move_info
bra end_partial
;==========================================================================
;This routine will transfer a request count of less than $20 characters to
;the user's buffer. We do this to speed things up.
;==========================================================================
send_small ;
stx temp_ptr
sta temp_ptr+2 ;pointer to the user's buffer
dey
tya
bmi end_partial ;zero bytes to move?
cmp #$0002 ;are we moving less then two bytes?
bcc send_8_bit
dec a ;second decrement for word check
ror a ;is it an even transfer?
bcs send_8_bit
dey
loop_16 ;
lda [temp_ptr],y
sta [users_buf_ptr],y
dey
dey
bpl loop_16
bra end_partial
send_8_bit ;
sep #$20 ;8 bit accum
loop_8 ;
lda [temp_ptr],y
sta [users_buf_ptr],y
dey
bpl loop_8
rep #$20
;==========================================================================
;Now that we have send the data adjust the pointers etc.
;==========================================================================
end_partial
lda math_temp
jsr bump_mark
lda user_req_cnt
ora user_req_cnt+2
beq all_done ;request count satisfied
ldy #fcr_status ;we are going to read another block so make sure
lda [pro_fcr_ptr],y ;the I/O buffer is clean
and #file_dirty
beq @not_dirty
jsr flush_io_buffer ;write the dirty blocks to disk
bcc @not_dirty
jmp main_exit
@not_dirty
clc
rts
all_done
sec
rts
;==========================================================================
;Below is the code used to handle the newline checking. For this code we
;set the accumulator into 8 bit mode so we do not overflow the user's
;buffer.
;==========================================================================
check_newline ;
tax ;save newline length for a second
lda curr_mark ;isolate block index
and #$01FF
tay
sta entries_checked
sec
lda users_buf_ptr
sbc entries_checked
sta temp_ptr
lda users_buf_ptr+2
sbc #$0000
sta temp_ptr+2
clc
tya
adc math_temp ;calculate the last byte address
sta entries_checked
dex ;see if this is a special case
bne slow_read
php ;save the mode please
longa off
sep #$20 ;short accumulator
fast_newline ;
lda [data_ptr],y
sta [temp_ptr],y
and newline_mask
cmp newline_char
beq found_newline
iny
cpy entries_checked ;are we done yet
bne fast_newline
plp ;be nice and restore last mode please.
bra end_partial
found_newline ;
plp ;restore last mode please
longa on
;==========================================================================
;Number of bytes := Ending position minus starting position
; Y = ending position
; curr_mark = starting position
;==========================================================================
lda curr_mark
and #$01FF
sta math_temp
sec
tya
sbc math_temp
inc a
jsr bump_mark ;adjust the pointers etc
stz user_req_cnt
stz user_req_cnt+2
sec
rts
;==========================================================================
;This routine is called when there is more then one newline character to
;check. This routine is slower then the above newline read since I have
;to scan all newline characters against a list
;==========================================================================
slow_read ;
;==========================================================================
;newline_len := newline_len-1 So I can use it as an index into the newline
;list.
;==========================================================================
*** removed 13-Jul-92 DAL to make multi-character newline mode keep working across blocks
*** (newline_len was getting decremented farther and farther on each block...ouch!)
;;; dec newline_len
*** end 13-Jul-92, part 1 of 2
php
longa off
sep #$20
slow_newline ;
lda [data_ptr],y
sta [temp_ptr],y
and newline_mask
tyx ;save index first
ldy newline_len
*** added 13-Jul-92 DAL, because I took out a dec newline_len a few lines above
dey
*** end 13-Jul-92, part 2 of 2
next_newline ;
cmp [newline_ptr],y
beq found_one
dey
bpl next_newline
txy ;restore index first
iny
cpy entries_checked ;are we done yet
bne slow_newline
plp ;be nice and restore last mode please.
brl end_partial
found_one ;
txy ;restore Index first
bra found_newline
ENDP
;==========================================================================
;fill_io_buf: This routine will load into the I/O buffer the correct
; index and data blocks. It will also adjust the FCR data
;
; WARNING: curr_mark must not point beyond the current
; structure of the file, even though that mark may be within
; the file's EOF. This could happen if a file has a trailing
; portion which is all sparse from a position <128K to a
; position >128K. In this case the file is stored as a
; sapling, not a tree, but owns a sparse area that is
; logically within the "tree" range. In such a case,curr_mark
; must never be in that range when entering this routine.
;
;Input: A = undefined
; X = indefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; curr_mark (must be correct)
; master_ptr (must pointer to master index block)
; index_ptr (must point to the correct index block)
; data_ptr
; pro_fcr_ptr (points to prodos data in FCR)
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; drvr_blk_num
; drvr_call_num
; drvr_buf_ptr
; drvr_req_cnt
; device_dispatcher
;
;
;==========================================================================
longa on
longi on
EXPORT fill_io_buf
fill_io_buf PROC
stz data_flag ;flag says load data block
bra fill_up
EXPORT load_indexes
load_indexes ;
lda #$FFFF ;flag says do not load data block
sta data_flag
fill_up ;
stz mark_changed
jsr setup_curr_eof
lda curr_mark ;is the mark zero?
ora curr_mark+2
beq mark_ok ;yes so don't subtract one
lda curr_mark+2
cmp curr_eof+2 ;see if current mark = eof
bcc mark_ok
lda curr_mark
cmp curr_eof
bcc mark_ok ;no problem
sbc #$0001 ;curr_mark := curr_mark-1
sta curr_mark
lda curr_mark+2
sbc #$0000
sta curr_mark+2
dec mark_changed
mark_ok ;
jsr standard_req ;read 512 bytes only
ldy #fcr_storage ;see if this is a subdirectory.
lda [pro_fcr_ptr],y
cmp #subdir_type
bcc standard_file ;File is not a directory
jsr fill_directory ;fill it up bud!
jsr restore_mark ;fix it if it was changed!
rts ;return to caller
standard_file ;
jsr fill_index ;fill up the index block
bcc chk_data
rts ;major error
chk_data ;
bit data_flag ;'-' = fill index block only
bpl do_data
jsr restore_mark
rts
do_data ;
jsr fill_data ;fill the data buffer
jsr restore_mark
rts ;exit
restore_mark ;
php ;save error flag
pha ;save error code
bit mark_changed
bpl no_change
inc curr_mark
bne no_change
inc curr_mark+2
no_change ;
pla
plp
rts
;==========================================================================
;Fill_index: This routine will fill up the index block if it can.
;
;Below is pascal type segment which indicates how fill_index works.
;
;IF master = defined THEN
; BEGIN
; blk_num := get_blk_address(curr_mark) ; {Get block address from index blk}
; IF blk_num = $0000 THEN
; sparse_blk(index_buffer) ; {Fill Index buffer with zeros}
; ELSE
; IF curr_blk <> blk_to_read THEN
; read_blk(index_buffer) ; {Load index block from disk}
; END
; ELSE {master undefined}
; IF curr_mark > $20000 THEN
; BEGIN
; IF sapling = defined THEN
; sparse_blk(index_buffer) {fill index block to zeros}
; ELSE
; BEGIN
; sparse_blk(data_buffer) ; {force data block to zeros}
; data_flag := data_flag-1 {This forces the calling routine
; to exit without loading the data
; block, since I have already loaded
; the blocks with zeros}
; END
; END
;
; WARNING: curr_mark must not point beyond the current
; structure of the file, even though that mark may be within
; the file's EOF. This could happen if a file has a trailing
; portion which is all sparse from a position <128K to a
; position >128K. In this case the file is stored as a
; sapling, not a tree, but owns a sparse area that is
; logically within the "tree" range. In such a case,curr_mark
; must never be in that range when entering this routine.
;
;==========================================================================
fill_index ;
ldy #fcr_status ;see if the master index block is
lda [pro_fcr_ptr],y ;active
and #master_active
beq no_master ;the master block is not defined
lda curr_mark+2 ;make an index into the index block
lsr a
and #$007F
tay
ora #$0100 ;build index for upper half of buffer
tax
lda [master_ptr],y
sta drvr_blk_num
txy
lda [master_ptr],y
sta drvr_blk_num+1 ;save the block number to read
stz drvr_blk_num+2
lda drvr_blk_num ;see if blk_num = $0000
beq sparse_index
ldy #index_blk_num ;see if the block is already loaded
cmp [pro_fcr_ptr],y
bne load_index
clc
rts ;no problem here
;==========================================================================
;If Y=$0100 then we know that the master index block is not really active
;If it were active then there would be a real index block. If this is the
;case then we should load in the index block if there is one available.
;==========================================================================
sparse_index ;
cpy #$0100 ;If non-first index is sparse then
bne zero_index ;zero the index block
ldy #fcr_storage ;is there an index block to load?
lda [pro_fcr_ptr],y
cmp #sapling_type
bne index_sparse ;just exit no index to load
ldy #fcr_key_blk
lda [pro_fcr_ptr],y
ldy #index_blk_num ;see if the block is already loaded
cmp [pro_fcr_ptr],y
beq index_sparse ;just exit since the block is loaded
sta drvr_blk_num
bra load_index
zero_index ;
jsr chk_index_clean ;make sure the current block is not dirty
bcc @no_problem
rts
@no_problem
ldy #index_blk_num ;see if the index block is sparse
lda [pro_fcr_ptr],y
beq index_sparse ;block is already sparse
ldx index_ptr
ldy index_ptr+2
jsr send_sparse_blk
ldy #index_blk_num
lda #$0000
sta [pro_fcr_ptr],y ;indicate index block loaded is sparse
index_sparse ;
clc
rts
load_index ;
jsr chk_index_clean ;make sure the current block is not dirty
bcc @no_problem
rts
@no_problem
lda index_ptr
sta drvr_buf_ptr
lda index_ptr+2
sta drvr_buf_ptr+2
jsr read_with_cache ;read in the block please
bcc index_loaded
rts
index_loaded
ldy #index_blk_num
lda drvr_blk_num
sta [pro_fcr_ptr],y ;update the loaded block number field
rts
;==========================================================================
;If we get here we know that there is no master index block defined.
;==========================================================================
no_master
lda curr_mark+2 ;see if the mark > $20000
lsr a
and #$007F
bne index_pain
clc
rts
index_pain ;see if the index block is defined
stz drvr_blk_num
jsr chk_index_clean
bcc @its_clean
rts ;return the error
@its_clean
ldy #fcr_status
lda [pro_fcr_ptr],y
and #index_active
bne sparse_index ;make the index block all zeros
sparse_data
jsr chk_data_clean ;make sure the data block is OK
bcc @good_data
rts
@good_data
ldy #data_blk_num ;see if the block is already sparse
lda [pro_fcr_ptr],y
beq data_sparse ;data block is alreay sparse
ldx data_ptr
ldy data_ptr+2
jsr send_sparse_blk ;make the data block sparse
ldy #data_blk_num ;see if the block is already sparse
lda #$0000 ;indicate block is sparse
sta [pro_fcr_ptr],y
data_sparse ;
dec data_flag ;indicate to the main routine that the
clc ;data block does not need checking
rts
;==========================================================================
;Chk_index_clean will determine if the current index block needs to be
;written to disk before the new block is loaded and will write it out if
;necessary.
;==========================================================================
chk_index_clean
phy
ldy #fcr_status ;we are going to read another block so make sure
lda [pro_fcr_ptr],y ;the I/O buffer is clean
and #index_dirty
beq @not_dirty ;easy case.
lda drvr_blk_num ;get the block number we want to load
ldy #index_blk_num ;see if the block is already loaded
cmp [pro_fcr_ptr],y
beq @not_dirty ;another easy case.
pha ;save the driver block number
jsr flush_index ;save the data to disk
plx ;get the drvr_blk_number back please
stx drvr_blk_num
ply
rts ;carry will return any error
@not_dirty
ply
clc
rts
;==========================================================================
;Fill_data: This routine will fill up the data block if it can.
;
;Below is pascal type segment which indicates how Fill_data works.
;
;IF index = defined THEN
; BEGIN
; blk_num := get_blk_address(curr_mark) ; {Get block address from index blk}
; IF blk_num = $0000 THEN
; sparse_blk(data_buffer) ; {Fill Index buffer with zeros}
; ELSE
; IF curr_blk <> blk_to_read THEN
; read_blk(index_buffer) ; {Load index block from disk}
; END
; ELSE
; IF curr_mark > $200 THEN
; sparse_blk(data_buffer) ; {Clear the data buffer}
; ELSE
; read_blk(key_block) ; {main block for file}
;
;==========================================================================
fill_data ;
ldy #fcr_status
lda [pro_fcr_ptr],y
and #index_active
beq no_index ;there is no index buffer
jsr get_data_num ;setup drvr_blk_num from index blk
lda drvr_blk_num
bne not_sparse ;make the data block sparse
cpy #$0100 ;was it the 1st entry
bne sparse_data ;make data block sparse
lda curr_mark+2 ;see if the master index is zero
lsr a
and #$007F
bne sparse_data
ldy #fcr_storage ;is there an index block to load?
lda [pro_fcr_ptr],y
cmp #seedling_type
bne sparse_data ;make block sparse please
ldy #fcr_key_blk ;this is a seedling so load the block
lda [pro_fcr_ptr],y
sta drvr_blk_num
not_sparse ;
jsr chk_data_clean ;make sure the data buffer is OK
bcc @still_ok
rts
@still_ok
lda drvr_blk_num
ldy #data_blk_num ;see if the block is already loaded
cmp [pro_fcr_ptr],y
bne load_data
exit_fill_data ;
clc
rts ;no problem here
load_data ;
lda data_ptr
sta drvr_buf_ptr
lda data_ptr+2
sta drvr_buf_ptr+2
jsr read_with_mount ;read in the block please
bcc store_data_num
rts
store_data_num ;
ldy #data_blk_num ;update current block loaded field
lda drvr_blk_num
sta [pro_fcr_ptr],y
rts
;==========================================================================
;If we get here we know that there is no index block loaded
;==========================================================================
no_index ;
lda curr_mark+1
lsr a
and #$00FF
beq @need_long
brl sparse_data ;make the data block sparse if needed
@need_long
ldy #fcr_key_blk
lda [pro_fcr_ptr],y
ldy #data_blk_num ;current block that is loaded
cmp [pro_fcr_ptr],y
bne reload_data
clc
rts ;block is already loaded
reload_data ;
sta drvr_blk_num
lda data_ptr
sta drvr_buf_ptr
lda data_ptr+2
sta drvr_buf_ptr+2
jsr read_with_mount
bcc data_read
rts
data_read ;
ldy #data_blk_num
lda drvr_blk_num ;update the current block loaded
sta [pro_fcr_ptr],y
rts
;==========================================================================
EXPORT get_data_num
get_data_num ;
lda curr_mark+1
lsr a
and #$00FF
tay
ora #$0100
tax
lda [index_ptr],y ;setup the block number
sta drvr_blk_num
txy
lda [index_ptr],y
sta drvr_blk_num+1
stz drvr_blk_num+2
rts
;==========================================================================
;Chk_data_clean will determine if the current data block needs to be
;written to disk before the new block is loaded
;==========================================================================
chk_data_clean
phy
ldy #fcr_status ;we are going to read another block so make sure
lda [pro_fcr_ptr],y ;the I/O buffer is clean
and #data_dirty
beq @not_dirty ;easy case.
lda drvr_blk_num ;get the block number we want to load
ldy #data_blk_num ;see if the block is already loaded
cmp [pro_fcr_ptr],y
beq @not_dirty ;another easy case.
pha ;save the driver block number
jsr flush_data_blk ;save the data to disk
plx ;get the drvr_blk_number back please
stx drvr_blk_num
ply
rts ;carry will return any error
@not_dirty
ply
clc
rts
fill_directory ;
;==========================================================================
;OK this file is a directory so lets handle this pain in the butt case.
;
;The way it works:
;
; The master index field contains the current block number
; that is loaded into the data buffer. So when I'm called
; with the current mark I calculate the block number that
; that is needed. If the block number is less then the
; current block number then I walk the backward links till
; I find the block needed. If the block wanted is greater
; then the current block I walk the links forward till I
; find the correct block, and at the end I update the FCR.
;
;==========================================================================
lda #in_cache
sta drvr_cache
lda data_ptr ;setup buffer pointer to data buffer
sta drvr_buf_ptr
lda data_ptr+2
sta drvr_buf_ptr+2
lda curr_mark+1
lsr a ;bytes to blocks
sta math_temp
ldy #data_blk_num
lda [pro_fcr_ptr],y
sta entries_blk_num
ldy #master_blk_num
lda [pro_fcr_ptr],y
cmp math_temp
bne have_to_look
clc
rts
have_to_look ;
sta last_blk
bcs look_backward ;IF current > wanted THEN go backward
;==========================================================================
;Of we get here we walk thru the forward links till we find the right blk
;==========================================================================
forward_loop ;
ldy #$0002
lda [data_ptr],y
beq bad_dir_blk ;This should NEVER happen!!!!!
sta drvr_blk_num
jsr go_get_block ;go read the block
bcc ar16
rts ;return the error
ar16 ;
lda [data_ptr] ;verify backward links please
cmp entries_blk_num
beq cont16
lda #dir_error
bra dir_bad
cont16 ;
ldy #data_blk_num
lda drvr_blk_num
sta [pro_fcr_ptr],y
sta entries_blk_num ;update both the FCR and Link verify
inc last_blk ;advance the block counter
lda last_blk
cmp math_temp ;See if this the right bloc number
bne forward_loop ;go and get the next block
end_links ldy #master_blk_num ;update the current block
sta [pro_fcr_ptr],y
clc
rts
;==========================================================================
;We need to set the mark backward. So we need to walk the links backward
;==========================================================================
look_backward ;
lda [data_ptr]
beq bad_dir_blk ;This should NEVER happen!!!!!
sta drvr_blk_num
jsr go_get_block ;go read the block
bcc ar18
rts ;return the error
ar18 ;
ldy #$0002
lda [data_ptr],y
cmp entries_blk_num
bne dir_bad
lda drvr_blk_num
ldy #data_blk_num
sta [pro_fcr_ptr],y
sta entries_blk_num
dec last_blk ;advance the block counter
lda last_blk
cmp math_temp ;See if this the right bloc number
bne look_backward ;go and get the next block
bra end_links ;we are done
;==========================================================================
bad_dir_blk ;
lda #parm_range_err
dir_bad ;
sec
jmp main_exit
;==========================================================================
go_get_block ;
jsr standard_req
jsr read_with_cache ;issue the read call
rts
data_flag DC.W 00
ENDP
;==========================================================================
;send_sparse_blk: This routine will zero out 512 bytes of memory.
;
;Input: A = undefined
; X = low word pointer to buffer
; Y = high word pointer to buffer
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: All registers
;
;==========================================================================
longa on
longi on
EXPORT send_sparse_blk
send_sparse_blk PROC
stx sparse_ptr
sty sparse_ptr+2
ldy #$01FE ;starting index
lda #$0000
sparse_loop ;
sta [sparse_ptr],y
dey
dey
bpl sparse_loop
rts
ENDP
;==========================================================================
;calc_seg_blks: This routine will calculate the number of sequential
; blocks in an index block. This routine will NOT calculate
; across index blocks.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; index_ptr (points to the index pointer)
; curr_mark (curr_mark must be on block boundary!)
;
;Output: A = number of blocks that are sequential
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: All registers
; math_temp (just a temp counter)
; comp_temp = and_mask
; check_blk = entries_checked
;==========================================================================
longa on
longi on
EXPORT num_seq_blks
num_seq_blks PROC
;==========================================================================
;Local equates for this routine
;==========================================================================
comp_temp equ and_mask ;(same ZP location different name)
check_blk equ entries_checked ;(same ZP location different name)
;==========================================================================
;Zero the counter and save the current file marker
;==========================================================================
stz math_temp
lda curr_mark
pha
lda curr_mark+2
pha
;==========================================================================
;first make sure the index blocks are loaded correctly
;==========================================================================
jsr load_indexes
;==========================================================================
;No go and get the first block from the Index block buffer.
;==========================================================================
jsr get_blk_num
sta check_blk
pha ;save starting block number
lda storage_type
cmp #subdir_type
bcs seq_exit ;always return zero for a directory
lda newline_len
bne seq_exit ;always return zero if newline enabled
calc_loop ;
;==========================================================================
;Calculate the index to the next block number.
;==========================================================================
jsr get_blk_num
bcs seq_exit ;we have looked at the entire block
dec a ;subtract one from the block
cmp #$FFFF ;see if this block is sparse
beq seq_exit ;this must be a sparse block!
cmp check_blk
bne seq_exit
inc a ;restore the block number
sta check_blk ;setup for next compare
inc math_temp
bra calc_loop
seq_exit ;
;==========================================================================
;restore the current marker and return the number of sequential blocks.
;==========================================================================
plx ;load X with starting block
pla
sta curr_mark+2
pla
sta curr_mark
lda math_temp ;return number of sequential blocks
rts
;==========================================================================
;get_blk_num: This routine will return the block number out of the index
; block based on the current file marker. It will also
; advance the file marker by 512 bytes.
;
;Input: curr_mark
;
;Output: carry set = end of block
; A = block number
;==========================================================================
get_blk_num ;
lda curr_mark+1
lsr a
and #$00FF
pha
tay
ora #$0100
tax
lda [index_ptr],y
and #$00FF
sta comp_temp
txy
lda [index_ptr],y
and #$00FF
xba
ora comp_temp
tax
clc
lda curr_mark
adc #blk_size
sta curr_mark
bcc ar11
inc curr_mark+2
clc
ar11 ;
txa ;block number
plx ;get the first index value
beq end_seq ;just set the carry
;==========================================================================
;WARNING!! --> Carry must be clear when we get here.
;==========================================================================
rts
end_seq ;
sec
rts
ENDP
;==========================================================================
;setup_curr_mark This routine will load the current file marker out of the
; FCR and store it in curr_mark
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; pro_fcr_ptr (points to my FCR data)
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; curr_mark (contains current file marker)
;
;Uses: All registers
;==========================================================================
longa on
longi on
EXPORT setup_curr_mark
setup_curr_mark PROC
ldy #fcr_curr_mark
lda [pro_fcr_ptr],y
sta curr_mark
iny
iny
lda [pro_fcr_ptr],y
sta curr_mark+2
rts
ENDP
;==========================================================================
;setup_curr_eof This routine will load the current end of file out of the
; FCR and store it in curr_eof
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; pro_fcr_ptr (points to my FCR data)
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; curr_eof (contains current end fo file)
;
;Uses: All registers
;==========================================================================
longa on
longi on
EXPORT setup_curr_eof
setup_curr_eof PROC
ldy #fcr_curr_eof
lda [pro_fcr_ptr],y
sta curr_eof
iny
iny
lda [pro_fcr_ptr],y
sta curr_eof+2
rts
ENDP
;==========================================================================
;save_curr_mark This routine will save the current end of file into the
; FCR.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; pro_fcr_ptr (points to my FCR data)
; curr_mark
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Uses: A and Y registers
;==========================================================================
longa on
longi on
EXPORT save_curr_mark
save_curr_mark PROC
ldy #fcr_curr_mark
lda curr_mark
sta [pro_fcr_ptr],y
iny
iny
lda curr_mark+2
sta [pro_fcr_ptr],y
rts
ENDP
;==========================================================================
;Write: This file contains code used to write data to a file.
; The types of files supported are:
;
; 1) seedling
; 2) sapling
; 3) tree
;
;Created: oct 06, 1987
;Modified: oct 15, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
; and lots of other stuff.
;
;Call Format:
;
;Class 0 Reference Number (2 Bytes)
; Pointer to Data Buffer (4 Bytes)
; Request Count (4 Bytes)
; Transfer Count (4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Reference Number (2 Bytes)
; Pointer to Data Buffer (4 Bytes)
; Request Count (4 Bytes)
; Transfer Count (4 Bytes)
; Cache Priority (2 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT write
write PROC
stz seq_write_cnt ;reset sequential block counter
jsr setup_params ;setup standard parameters
ldy #fcr_access ;see if the file is write protected
lda [my_fcr_ptr],y
and #ClrAllButAccess
cmp #write_access
bcs write_ok ;write access is enabled.
bad_access ; ;user cannot write to this file.
lda #invalid_access
sec
jmp main_exit
write_ok ;
ldy #vcr_dev ;last known location of volume
lda [my_vcr_ptr],y
sta drvr_dev_num ;I hope that the disk is still here.
jsr chk_disk_protect
not_protected ;
jsr read_write_setup
jsr setup_curr_mark
jsr setup_curr_eof
jsr setup_io_ptrs ;setup pointers to I/O buffer
jsr chk_swapped ;mount the volume if needed
bcc vol_online
jmp main_exit
;==========================================================================
; IF curr_mark NOT block_aligned THEN
;==========================================================================
vol_online ;
jsr users_to_drvrs ;move user's pointer to driver's ptr
jsr user_to_temp ;temp_ptr := user_buf_ptr
block_loop ;
lda curr_mark ;see if we are block aligned
and #$01FF
beq block_aligned ;yes we are block aligned
jsr partial_write ;write until data is block aligned.
bcs write_exit ;major problem
bra next_block
block_aligned ;
lda user_req_cnt+2 ;is req_cnt >= 512?
bne more_then_512
lda user_req_cnt
cmp #blk_size
bcc less_then_512
more_then_512 ;
jsr full_write ;transfer full 512 bytes.
bcs write_exit
bra next_block ;go to the next block please
;==========================================================================
;Less_then_512. we have less then 512 bytes to write.
;==========================================================================
less_then_512 ;
jsr partial_write ;write the partial block to disk
bcs write_exit ;major problem.
next_block ;
jsr rw_adjust ;adjust the following:
; ;(1) user_buf_ptr
; ;(2) tran_cnt
; ;(3) curr_mark
; ;(4) user_req_cnt
lda user_req_cnt+2 ;see if we are done yet
bne block_loop
lda user_req_cnt
bne block_loop
clc
write_exit
php
pha
bcc @10 ;there is no error so skip flush
jsr flush_file ;try to save the world
@10
jsr update_mark_eof ;make sure the mark is OK
ldy #fcr_curr_eof ;update the current end of file please
lda curr_eof
sta [pro_fcr_ptr],y
iny
iny
lda curr_eof+2
sta [pro_fcr_ptr],y
lda #file_dirty
jsr set_fcr_status ;indicate that something changed.
jsr standard_req ;make sure we write 512 bytes only
jsr flush_blocks ;flush any blocks to disk.
bcs write_failed ;bad news we cannot complete the flush!!!
;
;We know that the mark actually points to one past where the last byte is so
;lets reduce the mark by one please
;v00.06 a06
;
lda curr_mark
bne @no_high
dec curr_mark+2
@no_high
dec curr_mark
clc
jsr get_write_blk ;make sure I/O buffer is OK
bcs write_failed
txa ;block to load
beq holy_cow ;I hope not
jsr load_data ;load the data buffer please
bcs write_failed
holy_cow ;
inc curr_mark
bne @low_only
inc curr_mark+2
@low_only
pla ;restore error code
plp
jmp end_read_write ;exit thru wonder code in read file
write_failed
plx ;kill the old exit code
plp ;kill the old processor flag
sec ;make sure the error is set
jmp end_read_write ;exit please.
;==========================================================================
;Write blocks of 512 to disk
;==========================================================================
full_write
lda #blk_size
jsr range_chk_eof ;see if we overrun the EOF
bcs eof_error ;sorry we have an eof error
jsr standard_req ;transfer of 512 bytes
ldy #fcr_status ;see if the data block is dirty
lda [pro_fcr_ptr],y
and #data_dirty
beq clean_blk ;no flush needed
jsr flush_data_blk ;write the data block to disk
bcs write_exit ;get out of here with the error code
clean_blk ;
jsr user_to_temp ;temp_ptr := user_buf_ptr
clc
jsr get_write_blk ;is there a current block allocated?
bne block_allocated ;yes there is a block allocated.
jsr chk_sparse ;is the user's data sparse?
bcc do_flush ;data sparse so flush queued data blocks
; sec
jsr get_write_blk ;allocate a block please
bcc @no_error
brl write_exit ;cannot allocate a new block?
@no_error
pha
jsr user_to_temp ;point to user's buffer
pla
block_allocated ;
sta drvr_blk_num ;save the block number to write.
jsr user_to_data ;move user's data to data buffer
jsr write_data_blk ;write the data block to disk.
rts
do_flush ;
jsr flush_blocks ;issue the flush command
rts
user_to_data ;
lda users_buf_ptr ;setup driver buffer pointer to
sta drvr_buf_ptr ;point to the user's data!
lda users_buf_ptr+2
sta drvr_buf_ptr+2
rts
eof_error
lda #out_of_range ;you have pushed me too far!!!
sec
rts
; ldy #$01FE ;move 512 bytes please
;user_loop anop
; lda [drvr_buf_ptr],y
; sta [data_ptr],y
; dey
; dey
; bpl user_loop
; rts
;==========================================================================
;partial_write: This routine will handle reading/writing a partial block
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
;==========================================================================
partial_write ;
jsr build_index ;simulate a disk write
jsr range_chk_eof ;see if we overrun the EOF
bcs eof_error ;sorry we have an eof error
jsr standard_req ;we can only write 512 bytes!
jsr flush_blocks ;write any left over seq. blocks
bcs end_partial ;error condition
clc
jsr get_write_blk ;is there a current block allocated?
bne block_is_avail ;there is a block allocated.
; we know that the block is not allocated so check the data
jsr chk_sparse ;AND (data NOT sparse)
bcc is_sparse ;... (data IS sparse)
jsr get_write_blk ;allocate a block for the new data
bcs end_partial
pha ;save the block number
ldx data_ptr ;clear the new data block
ldy data_ptr+2
jsr clear_index
pla
block_is_avail ;
jsr load_data ;load in the data block
bcs end_partial
jsr user_to_temp
jsr merge_data ;add data to current block!
lda #file_dirty+data_dirty
jsr set_fcr_status ;indicate that something changed.
clc
rts
is_sparse ;
ldy #data_blk_num
lda [pro_fcr_ptr],y ;is block already loaded?
beq @sparse_loaded
ldy #fcr_status
lda [pro_fcr_ptr],y
and #data_dirty
beq @just_clear_buf ;no need to update the data block
jsr flush_data_blk
bcs end_partial
@just_clear_buf
ldx data_ptr ;clear the new data block
ldy data_ptr+2
jsr clear_index
ldy #data_blk_num
lda #$0000
sta [pro_fcr_ptr],y ;is block already loaded?
@sparse_loaded
jsr build_index ;simulate a disk write
sta drvr_req_cnt
stz drvr_req_cnt+2
clc
end_partial ;
rts
;==========================================================================
;Range_chk_eof This routine will make sure the write call will work with
; the three byte eof.
;
;Input: A = write_count
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; curr_eof ;the current eof
;
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
;==========================================================================
range_chk_eof
clc
adc curr_mark
lda #0000
adc curr_mark+2 ;check the high word
and #$FF00 ;clear the result
bne range_err
clc
rts
range_err
sec
rts
;==========================================================================
;write_data_blk This routine will write data blocks to disk. The code will
; determine if we can hold off writing to disk so we can
; gain performance by doing a multi-block transfer call.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; drvr_blk_num ;block number to write
; drvr_buf_ptr ;pointer to the data
;
;
;Output: A = error code if carry set otherwise undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
;==========================================================================
write_data_blk ;
lda seq_write_cnt ;do we have anything to write?
beq new_block ;no
lda drvr_blk_num ;is this a sequential block write?
dec a
cmp seq_write_blk
beq bump_seq ;bump the sequential counter
lda drvr_blk_num ;save block number to write
pha
lda drvr_buf_ptr
pha
lda drvr_buf_ptr+2
pha
jsr flush_blocks ;write any seq. blocks to disk
plx
stx drvr_buf_ptr+2
plx
stx drvr_buf_ptr
plx
stx drvr_blk_num
stz seq_write_cnt
bcs end_write_data ;error
new_block ;
inc seq_write_cnt ;advance the number of sequential blks
lda drvr_blk_num
sta seq_write_blk ;starting block for write call
jsr move_to_data_buf
lda drvr_buf_ptr
sta seq_write_start
lda drvr_buf_ptr+2
sta seq_write_start+2
end_write_data
lda #data_clean ;indicate that we have written block.
jsr clr_fcr_status
jsr standard_req ;set drvr_req_cnt to 512 bytes.
clc ;exit
rts
bump_seq
inc seq_write_cnt ;advance the block counter
inc seq_write_blk ;update new last sequential block
lda user_req_cnt+2
bne end_write_data
lda user_req_cnt
cmp #blk_size*2
bcs end_write_data
ldy #data_blk_num
lda drvr_blk_num
cmp [pro_fcr_ptr],y ;Should I Copy the data to the buffer
bne end_write_data ;Do not move the data.
jsr do_data_update
bra end_write_data
;
;This routine copies the user's data to the I/O Buffer
;
;input: A = block number
;
move_to_data_buf
ldy #data_blk_num
cmp [pro_fcr_ptr],y ;is block already loaded?
bne io_buf_ok ;don't worry about I/O buffer update
do_data_update
pei drvr_buf_ptr+2 ;address of user's data
pei drvr_buf_ptr
pei data_ptr+2 ;destination address
pei data_ptr
pea $0000 ;high word of count
pea blk_size ;number of bytes to send
pea move_sinc_dinc
jsl move_info
io_buf_ok rts
;==========================================================================
;flush_blocks: This routine will write out any data blocks that have not
; been written to disk. If will also make sure that the I/O
; buffer contains valid data.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = error code if carry set otherwise undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
;==========================================================================
EXPORT flush_blocks
flush_blocks ;
lda seq_write_cnt ;is there anything to write?
bne must_write
clc
rts
must_write ;
stz seq_write_cnt ;reset the sequential block counter
stz drvr_req_cnt ;setup the write count
asl a ;convert blocks to bytes
sta drvr_req_cnt+1
lsr a ;now get the starting block
dec a ;subtract one please
pha
sec
lda seq_write_blk
sbc 1,s
sta drvr_blk_num ;starting block number
pla ;fix the stack
lda seq_write_start
sta drvr_buf_ptr
lda seq_write_start+2
sta drvr_buf_ptr+2 ;pointer to the data
jsr write_with_mount
bcs flush_error
jsr standard_req
clc
flush_error ;
rts
;==========================================================================
;Set temp_ptr to the user's buffer pointer
;==========================================================================
user_to_temp ;
lda users_buf_ptr
sta temp_ptr ;set pointer to head of data
lda users_buf_ptr+2
sta temp_ptr+2
rts
users_to_drvrs ;
lda users_buf_ptr
sta drvr_buf_ptr ;set pointer to head of data
lda users_buf_ptr+2
sta drvr_buf_ptr+2
rts
;==========================================================================
;merge_data: This routine will add in the user's data into the I/O
; buffer.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; curr_mark ;current file marker
; pro_fcr_ptr ;points to file control record
; data_ptr ;points to data block in I/O buffer.
; temp_ptr ;points to user data
; user_req_cnt ;used by build_index
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
;
; b = k
;
; drvr_req_cnt ;equals bytes moved
;
;
;uses: all registers
; math_temp
;==========================================================================
merge_data ;
jsr build_index ;'A' = 1 to $1FF
sta math_temp ;save count
sta drvr_req_cnt ;simulate a write to disk.
dec a ;A,X,Y perserved by 'update..' routine
pha
tay ;'Y' = 0 to $1FE (move count)
clc
lda curr_mark
and #$01FF
adc 1,s ;make index from END of buffer
tax ;index into data buffer
pla ;fix the stack
longa off ;block after the merge
sep #$20
merge_loop ;
lda [temp_ptr],y ;pointer to user's data
phy ;save user index
txy
sta [data_ptr],y ;store in the I/O buffer
dex
ply
dey
bpl merge_loop ;I/O buffer updated.
longa on
rep #$20
clc
lda curr_mark ;save the mark
pha
adc math_temp
sta curr_mark
lda curr_mark+2
pha
adc #$0000
sta curr_mark+2
jsr clr_to_boundary ;Clear to blk boundary
pla
sta curr_mark+2
pla
sta curr_mark
rts
;==========================================================================
;See if we have to deal with a sparse block.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; curr_mark ;current file marker
; pro_fcr_ptr ;pointer to file control record
; temp_ptr ;pointer to user's data buffer
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = sparse
; 0 = not sparse
; b = k
;
;uses: all registers
; math_temp
;
;FUNCTION sparse():boolean ;
; BEGIN
; index := (curr_mark MOD 512) ;
; sparse := true ;
; While (index < block_size) and (index < user_req) and (sparse = true) DO
; BEGIN
; IF data[index] <> 0 THEN
; sparse := flase
; index := index + 1 ;
; END
; END
;==========================================================================
chk_sparse ;
jsr user_to_temp ;temp_ptr := user_buf_ptr
jsr build_index
tay ;number of bytes to scan
dey ;force 1 to $0200 to 0 to $01FF
longa off
sep #$20 ;short accum for test
scan_loop ;
lda [temp_ptr],y ;pointer to user's data
bne non_sparse
dey
bpl scan_loop
longa on
rep #$20 ;restore 16 bit mode
clc
rts ;user data is all sparse
non_sparse ;
longa on
rep #$20 ;restore 16 bit mode
sec
rts
build_index ;
lda curr_mark ;number of bytes to check in user buf.
and #$01FF ;0 to $1FF
pha
sec
lda #blk_size ;calc number of bytes to scan
sbc 1,s ;range $0001 to $0200
ply ;fix the stack
ldy user_req_cnt+2 ;how many bytes do we need to write?
bne full_scan
cmp user_req_cnt
bcc full_scan
lda user_req_cnt
full_scan ;
rts
ENDP
;==========================================================================
;update_mark_eof:
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; curr_mark ;current file marker
; curr_eof ;current end of file
; pro_fcr_ptr ;pointer to file control record
;
;Output: A = restored
; X = restored
; Y = restored
; P = nvmxdizc
; ..000...
;
; b = k
;==========================================================================
EXPORT update_mark_eof
update_mark_eof PROC
pha
phx
phy
lda curr_mark+2
cmp curr_eof+2
bcc end_update ;EOF still OK
beq chk_low_eof
bra update_the_eof
chk_low_eof ;
lda curr_mark
cmp curr_eof
beq end_update
bcc end_update ;EOF still OK
update_the_eof ;
lda curr_mark+2
sta curr_eof+2 ;save the high byte
lda curr_mark
sta curr_eof ;save the low byte
end_update ;
ply
plx
pla
rts
ENDP
;==========================================================================
;get_write_blk: This routine will return the block number for the new data.
; If the file has a block allocated then that block number
; is returned otherwise a new block is allocated for the file.
; If the file needs to be expanded then it will be.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = allocate a new block if block not allocated
; 0 = just return current block number. (0 or blk num)
; b = k
;
; curr_mark ;current file marker
; pro_fcr_ptr ;points to file control record
;
;Output: A = block number of error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;
;uses: all registers
; math_temp
;==========================================================================
EXPORT get_write_blk
get_write_blk PROC
php ;save check flag.
php
pla
sta alloc_chk_flag
lsr a ;are we looking for a
bcs do_allocation
jsr chk_allocation
rts
;
;If here then we need to allocate or return the current block.
;
do_allocation ;
clc
lda curr_mark
adc #$0001
sta data_eof
lda curr_mark+2
adc #$0000
sta data_eof+2
jsr eof_to_blks ;convert current position to blks.
lda data_storage ;storage type needed (seed,sapling,tree)
cmp #seedling_type
bne sapling_case
brl add_new_block ;just go and get the block please
sapling_case ;
cmp #sapling_type
bne tree_case
;
;IF new_type = sapling THEN
; IF old_type = seedling THEN
; seedling_to_sapling
; ELSE
; add_index
;
;
ldy #fcr_storage ;current storage type
lda [pro_fcr_ptr],y
cmp #seedling_type
bne add_new_block
jsr seed_to_sap ;convert a seedling to a sapling
bcc add_new_block
rts
;
;IF new_type = tree THEN
; IF old_type = seedling THEN
; BEGIN
; seedling_to_sapling
; sapling_to_tree
; END
; ELSE
; IF old_type = sapling THEN
; sapling_to_tree
; ELSE
; IF old_type = tree THEN
; BEGIN
; IF master_index[curr_mark] = nil THEN
; add_master_index
; add_index
; END
;
tree_case ;
ldy #fcr_storage ;current storage type
lda [pro_fcr_ptr],y
cmp #seedling_type
bne not_seed
jsr seed_to_sap ;convert seedling to a sapling
bcs abort_tree
jsr sapling_to_tree ;convert a sapling to a tree
bcc add_new_block
abort_tree rts
not_seed ;
cmp #sapling_type ;is this a sapling file
bne not_sapling
jsr sapling_to_tree ;convert a sapling to a tree
bcs abort_tree
bra add_new_block
not_sapling ;
lda curr_mark+2 ;see if the index block is allocated
lsr a
ldx master_ptr
ldy master_ptr+2
jsr get_index_blk ;get the index block out of master blk
tax
bne add_new_block ;we do not need to add an index block
jsr tree_to_tree ;add a new index block
bcs abort_tree
;==========================================================================
;Now lets see if we need to add a new block or return the current block.
;==========================================================================
add_new_block ;
jsr calc_free_blks ;make sure that we have a block avail.
bcs abort_tree ;just exit
tax
bne we_have_room
brl out_of_room
we_have_room ;
ldy #master_blk_num ;see if we should even have looked at
lda [pro_fcr_ptr],y
beq no_master ;there is no master block for this file
lda curr_mark+2 ;see if we need the master index block
lsr a
ldx master_ptr ;get the block number from index block
ldy master_ptr+2
jsr get_index_blk ;A = block number
tax
bne cont01 ;there is an index block
ldy #fcr_status ;see if old index needs to be flushed
lda [pro_fcr_ptr],y
and #index_dirty
beq no_index_flush
jsr flush_index
bcs abort_tree ;exit
no_index_flush ;
ldx master_ptr
ldy master_ptr+2
lda curr_mark+2
lsr a ;now build a new index block
jsr new_block
bcs abort_tree ;sorry major problem
pha ;save the block number
lda #master_dirty+index_dirty+file_dirty
jsr set_fcr_status ;indicate master block dirty
ldx index_ptr ;clear the new buffer
ldy index_ptr+2
jsr clear_index
pla
ldy #index_blk_num
sta [pro_fcr_ptr],y
cont01 ;
ldy #index_blk_num
cmp [pro_fcr_ptr],y ;is the block loaded?
beq no_master ;no problem
sta key_block ;save the block wanted.
jsr flush_index ;write any dirty blocks to disk
bcs end_write_blk ;major problems
lda key_block ;load in the index block please
pha
jsr load_index
plx ;restore block number
bcs end_write_blk
txa
ldy #index_blk_num
sta [pro_fcr_ptr],y
no_master ;
lda curr_mark+1
lsr a
and #$00FF
ldx index_ptr ;get the block number from index block
ldy index_ptr+2
jsr get_index_blk ;A = block number
tax
bne have_blk
ldy #fcr_status ;see if data block needs to be written
lda [pro_fcr_ptr],y
and #data_dirty
beq data1_clean
jsr flush_data_blk
bcs end_write_blk
data1_clean ;
ldx index_ptr
ldy index_ptr+2
lda curr_mark+1
lsr a ;now build a new index block
and #$00FF
jsr new_block
bcs end_write_blk ;sorry major problem
pha ;save the block number
ldy #data_blk_num
sta [pro_fcr_ptr],y ;save the new block number
lda #index_dirty+file_dirty+data_dirty
jsr set_fcr_status ;indicate master block dirty
pla
have_blk ;
jsr load_data
bcs end_write_blk
lda drvr_blk_num
clc
end_write_blk ;
rts
EXPORT load_data
load_data ; ;input A= block to load
ldy #data_blk_num
cmp [pro_fcr_ptr],y ;is block already loaded?
beq exit_load_data
pha ;save the block to load
ldy #fcr_status
lda [pro_fcr_ptr],y
and #data_dirty
beq dont_update
jsr flush_data_blk
bcs end_load_data
dont_update ;
lda data_ptr
sta drvr_buf_ptr
lda data_ptr+2
sta drvr_buf_ptr+2
lda 1,s ;get the block number to load
sta drvr_blk_num
jsr read_with_mount ;read in the new data block
bcs end_load_data
ldy #data_blk_num ;update current loaded block
lda drvr_blk_num
sta [pro_fcr_ptr],y
pla
exit_load_data ;
clc
sta drvr_blk_num
rts
end_load_data ;
plx ;fix the stack please
sec
rts
;==========================================================================
;new_block: This routine will allocate a new block from the system
; and add the block number in the index block pointed to
; by the X and Y registers.
;
;Input: A = index
; X = low word pointer to index block
; Y = high word pointer to index block
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = block number allocated or error code
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;
;uses: all registers
; temp4_ptr
;==========================================================================
new_block ;
stx temp4_ptr ;pointer to the index block
sty temp4_ptr+2
pha ;save the index
jsr calc_free_blks ;make sure that we a free block
bcs add_failed
tax
beq to_no_room
lda #$0001 ;allocate one block
ldx #$0000 ;start at block zero
jsr find_free_blks
bcs add_failed
phx ;save the block number
jsr bump_blks_used
lda 3,s ;get the index please
tay ;save the low index
ora #$0100
tax ;save the high index
pla
longa off
sep #$20 ;8 bit mode
sta [temp4_ptr],y
xba
txy
sta [temp4_ptr],y
longa on
rep #$20 ;16 bit mode
xba
plx ;fix the stack
clc
rts
add_failed plx ;fix the stack
rts
to_no_room pla ;fix the stack
brl out_of_room
bump_blks_used ;
ldy #fcr_blks_used
lda [pro_fcr_ptr],y
inc a ;advance the blocks used count
sta [pro_fcr_ptr],y
lda #dirEntry_dirty ;the directory entry needs updating when flushed
jsr set_fcr_status
rts
;==========================================================================
;get_index_blk: This routine will load a block number out of an index block
;
;Input: A = index
; X = low word pointer to index block
; Y = high word pointer to index block
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = block number
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;uses: all registers
; temp_ptr
;==========================================================================
EXPORT get_index_blk
get_index_blk ;
stx temp_ptr
sty temp_ptr+2
tax ;low word index
ora #$0100 ;high word index
tay
longa off
sep #$20 ;8 bit accum
lda [temp_ptr],y ;get the high word block number
xba
txy
lda [temp_ptr],y ;get the low word block number
longa on
rep #$20 ;16 bit accum
rts
;==========================================================================
;Convert a seedling file into a sapling file.
;==========================================================================
seed_to_sap ;
jsr flush_blocks ;flush anydata still around
bcc do_sap
rts
do_sap ;
jsr calc_free_blks ;see how many blocks we have
tax
bne lots_of_room
brl out_of_room ;sorry bud not today
lots_of_room ;
lda #$0001 ;go get me a new block
ldx #$0000 ;start looking at block zero
jsr find_free_blks
bcs end_to_sap
txa ;new key block
pha
ldy index_ptr
sty temp4_ptr
ldy index_ptr+2
sty temp4_ptr+2
dec a ;see if we should flip data and index
ldy #fcr_key_blk ;
cmp [pro_fcr_ptr],y
beq do_flip
txa
ldy #index_blk_num
sta [pro_fcr_ptr],y ;save the new index block number
jsr bump_blks_used
ldy #fcr_key_blk ;data block address
lda [pro_fcr_ptr],y
ldy #$0000 ;store as first entry in index block
jsr add_index
lda #index_active+index_dirty+file_dirty
jsr set_fcr_status ;mark index block active
pla ;restore key block
ldx #sapling_type
jsr update_new_type
rts
end_to_sap ;
rts
do_flip ;
ldy #fcr_status ;see if the data block is dirty
lda [pro_fcr_ptr],y
and #data_dirty
beq onward
jsr flush_data_blk
bcc onward
plx ;fix the stack!!!!
rts
onward ;
ldy #fcr_key_blk ;load in the data block
lda [pro_fcr_ptr],y
sta drvr_blk_num
ldy #data_blk_num
sta [pro_fcr_ptr],y ;update current data block
lda data_ptr
sta drvr_buf_ptr
lda data_ptr+2
sta drvr_buf_ptr+2
jsr read_with_mount ;read in the data block
bcc onward2
plx ;fix the stack
rts
onward2 ;
pla ;get the new block number
ldy #data_blk_num
sta [pro_fcr_ptr],y ;now the data block has changed
ldy #$0000 ;store as first entry in index block
jsr add_index
lda #index_active+index_dirty+file_dirty+data_dirty
jsr set_fcr_status ;mark index block active
jsr bump_blks_used
ldy #fcr_key_blk
lda [pro_fcr_ptr],y
ldy #index_blk_num
sta [pro_fcr_ptr],y ;now the index is new
ldx #sapling_type
jsr update_new_type
rts
out_of_room ;
lda #volume_full
sec
rts
sapling_to_tree ;
jsr calc_free_blks ;see how many blocks we have
tax
beq out_of_room ;sorry bud not today
lda #$0001 ;go get me a new block
ldx #$0000 ;start looking at block zero
jsr find_free_blks
bcs end_to_tree
txa ;new key block
pha
ldy #master_blk_num
sta [pro_fcr_ptr],y ;save the master index blk num
jsr bump_blks_used
lda master_ptr
sta temp4_ptr
lda master_ptr+2
sta temp4_ptr+2
ldy #fcr_key_blk ;current data block
lda [pro_fcr_ptr],y
ldy #$0000 ;store as first entry in index block
jsr add_index
lda #master_active+master_dirty+file_dirty
jsr set_fcr_status ;mark index block active
pla ;restore key block
ldx #tree_type
jsr update_new_type
end_to_tree ;
rts
tree_to_tree ;
jsr calc_free_blks ;see how many blocks we have
tax
beq out_of_room ;sorry bud not today
lda #$0001 ;go get me a new block
ldx #$0000 ;start looking at block zero
jsr find_free_blks
bcs end_to_tree
phx ;save the block number
lda master_ptr
sta temp4_ptr
lda master_ptr+2
sta temp4_ptr+2
jsr bump_blks_used ;must not use X register
lda curr_mark+2
lsr a
tay ;index into master block
txa ;new index block
jsr add_index
lda #master_dirty+file_dirty
jsr set_fcr_status ;indicate master index block is dirty
ldy #fcr_status ;see if current index block is dirty
lda [pro_fcr_ptr],y
and #index_dirty
beq mister_clean
jsr flush_index
bcc mister_clean
plx ;fix the stack
rts
mister_clean ;
ldy #index_blk_num ;the current index block
pla ;restore new index block number
sta [pro_fcr_ptr],y
lda #index_dirty+file_dirty
jsr set_fcr_status ;indicate that this block is dirty
ldx index_ptr
ldy index_ptr+2 ;clear the current index
jsr clear_index
clc
rts
EXPORT clear_index
clear_index ;
stx temp4_ptr ;zero out the buffer pointed to by
sty temp4_ptr+2 ;X and Y
ldy #$01FE
lda #$0000
clr_index sta [temp4_ptr],y
dey
dey
bpl clr_index
rts
;==========================================================================
;update_new_type:
; This routine will update the FCR to reflect the new storage
; type, and key block for a file
;
;Input: A = new key block
; X = new storage type
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; pro_fcr_ptr ;points to file control record
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;
;uses: all registers
;==========================================================================
update_new_type ;
pha ;save new key block
ldy #fcr_key_blk
sta [pro_fcr_ptr],y
ldy #fcr_entry_type
lda [pro_fcr_ptr],y ;is this a resource type?
cmp #resource_type
beq is_resource
txa ;save the new storage type
sta [pro_fcr_ptr],y
lda 1,s ;save the new key block
ldy #fcr_file_id
sta [pro_fcr_ptr],y
is_resource ;
pla ;fix the stack
ldy #fcr_storage ;store the new storage type
txa
sta [pro_fcr_ptr],y
jsr flush_file ;force directory update.
rts
ENDP
;==========================================================================
;chk_allocation:
; This routine will return the block number of a data block
; if the file contains a data block
;
;Input: A = undefined
; X = undefin
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; pro_fcr_ptr ;points to file control record
;
;Output: A = block number if 'z' = 0
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error and 'z' flag valid
; 1 = no block allocated
; 0 = A = block number
; b = k
;
;
;uses: all registers
;==========================================================================
;==========================================================================
;IF (curr_mark = tree) AND (storage <> tree) THEN
; no_block allocated
;
;==========================================================================
EXPORT chk_allocation
chk_allocation PROC ;see if there is block allocated.
lda curr_mark+2 ;see if we need the master index block
lsr a
ldx master_ptr ;get the block number from index block
ldy master_ptr+2
jsr get_index_blk ;A = block number
tax
bne cont01 ;there is an index block
;
;IF (type = tree) AND (index = sparse) THEN block_not_allocated
;
chk_tree ;
ldy #fcr_storage
lda [pro_fcr_ptr],y
cmp #tree_type ;does this puppy even have a master blk?>
bne chk_sapling ;there is no master index block
clc
rts
;
;IF (type <> tree) AND (curr_mark = tree) THEN block_not_allocated
;
chk_sapling ;
lda curr_mark+2 ;is the mark in tree range
lsr a
beq index_loaded ;mark not in tree range
bra no_block
cont01 ;
ldy #index_blk_num
cmp [pro_fcr_ptr],y ;is the block already loaded?
beq index_loaded
pha ;save the block number
ldy #fcr_status
lda [pro_fcr_ptr],y
and #index_dirty ;is the current index dirty?
beq index1_clean
jsr flush_index
bcc index1_clean
plx
rts
index1_clean ;
pla ;retore block number
ldy #index_blk_num
sta [pro_fcr_ptr],y ;save the new block please
jsr load_index
bcc index_loaded
rts
index_loaded ;
lda curr_mark+1
lsr a
and #$00FF
ldx index_ptr ;get the block number from index block
ldy index_ptr+2
jsr get_index_blk ;A = block number
tax
bne exit_chk_alloc
;
;IF (type = sapling) AND (data = sparse) THEN block_not_allocated
;
ldy #fcr_storage
lda [pro_fcr_ptr],y
cmp #sapling_type ;does this puppy even have an index blk
bcc chk_seedling ;there is no index block
txa ;reset the flag please
exit_chk_alloc ;
clc
rts
chk_seedling
lda curr_mark+1
lsr a
and #$00FF
beq get_key_blk ;mark < 512
no_block
ldx #$0000
sep #$02 ;set the 'Z' flag
clc
rts
get_key_blk ;
ldy #fcr_key_blk
lda [pro_fcr_ptr],y
tax ;setup the x register with the block number
clc
rts
EXPORT load_index
load_index
;enter with A = block number
;next 3 lines added 8/6/90 by Monte Benaresh
pha ;save block # for a moment
jsr standard_req ;set drvr_req_cnt to $200
pla ;restore block number
sta drvr_blk_num
lda index_ptr
sta drvr_buf_ptr
lda index_ptr+2
sta drvr_buf_ptr+2
jsr read_with_cache
rts
ENDP
;==========================================================================
;Clr_to_boundary This routine will clear data from the current EOF
; to the end of the block.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; curr_mark ;current file marker
; curr_eof ;current end of file
; pro_fcr_ptr ;points to file control record
; data_ptr ;points to data block in I/O buffer.
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
;
; b = k
;
;
; IF (eof >= (curr_mark AND $FE00 + 512)) THEN
; EXIT(clr_to_boundary)
; IF eof > curr_mark THEN
; clear_index := eof
; ELSE
; clear_index := curr_mark
; clear_block
;==========================================================================
longa on
longi on
EXPORT clr_to_boundary
clr_to_boundary PROC
lda curr_eof
pha
lda curr_eof+2 ;save the current end of file
pha
lda curr_mark
pha ;save the current mark
lda curr_mark+2
pha
lda curr_mark ;if block aligned then exit
and #$01FF
bne not_aligned
brl exit_clr ;we do not have to clear anything
not_aligned
;
;curr_mark := ((curr_mark and $FE00) + 512) ;
;
clc
lda curr_mark ;move to a block boundary
and #$FE00
adc #blk_size ;add in 512 bytes
sta curr_mark
lda curr_mark+2
adc #$0000
sta curr_mark+2
;
; IF (eof >= (curr_mark AND $FE00 + 512)) THEN
; EXIT(clr_to_boundary)
;
lda curr_eof+2
cmp curr_mark+2
beq chk_low
bcs exit_clr ;EOF not in this block
bcc second_if ;continue process
chk_low ;
lda curr_eof
cmp curr_mark
bcs exit_clr ;EOF not in this block
;
; IF EOF < curr_mark AND $FE00 THEN
; clear_index := curr_mark
;
second_if ;
lda 3,s ;get the low word of the mark
and #$FE00
sta curr_mark
lda curr_eof+2
cmp 1,s ;check high word
beq second_low
bcs third_if ;EOF is in this block
bcc use_mark ;EOF not in this block
second_low ;
lda curr_eof
cmp curr_mark
bcs third_if ;EOF is in the block
bcc use_mark ;EOF not in this block
;
; IF eof > curr_mark THEN
; clear_index := eof
;
third_if ;
lda curr_eof+2 ;is eof > curr_mark
cmp 1,s ;high word of curr_mark
beq third_low
bcs use_eof ;clear from EOF
bcc use_mark ;clear from current mark
third_low ;
lda curr_eof
cmp 3,s
bcs use_eof ;use the eof as the value
use_mark ;
lda 3,s ;get the low word of current mark
and #$01FF ;clear garbage
beq exit_clr ;do not clear the buffer
bra do_clear
use_eof ;
lda curr_eof
and #$01FF
beq exit_clr ;do not clear the buffer
do_clear
tay ;make an index into block
iny
cpy #blk_size
beq exit_clr
tya ;can we clear the block fast
lsr a
bcc clear_it_fast
lda #$0000 ;clear value
longa off
sep #$20 ;8 bit mode
bra enter ;start the loop
clear_it
sta [data_ptr],y
iny
enter
cpy #blk_size ;are we done
bcc clear_it
longa on
rep #$20
bra exit_clr
clear_it_fast
lda #$0000
bra enter_fast
fast_loop
sta [data_ptr],y
iny
iny
enter_fast
cpy #$0200 ;are we done
bcc fast_loop
exit_clr ;
pla ;restore the curr_mark
sta curr_mark+2
pla
sta curr_mark
pla
sta curr_eof+2 ;restore the current end of file
pla
sta curr_eof
lda #data_dirty+file_dirty
jsr set_fcr_status
rts
ENDP
;==========================================================================
;Close: This routine will close a file.
;
;Created: aug 04, 1987
;Modified: aug 04, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; fill_io_buf
;
;Call Format:
;
;Class 0 Reference Number (2 Bytes)
;
;Class 1 PCount (2 Bytes)
; Reference Number (2 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT close
close PROC
;==========================================================================
;First preprocess the call to setup the needed parameters.
;==========================================================================
jsr setup_params
dec close_flag ;call slug ripple please
jsr flush_file ;force data to the disk
bcc flushed
jmp main_exit
flushed ;
jsr remove_fcr
jmp main_exit
ENDP
;==========================================================================
;flush: This routine will flush a file.
;
;Created: aug 04, 1987
;Modified: aug 04, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; fill_io_buf
;
;Call Format:
;
;Class 0 Reference Number (2 Bytes)
;
;Class 1 PCount (2 Bytes)
; Reference Number (2 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT flush
flush PROC
;==========================================================================
;First preprocess the call to setup the needed parameters.
;==========================================================================
jsr setup_params
dec close_flag ;call slug_ripple please
; New code added March 27, 1990 to support fast/slow flush,
; by Monte Benaresh, based on Andy Stadler's proposed
; changes.
lda pcount ;check number of params given by caller
cmp #2 ;did he specify type of flush?
bcc slow_flush ;too few, default to full flush
choose_flush ;caller specified type of flush
; valid flush types are:
; $0000 = full flush, data and directory blocks
; $8000 = fast flush, data blocks only
ldy #4 ;offset to flush type in param block
lda [param_blk_ptr],y ;get flush type
asl a ;get high bit
beq flush_type_ok ;low bits must be 0!
bad_flush_type ;invalid value for flush type
lda #parm_range_err
sec
bra flush_done ;return with error
flush_type_ok ;get here with high bit of flush type in carry
bcc slow_flush ;flush type was $0000
fast_flush ;flush type was $8000
jsr do_fast_flush ;write out all dirty data blocks, dirty
; bitmap blocks, and the directory entry
; only if it's been changed (mod date doesn't count!)
bra flush_done ;handle error elsewhere
slow_flush
jsr flush_file ;force data to the disk
flush_done
jmp main_exit
ENDP
;==========================================================================
;Set_mark: This routine will set a file position marker.
;
;Created: may 23, 1987
;Modified: may 23, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; fill_io_buf
;
;Call Format:
;
;Class 0 Reference Number (2 Bytes)
; File Position (4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Reference Number (2 Bytes)
; Base (2 Bytes)
; 00 = Absolute
; 01 = Backward From EOF
; 02 = Forward From Current Mark
; 03 = Backward From Current Mark
; Displacement (4 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT set_mark
set_mark PROC
;==========================================================================
;First preprocess the call to setup the needed parameters.
;==========================================================================
stz base
jsr setup_params
beq class0
;==========================================================================
;Fall thru if this is a class1 call
;==========================================================================
ldy #pblk_16_base
jsr check_base
ldy #pblk_16_disp
bra main_entry
class0 ;
;==========================================================================
;If this is a class zero call then setup an index to the file position
;and continue on.
;==========================================================================
ldy #$0002 ;index to displacement
main_entry ;
;==========================================================================
;Save the user's requested displacement
;==========================================================================
lda [my_pblk_ptr],y
sta displacement
iny
iny
lda [my_pblk_ptr],y
sta displacement+2
;==========================================================================
;Setup 'curr_eof' and 'curr_mark'
;
; IF (curr_mark < 00) or (curr_mark > EOF) THEN
; BEGIN
; Error:= position_out_of_range_error
; EXIT(Set_Mark)
; END
;==========================================================================
jsr calc_curr_mark
bcc set_the_mark
lda #out_of_range
set_mark_exit
jmp main_exit
set_the_mark
;==========================================================================
;refill I/O buffer with the new data blocks needed.
;==========================================================================
jsr fill_io_buf
bcs set_mark_exit
;==========================================================================
;go and update the FCR with the new curr_mark.
;==========================================================================
jsr save_curr_mark
clc
;==========================================================================
;Exit all happy since the call was successful
;==========================================================================
bra set_mark_exit
ENDP
;==========================================================================
;check_base This routine will verify that the base is within range
;
;Created: may 23, 1987
;Modified: may 23, 1987
;Author: Rob Turner
;
;
;Enter: jsr
;
;Input: A = undefined
; X = undefined
; Y = offset to base field
; P = nvmxdizc
; ..000...
; b = k
;
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
; BASE (contains value of the base)
;
;Uses: All registers
;
;==========================================================================
longa on
longi on
EXPORT check_base
check_base PROC
lda [my_pblk_ptr],y
cmp #max_base+1
bcs range_error
sta base
rts
EXPORT range_error
range_error ;
lda #parm_range_err
jmp main_exit
ENDP
;==========================================================================
;calc_curr_mark This routine will calculate the current mark for a file
; given BASE and DISPLACEMENT as input.
;
;Created: may 23, 1987
;Modified: may 23, 1987
;Author: Rob Turner
;
;
;Enter: jsr
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; BASE (base from param block)
; $00 = Absolute
; $01 = Backward From EOF
; $02 = Forward From Current Mark
; $03 = Backward From Current Mark
;
; DISPLACEMENT (displacement from param block)
; pro_fcr_ptr (pointer to my data in FCR
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; .<<000..1 = error if out of range
; | 0 = no error
; |
; |__ 1 = error out of range any cases
; 0 = result greater then current EOF
;
; b = k
;
; curr_mark (contains correct position for mark)
;
;Uses: All registers
;
;==========================================================================
longa on
longi on
EXPORT calc_curr_mark
calc_curr_mark PROC
;==========================================================================
;Setup the current EOF
;==========================================================================
jsr setup_curr_eof
;==========================================================================
;Setup the current file marker.
;==========================================================================
jsr setup_curr_mark
;==========================================================================
;Now we need to decide which way we need to move the mark.
;==========================================================================
lda base
beq absolute
dec a
beq back_from_eof
dec a
beq forward_mark
;==========================================================================
;If we fall thru to here then we need to set the mark backward from the
;current mark.
;==========================================================================
backward_mark ;
sec
lda curr_mark
sbc displacement
sta curr_mark
lda curr_mark+2
sbc displacement+2
sta curr_mark+2
bcs mark_ok
bad_range ;
sep #carry+overflow ;set both carry and overflow flags
rts ;exit with carry set to indicate error
;==========================================================================
;User wants to set the mark backward from the current end of file
;==========================================================================
back_from_eof ;
sec
lda curr_eof
sbc displacement
sta curr_mark
lda curr_eof+2
sbc displacement+2
sta curr_mark+2
bcc bad_range
mark_ok ;
clc
rts
;==========================================================================
;Set the mark forward from the current mark.
;==========================================================================
forward_mark ;
clc
lda curr_mark
adc displacement
sta curr_mark
lda curr_mark+2
adc displacement+2
sta curr_mark+2
bcc check_range
greater_EOF ;
clv ;mark greater then current EOF!
rts ;bad mark bud!
;==========================================================================
;Set the file marker to an absolute position.
;==========================================================================
absolute ;
lda displacement
sta curr_mark
lda displacement+2
sta curr_mark+2
check_range ;
;==========================================================================
;Make sure the curr_mark =< EOF.
;==========================================================================
lda curr_mark+2
cmp curr_eof+2
beq check_low_word ;more checking to do
bcs greater_eof ;value is greater then EOF
rts ;carry clear no problem
check_low_word ;
lda curr_mark
cmp curr_eof
beq good_mark
bcs greater_eof ;value is greater then EOF
good_mark ;
clc
rts
ENDP
;==========================================================================
;Get_mark: This routine will return the file position marker.
;
;Created: may 23, 1987
;Modified: may 23, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: All registers
;
;Call Format:
;
;Class 0 Reference Number (2 Bytes)
; File Position (4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Reference Number (2 Bytes)
; File Position (4 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT get_mark
get_mark PROC
;==========================================================================
;Call standard setup routine.
;==========================================================================
jsr setup_params
;==========================================================================
;Get the current file marker out of the FCR data area
;==========================================================================
ldy #fcr_curr_mark
lda [pro_fcr_ptr],y
tax ;save low word
iny
iny
lda [pro_fcr_ptr],y
;==========================================================================
;Store the current file position into the user's parameter block.
;==========================================================================
ldy #pblk_17_pos+2 ;file position
sta [my_pblk_ptr],y
dey
dey
txa
sta [my_pblk_ptr],y
clc
jmp main_exit
ENDP
;==========================================================================
;Set_eof: This routine will set the EOF of a file.
;
;Created: jun 15, 1987
;Modified: jun 15, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: All registers
;
;Call Format:
;
;Class 0 Reference Number (2 Bytes)
; File Position (4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Reference Number (2 Bytes)
; Base (2 Bytes)
; 00 = Absolute
; 01 = Backward From EOF
; 02 = Forward From Current Mark
; 03 = Backward From Current Mark
; Displacement (4 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT set_eof
set_eof PROC
stz base ;set default to absolute
stz key_blk_changed
;==========================================================================
;Call standard setup routine.
;==========================================================================
jsr setup_params
beq class0 ;this is an old style call
;==========================================================================
;Now lets go and setup the base parameter please
;==========================================================================
ldy #pblk_18_base
jsr check_base
ldy #pblk_18_disp
bra main_entry
Class0 ;
ldy #pblk_18_odisp
main_entry ;
lda [my_pblk_ptr],y
sta displacement
iny
iny
lda [my_pblk_ptr],y
sta displacement+2
;==========================================================================
;make sure the file is write enabled.
;==========================================================================
ldy #fcr_access
lda [my_fcr_ptr],y
and #ClrAllButAccess
cmp #write_access
bcs set_eof_ok
lda #invalid_access
bra set_eof_error
set_eof_ok ;
jsr chk_disk_protect
;==========================================================================
;Get the current end of file out of the FCR data area and call routine
;that will calculate the new eof. The new value for the EOF is returned
;in 'curr_mark'
;==========================================================================
;brk $B2
jsr calc_curr_mark
php ;save results
lda curr_mark
sta displacement
lda curr_mark+2
sta displacement+2
plp
;Following code changed 7/21/90 by Monte Benaresh
;Since setup_real_eof finds the last physically allocated EOF of
;the file, and since that number can be greater than the logical
;EOF (when pre-allocated blocks exist beyond the logical EOF) or
;less (when a trailing portion of the file is entirely sparse),
;we need to call setup_real_eof before deciding whether we need
;to cut blocks.
;bcc less_or_equal ;new EOF is =< current EOF
;bvc eof_greater ;new EOF is > current EOF
bcc calc_ok ;new EOF is =< current EOF
bvc calc_ok ;new EOF is > current EOF
;The following error code was changed from "parm_range_err"
;to "out_of_range". 1/22/91 CAE
bad_eof ;
lda #out_of_range ;position out of range
set_eof_error
sec
set_eof_exit
jmp main_exit
calc_ok
;next call added 8/6/90 by Monte Benaresh to fix bug
;introduced by change of 7/21/90
jsr flush_file ;force any dirty blocks to disk
bcs set_eof_error ;Major problems bud.
jsr setup_real_eof ;generate EOF based on allocation not logical
bcs set_eof_error ;Major problems bud.
;code added 7/21/90 by Monte Benaresh (see note above)
lda curr_eof+2 ;this points to last physical allocation
cmp displacement+2 ;determine whether we need to cut blocks
bcc eof_greater
bne less_or_equal ;this line added 8/8/90 by Monte Benaresh
lda curr_eof
cmp displacement
bcs less_or_equal
;
eof_greater
lda displacement+2
and #$FF00
bne bad_eof
save_eof
jsr update_fcr_eof
lda key_blk_changed ;Did the Key Block for the file change?
beq @no_key_changed
jsr flush_file ;update both the Bitmap and Directory please
bra set_eof_exit
@no_key_changed
jsr flush_io_buffer ;update the disk please
bra set_eof_exit ;we are out of here
;If we get here we know that the new EOF is equal to (easy) or less then
;(hard) the current ("real") EOF. If the EOF is less then the current eof then
;we must release blocks from the disk.
less_or_equal
jsr flush_file ;force any dirty blocks to disk
bcs set_eof_error ;Major problems bud.
;jsr setup_real_eof ;generate EOF based on allocation not logical
;bcs set_eof_error ;Major problems bud.
;last 2 lines not needed now -- Monte Benaresh, 7/21/90
lda displacement+2
cmp curr_eof+2
bne must_be_less ;the new eof is less then old eof
lda displacement
cmp curr_eof
;bne must_be_less
beq save_eof ;same as what caller wants, but save this EOF
;since it was calculated by setup_real_eof
;Monte Benaresh -- 7/21/90
;next 2 removed 7/21/90
;clc
;bra set_eof_exit ;nothing to do
must_be_less
;If we get here we know the new EOF is less then the old EOF so we must
;do the cut back.
;convert current eof into relative block number
;last_block := ((current_eof-1) DIV 512)
;last_block = $0000 to $7FFF
lda curr_eof
ora curr_eof+2 ;if zero DO NOT subtract one
beq no_sub1
lda curr_eof
bne ar1
dec curr_eof+2
ar1 dec curr_eof
no_sub1
lda curr_eof+1 ;convert EOF to blocks
lsr a
sta last_blk ;save block number of last block
beq save_eof ;file is already a seedling
;convert new_eof into relative block number
;new_last := ((displacement-1) DIV 512)
;new_last = $0000 to $7FFF
lda displacement
sta tran_cnt
lda displacement+2
sta tran_cnt+2
lda tran_cnt
ora tran_cnt+2 ;if zero DO NOT subtract one
beq no_sub
lda tran_cnt
bne ar2
dec tran_cnt+2
ar2 dec tran_cnt
no_sub
lda tran_cnt+1 ;convert EOF to blocks
lsr a
sta new_last ;save block number of last block
;WHILE last_blk <> new_last DO
while_loop
lda last_blk
cmp new_last
beq save_eof ;just exit with new EOF
;We now need to remove the last block in the file
jsr cut_block ;reduce blocks by one
bcs update_and_exit ;error occured
dec last_blk
bra while_loop
update_and_exit
pha
jsr update_fcr_eof
pla
sec
brl set_eof_error
;Update the FCR with the new EOF value. Also mark the FCR dirty
update_fcr_eof
ldy #fcr_eof
lda displacement ;store the NEW eof in the FCR
tax
sta curr_eof
sta [pro_fcr_ptr],y
iny
lda displacement+1
sta [pro_fcr_ptr],y
sta curr_eof+1
ldy #fcr_curr_eof ;update working EOF as well
txa
sta [pro_fcr_ptr],y
iny
lda displacement+1
sta [pro_fcr_ptr],y
ldy #fcr_status
lda [pro_fcr_ptr],y ;update file status word
ora #file_dirty ;we need to write to disk on close
sta [pro_fcr_ptr],y
;IF the new EOF is =< curr_mark THEN
; curr_mark := EOF
jsr setup_curr_mark
jsr fill_io_buf ;added 1/23/90 to fix "Bredon" sparse file shrink bug
bcc @filledUp
rts
@filledUp
lda curr_mark+2
cmp displacement+2
beq check_lower
bcs adjust_mark
bra adjust_buffer ;current_mark < new EOF
check_lower
lda curr_mark
cmp displacement
beq no_adjust
bcs adjust_mark
no_adjust
bra adjust_buffer
adjust_mark
lda displacement
sta curr_mark
lda displacement+2
sta curr_mark+2
jsr save_curr_mark ;update the FCR
adjust_buffer
jsr clr_to_boundary
clc
rts
;==========================================================================
;Cut_block: This routine will remove the last block from the file.
;
;Input: last_blk (contains relative block number to delete. 0 thur n)
;
;output: A = error code if carry set.
;
; WARNING: last_blk must not point beyond the current
; structure of the file, even though that block num may be within
; the file's EOF. This could happen if a file has a trailing
; portion which is all sparse from a position <128K to a
; position >128K. In this case the file is stored as a
; sapling, not a tree, but owns a sparse area that is
; logically within the "tree" range. In such a case, last_blk
; must never be in that range when entering this routine.
;
cut_block
;The first thing we do is to convert last_blk into a mark value.
stz curr_mark
stz curr_mark+2
lda last_blk
asl a
sta curr_mark+1
jsr load_indexes ;load the I/O buffer correctly
bcc @no_error
rts
@no_error
;After we call data_blk_num we know that drvr_blk_num contains the data
;block that is to be removed from the file.
jsr get_data_num ;setup drvr_blk_num
ldx drvr_blk_num
beq gone ;sparse block
jsr dealloc_block ;remove the block please
bcc dec_gone
rts ;return error
dec_gone
jsr dec_blk_count ;blks_used := blks_used-1
gone
;IF (index_block = true) AND (deleted_entry > 1) THEN
; BEGIN
;if this is anything but a seedling, we must check whether it's time toÉ
;Érelease an index block and possibly change storage type
ldy #fcr_storage ;IF (index_block = true)
lda [pro_fcr_ptr],y
cmp #sapling_type
bcs index_true ;index_block = TRUE
rts ;we MUST be done
index_true
lda curr_mark+1 ;AND (deleted_entry > 1) THEN
lsr a
and #$00FF
cmp #$0002
bcc first_ELSE ;deleting first or second entry
; zero_index(deleted_blk) ; ;clear the entry from index block
; index_dirty := true ; ;and mark index dirty
;fall through if not on 1st or 2nd entry
not_sapling
jsr zero_one_index ;zero an entry in index blk
update_eof
clc
rts
; END
;ELSE
; IF (index_block = true) and (one_entry = true) and (storage = sapling) THEN
; BEGIN
; convert_sapling_to_seedling
; END
first_else ;we know index_block = true
cmp #$0001 ;did we just delete entry #0?
bcc second_else ;check tree case
ldy #fcr_storage
lda [pro_fcr_ptr],y
cmp #sapling_type ;(stoarge = sapling)?
bne not_sapling
jsr zero_one_index
jsr sapling_seed ;convert the sapling file to seedling
bcc update_eof
rts
; ELSE
; IF (index_block = true) and (one_entry = true) and (storage = tree) THEN
; BEGIN
second_else ;we are deleting the 1st entry in index blk
jsr remove_index ;remove the index and update master
bcc cont01
rts
cont01 ;did we just eliminate 2nd index block?
lda curr_mark+2
lsr a
and #$007F ;see if we are going to be a sapling
cmp #$0001 ;see if there is only one entry left
bne update_eof ;no conversion yet
jsr tree_sapling
bcc update_eof
rts
;===============END of CUT_BLOCK
;==========================================================================
;This routine will setup curr_eof with the EOF based on blocks allocated
;to the file instead of logical EOF. For example, an file can have an
;EOF of 0, but have hundreds of blocks allocated to it. This happens
;when a file is created with a request to pre-allocate space.
;Diagram: (A) (B) (C)
;
; | | $00 to $7F | $00 to $FF | $000 to $1FF |
; | | | | |
; | MUST BE ZERO |<- master ->|<- index ->|<- Byte index ->|
; |_______________|_____________|_______________|_________________|
; | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
; |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
; | | | | |
; 3 2 2 4 1 6 0 8 0 0
;
; WARNING: Index blocks must be clean because they will be
; overwritten!
;
;==========================================================================
setup_real_eof
stz new_eof
stz new_eof+2 ;init new eof
ldy #fcr_storage
lda [pro_fcr_ptr],y
cmp #tree_type ;(storage = tree)?
beq @calc_tree
cmp #sapling_type
beq @calc_sapling
clc
rts ;seedling works OK
@calc_tree
ldx master_ptr ;setup pointer to master index block
ldy master_ptr+2
jsr get_last_index ;get the lndex to the last index block out of master index
clc ;A=index $00 thru $7F, X = block number.
asl A ;double index
sta new_eof+2
txa ;load block if needed
ldy #index_blk_num
cmp [pro_fcr_ptr],y ;is the block already loaded?
beq @calc_sapling
jsr load_index ;load in the index block
;bcc @calc_sapling
bcc @index_ok ;changed 8/7/90 by Monte Benaresh
rts ;sorry friend no EOF change today!
@index_ok ;next lines added 8/7/90 by Monte Benaresh
ldy #index_blk_num
lda drvr_blk_num ;save the block num of the index block
sta [pro_fcr_ptr],y
@calc_sapling
ldx index_ptr ;get address of the index block in memory
ldy index_ptr+2
jsr get_last_index ;get the lndex to the last data block out of index block
clc ;A=index $00 thru $FF, X is not used!
asl A ;double index
ora new_eof+1 ;set the new value
sta new_eof+1
clc
lda new_eof
adc #blk_size
;change added 6/21/90 by Monte Benaresh
;reverted 7/21/90 by Monte Benaresh because the "fix" could cause new_eof
;to point to a sparse portion of the file that was out of the range of
;the current structure (e.g., beyond the 128K mark in a sapling), a
;condition which is illegal for the cut_block routine which gets called
;by set_eof
sta curr_eof
lda new_eof+2
adc #$0000
sta curr_eof+2 ;update the global EOF that is used by SET_EOF
clc
rts
;==========================================================================
;Get_Last_index will walk an index block looking for the last valid entry.
;
;input: X&Y = pointer to index buffer
;
;output: A = index offset 0 thru $FF
; X = block number
get_last_index
stx temp2_ptr ;setup pointer to lower half of the buffer
sty temp2_ptr+2
clc
txa
adc #$0100 ;point to high half of the index block
sta temp3_ptr
tya
adc #$0000
sta temp3_ptr+2
ldy #$00FF ;start from end of index block
@loop
longa off
sep #$20
lda [temp3_ptr],y ;grab the high block number
xba ;put high block number in upper byte
lda [temp2_ptr],y ;get the low block number
longa on
rep #$20
tax ;check for zero
bne @done ;we have the block number
dey
bpl @loop ;try the next location
sec
rts
@done
tya
clc ;A=Index, X = block number
rts
;==========================================================================
;This routine will zero out one entry in the master index block. It will
;also mark the index block as being dirty and decrement the block count
remove_index
ldy #index_blk_num ;is the index block sparse?
lda [pro_fcr_ptr],y
bne @not_sparse ;there is an index block
rts
@not_sparse jsr dec_blk_count
lda curr_mark+2
lsr a
and #$007F
tay
ora #$0100
tax
longa off
sep #$20
lda [master_ptr],y
sta drvr_blk_num ;save the one we are deleting
lda #$00
sta [master_ptr],y ;zero the deleted entry please
txy
lda [master_ptr],y
sta drvr_blk_num+1 ;high byte of one to delete
lda #$00
sta [master_ptr],y
longa on
rep #$20
ldy #fcr_status
lda [pro_fcr_ptr],y
ora #master_dirty+file_dirty
sta [pro_fcr_ptr],y
ldx drvr_blk_num ;remove the index block please
jsr dealloc_block
rts
;==========================================================================
;This routine will zero out one entry in the current index block. It will
;also mark the index block as being dirty
;==========================================================================
zero_one_index
ldy #index_blk_num ;is the index block sparse?
lda [pro_fcr_ptr],y
bne @not_sparse ;there is an index block
rts
@not_sparse lda curr_mark+1
lsr a
and #$00FF
tay
ora #$0100
tax
longa off
sep #$20
lda #$00
sta [index_ptr],y ;zero the deleted entry please
txy
sta [index_ptr],y
longa on
rep #$20
ldy #fcr_status
lda [pro_fcr_ptr],y
ora #index_dirty+file_dirty
sta [pro_fcr_ptr],y
rts
EXPORT dec_blk_count
dec_blk_count ; reduce number of blocks used by a file by one
ldy #fcr_blks_used
lda [pro_fcr_ptr],y
dec a
;next 2 checks removed 8/5/90 by Monte Benaresh:
; The bmi is wrong since a 16-megabyte file can have
; $8041 blocks,
;
; The beq doesn't do anything except return without updating
; the directory.
;beq bad_news
;bmi bad_news
sta [pro_fcr_ptr],y
lda #dirEntry_dirty ;the directory entry needs updating when flushed
jsr set_fcr_status
bad_news
rts
ENDP
;==========================================================================
;sapling_seed: This routine will convert a sapling file into a seedling
; file. (I hope) Of course in the long run it will do the
; conversion.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; pro_fcr_ptr (points to VCR local data)
; index_ptr (MUST point to the index block)
;
;Output: A = error code if carry set otherwise undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: All registers
; pro_fcr_ptr
;
;
;==========================================================================
longa on
longi on
EXPORT sapling_seed
sapling_seed PROC
;==========================================================================
;Grab the first entry out of the index block. This is to become the new
;key pointer for the file.
;==========================================================================
lda [index_ptr]
sta drvr_blk_num
ldy #blk_size-$0100
lda [index_ptr],y
sta drvr_blk_num+1
lda #$0000 ;now delete the block from the index
sta [index_ptr]
sta [index_ptr],y ;get the high byte
ldx drvr_blk_num
jsr update_key_blk
ldy #fcr_storage
lda #seedling_type
tax
sta [pro_fcr_ptr],y ;change storage type to seedling
ldy #fcr_entry_type
lda [pro_fcr_ptr],y
cmp #resource_type
beq is_res_type1
txa
sta [pro_fcr_ptr],y
is_res_type1 ;
jsr dec_blk_count ;reduce count by one
lda #index_clean
jsr clr_fcr_status ;do not write a index block
ldy #index_blk_num
lda [pro_fcr_ptr],y ;block number for index block
tax ;used by deallocate
beq exit
lda #$0000
sta [pro_fcr_ptr],y ;zero the block number
jsr dealloc_block ;remove the block please
bcc exit
rts
exit ;
stz curr_mark ;reload the correct blocks
stz curr_mark+2
jsr fill_io_buf
rts
;==========================================================================
;We need to see if this is a resource file. IF it not then we need to
;update both the key block and file id fields. Otherwise we only adjust
;the key block field.
;
;input: X = new key block
;==========================================================================
EXPORT update_key_blk
update_key_blk ;
ldy #fcr_file_id
lda [pro_fcr_ptr],y
ldy #fcr_key_blk ;update the key block pointer
cmp [pro_fcr_ptr],y
bne only_key_blk ;update only the key block
txa
ldy #fcr_file_id
sta [pro_fcr_ptr],y
only_key_blk ;
txa
ldy #fcr_key_blk
sta [pro_fcr_ptr],y
inc key_blk_changed ;indicate that the key block has changed
rts
ENDP
;==========================================================================
;tree_sapling: This routine will convert a tree file into a sapling
; file. (I hope) Of course in the long run it will do the
; conversion.
;
;Input: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
; pro_fcr_ptr (points to VCR local data)
; master_ptr (MUST point to the master block)
;
;Output: A = error code if carry set otherwise undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: All registers
; pro_fcr_ptr
;
;
;==========================================================================
longa on
longi on
EXPORT tree_sapling
tree_sapling PROC
;==========================================================================
;Grab the first entry out of the master index block. This is to become the
;new key pointer for the file.
;==========================================================================
stz curr_mark ;force mark to zero for correct
stz curr_mark+2 ;loading of index blocks
jsr fill_io_buf ;make sure the I/O buffer looks Ok
bcc have_index
rts
have_index ;
lda [master_ptr]
sta drvr_blk_num
ldy #blk_size-$0100
lda [master_ptr],y
and #$00FF ;clear any garbage left
sta drvr_blk_num+1
lda #$0000
sta [master_ptr] ;clear the last entry please
sta [master_ptr],y
jsr dec_blk_count
ldx drvr_blk_num
jsr update_key_blk
ldy #fcr_storage
lda #sapling_type
tax
sta [pro_fcr_ptr],y ;change storage type to sapling
ldy #fcr_entry_type
lda [pro_fcr_ptr],y
cmp #resource_type
beq is_res_type2
txa
sta [pro_fcr_ptr],y
is_res_type2 ;
lda #master_clean
jsr clr_fcr_status ;do not write a index block
ldy #master_blk_num
lda [pro_fcr_ptr],y ;block number for master index block
tax ;used by deallocate
beq exit
lda #$0000
sta [pro_fcr_ptr],y ;zero the block number
jsr dealloc_block ;remove the block please
rts
exit ;
clc
rts
ENDP
;==========================================================================
;Get_eof: This routine will return the current EOF for an open file.
;
;Created: may 23, 1987
;Modified: may 23, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = k
;
;Uses: All registers
;
;Call Format:
;
;Class 0 Reference Number (2 Bytes)
; EOF (4 Bytes)
;
;Class 1 PCount (2 Bytes)
; Reference Number (2 Bytes)
; EOF (4 Bytes)
;==========================================================================
longa on
longi on
EXPORT get_eof
get_eof PROC
;==========================================================================
;Call standard setup routine.
;==========================================================================
jsr setup_params
;==========================================================================
;Get the current end of file out of the FCR data area
;==========================================================================
ldy #fcr_curr_eof
lda [pro_fcr_ptr],y
tax ;save low word
iny
iny
lda [pro_fcr_ptr],y
ldy #pblk_19_eof+2
sta [my_pblk_ptr],y
dey
dey
txa
sta [my_pblk_ptr],y
clc
jmp main_exit
ENDP
;==========================================================================
;Get_dir_entry: This routine will return a single entry from a directory.
;
;
;Created: Nov 10, 1987
;Modified: Nov 10, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; find_file
;
;Call Format:
;
;Class 0 Reference number (2 Bytes)
; RESERVED (2 Bytes)
; Base (2 Bytes)
; $0000 = Absolute
; $0001 = Forward releative
; $0002 = Backward relative
; Displacement (2 Bytes)
; Pointer to name buffer (4 Bytes)
; Entry Number (2 Bytes) Absolute entry read
; File Type (2 Bytes)
; EOF (4 Bytes)
; Blocks Used (4 Bytes)
; Create Date/Time (8 Bytes: Misc. Tools Format)
; Mod Date/Time (8 Bytes: Misc. Tools Format)
; Access (2 Bytes)
; Aux Type (4 Bytes)
; File System Id (2 Bytes)
;
;Class 1 PCount (2 Bytes: Minimum = 5)
; Reference Number (2 Bytes)
; RESERVED (2 Bytes)
; Base (2 Bytes)
; $0000 = Absolute
; $0001 = Forward releative
; $0002 = Backward relative
; Displacement (2 Bytes)
; Pointer to name buffer (4 Bytes)
; Entry Number (2 Bytes) Absolute entry read
; File Type (2 Bytes)
; EOF (4 Bytes)
; Blocks Used (4 Bytes)
; Create Date/Time (8 Bytes: Misc. Tools Format)
; Mod Date/Time (8 Bytes: Misc. Tools Format)
; Access (2 Bytes)
; Aux Type (4 Bytes)
; File System Id (2 Bytes)
; Option List Pointer (4 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT get_dir_entry
get_dir_entry PROC
import gde_temp
jsr setup_params ;general setup routine
php ;save status
lda my_pblk_ptr+2
sta temp2_ptr+2
clc
lda my_pblk_ptr
sta temp2_ptr ;save a pointer for extended bit
adc #$0004 ;pass ref # and Reserved fields
sta my_pblk_ptr
bcc not_high
inc my_pblk_ptr+2
not_high ;
plp
bne class1 ;this is a new call
lda #$000E ;make PCount = to class 0 normal
sta pcount
class1 ;
ldy #fcr_storage ;make sure this is a directory
lda [pro_fcr_ptr],y
cmp #subdir_type
bcs type_ok
sec
lda #bad_file_format
jmp main_exit
type_ok ;
lda [my_pblk_ptr] ;load the base field
sta base ;save user's base value
cmp #gde_max_base+1
bcc base_good
lda #parm_range_err
gde_exit ;
jmp main_exit
directory_err ;
sec
lda #dir_error ;report error to user
bra gde_exit
base_good ;
ldy #pblk_1c_name
lda [my_pblk_ptr],y
sta temp_ptr ;save the pointer
iny
iny
lda [my_pblk_ptr],y
and #$00FF ;clear the garbage
sta temp_ptr+2
ldy #pblk_1c_disp ;offset to displacement
lda [my_pblk_ptr],y
sta displacement
ora base ;is base and displacement zero?
bne nothing_special
jsr reset_gde ;reset the call please
nothing_special ;
ldy #pblk_1c_extend ;clear the extended word
lda #$0000
sta [temp2_ptr],y
jsr standard_req
ldy #gde_curr_blk ;have we loaded a directory yet?
lda [pro_fcr_ptr],y
sta drvr_blk_num
bne dir_loaded ;yes.
ldy #fcr_key_blk ;key block of the directory
lda [pro_fcr_ptr],y
sta drvr_blk_num
jsr read_gde_blk ;read in a block please
bcs gde_exit ;bad news bud
lda [index_ptr] ;make sure this is a directory
bne directory_err
ldy #$0004 ;index to storage type
lda [index_ptr],y
tax
and #$00F0 ;check the storage type
cmp #subdir_header
bcc directory_err ;a bad directory
txa
and #$000F ;check the name length
beq directory_err
ldy #header_file_cnt
lda [index_ptr],y ;save the header file count
ldy #gde_tot_files
sta [pro_fcr_ptr],y
ldy #gde_curr_blk ;save the current block also
lda drvr_blk_num
sta [pro_fcr_ptr],y
lda #$0004 ;save gde_offset
ldy #gde_offset
sta [pro_fcr_ptr],y
dir_loaded ;
ldy #gde_offset
lda [pro_fcr_ptr],y ;setup the current offset
sta entry_offset
lda base ;see if we are returning total files
tax
ora displacement
bne not_first
jsr send_tot_files
clc
jmp main_exit ;exit back to caller
not_first ;
ldy #gde_entry_num
lda [pro_fcr_ptr],y
sta math_temp ;current entry number
tay
txa ;restore base
beq absolute ;do absolute entry please
dec a
beq forward ;do forward relative please
backward ;
sec
tya ;get the last entry we looked at
sbc displacement ;entry that we want
bcc end_dir_err
bne find_entry ;go find the entry please
end_dir_err ;
sec
lda #end_of_dir
jmp main_exit
absolute ;
lda displacement ;get this entry please
bra to_forward
forward ;
clc
tya ;get the current entry please
adc displacement
bcs end_dir_err ;over flow!
to_forward ;
ldy #gde_tot_files ;see if we need to look
cmp [pro_fcr_ptr],y
beq find_entry
bcs end_dir_err
find_entry ;
ldy entry_offset ;pre-load the entry offset
sta find_this_entry ;save entry number to find
cmp math_temp ;compare to current entry
beq send_entry ;we have the correct entry
bcc search_backward ;do a backward search
search_forward ;
cpy #$01FF
beq get_forward_blk ;go read the next block please
jsr add_entry_len
forward_loop ;
lda [index_ptr],y ;load the entry please
and #$00F0 ;is this entry active?
beq search_forward ;look at the next entry please
sty entry_offset ;save the offset
inc math_temp ;bump the current entry by one
lda math_temp
cmp find_this_entry ;is this the correct entry
bne search_forward
bra send_entry
get_forward_blk ;
ldy #$0002 ;index to forward link
lda [index_ptr],y
beq end_dir_err ;sorry no more directory blocks
sta drvr_blk_num
jsr read_gde_blk ;load the block please
ldy #$0004 ;index to first entry
bcc forward_loop
jmp main_exit ;tell user about the error
search_backward ;
cpy #$0004
beq get_back_blk ;go read the prev block please
jsr sub_entry_len
backward_loop ;
lda [index_ptr],y ;load the entry please
and #$00F0 ;is this entry active?
beq search_backward ;look at the next entry please
sty entry_offset ;save the offset
dec math_temp ;bump the current entry by one
lda math_temp
cmp find_this_entry ;is this the correct entry
bne search_backward
bra send_entry
get_back_blk ;
lda [index_ptr]
beq end_dir_err ;sorry no more directory blocks
sta drvr_blk_num
jsr read_gde_blk ;load the block please
ldy #$01FF ;index to first entry
bcc backward_loop
jmp main_exit ;tell user about the error
send_entry ;
ldy #gde_curr_blk
lda drvr_blk_num
sta [pro_fcr_ptr],y ;save the current dir block
ldy #gde_entry_num
lda math_temp
sta [pro_fcr_ptr],y ;save the current entry number
ldy #gde_offset
lda entry_offset
sta [pro_fcr_ptr],y ;save the entry offset
tay
stz res_storage ;default is type is not resource
lda [index_ptr],y ;get the storage type
and #$00F0
cmp #resource_type
bne not_extended
ldy #pblk_1c_extend
lda #$8000
sta [temp2_ptr],y
sta res_storage ;type of file is extended
not_extended ;
;
;Now move the entry into the user's data area
;
lda entry_offset
clc
adc index_ptr
sta drvr_buf_ptr
lda #$0000
adc index_ptr+2
sta drvr_buf_ptr+2
jsr move_dir_entry ;move the entry to 'one_entry'
lda #buff_too_small ;assume user screwed up
sta math_temp
lda [temp_ptr]
cmp #$0004
bcc no_name ;sorry no name avail
ldy #$0002 ;store the name length
lda alloc_entry
sta [temp_ptr],y
clc
adc #$0004
cmp [temp_ptr] ;load the buffer size
beq len_ok ;length is ok
bcc len_ok
;
;(BufferSize - 4)
; sec
lda [temp_ptr] ;use the user's buf length
sbc #$0004
beq no_name ;a buffer size of 4 was used so store length only.
bcc no_name ;do not send any of the name
bra do_name ;use the user's length as count to send.
len_ok ;
stz math_temp ;no error
lda alloc_entry ;number of bytes to move
;
;Code added to fix a bug cataloging a bad disk.
;
bne do_name
lda #dir_error ;the directory entry is bad
sta math_temp
bra end_gde ;exit with the error please
do_name
tay ;number of bytes to send
clc ;make pointer to ascii string
lda temp_ptr
adc #$0003
sta temp_ptr
lda temp_ptr+2
adc #$0000
sta temp_ptr+2
stz case_bits ;all upper case is the defalut
lda fst_attr ;if the high bit is set skip Upper/Lower stuff
bmi @its_upper
lda one_entry+version_index
bpl @its_upper
asl a ;remove the case ON/OFF bit
sta case_bits ;set the case bits for this file.
@its_upper
sty gde_temp ;save the count please
ldy #$0001 ;start at the front
longa off
sep #$20
looper
lda one_entry,y
rep #$20
rol case_bits ;see if this char is lower case
sep #$20
bcc @its_upper
if AllowLCPeriods then ;added 7-Aug-91 DAL
cmp #'.'
bne @not_lc_period
lda #0 ;let it ORA our 0 into a $20 (blank)
@not_lc_period
endif ;end 7-Aug-91 DAL
ora #$20 ;set the lower case bit please
@its_upper
sta [temp_ptr],y
cpy gde_temp
beq @we_done
iny
bra looper ;move the name to the user's buffer
@we_done
longa on
rep #$20
lda math_temp
cmp #buff_too_small ;if the error was buffer too small then leave it.
beq no_name
stz math_temp ;reset the error code please
no_name
sec ;adjust the pcount please
lda pcount
sbc #$0005
sta pcount
beq end_gde
jsr send_gde_stuff ;send the info that the user wants
end_gde ;
lda math_temp ;was there an error?
cmp #$0001
jmp main_exit
send_gde_stuff ;
lda my_pblk_ptr
sta temp_ptr
lda my_pblk_ptr+2
sta temp_ptr+2
ldy #pblk_1c_entry ;return the absolute entry
lda find_this_entry
sta [temp_ptr],y
dec pcount
beq to_end_send_gde
ldy #pblk_1c_ftype
lda one_entry+file_type_index
and #$00FF
sta [temp_ptr],y
dec pcount
beq to_end_send_gde
lda res_storage ;is the file extended?
bpl @not_extended ;no it is not
jsr load_ext_blk ;load in the extended block please
@not_extended
jsr send_data_eof ;send the eof for data fork
dec pcount
beq to_end_send_gde
jsr send_data_blks ;send the blks_used for data fork
dec pcount
beq to_end_send_gde
ldx one_entry+create_index
ldy one_entry+create_index+2
jsr unpack_time
ldy #pblk_1c_create
jsr send_time
dec pcount
beq to_end_send_gde
ldx one_entry+last_mod_index
ldy one_entry+last_mod_index+2
jsr unpack_time
ldy #pblk_1c_mod
jsr send_time
dec pcount
beq to_end_send_gde
lda one_entry+access_index
and #$00E7
ldy #pblk_1c_access
sta [temp_ptr],y
dec pcount
beq to_end_send_gde
lda one_entry+aux_type_index
ldy #pblk_1c_aux
sta [temp_ptr],y
lda #$0000
iny
iny
sta [temp_ptr],y
dec pcount ;does the user want FST ID?
to_end_send_gde
beq end_send_gde ;No
lda #pro_id
ldy #pblk_1c_id
sta [temp_ptr],y
dec pcount ;Skip the option list please
beq end_send_gde
;==========================================================================
; We now store the ProDOS FST ID in the 5th byte of the option list buffer
;==========================================================================
ldy #pblk_1c_opt ;index to option list pointer
lda [temp_ptr],y ;get the address of the option list buffer
tax
iny
iny
lda [temp_ptr],y
tay
bne @save_fst_id
txa
beq @skip_save
@save_fst_id
pei temp_ptr ;save the working temp_ptr since I am going to use it
pei temp_ptr+2 ;to handle the option list pointer
stx temp_ptr
sty temp_ptr+2
;
;Now I need to verify the option list length. I need at least 6 bytes.
;
lda [temp_ptr] ;get the length of the option list
cmp #$0004 ;is the minimum count OK
bcs @min_ok ;the user has the min
lda #parm_range_err ;store the error
bra option_list_err
@min_ok
tax ;save the count for data area check
lda #$0002 ;length of the ProDOS FST ID.
tay
sta [temp_ptr],y ;save the number of bytes stored
txa
cmp #$0006
bcs @size_fine
lda #buff_too_small ;store the error
bra option_list_err
@size_fine
iny
iny
lda #pro_id
sta [temp_ptr],y
;new code added 2/13/91 CAE
txa
cmp #6+32 ;is buffer big enough to hold HFS Finder Info?
bcc @no_info ;no
lda res_storage ;is the file extended?
bpl @no_info ;no - so no HFS info
jsr get_hfs_info ;yes - so get the HFS info if we have it
@no_info
;end of new code
pla
sta temp_ptr+2 ;restore
pla
sta temp_ptr
@skip_save
dec pcount ;does the user want resource eof?
beq end_send_gde ;no
jsr send_res_eof ;send the resource EOF please
dec pcount ;does the user want resource blocks?
beq end_send_gde ;no
jsr send_res_blks
end_send_gde
rts
option_list_err
sta math_temp
pla
sta temp_ptr+2 ;restore
pla
sta temp_ptr
rts
send_data_eof
lda res_storage ;is the file extended?
bpl @not_extended
ldy #$0005 ;index to EOF field
lda [drvr_buf_ptr],y
sta one_entry+eof_index
iny
lda [drvr_buf_ptr],y
sta one_entry+eof_index+1
@not_extended
ldy #pblk_1c_eof
lda one_entry+eof_index
sta [temp_ptr],y
iny
iny
lda one_entry+eof_index+2
and #$00FF
sta [temp_ptr],y
rts
send_data_blks
lda res_storage ;is the file extended?
bpl @not_extended
ldy #$0003 ;index to blocks used field
lda [drvr_buf_ptr],y
sta one_entry+blks_used_index
@not_extended
lda one_entry+blks_used_index
ldy #pblk_1c_blks
sta [temp_ptr],y
iny
iny
lda #$0000
sta [temp_ptr],y ;Zero the high word
rts
send_res_eof
lda res_storage ;is the file extended?
bpl @not_extended
ldy #$0105 ;index to eof field
lda [drvr_buf_ptr],y
tax ;low word of eof
iny
iny
lda [drvr_buf_ptr],y
and #$00FF
tay ;upper word of EOF
txa ;save low word in 'A'
tyx ;save upper word in 'X'
bra @send_res_eof
@not_extended lda #$0000 ;eof is nothing
tax
@send_res_eof
ldy #pblk_1c_res_eof
sta [temp_ptr],y
iny
iny
txa
sta [temp_ptr],y ;save upper word please
rts
send_res_blks
lda res_storage ;is the file extended?
bpl @not_extended
ldy #$0103 ;index to blocks used field
lda [drvr_buf_ptr],y
bra @send_res_blks
@not_extended lda #$0000 ;no blocks
@send_res_blks
ldy #pblk_1c_res_blks
sta [temp_ptr],y
iny
iny
lda #$0000
sta [temp_ptr],y ;save upper word please
rts
add_entry_len
tya
clc
adc #entry_length
tay
rts
sub_entry_len
tya
sec
sbc #entry_length
tay
rts
send_tot_files ;
lda pcount ;is there room
cmp #$0006
bcc end_tot
ldy #gde_tot_files
lda [pro_fcr_ptr],y ;get the total number of entries
ldy #pblk_1c_entry
sta [my_pblk_ptr],y
end_tot
rts
reset_gde
lda #$0000 ;reset gde by zero all data
ldy #gde_entry_num
sta [pro_fcr_ptr],y
ldy #gde_offset
sta [pro_fcr_ptr],y
ldy #gde_curr_blk
sta [pro_fcr_ptr],y
ldy #gde_tot_files
sta [pro_fcr_ptr],y
rts
read_gde_blk
lda index_ptr ;pointer to GDE buffer
sta drvr_buf_ptr
lda index_ptr+2
sta drvr_buf_ptr+2
jsr read_with_cache ;read in directory block
rts
load_ext_blk
jsr set_default_buf ;use the standard buffer please
jsr standard_req
lda one_entry+key_blk_index
sta drvr_blk_num
jsr read_with_cache
bcc @no_problem
jmp main_exit ;abort please
@no_problem
rts
ENDP
;==========================================================================
;get_dev_num This file contains code that will find a volume and return
; a device number.
;
;Created: aug 17, 1987
;Modified: aug 17, 1987
;Author: Rob Turner
;
;Enter: jml
;
;Input: A = undefined
; X = call number times 2
; Y = class times 2
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
; and lots of other stuff to.
;
;Call Format:
;
;Class 0 Dev/Vol Name Pointer (4 Bytes)
; Device Number (2 Bytes)
;
;Class 1 PCount (2 Bytes)
; Dev/Vol Name Pointer (4 Bytes)
; Device Number (2 Bytes)
;
;==========================================================================
longa on
longi on
EXPORT get_dev_num
get_dev_num PROC
jsr setup_params ;go ahead do your thing
lda dev_num ;did the shell find a device?
bne have_device
lda #no_dup ;allow me to handle file not found
sta search_flag
jsr process_path ;do ahead and find the volume
bcc no_err ;no problem
cmp #file_not_found ;was the error file not found?
bne @dev_err_exit
lda dev_num
bne no_err ;give him the device number
lda #bad_path_syntax
@dev_err_exit
sec
jmp main_exit
no_err
lda drvr_dev_num
have_device ;
ldy #$0004 ;return the device number
sta [my_pblk_ptr],y
clc
jmp main_exit
ENDP
;======================================================================
;read_block and write_block calls
;
; This routine will read or write
; one block from the device number specified
;
;created: mar 23, 1987
;modified: jan 06, 1988
;author: Rob Turner
;
;Enter: jmp
;
;input: param_blk_ptr = pointer to user's parameter block
; A = undefined
; X = call number * 2
; Y = class of call (0 thru 7)
; P = nvmxdizc
; ..000...
; b = k
;
;output: A = error code if carry set otherwise undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;uses: all register
; param_blk_ptr (direct page)
; uses dispatcher (system call)
;
;exit: via common exit routine
;
;Call Format for Both Read and Write:
;
;Class 0 Device Number (2 Bytes)
; Pointer to Data Buffer (4 Bytes)
; Block Number (2 Bytes)
;
;Class 1 PCount (2 Bytes)
; Device Number (2 Bytes)
; Pointer to Data Buffer (4 Bytes)
; Block Number (2 Bytes)
;
;======================================================================
longa on
longi on
EXPORT read_block
read_block PROC
lda #drvr_read
bra common_block ;common code for block read/write
EXPORT write_block
write_block ;
lda #drvr_write ;do a write call
common_block ;
sta drvr_call_num
tya
lsr a
tay
dey ;y = class of the call
bmi class0 ;old style call...
class1 ;
lda [param_blk_ptr] ;get the PCount
cmp #0003 ;min and max PCount for this call
beq good_pcount
lda #invalid_pcount ;I have a headache tonight
error_out ;
sec
read_out ;
jmp main_exit
good_pcount ;
iny
class0 ;
iny ;make index to 'Device Number'
lda [param_blk_ptr],y
sta drvr_dev_num
phy
jsr chk_owner ;see if this is my disk
ply
bcs error_out
iny
iny ;index to the buffer pointer
lda [param_blk_ptr],y
sta drvr_buf_ptr
iny
iny
lda [param_blk_ptr],y
sta drvr_buf_ptr+2
iny
iny ;index to the block number
lda [param_blk_ptr],y
sta drvr_blk_num
stz drvr_blk_num+2
;init all other parameters that the driver/dispatcher will use
lda #blk_size
sta drvr_req_cnt ;read one block only
stz drvr_req_cnt+2
try_again jsr device_call ;call the driver
bcs check_error
bra read_out ;error is returned
check_error ;
cmp #drvr_disk_sw ;disk switched behind my back?
bne error_out
stz prodos_vcr_ptr ;indicate that we do not know the owner.
stz prodos_vcr_ptr+2
bra try_again ;tell the user
;======================================================================
;chk_owner: This routine will try to find a VCR associated with
; the device we are going to write to. If we find the
; VCR we will see if it is a ProDOS volume, if it is then
; we will mark the bitmap undefined. If the bitmap is dirty
; or there are open files on the volume we will not allow
; the caller to write to the disk.
;======================================================================
chk_owner
lda drvr_dev_num ;see if there is a VCR
sta dev_num
jsr check_vcr_first
bcs exit_owner ;there is no VCR
jsr setup_my_vcr
stx prodos_vcr_ptr
sty prodos_vcr_ptr+2
jsr reset_vcr
rts
exit_owner
stz prodos_vcr_ptr ;indicate that we do not know the owner.
stz prodos_vcr_ptr+2
clc
rts
EXPORT reset_vcr
reset_vcr
lda drvr_call_num
cmp #drvr_write ;is this a write block call?
beq @is_write
clc
rts
@is_write
ldy #bitmap_dirty ;see if the bitmap is dirty
lda [prodos_vcr_ptr],y
bne no_action ;sorry not tonight
ldy #vcr_open_cnt ;see if files are open
lda [my_vcr_ptr],y
bne no_action
lda #$FFFF ;reset the volume please
ldy #curr_bitmap ;flush bitmap blocks
sta [prodos_vcr_ptr],y
ldy #free_blocks ;unknown free block count
sta [prodos_vcr_ptr],y
ldy #avail_bitmap
sta [prodos_vcr_ptr],y
clc
rts
no_action ;
lda #invalid_access
sec
rts
ENDP
;======================================================================
;format/Erase This file will format or erase of volume
;
;Created: dec 05, 1987
;Modified: mar 15, 1988
;Author: Rob Turner
;
;Enter: From GS/OS jmp table
;
;Input: A = undefined
; X = (call number * 2)
; Y = (class number * 2)
; P = nvmxdizc
; ..000...
; b = k
;
;Output: A = error code if carry set (exit via system exit routine)
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000..1 = error
; 0 = no error
; b = k
;
;Uses: All registers
; Device Dispatcher (subroutine)
; General I/O Buffer (1K buffer, I read 512 bytes)
;
;Call Format:
;
;Class 0 Device Name Pointer (4 Bytes)
; Volume Name Pointer (4 Bytes)
; FST ID (2 Bytes)
;
;Class 1 PCount (2 Bytes)
; Device Name Pointer (4 Bytes)
; Volume Name Pointer (4 Bytes)
; FST ID (2 Bytes)
;
;======================================================================
longa on
longi on
EXPORT format_disk
format_disk PROC
import boot_size
jsr setup_params ;standard setup routine
** 2/28/92 GAB
jsr check_spans ;make sure the volume name isn't too long
**
lda dev_num ;move device number from SCM to
sta drvr_dev_num ;driver area on direct page
jsr clear_any_vcrs ;keep VCRs in order
jsr setup_vol_name ;move the volume name please
jsr hard_format ;issue a hard format please
bcs init_exit
bra continue
EXPORT erase_disk
erase_disk ;
jsr setup_params ;standard setup routine
** 2/28/92 GAB
jsr check_spans ;make sure the volume name isn't too long
**
lda dev_num ;move device number from SCM to
sta drvr_dev_num ;driver area on direct page
jsr clear_any_vcrs ;keep VCRs in order
jsr setup_vol_name ;move the volume name please
continue ;
jsr set_partition ;assign the partion map name
;
;Start of code that will write to disk
;
jsr write_boot_code ;write the boot code out to disk
bcs init_exit
jsr build_directory ;build the directory please
bcs init_exit
jsr build_bitmap ;build the correct bitmap please
init_exit ;
jmp main_exit
;======================================================================
;setup_vol_name This routine will verify the volume name is ok, and
; move the name into the format header.
;======================================================================
setup_vol_name ;
clc
lda path2_ptr
adc #$0003
sta temp_ptr ;setup pointer to the name
lda path2_ptr+2
adc #$0000
sta temp_ptr+2
jsr set_case_bits ;setup the case please (Temp_Ptr is Input)
lda temp_ptr ;subtract one from the pointer.
bne @do_low
dec temp_ptr+2
@do_low dec temp_ptr
lda #$0001
sta chars_checked ;parameter for setup_name
jsr setup_name
ldy #15 ;first clear the format header
lda #$0000
clr_loop ;
sta new_vol_name,y
dey
bpl clr_loop
lda search_length ;length of the volume name
and #$000F
tay
ora #volume_header
sta new_vol_name ;the length is now stored
longa off
sep #$20 ;8 bit Accum
move_vol ;
lda search_name-1,y
sta new_vol_name,y
dey
bne move_vol
longa on
rep #$20 ;16 bit Accum
;
;Save the case please
;
stz new_vol_date-2 ;zap any old data that might be around
lda case_bits ;get the user's case
beq @skip_save
ora #$8000
sta new_vol_date-2
@skip_save
jsr pack_time ;set the create date
stx new_vol_date
stx new_vol_mod
sty new_vol_date+2
sty new_vol_mod+2 ;set the mod time to the create time
rts
;======================================================================
;hard_format: This routine will issue the hard format call to the
; device driver.
;======================================================================
hard_format ;
lda #drvr_control ;issue hard format call to device
sta drvr_call_num
lda #drvr_format ;issue the format call
sta drvr_ctrl_code
lda #^format_ctrl
sta drvr_clist_ptr+2
lda #format_ctrl
sta drvr_clist_ptr
lda #$0002 ;length of control list
sta drvr_req_cnt
stz drvr_req_cnt+2
jsr device_call ;issue the format call to the device
bcc formatted
cmp #drvr_disk_sw ;is it a disk switched problem?
beq hard_format ;try again
sec
formatted ;
rts ;return with the error code.
;======================================================================
;indicate_erase This routine will tell the driver what name to store
; in the device partion map.
;======================================================================
set_partition ;
lda #drvr_control ;setup for a control call
sta drvr_call_num
lda #$0006 ;setup control code
sta drvr_ctrl_code
lda #^control_list ;setup pointer to the parameter list
sta drvr_clist_ptr+2
lda #control_list
sta drvr_clist_ptr
stz drvr_req_cnt
stz drvr_req_cnt+2 ;no request count
jsr device_call
rts
;======================================================================
;write_boot_code This routine will write out to disk the ProDOS
; boot code. Note that the SOS boot code has
; been removed from the boot blocks. This means that
; a disk formatted with the ProDOS FST cannot be booted
; on the Apple ///.
;======================================================================
write_boot_code ;
jsr set_default_buf ;use general work buffer
jsr standard_req ;512 byte write
jsr clear_gbuf_low ;clear the working buffer with zeros.
lda #drvr_write
sta drvr_call_num
stz drvr_blk_num
stz drvr_blk_num+2
ldy #boot_size-2 ;move the boot code to please
boot_loop ;
lda boot_code_start,y
sta [drvr_buf_ptr],y
dey
bpl boot_loop
re_boot ;
jsr device_call ;issue the device call please
bcc boot_done
cmp #drvr_disk_sw
beq re_boot ;try again
sec
boot_done ;
rts
;======================================================================
;Build_directory This routine will build the first four blocks of the
; directory.
;======================================================================
build_directory
jsr get_dev_blks ;get the number of blocks on the device.
bcs exit_dir ;sorry we are out of here.
sta new_tot_blks ;number of blocks on the volume
jsr set_default_buf ;use general work buffer
jsr standard_req ;512 byte write
jsr clear_gbuf_low ;clear the working buffer with zeros.
lda #drvr_write ;setup for write calls
sta drvr_call_num
lda #vol_dir_start
sta drvr_blk_num
stz drvr_blk_num+2
ldy #entry_length+2 ;now move the entry to work buffer
header_loop ;
lda format_header,y
sta [drvr_buf_ptr],y
dey
bpl header_loop
;
;Write Block Two To Disk
;
jsr device_call ;issue the device call please
bcs exit_dir
;
;Write Block Three To Disk
;
jsr clear_gbuf_low ;clear the block again
ldx #vol_dir_start ;backward link to block 2
ldy #vol_dir_start+2 ;forward link to block 3
jsr do_dir_blk ;write directory block to disk
bcs exit_dir
;
;Write Block Four To Disk
;
ldx #vol_dir_start+1 ;Backward link to block 3
ldy #vol_dir_start+3 ;Forward link to block 5
jsr do_dir_blk
bcs exit_dir
;
;Write Block Five To Disk
;
ldx #vol_dir_start+2 ;Backward link to block 4
ldy #$0000 ;Forward link NIL
jsr do_dir_blk
exit_dir ;
rts
do_dir_blk ;
txa ;previous link
sta [drvr_buf_ptr]
tya
ldy #$0002 ;index to forward link
sta [drvr_buf_ptr],y
inc drvr_blk_num ;move to the next block please
jsr device_call ;issue write call
rts
;======================================================================
;build_bitmap This routine will build a bitmap for the device
; being formatted.
;======================================================================
build_bitmap ;
lda #bitmap_strt_blk ;starting block number of bitmap
sta drvr_blk_num
;
;Calculate the number of bitmap blocks needed for this volume
;
ldx #$0001 ;default = one bitmap block
lda new_tot_blks ;number of blocks on volume
count_blks ;
sec
sbc #4096 ;one bitmap block
beq end_count
bcc end_count
inx ;add one to the block count
bra count_blks
end_count ;
stx bitmap_blks ;total number of bitmap blocks
txa
clc
adc #$0005 ;add in boot blks and directory
sta last_bitmap ;blk number of last bitmap blk
jsr fill_gbuf_low ;allocate 4096 blocks please
ldx last_bitmap ;block number of last bitmap block
dir_build ;
jsr alloc_bit_blk ;allocate a bitmap block please
dex ;allocate the next block please
bpl dir_build
bra chk_last ;see if we are done
full_loop ;
jsr fill_gbuf_low ;allocate 4096 blocks
chk_last ;
lda drvr_blk_num ;our we done?
cmp last_bitmap
beq clear_to_end ;yes we are
jsr device_call ;issue write call
bcs end1_bitmap
inc drvr_blk_num ;move to the next block please
bra full_loop
clear_to_end ; ;clear from last bit to the end
lda new_tot_blks
tax
and #$0FFF ;do we need to clear anything?
beq just_write
txa ;we must clear some bits
and #$0007 ;are we byte aligned?
beq fast_clr ;yes we are
jsr alloc_bit_blk ;allocate the odd block
inx
stx new_tot_blks
bra clear_to_end
fast_clr ;
jsr calc_bit ;get the index please
lda #$0000
fast_loop sta [drvr_buf_ptr],y
iny
iny ;WARNING! we might overwrite a byte
cpy #$0200 ;past $200 this is OK with a 1K buffer
bcc fast_loop ;since we are using 512 bytes only
just_write ;
jsr device_call ;issue write call
end1_bitmap ;
rts
alloc_bit_blk ;
phx ;save the block number
jsr calc_bit ;convert block number to bit pattern
eor #$FFFF ;clear the correct bit please
and [drvr_buf_ptr],y
sta [drvr_buf_ptr],y
plx ;restore block number
rts
;======================================================================
;get_dev_blks This routine will return the number of blocks on
; the volume.
;
;Output: A = number of blocks
;======================================================================
get_dev_blks ;
lda #drvr_status ;issue a status call to the device
sta drvr_call_num
lda #^status_list ;setup a pointer to the status list
sta drvr_slist_ptr+2
lda #status_list
sta drvr_slist_ptr
lda #$0006 ;a six byte request count
sta drvr_req_cnt
stz drvr_req_cnt+2
lda #blk_size
sta drvr_blk_size
stz drvr_stat_code ;issue a 'driver status' call.
jsr device_call ;blast off!!
bcs holy_cow_batman ;An error?
ldx #$FFFF ;a 32 meg volume
lda return_blks+2 ;return the blocks please
bne big_boy
ldx return_blks
cpx #$0007
bcc range_problem ;sorry this device is too small
big_boy ;
txa ;number of blocks on volume
clc
holy_cow_batman
rts
range_problem
lda #drvr_bad_block ;we will not do it.
sec
rts
;======================================================================
;This routine will mark any VCR's that are logged on this device as
;being swapped. This will allow any FST to rebuild the VCR after the
;media has been erased or formatted.
;======================================================================
clear_any_vcrs ;
lda dev_num ;device number we are going to erase
jsl swap_out ;mark volumes offline
rts
ENDP
;==========================================================================
;This file contians the calls the handle FST specific calls
;
;created: Mar 19, 1988
;modified: Mar 19, 1988
;Author: Rob Turner
;
;
;==========================================================================
longa on
longi on
EXPORT fst_specific
fst_specific PROC
ldy #$0004 ;index to command number
lda [param_blk_ptr],y
cmp #set_time_stamp
beq set_time
cmp #get_time_stamp
beq get_time
cmp #set_upper_case
beq set_fst_attr ;set the Upper case flag please
cmp #get_upper_case
beq get_fst_attr
IF debug_on<>0 THEN
jsr fst_debug ;check special commands for debugger
bra exit ;exit with error
ENDIF
lda #invalid_fst_op
bra error_out
parm_range
lda #parm_range_err ;parameter out of range
bra error_out
bad_pcnt
lda #invalid_pcount
error_out
sec
exit
jmp main_exit
;==========================================================================
;This call allows the user to set the time stamp option. Below is the valid
;list of values.
;
; $0000 = Time stamp files only
; $0001 = Time stamp file + Parent Directory
; $0002 = Time stamp file + all directories to the root
;
;==========================================================================
set_time
lda [param_blk_ptr] ;verify the Pcount
cmp #$0003
bne bad_pcnt
iny
iny ;point to the stamp size
lda [param_blk_ptr],y
cmp #$0003
bcs parm_range ;sorry bad
sta time_stamp_opt
clc
bra exit
;==========================================================================
;This call returns the current time stamp option. Below is the valid
;list of values.
;
; $0000 = Time stamp files only
; $0001 = Time stamp file + Parent Directory
; $0002 = Time stamp file + all directories to the root
;
;==========================================================================
get_time
lda [param_blk_ptr] ;verify the Pcount
cmp #$0003
bne bad_pcnt
iny
iny ;point to the stamp size
lda time_stamp_opt
sta [param_blk_ptr],y
clc
bra exit
;==========================================================================
;This call allows the user to set the upper case state to on or off
;If set to off all file names will be returned in upper case, otherwise
;they will be returned in the cae they were created in.
;
; $0000 = Turn OFF upper/lower case
; $0001 = Return case as created/renamed
;
;==========================================================================
set_fst_attr
lda [param_blk_ptr] ;verify the Pcount
cmp #$0003
bne bad_pcnt
iny
iny ;point to the input parameter
lda [param_blk_ptr],y
beq @set_high_bit
dec a
bne parm_range ;sorry bad
lda fst_attr ;clear the bit please
and #$7FFF
sta fst_attr
clc
bra exit
@set_high_bit
lda fst_attr ;turn off upper/lower case.
ora #$8000
sta fst_attr
clc
bra exit
;==========================================================================
;This call allows the user to get the upper case state.
;
; $0000 = files are returned in all UPPERCASE
; $0001 = Return case as created/renamed
;
;==========================================================================
get_fst_attr
lda [param_blk_ptr] ;verify the Pcount
cmp #$0003
bne bad_pcnt
iny
iny ;point to the input parameter
ldx #$0000
lda fst_attr
bmi @return_zero
inx
@return_zero
txa
sta [param_blk_ptr],y
clc
bra exit
ENDP
EXPORT end_segment
end_segment PROC
EXPORT end_code
end_code
ENDP
end