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

string asis
blanks off
print push
print off
include 'common.equ.src'
include 'driver.equ.src'
print pop
* change the following equate to update the version number
VersNum Equ $0400 ;version 4.00
;================================================================
;
; CFST.EQUATES
;
; Equates file for the Character File System Translator.
;
; Created: July 8, 1987
; Modified: November 18, 1987
; Author: Andy Stadler
;
;================================================================
cfst_id equ 9 ; my ID number
read_access equ $01
write_access equ $02
read_write_acc equ $03
max_call equ $4A ; last valid command * 2
max_sys equ $4 ; last valid command * 2
;================================================================
;
; Dev_Data structure
;
; This specifies the structure of dev_data records. These are
; a linked list of structures, one for each open device.
;
; NOTE: A device may be multiply opened. For each open instance
; there is an FCR, however there is only one Dev_Data for
; each device.
;
;================================================================
dd_dev_num equ 0 ; device number
dd_next_dev equ dd_dev_num+2 ; VP to next device
dd_open_stat equ dd_next_dev+4 ; how many times opened.
dd_size equ dd_open_stat+2
;================================================================
;
; cfst's fcr
;
; Because devices opened as files have no associated "volume",
; there is no associated VCR. I need to store the device number
; somewhere - normally found in the VCR. So I add a field to
; the end of the fcr.
;
;================================================================
fcr_fst_ptr equ fcr_access+2
fcr_dev_num equ fcr_fst_ptr ; device number
my_fcr_length equ fcr_dev_num+2 ; length of cfst's fcr
;================================================================
;
; CFST's direct page usage. Starts at $80, the FST workspace.
;
;================================================================
my_ref_num equ fst_start ; working reference #
my_dev_num equ my_ref_num+2 ; working device #
cur_dev equ my_dev_num+2 ; vp of current dev_data
cur_dev_ptr equ cur_dev+4 ; ptr to current dev_data
prev_dev equ cur_dev_ptr+4 ; vp of previous dev_data
my_fcr equ prev_dev+4 ; vp of current fcr
my_fcr_ptr equ my_fcr+4 ; ptr to current fcr
newl_buf equ my_fcr_ptr+4 ; vp of fcr's newline buffer
newl_buf_ptr equ newl_buf+4 ; ptr to fcr's newline buffer
call_class_2 equ newl_buf_ptr+4 ; incoming call class x 2
my_open_flag equ call_class_2+2 ; used for open's error recovery
req_access equ my_open_flag+2 ; requested access bits
dev_access equ req_access+2 ; device's access bits
grant_access equ dev_access+2 ; computed access bits
my_req_count equ grant_access+2 ; request count for read/write
my_pcount equ my_req_count+4 ; incoming parameter count
my_newl_count equ my_pcount+2 ; newline list length
my_newl_mask equ my_newl_count+2 ; newline mask/enable
src_list_ptr equ my_newl_mask+2 ; source pointer for newline copy
my_trans_count equ src_list_ptr+4 ; working transfer count
;-----------------------------------------------
;
; Forward addresses and entries
;
;-----------------------------------------------
ENTRY bad_cmd
ENTRY block_cmd
ENTRY do_close
ENTRY do_flush
ENTRY do_open
ENTRY do_read
ENTRY do_shutdn
ENTRY do_startup
ENTRY do_write
ENTRY error_exit
eject
;================================================================
;
; CFST.MAIN
;
; Contains header, entry points, and exit points of the
; character file system translator.
;
; Created: July 27, 1987
; Modified: January 30, 1988
; Author: Andy Stadler
;
; Copyright (c) 1987-1988, Apple Computer, Inc.
; All Rights Reserved.
;
;================================================================
;
; This is the Character FST header.
;
; FST Header Definition:
;
; dc c'FST ' ; FST identifier
; dc I4'Application_Entry' ; Entry Point #1
; dc I4'System_Calls' ; Entry Point #2
; dc I2'FST_ID' ; FST's ID #
; dc I2'$C000' ; attribute 110xxxxxxxxxxxxx
; bit 15 = 1=uppercase paths
; 14 = 0=block/1=character
; 13 = 1=supports formatting
; dc I2'version'
; dc I2'0' ; block size
; dc I4'0' ; max blocks
; dc I4'0' ; min blocks
; dc I4'0' ; max file in bytes
; dc I4'0' ; reserved field, must be 0
; dc I1'name length' ; file system name
; dc c'Character FST'
; dc I2'Comment Length' ; install time comment
; dc C'Comment goes here'
; dc i2'0'
;
; Revision:
;
; Mar. 28, 1988 By Rob Turner
;
; Changed the open routine. The open routine was only
; setting up 'drvr_dev_num' if the driver was currently
; closed. This caused 'get_last_dev' to fail.
;
; files changed:
; (1) cfst.open
;
; Jun. 28, 1988 by Rob Turner
;
; We now set the block size to zero on entry to the
; character fst.
;
; Feb. 01, 1989 by Rob Turner
;
; Fixed a bug in Read and Write. These routines
; were not honoring the access that was assigned
; at the time of the open call.
;
; Mar 27, 1990 by Monte Benaresh
;
; The FLUSH call now supports two types of flush: a "full"
; flush, which flushes a file's dirty data blocks and dirty
; directory blocks, and a "fast" flush which flushed dirty
; data blocks only. Since this concept is only meaningful
; for block FST's, the code in this file was simply changed
; to allow a P_COUNT of 2. The FLUSH_TYPE parameter is
; ignored entirely.
;
; New FLUSH parameter block:
;
; |---------------|
; | P_COUNT | (word)
; |---------------|
; | REF_NUM | (word)
; |---------------|
; | FLUSH_TYPE | (word)
; |---------------|
;
; Valid values for FLUSH_TYPE are:
; $0000 = full flush (default)
; $8000 = fast flush
;
; August 31, 1991 by Greg Branche
;
; Logic added to speed up writes to the .CONSOLE driver.
; At Startup, the FST now scans the device list looking
; for the .CONSOLE driver. When it's found, it stores
; the device number for later comparisons, performs an
; Add_Trap call to the driver, and then a Reset_Trap to
; learn the address of the driver's internal entry point.
; During a Write call, the device number is compared to
; the .CONSOLE device number and, if they match, the write
; call to the driver is dispatched directly to the driver's
; internal entry point as opposed to going through the
; device dispatcher.
;
; December 8, 1991 by Greg Branche
;
; Now that SCM skips the pCount validation if the
; callers makes a Read or Write call to the
; Character FST, the FST has to do it for itself.
; I've changed setup_rw_params to check for a minimum
; pCount of 4 and return an invalid_pcount error if
; the caller's parameter contains less than that.
;
;================================================================
;Revisions
fst_header PROC
Import appl_call,system_call
DC.B 'FST '
DC.L appl_call
DC.L system_call
DC.W cfst_id
DC.W $C000
DC.W VersNum ; version number
DC.W 0
DC.L 0
DC.L 0
DC.L 0
DC.L 0
DC.B 13
DC.B 'Character FST'
string pascal
DC.B 'Character FST v04.00'
DC.W 0000
dc.b 'Character FST by Andy Stadler and Rob Turner'
string asis
ENDP
;================================================================
;
; appl_call
;
; This is the entry point for application calls.
;
; Input: X = Call number * 2
; Y = Class number * 2
;
;================================================================
appl_call PROC
Import cmd_tbl:data
phk
plb
rep #$30
cpx #max_call+1
bcs bad_cmd
stz <drvr_blk_size ; Set blk size to zero for char devices
jmp (|cmd_tbl-2,x)
;================================================================
;
; bad_cmd
;
; An out-of range system call was received.
;
;================================================================
Entry bad_cmd
bad_cmd
lda #bad_system_call
; bra error_exit
; fall into error_exit
;================================================================
;
; error_exit
;
; Return to the caller with an error condition.
;
; Input: A = Error Code
;
;================================================================
entry error_exit
error_exit
sec
jml sys_exit
ENDP
;================================================================
;
; system_call
;
; Entry point for FST system calls.
;
; Input: X = Call number * 2
; Y = Class number * 2
;
;================================================================
system_call PROC
Import sys_tbl:data
phk
plb
cpx #max_sys+1
blt system_call_1
lda #bad_system_call
sec
rtl
system_call_1
stz <drvr_blk_size ; Set blk size to zero for char devices
jmp (|sys_tbl-2,x)
ENDP
;================================================================
;
; block_cmd
;
; A block-oriented command was received. This is a character
; I/O program.
;
;================================================================
block_cmd PROC
lda #not_block_dev
bra error_exit
ENDP
;================================================================
;
; pcount_error
;
; A parameter block was received without enough parameters.
;
;================================================================
pcount_error PROC
lda #invalid_pcount
bra error_exit
ENDP
;================================================================
;
; ok_exit
;
; Return to the caller after a successful operation.
;
; OUTPUTS: A = no_error
; C = clear
;
;================================================================
ok_exit PROC
clc
lda #no_error
jml sys_exit
ENDP
eject
;================================================================
;
; CFST.DATA
;
; Data Tables for Character File System Translator.
;
; Created: July 08, 1987
; Modified: See modification history
; Author: Andy Stadler
;
; Copyright 1987 Apple Computer, Inc.
; All rights reserved.
;
;================================================================
;
; Modification history:
;
; 01-Feb-88 ADS Initial release
; 02-Feb-88 ADS string_buffer added
; dispatch table cleaned up
;
;================================================================
;================================================================
;
; dispatch_tables
;
; These are the jump tables used for the various commands.
;
;================================================================
cmd_tbl Record
DC.W block_cmd ; $01 Create
DC.W block_cmd ; $02 Destroy
DC.W bad_cmd ; $03 no call here
DC.W block_cmd ; $04 Change Path
DC.W block_cmd ; $05 Set File Info
DC.W block_cmd ; $06 Get File Info
DC.W bad_cmd ; $07 no call here
DC.W block_cmd ; $08 Volume
DC.W bad_cmd ; $09 Set Prefix
DC.W bad_cmd ; $0A Get Prefix
DC.W block_cmd ; $0B Clear Backup Bit
DC.W bad_cmd ; $0C Destroy Purge
DC.W bad_cmd ; $0D Null
DC.W bad_cmd ; $0E Expand Path
DC.W bad_cmd ; $0F Alter Dir
DC.W do_open ; $10 Open
DC.W bad_cmd ; $11 Newline (handled by SCM)
DC.W do_read ; $12 Read
DC.W do_write ; $13 Write
DC.W do_close ; $14 Close
DC.W do_flush ; $15 Flush
DC.W block_cmd ; $16 Set_mark
DC.W block_cmd ; $17 get_mark
DC.W block_cmd ; $18 set_eof
DC.W block_cmd ; $19 get_eof
DC.W bad_cmd ; $1A set_level
DC.W bad_cmd ; $1B get_level
DC.W block_cmd ; $1C get_dir_entry
DC.W bad_cmd ; $1D Reserved
DC.W bad_cmd ; $1E Reserved
DC.W bad_cmd ; $1F Reserved
DC.W bad_cmd ; $20 Get_dev_number
DC.W bad_cmd ; $21 get_last_dev
DC.W block_cmd ; $22 read_block
DC.W block_cmd ; $23 write_block
DC.W block_cmd ; $24 format
DC.W block_cmd ; $25 erase_disk
EndR
sys_tbl Record
DC.W do_startup ; $01 Startup
DC.W do_shutdn ; $02 Shutdown
EndR
eject
;================================================================
;
; my_data
;
; Private data area for the CFST.
;
;================================================================
cfst_data Record
open_devices DC.L 0000 ; 1st vp of a linked list
string_buffer DS.B 33 ; for converting strings
cons_dev_num ds.w 1 ; console device number
cons_dib_ptr ds.l 1 ; console DIB pointer
ENDP
eject
;================================================================
;
; add_dev
;
; Adds a device to the device list. Note that there better
; not be another occurence of this device, because I make no
; attempt to fish out duplicates.
;
; Inputs: A = Device Number
;
; Outputs: open_devices = VP of new device
; cur_dev = (same)
; cur_dev_ptr = Ptr to new device
; C = Set if couldn't allocate it
;
; New Device Initialized to:
;
; next_dev = previous first dev (head insertion)
; dev_num = new device number
; open_stat = 0 (not yet opened)
;
;================================================================
add_dev PROC
with cfst_data
pha ; remember new dev #
lda |open_devices+2 ; preserve link
pha
lda |open_devices
pha
lda #dd_size ; get some memory
jsl alloc_seg
bcc add_device_ok ; ok
pla ; clean up stack
pla
pla
lda #out_of_mem
rts ; error exit
add_device_ok
stx <cur_dev ; save the new vp in 2 places
sty <cur_dev+2
stx |open_devices
sty |open_devices+2
jsl deref
stx <cur_dev_ptr
sty <cur_dev_ptr+2
ldy #dd_next_dev ; reconnect chain
pla
sta [<cur_dev_ptr],y
iny
iny
pla
sta [<cur_dev_ptr],y
ldy #dd_dev_num ; store device number
pla
sta [<cur_dev_ptr],y
ldy #dd_open_stat ; store new open status
lda #0000
sta [<cur_dev_ptr],y
clc ; success exit
rts
ENDP
eject
;================================================================
;
; find_dev
;
; Given a device number, see if it is currently open. We do
; this by looking for a Dev_Data record with the device number.
;
; Inputs: my_dev_num = Device Number
;
; Outputs: cur_dev = vp of the device record
; cur_dev_ptr = pointer to device record
; prev_dev = vp of the previous record (0 if 1st device)
; C = Set if not found, clear if found
; (above fields =0L if not found)
;================================================================
find_dev PROC
with cfst_data
stz <prev_dev
stz <prev_dev+2
lda |open_devices ; any open?
ora |open_devices+2
beq none_found
ldx |open_devices ; setup to check 1st
stx <cur_dev
ldy |open_devices+2
sty <cur_dev+2
jsl deref
find_dev_lp
stx <cur_dev_ptr ; check device number
sty <cur_dev_ptr+2
ldy #dd_dev_num
lda [<cur_dev_ptr],y
cmp <my_dev_num
beq dev_found
lda <cur_dev ; move to next open device
sta <prev_dev
lda <cur_dev+2
sta <prev_dev+2
ldy #dd_next_dev
lda [<cur_dev_ptr],y
sta <cur_dev
iny
iny
lda [<cur_dev_ptr],y
sta <cur_dev+2
ora <cur_dev ; end of list?
beq none_found
ldx <cur_dev
ldy <cur_dev+2
jsr deref
bra find_dev_lp
none_found
stz <cur_dev
stz <cur_dev+2
sec
rts
dev_found clc
rts
ENDP
eject
;================================================================
;
; release_dev
;
; Removes a closed device from the device chain. Assumes the
; device was found using find_dev, i.e. cur_dev, cur_dev_ptr,
; and prev_dev are all set up.
;
;================================================================
release_dev PROC
with cfst_data
lda <prev_dev ; special case if 1st entry
ora <prev_dev+2
bne normal_remove
ldy #dd_next_dev ; remove the 1st item in the list
lda [<cur_dev_ptr],y ; by making the 2nd item into
sta |open_devices ; the 1st item
iny
iny
lda [<cur_dev_ptr],y
sta |open_devices+2
bra dispose_device ; and dump the memory
normal_remove
ldy #dd_next_dev ; get the link
lda [<cur_dev_ptr],y
pha
iny
iny
lda [<cur_dev_ptr],y
pha
ldx <prev_dev ; and connect it one back
ldy <prev_dev+2
jsl deref
stx <cur_dev_ptr
sty <cur_dev_ptr+2
ldy #dd_next_dev+2 ; (restore comes off backwards)
pla
sta [<cur_dev_ptr],y
dey
dey
pla
sta [<cur_dev_ptr],y
dispose_device
ldx <cur_dev ; and deallocate the memory
ldy <cur_dev+2
jsl release_seg
rts
ENDP
eject
;================================================================
;
; setup_rw_params
;
; Copy the data out of a read or write parameter block and
; get the fcr.
;
; INPUTS: X = Call number x 2
; Y = Call Class x 2
;
; OUTPUTS: my_ref_num = file reference number
; drvr_buf_ptr = user buffer pointer
; my_req_count = requested transfer count
; my_fcr = vp of active fcr
; my_fcr_ptr = ptr to active fcr
;
; This call will return to the shell if there is a parameter
; count error.
;
;================================================================
setup_rw_params PROC
sty <call_class_2 ; which class
tya
beq read_params ; 0
lda [<param_blk_ptr] ; 1- get pcount
cmp #$0004 ; at least minimum pCount?
bcc @bad_pCount ; no...
cmp #$0006 ; max pcount + 1
blt read_params
@bad_pCount
brl pcount_error ; no.
read_params
lda [<param_blk_ptr],y
sta <my_ref_num
iny
iny
lda [<param_blk_ptr],y
sta <drvr_buf_ptr
iny
iny
lda [<param_blk_ptr],y
sta <drvr_buf_ptr+2
iny
iny
lda [<param_blk_ptr],y
sta <my_req_count
iny
iny
lda [<param_blk_ptr],y
sta <my_req_count+2
; Get this guy's fcr
;================================================================
;
; set_fcr_ptr
;
; On refnum calls, the SCM sets up fcr_ptr with the VP of
; the corresponding fcr. Pull that value and convert it
; to a true pointer 'my_fcr_ptr'.
;
;================================================================
Entry set_fcr_ptr
set_fcr_ptr
ldx <fcr_ptr
ldy <fcr_ptr+2
jsl deref
stx <my_fcr_ptr
sty <my_fcr_ptr+2
rts
ENDP
eject
;================================================================
;
; store_transfer
;
; Compute an offset into the parameter block and save the
; transfer count passed in X. Used for read and write.
;
; Note: Since the minimum pcount for read/write includes
; the transfer_count position, we don't need to
; verify before writing into it.
;
; INPUTS: A,X = transfer count lo, hi
; call_class_2 = call class x 2
; param_block_ptr = ptr to paramter block
;
; OUTPUTS: transfer_count field of parameter block contains
; the transfer count.
;
;================================================================
store_transfer PROC
pha ; store 1/2 of xfer ct
clc ; determine xfer ct position
lda <call_class_2 ; class 0 - offset 10
adc #0010 ; class 1 - offset 12
tay ; offset -> Y
pla ; get lo word
sta [<param_blk_ptr],y
iny
iny
txa ; hi word -> A
sta [<param_blk_ptr],y
rts
ENDP
eject
;================================================================
;
; do_startup
;
; Initialize the FST.
;
; Output: C = clear (call can't fail!)
;
;================================================================
do_startup PROC
with cfst_data
ConsVersNum equ $3000
stz |open_devices ; no open devices yet
stz |open_devices+2
; Scan the device list looking for the .CONSOLE driver
stz <drvr_dev_num ; call the dispatcher directly
lda #get_dib_ptr ; get_dib_ptr call
sta <drvr_call_num
stz <dev_id_ref ; start at device #0 (incremented before use)
@scan_loop
inc <dev_id_ref ; bump the device number
jsl <dev_dispatcher ; get the DIB pointer
bcc @check_info
@not_found
stz cons_dev_num ;we don't know where the console driver is
clc
rtl
@check_info
ldy #dev_id_num ; check the device ID first
lda [<drvr_dib_ptr],y
cmp #$000A
bne @scan_loop ; if not console driver, skip to next device
ldy #dev_char ; look at the characteristics
lda [<drvr_dib_ptr],y
and #%1111101111111111 ; doesn't matter whether the name is fixed or not (gab)
cmp #$0B60
bne @scan_loop ; sorry I don't know this console
ldy #slot_num ; check the slot number
lda [<drvr_dib_ptr],y
cmp #$8003 ; fake slot 3
bne @scan_loop
ldy #unit_num
lda [<drvr_dib_ptr],y
dec a
bne @scan_loop ; we want unit 1
ldy #ver_num
lda [<drvr_dib_ptr],y ; we can only use version 3 or greater
and #$FFF0 ; don't care about alpha, beta, or whatever
cmp #ConsVersNum
bcc @scan_loop
; this particular driver has passed all the tests, so it must be the REAL
; .CONSOLE driver.
lda <dev_id_ref ; passed all tests, copy info to
sta cons_dev_num ; private variables
sta <drvr_dev_num ; set up for following driver calls
lda <drvr_dib_ptr
sta cons_dib_ptr
lda <drvr_dib_ptr+2
sta cons_dib_ptr+2
; Now that we've found the console driver, make an add_trap call to tie into
; the console driver's execution sequence, then a reset_trap to get control during
; a driver call to learn the location of the driver's internal entry point.
;
;Before we can use the console driver we must open it first
;
lda #drvr_open
sta <drvr_call_num
jsl dev_dispatcher
bcs @not_found ;oops!
;
;Issue a reset_trap call to the console driver. This will nuke anyone's installed handler
;
stz <drvr_req_cnt ;zero request count for this call
stz <drvr_req_cnt+2
stz <drvr_clist_ptr ;no control list for this one
stz <drvr_clist_ptr+2
lda #$8007 ;reset trap address
sta <drvr_ctrl_code
lda #drvr_control ;it's a control call
sta <drvr_call_num
jsl dev_dispatcher ;call the dispatcher
bcs @error_close ;oops!
;
;Add in my console driver handling routine.
;
lda #$0004 ;setup the request count
sta <drvr_req_cnt
stz <drvr_req_cnt+2
lda #ctrl_list ;address of control list
sta <drvr_clist_ptr
lda #^ctrl_list
sta <drvr_clist_ptr+2
lda #$8006 ;Add_Trap address
sta <drvr_ctrl_code
lda #drvr_control ;it's a control call
sta <drvr_call_num
jsl dev_dispatcher ;call the dispatcher
bcs @error_close ;oops!
;
;Reset the handler again.
; Note: this call will cause our Trap_Handler to be called, then
; removed. How's that for trickyness?!?
;
stz <drvr_req_cnt ;zero request count for this call
stz <drvr_req_cnt+2
stz <drvr_clist_ptr ;no control list for this one
stz <drvr_clist_ptr+2
lda #$8007 ;reset trap address
sta <drvr_ctrl_code
lda #drvr_control ;it's a control call
sta <drvr_call_num
jsl dev_dispatcher ;call the dispatcher
;
; Now we can close the driver again
;
@error_close
php
pha ;save error code if any
lda #drvr_close
sta <drvr_call_num
jsl dev_dispatcher
pla
plp
bcc @all_done
brl @not_found
@all_done
rtl
ctrl_list dc.l trap_handler
;====================================================================================================
;trap_handler: This routine is called directly by the console driver!
;
;Input: A = call number
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = console driver bank.
;
;
;Output: A = undefined
; X = undefined
; Y = undefined
; P = nvmxdizc
; ..000...
; b = console driver bank
;
;
;====================================================================================================
longa on
longi on
trap_handler
Import Console_Address
php
pha
;
;Picture of the stack:
;
; --------------
; A Register $0001
; --------------
; Process $0003
; --------------
; Return addr $0004
; --------------
;
sec
lda 4,s ;get the return address
sbc #$0003 ;get address of Vector call routine!
tax
lda 6,s
and #$00FF
sbc #$0000
;
;Console_Address jsl $000000 ;call console
;
; $22 00 00 00
;
xba ;put bank address in high byte of accum.
sta >console_address+2
txa ;low 16 bits of console address.
sta >console_address+1
pla ;restore A register and flags
plp
rtl ;let the call continue please
ENDP
eject
;================================================================
;
; do_shutdn
;
; Shut down the FST.
;
; Output: C = Clear? gotta check this....
;
;================================================================
do_shutdn PROC
clc
rtl
ENDP
eject
;================================================================
;
; do_open
;
; This is the entry point for the open command.
;
; If it's class 0, the device is opened as available,
; and a refnum is returned.
;
; If it's class 1, the device is opened per the request
; access, a refnum is returned, and if there are enough
; params, the access type is returned. All other fields
; are untouched.
;
; INPUTS: X = Call Number * 2
; Y = Class number * 2
; dev_num = device number (set up by SCM)
;
;================================================================
do_open PROC
with cfst_data
stz <req_access ; default=0=open as permitted
stz <my_pcount ; disables stores if class 0
sty <call_class_2 ; which class
tya
beq open_1
lda [<param_blk_ptr]
sta <my_pcount
cmp #$0010 ; max pcount + 1
blt open_0
brl pcount_error
open_0
cmp #0003
blt open_1 ; <req_access specified?
ldy #0008
lda [<param_blk_ptr],y
sta <req_access
;================================================================
;
; Step 1: Based on the access (a) requested and (b) allowed,
; generate an access bits word for this open.
;
; In addition, I check the driver type. If its a
; block device, I must coerce the SCM into more
; bouncing.... So I return 'File System Not Supported.'
;
;================================================================
open_1
stz <drvr_dev_num ; make get_dib_ptr call
lda #get_dib_ptr
sta <drvr_call_num
lda <dev_num
sta <drvr_buf_ptr
jsl dev_dispatcher
bcc got_the_dib
lda #file_not_found
brl error_exit ; probably bad device number
got_the_dib
ldy #8 ; address of characteristics
lda [<drvr_dib_ptr],y
and #$0080 ; block dev or char?
beq dib_says_char
lda #unknown_vol ; bounce!
brl error_exit
dib_says_char
lda [<drvr_dib_ptr],y ; now check access bits
asl a ; xxxxxxxxxWRxxxxx ->
asl a ; 00000000000000WR
asl a
xba
and #0003
sta <dev_access
lda <req_access ; did they request 'as available'
beq as_avail
and #$FFFC ; any other modes requested?
beq set_access
lda #invalid_access ; yes, they're not supported!
brl error_exit
set_access
lda <req_access ; ok, take the requested modes
and <dev_access
bra store_access
as_avail
lda <dev_access ; take whatever's available
store_access
sta <grant_access
ldx <my_pcount ; response requested?
cpx #0005
blt open_2
ldy #0012 ; yes, store it
sta [<param_blk_ptr],y
;================================================================
;
; Step 2: See if we already know this device. If so, just
; increment its open_stat. If not, create a new
; dev_data and actually open the device.
;
;================================================================
open_2
stz <my_open_flag
lda <dev_num
sta <my_dev_num
lda <dev_num ; open the driver
sta <drvr_dev_num
jsr find_dev ; do we already know it?
bcc inc_open
lda #drvr_open
sta <drvr_call_num
jsl dev_dispatcher
bcc open_ok
brl error_exit
open_ok
inc <my_open_flag ; 1 = opened this call
lda <dev_num ; create the new device data
jsr add_dev
bcc inc_open
pha ; (stack futz, see open_punt)
brl punt_2 ; can't add! close the driver.
inc_open ; ; flag of # opens
ldy #dd_open_stat
lda [<cur_dev_ptr],y
inc a
sta [<cur_dev_ptr],y
;================================================================
;
; Step 3: Create an FCR. Certain fields will have to be
; filled in.
;
;================================================================
ldy <call_class_2 ; class x 2 -> path ptr offset
iny
iny
lda [<param_blk_ptr],y
tax ; path ptr -> X,Y
iny
iny
lda [<param_blk_ptr],y
tay
lda <call_class_2 ; class 0 needs conversion
bne get_the_fcr
; convert class 0 string to class 1 string in local buffer
phy ; source ptr
phx
pea string_buffer>>16 ; dest ptr
pea string_buffer
jsl cvt_0to1
ldx #<string_buffer
ldy #^string_buffer
; insert the new (or the original) string into a new fcr
get_the_fcr
lda #my_fcr_length
jsl alloc_fcr ; get the fcr memory
bcc fcr_ok
lda #out_of_mem
brl open_punt
fcr_ok
stx <my_fcr ; make a real pointer to it
sty <my_fcr+2
jsl deref
stx <my_fcr_ptr
sty <my_fcr_ptr+2
lda <dev_num ; fill in the device number
ldy #fcr_dev_num
sta [<my_fcr_ptr],y
lda #cfst_id ; fill in my ID number
ldy #fcr_fst_id
sta [<my_fcr_ptr],y
lda #$FFFF ; 'no associated volume'
ldy #fcr_vol_id
sta [<my_fcr_ptr],y
lda <grant_access
ldy #fcr_access
sta [<my_fcr_ptr],y ; access
lda #0000 ; newline starts out disabled
ldy #fcr_mask
sta [<my_fcr_ptr],y
ldy #fcr_ref_num ; return file ref number
lda [<my_fcr_ptr],y
ldy <call_class_2 ; class + 0 = ref # index
sta [<param_blk_ptr],y
brl ok_exit ; success!
;================================================================
;
; open_punt
;
; Some sort of error occurred, probably out of memory. If
; this was the first instance of opening the driver, close
; it before returning the error.
;
;================================================================
open_punt
ldx <my_open_flag ; did we just open it?
beq go_error
; this call opened the driver. Since something along the line
; failed, we need to close it again.
pha ; remember the error code
jsr find_dev ; seek out the new record
jsr release_dev ; and nuke it
; entry point which just closes (from add_dev failure)
punt_2
lda <dev_num ; and close the driver
sta <drvr_dev_num
lda #drvr_close
sta <drvr_call_num
jsl dev_dispatcher
pla
go_error
brl error_exit
ENDP
eject
;================================================================
;
; do_read
;
; This implements the read call. Basically its a straight-
; through translation from GS/OS to device-driver format.
; Newline is implemented at this level, so for optimization,
; there are two methods used: If newline is disabled, the
; entire requested block is transferred by the device. If
; newline is enabled, bytes are transferred one at a time
; until the newline is matched.
;
; Created: July 21, 1987
; Modified: November 4, 1987
; Author: Andy Stadler
;
; Copyright (C) 1987 Apple Computer, Inc.
; All Rights Reserved.
;
; INPUTS: X = Call number x 2
; Y = Call Class x 2
; Param_Block containing:
; Reference #
; Buffer Pointer
; Request Count
;
; OUTPUTS: C = clear if no error, set if error
; A = Error Code
; Param_Block with response:
; Transfer Count
;
; Possible Errors:
; $23 Driver not open
; $27 IO Error
; $2F Off-line error
; $4E Access error (not read-enabled)
;
;================================================================
do_read PROC
jsr setup_rw_params ; get data from param block
; setup default data for the device driver
ldy #fcr_access ; lets check the access please
lda [<my_fcr_ptr],y
and #read_access ; see if the caller has read access
bne @access_ok
lda #invalid_access
sec
bra @exit
@access_ok
ldy #fcr_dev_num ; get the device number
lda [<my_fcr_ptr],y
sta <drvr_dev_num
lda #drvr_read
sta <drvr_call_num
stz <drvr_blk_size ; 0 = character mode
; Split here for newline or raw modes
ldy #fcr_mask ; if mask == 0, newline disabled
lda [<my_fcr_ptr],y
bne newline_read
; Execute a raw read. Read the entire requested
; amount into the user's buffer.
lda <my_req_count ; read full amount
sta <drvr_req_cnt
lda <my_req_count+2
sta <drvr_req_cnt+2
jsl dev_dispatcher
@exit
pha ; save error status
php
lda <drvr_tran_cnt
ldx <drvr_tran_cnt+2
jsr store_transfer ; return transfer count
plp ; get the error status
pla
bcc ok_read_1
brl error_exit ; quit with error code
ok_read_1
brl ok_exit
; Execute a newline read. We must read characters from the
; device driver one-at-a-time into the destination buffer.
; Then we compare them with the newline data. If the newline
; character is found, terminate the read operation.
newline_read
ldy #fcr_newline ; get the newline data
lda [<my_fcr_ptr],y
tax
iny
iny
lda [<my_fcr_ptr],y
tay
jsl deref ; make a pointer
stx <newl_buf_ptr
sty <newl_buf_ptr+2
ldy #fcr_mask ; get mask
lda [my_fcr_ptr],y
and #$00FF ; make sure hi byte is 0's!!!!
sta <my_newl_mask
ldy #fcr_newline_len ; get count
lda [my_fcr_ptr],y
sta <my_newl_count
stz <my_trans_count
stz <my_trans_count+2
lda #0001 ; read 1 at a time
sta <drvr_req_cnt
stz <drvr_req_cnt+2
; loop here reading one character at a time
read_lp
jsl dev_dispatcher
bcc read_ok
pha ; read error!
php
lda <my_trans_count
ldx <my_trans_count+2 ; read stopped by error
jsr store_transfer
plp
pla
brl error_exit
read_ok
inc <my_trans_count ; are we full yet?
bne read_ok_1
inc <my_trans_count
read_ok_1
lda <my_trans_count
cmp <my_req_count
bne read_ok_2
lda <my_trans_count+1
cmp <my_req_count+1
beq q_read
read_ok_2
lda [<drvr_buf_ptr] ; take a look at the character
and <my_newl_mask ; (removing bits of next char)
pha
ldy <my_newl_count
newline_lp
dey
lda [<newl_buf_ptr],y ; packed byte array
and #$00FF
cmp 1,s
beq hit_newline
tyx
bne newline_lp
pla ; no, not end of line.
inc <drvr_buf_ptr ; move to next char position
bne read_lp
inc <drvr_buf_ptr+2
bra read_lp ; and keep reading.
hit_newline
pla ; trash work value
q_read
lda <my_trans_count
ldx <my_trans_count+2
jsr store_transfer
brl ok_exit
ENDP
eject
;================================================================
;
; do_write
;
; Handles the write command for the character file system
; translator. Simply reprocesses the command into a device
; driver format and lets the device driver do the work.
;
; Created: July 22, 1987
; Modified: November 4, 1987
; Author: Andy Stadler
;
; INPUTS: X = Call number x 2
; Y = Call Class x 2
; Param_Block containing:
; Reference #
; Buffer Pointer
; Request Count
;
; OUTPUTS: C = clear if no error, set if error
; A = Error Code
; Param_Block with response:
; Transfer Count
;
; Possible Errors:
; $23 Driver not open
; $27 IO Error
; $2B Write protected
; $2F Off-line error
; $4E Access error (not write-enabled)
;
;================================================================
do_write PROC
entry Console_Address
with cfst_data
jsr setup_rw_params ; get info from param block
ldy #fcr_access ; lets check the access please
lda [<my_fcr_ptr],y
and #write_access ; see if the caller has write access
bne @access_ok
lda #invalid_access
sec
bra exit
@access_ok
lda #drvr_write
sta <drvr_call_num
stz <drvr_blk_size ; 0 = character mode
lda <my_req_count ; write full amount
sta <drvr_req_cnt
lda <my_req_count+2
sta <drvr_req_cnt+2
ldy #fcr_dev_num ; get the device number
lda [<my_fcr_ptr],y
sta <drvr_dev_num
cmp cons_dev_num ; is it the console driver?
beq @call_console ; yes, call the driver directly
jsl dev_dispatcher ; write the data
bra exit
@call_console
lda cons_dib_ptr ; copy the dib pointer to direct page
sta <drvr_dib_ptr
lda cons_dib_ptr+2
sta <drvr_dib_ptr
lda #drvr_write ; call # must be in accumulator
phb ; drivers are allowed to modify DBR
Console_Address jsl $000000 ; call console (address is filled in at startup
plb
exit
pha
php ; remember the error status
lda <drvr_tran_cnt
ldx <drvr_tran_cnt+2
jsr store_transfer ; return transfer count
plp
pla
bcc write_ok
brl error_exit ; exit with error status
write_ok
brl ok_exit
ENDP
eject
;================================================================
;
; do_close
;
; Attempt to close the specified refnum. If the device is
; multiply open, simply decrement the open_stat flag in its
; dev_data record, and scrap the fcr. If it's really closing,
; issue a drvr_close call.
;
; INPUTS: X = Call Number * 2
; Y = Class number * 2
;
; POSSIBLE ERROR CODES:
;
; $27 drvr_io_error
;
;================================================================
do_close PROC
tyx ; test for max pcount
beq close_0
lda [<param_blk_ptr]
dec a ; must = 1
beq close_0
brl pcount_error
close_0
jsr set_fcr_ptr
lda [<my_fcr_ptr] ; ref # is 1st entry
sta <my_ref_num
ldy #fcr_dev_num ;get device # from fcr
lda [<my_fcr_ptr],y
sta <my_dev_num
ldy #fcr_mask ; if newline data, release it
lda [<my_fcr_ptr],y
beq close_1
ldy #fcr_newline ; kill the newline buffer
lda [<my_fcr_ptr],y
tax
iny
iny
lda [<my_fcr_ptr],y
tay
jsl release_seg
close_1
lda <my_ref_num ; kill the fcr
jsl release_fcr
jsr find_dev
ldy #dd_open_stat ;how many opens?
lda [<cur_dev_ptr],y
dec a
sta [<cur_dev_ptr],y ;if other opens,
bne sk_close ;don't close the device
; This is the last close for this device, remove its
; device info record and issue a close call.
lda <my_ref_num
jsr release_dev ; remove my device record
lda <my_dev_num ;close the device
sta <drvr_dev_num
lda #drvr_close
sta <drvr_call_num
jsl dev_dispatcher
bcc sk_close
brl error_exit
sk_close
brl ok_exit ; all done!
ENDP
eject
;================================================================
;
; do_flush
;
; Sends the flush command to the device driver, if that device
; is open.
;
; Inputs: X = Call number x 2
; Y = Class number x 2
;
; Outputs: A = error code
; C = clear if ok, set if error
;
; Error Codes:
;
; $27 not open (someone else closed the device)
; $2F off line
;
;================================================================
do_flush PROC
tya ; check class
beq params_ok
lda [<param_blk_ptr]
dec a ; pcount must = 1 or 2
beq params_ok
dec a ; pcount of 2 now allowed 3/27/90
beq params_ok ; by Monte Benaresh
brl pcount_error
params_ok
lda [<param_blk_ptr],y
sta <my_ref_num
jsr set_fcr_ptr
ldy #fcr_dev_num ; get device number
lda [<my_fcr_ptr],y
sta <drvr_dev_num
lda #drvr_flush ; send command to driver
sta <drvr_call_num
jsl dev_dispatcher
bcc flush_ok
brl error_exit ; send the error through
flush_ok
brl ok_exit
ENDP
END