antoine-source/scsi2/SCSI.Drivers/SCSI Driver Mgmt

1 line
163 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;*******************************************************
;
; SCSI Driver Management Routines.
;
; Written by Matt Gulick. Started May 16,1988
;
; Copyright Apple Computer, Inc. 1988,89
;
;*******************************************************
;*******************************************************
;
; This file contains the subroutines needed by the
; SCSI Driver for things such as Getting RAM, Building
; data areas, calling outside routines for DIB
; management, as well as other generic routines that
; are used to make life easier for the driver. Also
; included in this file is the record definition for
; the DIB structure including all it's extensions.
;
;*******************************************************
;*******************************************************
;
; Revision History:
;
;*******************************************************
; May 16, 1988 File started.
; May 17, 1988 DIB Record defined.
; June 6, 1988 Main Driver Written.
; Jun 20, 1988 Update Input/Output Data in Comments
; Apr 14, 1989 Added Conditional Logic to remove
; block routines from character device
; assemblies.
STRING PASCAL
BLANKS OFF
PAGESIZE 70
PRINT NOGEN
PRINT NOMDIR
MACHINE M65816
IMPORT stat_cont
IMPORT master_uid
IMPORT scsi_uid
IMPORT scsi_mgrnum
IMPORT rout2_s_disp
IMPORT hndl_offset
IMPORT buff_len
IMPORT page_cnt
IMPORT dpi_overide
IMPORT call_type
IMPORT main_drvr
IMPORT internal
; IMPORT disk_switch
IMPORT ram_page_cnt
IMPORT internal_buff
IMPORT default_dib
IMPORT first_time
IMPORT only_one
IMPORT part_cnt
IMPORT t_dvc_blocks
IMPORT f_partition
IMPORT part_num
IMPORT vPart_cnt
IMPORT rebuild
IMPORT trash_it
IMPORT direct_page
IMPORT exit_dpage
IMPORT gsos_dpage
IMPORT saved_zp
IMPORT valid
IMPORT dvc_ram
IMPORT sense_data
IMPORT dvc_count
IMPORT result
IMPORT divend
IMPORT divsor
IMPORT max_blk_cnt
IMPORT tot_dib_cnt
IMPORT dib_data_struct
IMPORT main_caller
IMPORT curr_hndl
IMPORT new_dib
IMPORT scratch0
IMPORT scratch1
IMPORT scratch2
IMPORT scratch3
IMPORT killer_blk
IMPORT auto_sense_data
IMPORT temp_acc
IMPORT pdi_flag
IMPORT new_list
IMPORT new_dib_cnt
IMPORT new_dib_list
IMPORT real_unit ;*************************
IMPORT m_blk_size
IMPORT m_blk_cnt
IMPORT m_rslt
ENTRY zero_mem
ENTRY get_space
ENTRY make_dib
ENTRY find_empty
ENTRY chk_ram
ENTRY rebld_dibs
ENTRY dibicise_new_ram
ENTRY dibicise_new_dib
ENTRY set_link_ptrs
ENTRY major_update
ENTRY minor_update
ENTRY fix_unit_1
ENTRY add_dib_ptr
ENTRY do_post_install
ENTRY read_pm_blk
ENTRY rpm_blk_num
; ENTRY wpm_blk_num
ENTRY chk_map
ENTRY no_partition
ENTRY check_map_entry
ENTRY c_strt_buffer
ENTRY c_strt_rqst_cnt
ENTRY c_strt_blk_num
ENTRY cache_valid
ENTRY put_in_cache
ENTRY get_from_cache
ENTRY test_unit_rdy
ENTRY read_capacity
ENTRY mode_sense
ENTRY mode_sense2
ENTRY start_unit
ENTRY stop_unit
ENTRY set_512_mode
ENTRY notify_me
ENTRY set_disk_sw
ENTRY trash_volume
ENTRY save_dp
ENTRY restore_dp
ENTRY set_our_dp
ENTRY check_532_rw
ENTRY munge_532
ENTRY divide
PRINT OFF
INCLUDE 'scsihd.equates'
INCLUDE 'M16.MEMORY'
INCLUDE 'M16.UTIL'
INCLUDE 'M16.MISCTOOL'
PRINT ON
EJECT
;____Mem_Manager_____
;*******************************************************
;
; This routine is used to Get a User ID from the
; memory manager for the SCSI Driver. This is
; required to get a userID for access reference.
;
; Inputs: None.
;
; Outputs: SCSID_uID = userID.
; Registers = Scrambled
;
; Errors: $0207 iderr Invalid userID.
;
;*******************************************************
EXPORT get_mm_id
get_mm_id PROC
;
; Get userID.
;
pushword #$0000
pushword #scsi_duid
_GetNewID
pla
sta |master_uid
sta |scsi_uid
@rts rts
ENDP
EJECT
;*******************************************************
;
; This routine is used to get the SCSI Manager Number
; from the Supervisory Dispatcher at startup time.
;
; Inputs: None.
;
; Outputs: scsi_mgrnum = SCSI Manager Number
; All Registers = scrambled
;
; Errors: None.
;
;*******************************************************
EXPORT get_scsimgr
get_scsimgr PROC
;
; Get userID.
;
lda #$0000 ; Supervisor Driver Number (Unknown at this time)
tax ; Supervisor Call Number ($0000)
ldy #$0002 ; Spervisor ID for SCSI Manager ($0002)
jsr |rout2_s_disp
stx |scsi_mgrnum ; Preserve SCSI Driver Number for later use.
rts
ENDP
EJECT
;*******************************************************
;
; 'zero_mem'
;
; This routine will fill a section of memory with
; $00s.
;
; Inputs: Acc = Buffer Size
; X = Low Byte of new RAM Address
; Y = High Byte of new RAM Address
;
; Outputs: scsi_zp0 = Long Pointer to new RAM
; Address
; Acc = Scrambled
;
; Errors: None.
;
;*******************************************************
EXPORT zero_mem
zero_mem PROC
;
; Save the buffer size.
;
sta @buff_size
;
; Setup MOVE_INFO call for a
; non-incrementing source and an
; incrementing destination.
;
pushlong #null_buff ;Source
phy ;Destination High Word
phx ;Destination Low Word
;
; Restore buffer size. If = 0 then
; we are to do an entire bank or at
; least 64k bytes. This may cross
; banks.
;
lda @buff_size
beq @do_64k
pea $0000
pha
bra @move_type
@do_64k pea $0001
pea $0000
@move_type pea #move_scon_dinc
jsl move_info
@rts rts
;
; Data for this call
;
@buff_size dc.w null
null_buff dc.w null
ENDP
EJECT
;*******************************************************
;
; This routine is used to request a section of RAM from
; the memory manager for use by the driver. This
; routine allocates space for as requested by the byte
; count in the Acc.
;
; Inputs: Acc = Number of bytes requested.
; 0 = 1 bank
;
; Y = Offset to where the Handle
; should be stored within
; the allocated structure.
;
; Outputs: X = Low Word of new RAM Address
; Y = High Word of new RAM Address
; scsi_zp0 = Long Pointer to new RAM
; Address
; Acc = Scrambled
;
; Errors: Not enough memory if carry set.
;
;*******************************************************
EXPORT get_space
get_space PROC
;
; Request Acc bytes of RAM from
; Memory Manager.
;
sty |hndl_offset
pushlong #00000000 ;Space for result handle
cmp $0000 ;Is it Null?
beq @null ;Yes. Request 1 bank
pushword #0000 ;Size in bytes of requested memory.
bra @stuff_a
@null pushword #0001 ;Request 1 bank
@stuff_a sta |buff_len
pha
pushword scsi_uid ;Our User ID
;Attributes of requested mem.
pea attrfixed++\
attrnospec++\
attrpage
pushlong #00000000 ;Location Pointer. Unused.
_newhandle
plx ; Get handle
ply
bcs @rts ; Exit if an error.
;
; Save Handle for later
;
phy
phx
;
; DEREF the Handle and get a
; pointer for the new RAM.
;
stx <scsi_zp0
sty <scsi_zp0+2
ldy #$0002
lda [scsi_zp0]
tax
lda [scsi_zp0],y
tay
;
; Stuff pointer for indirect access.
;
stx <scsi_zp0
sty <scsi_zp0+2
;
; Call routine to zero out the
; new memory
;
lda |buff_len
jsr zero_mem
;
; Stuff Handle in RAM. Low byte first
;
ldy |hndl_offset
pullword [scsi_zp0],y
iny
iny
pullword [scsi_zp0],y
;
; Restore X and Y to pointer status
;
ldx <scsi_zp0
ldy <scsi_zp0+2
;
; Clean Exit.
;
clc
@rts rts
ENDP
EJECT
;*******************************************************
;
; This routine is used to request a section of RAM from
; the memory manager for use by the driver. This
; routine allocates space for the get_devices call to
; the SCSI Manager.
;
; Inputs: None.
;
; Outputs: dvc_list = Long Pointer to device List
; next_dib = Long Pointer to First DIB
; first_dib = Long Pointer to the beginning
; of the RAM Allocated for the
; new DIBs.
; Acc = Scrambled
;
; Errors: Not enough memory if carry set.
;
;*******************************************************
EXPORT get_dvc_ram
get_dvc_ram PROC
;
; Request the maximum device list
; size bytes of RAM from GS/OS
; System Service calls.
;
lda #max_gdvc_buf+4 ; Allocate space for list.
ldy #$0000
ldx |direct_page ;If this is the first time, then
bne @use_ours
phx ;we don't want a DP setting of
;NULL
;
; Save away GS/OS Direct Page values
; until we have our own direct Page.
;
jsr save_dp
lda #max_gdvc_buf+4 ; Allocate space for list.
ldy #$0000
ldx |gsos_dpage
stx |direct_page
jsr |get_space
pla ;Restore our settings preserving X & Y
sta |direct_page
sta |exit_dpage
bra @over_use_ours
@use_ours jsr |get_space
;
; Store newly allocated buffer
; pointer into the parm list for
; the get devices call. Account
; for the first 4 bytes that
; contain the handle associated
; with this memory segment.
;
@over_use_ours bcs @rts_long
clc
txa
sta |dvc_ram
adc #$0004
sta |dvc_buff
sta <dvc_list
tya
sta |dvc_ram+2
adc #$0000
sta |dvc_buff+2
sta <dvc_list+2
;
; Setup Pointer to our parm list
; in GS/OS Direct Page.
;
ldx |gsos_dpage
lda #get_dev_lst
sta >smgr_pl_ptr,x
lda #^get_dev_lst
sta >smgr_pl_ptr+2,x
;
; Load registers with their
; required values.
;
lda |scsi_mgrnum
ldx #cmd_get_dvc
jsr |rout2_s_disp
@rts_long bcs @rts
;
; Restore our direct page Device List
;
lda |dvc_buff
sta <dvc_list
lda |dvc_buff+2
sta <dvc_list+2
;
; First word in [dvc_list}] contains
; the address of a 1 page area that
; we will be using for our direct
; page. Get it and move the
; 'dvc_list' pointer to our direct
; page area, but only if we have not
; yet done it.
;
lda |direct_page
bne @we_did_it ;It's already been done.
lda [dvc_list]
tax
lda <dvc_list
sta >dvc_list,x
lda <dvc_list+2
sta >dvc_list+2,x
txa
sta |direct_page
sta |exit_dpage
;
; Undocumented feature. Store the SCSI
; Managers Direct Page in the Default DIB
; for external Access.
;
clc
adc #$0100
sta >default_dib+dib.handle
;
; Set our Direct Page with the first
; 'x' Bytes equal to the GS/OS DP
; settings.
;
;
clc ; Calculate Source Address of GS/OS
tdc ; Direct Page that we want.
sta |gsos_dpage ; Preserving GS/OS DP for later use.
adc #dev_num
pea $0000
pha
;
clc ; Calculate Destination Address of
lda |direct_page ; our Direct Page that we want.
adc #dev_num
pea $0000
pha
pushlong #dib_ptr+4 ;Length of the move
pushword #move_sinc_dinc
jsl move_info ;Move the data
;
; Restore the Direct Page that we
; borrowed until this was done.
;
jsr restore_dp
;
; Set our Direct Page.
;
lda |direct_page
tcd
;
; Check for zero Devices.
;
@we_did_it ldy #$0002
lda [dvc_list],y
beq @no_devices
;
; Ensure that device count is
; in range.
;
cmp #$0100+1 ;No more than 256 devices
blt @count_ok
lda #max_dvc_cnt ;Max Count
@count_ok sta |dvc_count
jsr |make_dib
stx <next_dib
stx <first_dib
sty <next_dib+2
sty <first_dib+2
lda #no_error
clc
;
; Restore the Direct Page that we
; borrowed until this was done.
;
@rts pha
php
jsr restore_dp
plp
pla
rts
;
; No Devices Error.
;
; Restore the Direct Page that we
; borrowed until this was done.
;
@no_devices jsr restore_dp
lda #drvr_no_dev
sec
rts
get_dev_lst dc.w scsi_dtype
dc.l notify_me
dvc_buff dc.l null
ENDP
EJECT
;*******************************************************
;
; This routine is the inverse of the above routine.
; It calls the memory manager to deallocate the space
; we used to request the device list from the SCSI
; manager.
;
; Inputs: X = Low byte of Buffer pointer
; Y = High byte of Buffer pointer
;
; Outputs: All Regs = Scrambled
;
; Errors: Not enough memory if carry set.
;
;*******************************************************
EXPORT fre_dvc_ram
fre_dvc_ram PROC
;
; Remove the device list from
; active use and free the mem.
;
; Stuff pointer for indirect access.
;
lda |dvc_ram
sta <scsi_zp0
lda |dvc_ram+2
sta <scsi_zp0+2
ldy #$0002 ;High byte first on stack
pushword [scsi_zp0],y
pushword [scsi_zp0]
_disposehandle
rts
ENDP
EJECT
;____DIB_Mem_Mgr_____
;*******************************************************
;
; This routine is used to request a section of RAM from
; the memory manager for use by the driver. This
; routine allocates space for 1 DIB.
;
; Inputs: Acc = $00xx = DIB Count.
; Null = $0100
;
; Outputs: X = Low Byte of new RAM Address
; Y = High Byte of new RAM Address
; scsi_zp0 = Long Pointer to new RAM
; Address
; Acc = Scrambled
;
; Errors: Not enough memory if carry set.
;
;*******************************************************
EXPORT make_dib
make_dib PROC
;
; Request $100 bytes of RAM for
; each DIB from GS/OS System
; Service calls.
;
and #$00ff
sta |ram_page_cnt
xba ;Cheap multiply by $0100
ldy #dib.handle ;Offset to where we want the handle
jsr |get_space ;Allocate space for DIBs.
bcc @rts
stz |ram_page_cnt
@rts rts
ENDP
EJECT
;*******************************************************
;
; This routine is called via a 'jsl' and returns the
; page number of the caller in X and Y with X being
; the low byte. This is intended for use by the
; completion routines in the DIB as a tool to find
; the DIB's starting location in memory
;
; Inputs: None.
;
; Outputs: X = Low byte of callers page
; Y = High byte of callers page
; Acc = Scrambled
;
; Errors: None.
;
;*******************************************************
EXPORT g_dib_ptr
g_dib_ptr PROC
;
; Get the callers return
; address from the stack,
; strip down to a page number,
; transfer to X and Y and
; return to caller via an 'rtl'
;
lda <1,s
sec
sbc #dib.complet+3-dib.linkptr
tax
lda <3,s
and #$00ff
tay
rtl
ENDP
EJECT
;*******************************************************
;
; 'ADD_DIB_RAM'
;
; This routine searches for the last DIB for this
; driver, allocates enough ram for 4 additional DIBs
; and links them in.
;
; Inputs: dib_ptr = Last DIB in Device link
; add_dib_here = Where the new DIB is to be built
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if not enough memory
;
;*******************************************************
EXPORT add_dib_ram
add_dib_ram PROC
;
; 'ADD_DIB_HERE' Pointer now points
; to the last DIB currently allocated
; and preserve location of last DIB.
;
lda <add_dib_here
sta <last_dib
pha ;Save and later restore
lda <add_dib_here+2
sta <last_dib+2
pha ;Save and later restore
;
; Get some RAM for a few more DIBs..
;
lda #dibs_2_make
jsr make_dib
bcc @over_0
;
; Got an Error. Fix the Stack and Exit.
;
tax
pla ;Saved, and now restore
sta <last_dib+2
pla ;Saved, and now restore
sta <last_dib
txa
rts
;
; Do First link
;
@over_0 sty <first_dib+2
stx <first_dib
;
; Dibicise first new DIB in the new
; Memory.
;
sec ;Don't do the links.
jsr dibicise_new_ram
;
; Do the Head and Forward links also.
;
lda #null
ldy #dib.headlnk
sta [first_dib],y
ldy #dib.fdvclnk
sta [first_dib],y
ldy #dib.unitnum ;Don't forget the unit number
sta [first_dib],y
;
; Finalise the new DIB Structure.
;
dec |main_caller
;********
;******** 6/12/89 Begin fix MSG
;******** rebuild flag being destroyed.
;********
lda |rebuild ;The state of this flag must be preserved.
pha
stz |rebuild ;Set to zero. (Full Rebuild)
jsr set_link_ptrs ;Now we do the links.
pla
sta |rebuild ;Restore the callers state for this flag.
; stz |rebuild ;Set to zero. (Full Rebuild)
; jsr set_link_ptrs ;Now we do the links.
;********
;******** 6/12/89 End fix MSG
;******** rebuild flag being destroyed.
;********
;
; Finish the last three links. They
; live in the new memory segment.
;
lda #dibs_2_make-1
sta @loop_cnt
@link_loop clc
lda <next_dib
sta <last_dib
adc #dib_size
sta <next_dib
lda <next_dib+2
sta <last_dib+2
adc #^dib_size
sta <next_dib+2
dec |main_caller
;********
;******** 6/12/89 Begin fix MSG
;******** rebuild flag being destroyed.
;********
lda |rebuild ;The state of this flag must be preserved.
pha
stz |rebuild ;Set to zero. (Full Rebuild)
jsr dibicise_new_dib
pla
sta |rebuild ;Restore the callers state for this flag.
; stz |rebuild
; jsr dibicise_new_dib
;********
;******** 6/12/89 End fix MSG
;******** rebuild flag being destroyed.
;********
dec @loop_cnt
bne @link_loop
;
; Clean Exit
;
; Reinitialise mem_dib_cnt to zero.
;
lda #null
ldy #dib.mem_dib_cnt
sta [first_dib],y
;
; Wipe out last link pointer to no where.
;
sta [next_dib]
ldy #dib.linkptr+2
sta [next_dib],y
pla ;Saved, and now restore
sta <last_dib+2
pla ;Saved, and now restore
sta <last_dib
clc
rts
;
; Data Area.
;
@loop_cnt dc.w null
ENDP
EJECT
;*******************************************************
;
; FIND_EMPTY
;
; This routine serches forward from the current DIB
; looking for an empty. If it exits with the carry
; clear, then one has been found. If the carry is
; set, then it points to the last DIB in the linked
; list and found non empty.
;
; Inputs: <DIB_PTR = Last DIB Location
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: <ADD_DIB_HERE = Empty DIB if found
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if no empties were found
;
;*******************************************************
EXPORT find_empty
find_empty PROC
;
; Are we at the last DIB? Set beginning
; pointer for check.
;
lda <dib_ptr
sta <last_dib
sta <add_dib_here
lda <dib_ptr+2
sta <last_dib+2
sta <add_dib_here+2
;
; Are we at the last DIB? (LOOP)
;
@fnd_empty_lup ldy #$0002
lda [add_dib_here]
tax
lda [add_dib_here],y;Should never be in Bank $00
bne @advance_ptr ;No.
@sec sec ;Yes. Exit
rts
;
; Advance Ptr to next DIB.
;
@advance_ptr sta <add_dib_here+2
stx <add_dib_here
;
; Is it Free?
;
; Must have a zero Head Link Ptr,
; Forward Device Link Ptr, and
; DIB Device Number to be considered
; free.
;
ldy #dib.devnum
lda [add_dib_here],y
bne @fnd_empty_lup ;No. Check next one.
ldy #dib.headlnk
lda [add_dib_here],y
bne @fnd_empty_lup ;No. Check next one.
ldy #dib.fdvclnk
lda [add_dib_here],y
bne @fnd_empty_lup ;No. Check next one.
;
; Make the DIB Device Number non-zero
; to prvent it from being selected
; again before it is started up by
; the operating system.
;
ldy #dib.devnum
lda [add_dib_here],y
inc a
sta [add_dib_here],y
clc ;Yes. Exit
rts
ENDP
EJECT
;*******************************************************
;
; 'chk_ram'
;
; This routine checks to see if we have any room left
; in this memory segment for another DIB. If not then
; the memory manager is called and the new RAM is
; dibicised.
;
; Inputs: Acc = Unspecified
; Carry = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Carry = 1
; Acc = Error
; Carry = 0
; Acc = ram_page_cnt
; = 0 no more devices
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
;
; Start of the code segment for this.
;
EXPORT chk_ram
chk_ram PROC
;
; All done with the DIB Update now
; do we have any more RAM left to
; build further DIBs?
;
dec |ram_page_cnt
bne @adv_ptrs ;Yes we do. Continue on.
;
; Account for 'part_cnt' being equal
; to the Actual Partition Count - 1
;
sec
; clc ;Bug Fix 4/3/89
lda |dvc_count ;No we don't. Get the remaining device
adc |part_cnt ;count and add the remaining Part count
; dec a ;Subtract our current working device
; dec a ;Subtract our current working DIB
beq @exit ;Are we done? Yes.
bmi @exit ;Yes.
cmp #$00ff+1 ;No. Bound check the result and
blt @call_make_dib
@set_max lda #$00ff ;Set to max if greater.
@call_make_dib jsr make_dib ;Call routine to make the DIB RAM
bcc @update_new ;available. ERROR?
cmp #drvr_no_dev ;Yes. Then Exit
bne @sec
clc
rts
@sec sec
rts
;
; We had room in the current RAM
; Space so we will advance all the
; pointers to their next values
; within that space.
;
@adv_ptrs clc
lda <next_dib
sta <last_dib
adc #dib_size
sta <next_dib
lda <next_dib+2
sta <last_dib+2
adc #^dib_size
sta <next_dib+2
jsr dibicise_new_dib
bra @exit
;
; We have a new memory segment so
; we need to account for this when
; we advance our pointers.
;
@update_new stx <first_dib
sty <first_dib+2
lda <next_dib
sta <last_dib
lda <next_dib+2
sta <last_dib+2
clc ;Do Links Also
jsr dibicise_new_ram
;
; Clean exit.
;
@exit lda |ram_page_cnt
clc
rts
ENDP
EJECT
;____DIB_Building____
;*******************************************************
;
; REBLD_DIBS
;
; It is the function of this routine to blindly
; rebuild the DIBs for the physical device attached to
; the current DIB. If that device is non-partitioned,
; then only one DIB will be built, and a DISK SWITCHED
; will be issued. If there are more than one device,
; as in partitioned media, then all the DIBs for the
; partitions will be re-built. This is treated as if
; the device just came online for the first time. If
; the new device has fewer partitions than there are
; DIBs that are already allocated, then the left over
; will be marked as being offline. If, on the other
; hand, there are more partitions than DIBs, then new
; ones will be allocated and built. These will then be
; permanantly linked to that physical device.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if operation could not be
; completed. Out of memory for example.
;
;*******************************************************
;
; Start of the code segment for this.
;
EXPORT rebld_dibs
rebld_dibs PROC
;
; Clear Linked Device Flag. This will
; account for the situation where a
; partitioned disk has been reduced to
; a single volume.
;
stz @linked
;
; Zero the count of new devices to be
; started.
;
stz |new_dib_cnt
;
; Point to first DIB in link if any.
; If it is zero, then we are already
; there.
;
ldy #dib.headptr+2
lda [dib_ptr],y
ldy #dib.headptr
ora [dib_ptr],y
beq @over
;
; It's non-zero. Set up DIB_PTR
;
lda [dib_ptr],y
tax
ldy #dib.headptr+2
lda [dib_ptr],y
sta <dib_ptr+2
stx <dib_ptr
;
; Set Temp Pointer to first DIB in the link.
;
@over lda <dib_ptr
sta <scsi_zp6
lda <dib_ptr+2
sta <scsi_zp6+2
;
; Mark each DIB in the link as offline
; and switched.
;
@loop ldy #dib.dvcflag
lda [scsi_zp6],y
and #(dvc_online++\
pdos_part)--\
$ffff
ora #dvc_switch++\
dvc_hardofl
sta [scsi_zp6],y
;
; Mark each DIB in the link as Unlinked.
;
ldy #dib.dvcchar
lda [scsi_zp6],y
and #linked_dvc--\
$ffff
sta [scsi_zp6],y
;
; Zero Partition Offset for each DIB
;
lda #null
ldy #dib.start_blk
sta [scsi_zp6],y
ldy #dib.start_blk+2
sta [scsi_zp6],y
;
; It's non-zero. Set up next DIB_PTR
;
clc
ldy #dib.fdvcptr
lda [scsi_zp6],y
tax
beq @null
sec
@null ldy #dib.fdvcptr+2
lda [scsi_zp6],y
bne @doit
bcc @over_0
@doit sta <scsi_zp6+2
stx <scsi_zp6
bra @loop
;
; Initialize a few values.
;
@over_0 lda #$ffff ;The following values will be incrimented
sta |first_time ;with zero indicating special action.
sta |rebuild ;This is set to $ffff so that it will only
sta |only_one ;occur on the first pass through.
stz |part_cnt
stz |rpm_blk_num
stz |new_dib
;
; MAIN LOOP OF CODE SEGMENT.
;
@main_loop jsr read_pm_blk
bcc @with_data ;This device is with data
lda auto_sense_data+\ ;Is it an audio disk?
rqst_sens.addnl_sens_code
and #$00fe ;Checking for $84, $64, or $63
cmp #$0064 ;This cover $63, $64
beq @single
cmp #$0084
beq @single
sec
rts ;There was an error!
;
; Valid Partition Map?
;
@with_data jsr chk_map
bcs @done
bne @main
;
; We have a vaild Partition Map so
; we need to make an entry for each
; partition other than those with
; the partition types of 'MAP',
; 'SCRATCH', and 'FREE'. All others
; will be considered valid. We also
; only accept up to 'max_partitions'
; partitions per disk.
;
; How many partitions are there?
;
lda |pm.MapBlkCnt\
+internal_buff
bne @force_max
lda |pm.MapBlkCnt+2\
+internal_buff
xba ;Value is High --> Low
cmp #max_partitions+1
blt @cnt_ok
@force_max lda #max_partitions
@cnt_ok sta |part_cnt
cmp #$0002+1
blt @non_linked
dec @linked
@non_linked dec |part_cnt
bpl @main
;
; No valid entry. Set as if no
; partitions.
;
@single jsr no_partition ;Marks DIB as online and switched also.
jsr trash_volume ;Trash the volume if caller wishes.
;See |trash_it flag in trash_volume
lda #null
clc
rts
;
; Check validity of partiton map.
;
@main jsr check_map_entry
bcc @chk_pdos
;
; It was undefined or unusable
;
; Is the Pointer good?
;
; Is it a cold DIB?
;
ldy #dib.dvcflag
lda [add_dib_here],y
and #cold_dib
beq @over_cold ;No. Skip clear code.
;
; Mark DIB to the Default.
;
ldy #dib.dvcflag
lda #wait_mode++\ ; Wait Mode is default
cold_dib ; and they start cold.
sta [add_dib_here],y
;
; Must set Head Link Ptr, Forward
; Device Link Ptr, and DIB Device
; Number to Null.
;
lda #null
ldy #dib.devnum
sta [add_dib_here],y
ldy #dib.headlnk
sta [add_dib_here],y
ldy #dib.fdvclnk
sta [add_dib_here],y
;
; And skip it.
; Is there more to do?
;
@over_cold dec |part_cnt
bmi @do_dpi ;No!
jmp @main_loop ;Yes!
;
; Check to see if the post driver install
; list contains any entries.
;
@do_dpi jsr do_post_install
;
; Check the only one flag. If true
; then unlink it.
;
lda |only_one
bne @done
ldy #dib.dvcchar
lda [rebuild_zp],y
and #linked_dvc--\
$ffff
sta [rebuild_zp],y
;
; Clean exit.
;
@done
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
IF scsi_dtype = mcd_40\
OR scsi_dtype = direct_acc THEN
jsr mode_sense
lda |internal_buff+1
bpl @write_enable
@write_protect ldy #dib.dvcchar
lda [dib_ptr],y
and #write_allow--\
$ffff
bra @wp_chk
@write_enable ldy #dib.dvcchar
lda [dib_ptr],y
ora #write_allow
@wp_chk sta [dib_ptr],y
lda #$0000
ENDIF
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
clc
@rts rts
;
; Check the Overflow bit. If it is set,
; then this is a ProDOS Partition and we
; need to set the correct bit in the DIB.
;
@chk_pdos bvc @over_1
;
; Set the ProDOS Bit.
;
ldy #dib.dvcflag
lda [dib_ptr],y
ora #pdos_part
sta [dib_ptr],y
;
; Issue call to fix unit 1. This will
; guarantee that the first unit is also
; the first ProDOS Partition on the disk.
;
jsr fix_unit_1
;
; Get the Block Count for this
; Partition and store it in the
; DIB. The count as it exists
; in the partition data is stored
; High byte to Low byte. We will
; need to alter this to Low --> High
; format.
;
@over_1 ldy #dib.blkcnt
lda |pm.PartBlkCnt+2\
+internal_buff
xba
sta [dib_ptr],y
ldy #dib.blkcnt+2
lda |pm.PartBlkCnt\
+internal_buff
xba
sta [dib_ptr],y
;
; Now we need to get the starting
; location for this partition. This
; will be added to the requested
; block to generate a real block
; address. Also in High --> Low format.
;
ldy #dib.start_blk+2
lda |pm.PyPartStart+2\
+internal_buff
sta [dib_ptr],y
ldy #dib.start_blk
lda |pm.PyPartStart\
+internal_buff
sta [dib_ptr],y
;
; Get Location of this partition on disk.
;
ldy #dib.part_blk
lda |rpm_blk_num
sta [dib_ptr],y
;
; Mark DIB as online and switched.
;
ldy #dib.dvcflag
lda [dib_ptr],y
and #dvc_hardofl--\
$ffff
ora #dvc_switch++\
dvc_online
sta [dib_ptr],y
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
;
; Set the Write Allow/Read Allow
; Mask byte to allow all bits to pass.
;
; This is used to filter or strip out the
; Write Enable bit in the case where the
; DIB Status says that the partition is
; write enabled but the disk is physically
; write protected. This can happen with a
; Partitioned Syquest Drive.
;
lda #$ffff
sta @mask_flag
;
; Is the device Removable?
;
ldy #dib.dvcchar
lda [dib_ptr],y
and #removable
beq @non_remove ;No.
;
; Issue the MODE SENSE Call to see if
; Write Protected.
;
jsr mode_sense2 ;Issue call to use Sense Buffer
bcs @non_remove
;
; Check the Write Protected Bit.
;
lda |sense_data+\
mode.w_protect-1 ;But bit in Hi Byte
bpl @non_remove
;
; Set the Write Allow/Read Allow
; Mask byte to filter the Write Allow out.
;
lda #write_allow--\
$ffff
sta @mask_flag
;
; Set the Characteristics to Match the
; Read and Write Enables in the Partition
; Status Field.
;
@non_remove lda |pm.PartStatus+2\
+internal_buff
xba
and #vconf_wr_enable++\
vconf_rd_enable
asl a
sta @temp
ldy #dib.dvcchar
lda [dib_ptr],y
and #write_allow++\
read_allow--\
$ffff
ora @temp
and @mask_flag
sta [dib_ptr],y
ENDIF
;-------------------------------------------------------------------------------
;
; Mark DIB as Linked.
;
lda @linked
bpl @skip_link
ldy #dib.dvcchar
lda [dib_ptr],y
ora #linked_dvc
sta [dib_ptr],y
;
; Inc the ONLY_ONE Flag. If it is
; null then save the dib_ptr. On
; exit we will check this flag and
; if = 0 we will use this pointer
; to clear the linked flag.
;
inc |only_one
bne @skip_link
lda <dib_ptr
sta <rebuild_zp
lda <dib_ptr+2
sta <rebuild_zp+2
;
; Wipe out block 2 of partition or
; volume
;
@skip_link jsr trash_volume
;
; Is this a new dib?
;
bit |new_dib
bpl @set_dsw ;No. Set disk switch
ldx <dib_ptr
ldy <dib_ptr+2
jsr add_dib_ptr ;Yes. Add to the list for post driver
bra @over_dsw ; install.
;
; Set the DISK SWITCHED
;
@set_dsw
; jsr set_disk_sw
;
; Is there more to do?
;
@over_dsw stz |new_dib
dec |part_cnt
bpl @over_do_dpi ;Yes!
jmp @do_dpi ;No!
;
; All done with the DIB Update now
; do we have any more DIBs, for this
; linked device?
;
@over_do_dpi ldy #dib.fdvcptr
lda [dib_ptr],y
tax
ldy #dib.fdvcptr+2
ora [dib_ptr],y
beq @over_2
lda [dib_ptr],y
sta <dib_ptr+2
stx <dib_ptr
;
; Do the next one.
;
@jmp_main jmp @main_loop
;
; It's zero. Need to add DIBs
;
; Is there an empty DIB some where?
;
@over_2 jsr find_empty
bcc @found_empty
;
; Get room for some more DIBs.
;
jsr add_dib_ram
bcc @over_2
rts
;
; We need to set some pointers for
; the routines that we will be
; calling. These pointers are similar
; to those used in the startup code
;
; 1st. Set the pointer to the next
; DIB to be build.
;
@found_empty lda <add_dib_here
sta <dib_ptr
sta <next_dib
lda <add_dib_here+2
sta <dib_ptr+2
sta <next_dib+2
;
; Preserve the Link Pointer. Otherwise
; it will be destroyed by the dibicise
; routine.
;
ldy #dib.linkptr+2
lda [next_dib]
pha
lda [next_dib],y
pha
;
; Preserve the Handle for this RAM Segment.
; Otherwise it will be destroyed by the
; dibicise routine.
;
ldy #dib.handle+2
lda [next_dib],y
pha
ldy #dib.handle
lda [next_dib],y
pha
;
; Preserve the current Device Flags.
;
ldy #dib.dvcflag
lda [next_dib],y
pha
;
; Set the 'first_dib' pointer to the
; first dib in the same memory segment
; from the memory manager as the one
; that contains the space to be used
; for this new dib. first we get the
; handle.
;
ldy #dib.handle
lda [next_dib],y
sta <first_dib
ldy #dib.handle+2
lda [next_dib],y
sta <first_dib+2
;
; then we deref the handle.
;
ldy #$0002
lda [first_dib]
tax
lda [first_dib],y
sta <first_dib+2
stx <first_dib
;
; Tell the dibicize routine that this
; is not from the startup code. This
; also forces the descriptive names
; minor id to be changed leaving the
; major or device portion as it is.
;
dec |main_caller
;
; Dibicise the new dib.
;
jsr dibicise_new_dib
;
; Restore the current Device Flags.
;
pla
ldy #dib.dvcflag
sta [next_dib],y
;
; Restore the Handle for this RAM.
; Otherwise it is destroyed by the
; dibicise routine.
;
pla
ldy #dib.handle
sta [next_dib],y
pla
ldy #dib.handle+2
sta [next_dib],y
;
; Restore the Link Pointer. Otherwise
; it is destroyed by the dibicise
; routine.
;
pla
ldy #dib.linkptr+2
sta [next_dib],y
pla
sta [next_dib]
;
; Restore the DIB Extension Pointer.
; Otherwise it is destroyed by the
; dibicise routine.
;
clc ;we are going to add
lda <next_dib ;get low word of this dib
adc #dib.start_blk ;adc offset to ext dib ptr
ldy #dib.ext_ptr ;get offset to where this goes
sta [next_dib],y ;and store low word of ptr
lda <next_dib+2 ;get low word of this dib
adc #^dib.start_blk ;adc high offset to ext ptr
ldy #dib.ext_ptr+2 ;get offset to where this goes
sta [next_dib],y ;and store the high word ptr
;
; Rev the unit number to be unique
;
ldy #dib.unitnum
lda [next_dib],y
tax
and #max_p_mask
cmp #max_p_mask
beq @all_done
txa
clc
adc #p_mask_adder
sta [next_dib],y
;
; This flag indicates that this is a
; new dib and that the routine that
; finalizes this dib will have to
; issue a post driver install call.
;
dec |new_dib
;
; Go back to the main loop where this
; dib will be finalized and installed.
;
jmp @main_loop
;
; Clean Exit
;
@all_done clc
rts
;
; Misc Data for this sub-routine
;
@linked dc.w null
@part_num dc.w null
@vPart_cnt dc.w null
@temp dc.w null
@mask_flag dc.w null
ENDP
EJECT
;*******************************************************
;
; 'do_part_dib'
;
; This routine issues a READ call to the device and
; then uses the data returned to check for any
; partitions on the media. If partitions do exist, it
; then creates the needed dibs from those that have
; been allocated but unused, or if needed will create
; a new memory segment and continue on.
;
; Inputs: <next_dib = Next DIB start (LONG)
; Acc = Unspecified
; Carry = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: <next_dib = last DIB Built (LONG)
; Acc = Unspecified
; Carry = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
;
; Start of the code segment for this.
;
EXPORT do_part_dib
do_part_dib PROC
;-------------------------------------------------------------------------------
IF part_suprt = true THEN
;
; MAIN LOOP OF CODE SEGMENT.
;
@main_loop jsr read_pm_blk
bcc @chk_map ;Was there an error?
jsr no_partition ;
bra @done
; Valid Partition Map?
;
@chk_map jsr chk_map
bcs @done
bne @main
;
; We have a vaild Partition Map so
; we need to make an entry for each
; partition other than those with
; the partition types of 'MAP',
; 'SCRATCH', and 'FREE'. All others
; will be considered valid. We also
; only accept up to 'max_partitions'
; partitions per disk.
;
; How many partitions are there?
;
lda |pm.MapBlkCnt\
+internal_buff
bne @force_max
lda |pm.MapBlkCnt+2\
+internal_buff
xba ;Value is High --> Low
cmp #max_partitions+1
blt @cnt_ok
@force_max lda #max_partitions
@cnt_ok sta |part_cnt
dec |part_cnt
bpl @main
;
; No valid entry. Set as if no
; partitions.
;
jsr no_partition
clc
rts
;
; Check validity of partiton map.
;
@main jsr check_map_entry
bcc @chk_pdos
;
; It was undefined or unusable
; Mark DIB to the Default.
;
ldy #dib.dvcflag
lda #wait_mode++\ ; Wait Mode is default
cold_dib ; and they start cold.
sta [next_dib],y
;
; Must set Head Link Ptr, Forward
; Device Link Ptr, and DIB Device
; Number to Null.
;
lda #null
ldy #dib.devnum
sta [next_dib],y
ldy #dib.headlnk
sta [next_dib],y
ldy #dib.fdvclnk
sta [next_dib],y
;
; And skip it.
; Is there more to do?
;
dec |part_cnt
bpl @main_loop
;
; Clean exit.
;
@done clc
@rts rts
;
; Check the Overflow bit. If it is set,
; then this is a ProDOS Partition and we
; need to set the correct bit in the DIB.
;
@chk_pdos bvc @over
;
; Set the ProDOS Bit.
;
ldy #dib.dvcflag
lda [next_dib],y
ora #pdos_part
sta [next_dib],y
;
; Issue call to fix unit 1. This will
; guarantee that the first unit is also
; the first ProDOS Partition on the disk.
;
jsr fix_unit_1
;
; Get the Block Count for this
; Partition and store it in the
; DIB. The count as it exists
; in the partition data is stored
; High byte to Low byte. We will
; need to alter this to Low --> High
; format.
;
@over ldy #dib.blkcnt
lda |pm.PartBlkCnt+2\
+internal_buff
xba
sta [next_dib],y
ldy #dib.blkcnt+2
lda |pm.PartBlkCnt\
+internal_buff
xba
sta [next_dib],y
;
; Now we need to get the starting
; location for this partition. This
; will be added to the requested
; block to generate a real block
; address. Also in High --> Low format.
;
ldy #dib.start_blk+2
lda |pm.PyPartStart+2\
+internal_buff
sta [next_dib],y
ldy #dib.start_blk
lda |pm.PyPartStart\
+internal_buff
sta [next_dib],y
;
; Get Location of this partition on disk.
;
ldy #dib.part_blk
lda |rpm_blk_num
sta [next_dib],y
;
; Set this device to ONLINE.
;
ldy #dib.dvcflag
lda [next_dib],y
ora #dvc_online
sta [next_dib],y
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
;
; Set the Characteristics to Match the
; Read and Write Enables in the Partition
; Status Field.
;
lda |pm.PartStatus+2\
+internal_buff
xba
and #vconf_wr_enable++\
vconf_rd_enable
asl a
sta @temp
ldy #dib.dvcchar
lda [next_dib],y
and #write_allow++\
read_allow--\
$ffff
ora @temp
sta [next_dib],y
ENDIF
;-------------------------------------------------------------------------------
;
; Increment the partition count.
;
inc |part_num
beq @chk_ram
;
; Update the High seven bits of the
; unit number checking for the max
; value of 'max_partitions'.
;
ldy #dib.unitnum
lda [next_dib],y
tax
and #max_p_mask
cmp #max_p_mask
beq @all_done
txa
clc
adc #p_mask_adder
sta [next_dib],y
inc |vPart_cnt
;
; All done with the DIB Update now
; do we have any more DIBs, and if
; so, do we have any more RAM left
; to build further DIBs?
;
@chk_ram dec |part_cnt
bmi @all_done
dec |main_caller
jsr chk_ram
bcs @bad_call ;ERROR?
jmp @main_loop ;No. Do the next one.
@bad_call cmp #drvr_no_dev ;Yes. Then Exit
bne @sec
clc
rts
@sec sec
rts
;
; Clean Exit
;
@all_done clc
rts
;
; Data Area
;
@temp dc.w null
ELSE
;-------------------------------------------------------------------------------
;
; Clean Exit
;
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'dibicise_new_ram'
;
; This routine takes the DIB located at [last_dib] and
; copies it into the new ram space. It does this
; while preserving the handle at offset 'dib.handle'.
; By so doing, it will be possible to free this ram
; space at some later time if that is deemed correct.
;
; Inputs: <last_dib = Last DIB built (LONG)
; <first_dib = Pointer to new (LONG)
; ram space.
; Acc = Unspecified
; Carry Clear = Do Link Pointers
; Set = Skip Link Pointers
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: <last_dib = unchanged (LONG)
; <first_dib = unchanged (LONG)
; <next_dib = <first_dib (LONG)
; |curr_hndl = Handle for new ram (LONG)
;
; Acc = Unspecified
; Carry = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; If Carry Set on entry then
;
; |tot_dib_cnt = $0000 (WORD)
;
; If Carry Clear on entry then
;
; [last_dib] = points to this DIB (LONG)
; [first_dib] = defaults set (PTR)
; 'Int Pntrs' = Set to this DIB (LONG)s
; 'dispname' = Device Name (STR)
; |tot_dib_cnt = # of valid DIBs (WORD)
;
; Errors: None.
;
;*******************************************************
EXPORT dibicise_new_ram
dibicise_new_ram PROC
;
; Save Do Links Flag.
;
php
;
; Preserve Handle for this
; memory segment.
;
ldy #dib.handle
lda [first_dib],y
sta |curr_hndl
ldy #dib.handle+2
lda [first_dib],y
sta |curr_hndl+2
;
; Using the built in move routine,
; copy the last DIB built to the
; first_dib location for the next
; dib to be built.
;
pushlong <last_dib
pushlong <first_dib
pushlong #dib_size
pushword #move_sinc_dinc
jsl move_info ;Move the data
;
; Restore the handle to it's
; origonal location. This will
; then be copied to every other
; dib that is built in this
; memory segment.
;
ldy #dib.handle
lda |curr_hndl
sta [first_dib],y
ldy #dib.handle+2
lda |curr_hndl+2
sta [first_dib],y
;
; Set 'next_dib' pointer to the
; first dib in this memory
; segment.
;
lda <first_dib
sta <next_dib
lda <first_dib+2
sta <next_dib+2
ldy #dib.dvcflag
; lda [first_dib],y
; ora #cold_dib ;New DIB. Do Cold Start.
lda #wait_mode++\ ;New DIB. Do Cold Start and in Wait Mode.
cold_dib
sta [first_dib],y
;
; Reinitialise mem_dib_cnt to zero.
;
lda #null
ldy #dib.mem_dib_cnt
sta [first_dib],y
;
; Reinitialise DIB Dev Number also.
;
ldy #dib.devnum
sta [first_dib],y
;
; Finalise the new DIB Structure?
;
plp
bcc set_link_ptrs
rts
ENDP
EJECT
;*******************************************************
;
; 'dibicise_new_dib'
;
; This routine takes the DIB located at [last_dib] and
; copies it into the new ram space.
;
; Inputs: <last_dib = Last DIB built (LONG)
; <next_dib = Next DIB start (LONG)
; Acc = Unspecified
; Carry = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: <last_dib = unchanged (LONG)
; <next_dib = unchanged (LONG)
; [last_dib] = points to this DIB (LONG)
; [next_dib] = defaults set (PTR)
; 'Int Pntrs' = Set to this DIB (LONG)s
; 'dispname' = Device Name (STR)
; |tot_dib_cnt= # of valid DIBs (WORD)
; Acc = Unspecified
; Carry = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
EXPORT dibicise_new_dib
dibicise_new_dib PROC
;
; Using the built in move routine,
; copy the last DIB built to the
; next_dib location for the next
; dib to be built.
;
pushlong <last_dib
pushlong <next_dib
pushlong #dib_size
pushword #move_sinc_dinc
jsl move_info ;Move the data
;
; Finalise the new DIB Structure.
;
EXPORT set_link_ptrs
;
; Update Links and DIB ext ptrs
;
set_link_ptrs bit |rebuild
bmi @rebuild_links
clc ;we are going to add
lda <next_dib ;get low word of this dib
sta [last_dib] ;store in last dib (implied y = 0)
adc #dib.start_blk ;adc offset to ext dib ptr
ldy #dib.ext_ptr ;get offset to where this goes
sta [next_dib],y ;and store low word of ptr
ldy #dib.linkptr+2 ;get offset to high word
lda <next_dib+2 ;get the high word and
sta [last_dib],y ;store it in the last dib
adc #^dib.start_blk ;adc high offset to ext ptr
ldy #dib.ext_ptr+2 ;get offset to where this goes
sta [next_dib],y ;and store the high word ptr
;
; Zero out the Link for this DIB
;
lda #null
sta [next_dib]
ldy #dib.linkptr+2
sta [next_dib],y
;
; Zero out the Starting Block for this DIB
;
@rebuild_links lda #null
ldy #dib.start_blk
sta [next_dib],y
ldy #dib.start_blk+2
sta [next_dib],y
;
; Increment the number of active DIBs in
; this memory segement.
;
ldy #dib.mem_dib_cnt
lda [first_dib],y
inc a
sta [first_dib],y
;
; In the DIB Extension as defined for this
; driver is the root structure of the
; pointers that are used to call the SCSI
; Manager. At this time we need to update
; them so that they too point into this
; dib and not the last one.
;
clc
lda <next_dib ;Low word of Completion Pointer
adc #dib.complet
ldy #dib.compvec
sta [next_dib],y
lda <next_dib+2 ;High word of Completion Pointer
adc #^dib.complet
ldy #dib.compvec+2
sta [next_dib],y
clc
lda <next_dib ;Low word of Command Packet Pointer
adc #dib.scsicmd
ldy #dib.cp_ptr
sta [next_dib],y
lda <next_dib+2 ;High word of Command Packet Pointer
adc #^dib.scsicmd
ldy #dib.cp_ptr+2
sta [next_dib],y
clc
lda <next_dib ;Low word of Send Structure Pointer
adc #dib.trx_buff
ldy #dib.trx_ptr
sta [next_dib],y
lda <next_dib+2 ;High word of Send Structure Pointer
adc #^dib.trx_buff
ldy #dib.trx_ptr+2
sta [next_dib],y
;
; Were we called from the
; main outside loop?
;
lda |main_caller
bne @skip_links ; No. Do device links.
;
; Zero out the HEAD LINK and FORWARD LINK
; pointers.
;
ldy #dib.headlnk
sta [next_dib],y ;Acc = 0. See Load.
ldy #dib.fdvclnk
sta [next_dib],y
ldy #dib.headptr
sta [next_dib],y ;Acc = 0. See Load.
ldy #dib.fdvcptr
sta [next_dib],y
ldy #dib.headptr+2
sta [next_dib],y ;Acc = 0. See Load.
ldy #dib.fdvcptr+2
sta [next_dib],y
;
; Set slot and device words in dib
;
lda [dvc_list]
ldy #dib.slotnum
sta [next_dib],y
ldy #$0002
lda [dvc_list],y
ldy #dib.unitnum
sta [next_dib],y
;
; Set pointer to the descriptive or
; device name so that we can rev the
; name to prevent duplicates.
;
@skip_links clc
lda <next_dib
adc #dib.namelen
sta <scsi_zp0
lda <next_dib+2
adc #^dib.namelen
sta <scsi_zp0+2
;
; Was internal or externally called?
; If internal, then we need to update
; the major revision part of the
; descriptive name. If it was external,
; then we will update the minor revision
; part only.
;
lda |main_caller
beq @do_major
;
; Do the minor revision.
;
jsr minor_update
bra @clear_busy
;
; Do the major revision.
;
@do_major jsr major_update
;
; Clear artifacts. We copied this data
; from the first dib that has the
; call_active bit set. Clear the sucker.
;
@clear_busy ldy #dib.dvcchar
lda [next_dib],y
and #call_active--\
$ffff
sta [next_dib],y
;
; Clear the ProDOS Bit.
;
ldy #dib.dvcflag
lda [next_dib],y
and #pdos_part--\
$ffff
sta [next_dib],y
;
; Inc the DIB Count at this point.
; Every DIB that is succesfully built
; will be modified by the above code.
;
stz |main_caller
inc |tot_dib_cnt
clc
rts
ENDP
EJECT
;*******************************************************
;
; MAJOR_UPDATE
;
; This routine updates the major revision portion of
; the descriptive name portion of the DIB.
;
; Inputs: <DIB_PTR = DIB Location
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Better be none.
;
;*******************************************************
EXPORT major_update
major_update PROC
;
; Change name so as not to conflict
; with any other name already built.
; All names end with '00' - '99'.
;
short
ldx #$02 ;Don't change this value.
sec
lda [scsi_zp0]
;
; Reset Minor Revision to '00'.
;
pha
tay
lda #'0'
sta [scsi_zp0],y
dey
sta [scsi_zp0],y
pla
sbc #$03
tay
@name_loop lda [scsi_zp0],y
inc a
cmp #'9'+1
blt @update
lda #'0'
dex
@update sta [scsi_zp0],y
dey
dex
beq @name_loop
longmx
rts
ENDP
EJECT
;*******************************************************
;
; MINOR_UPDATE
;
; This routine updates the minor revision portion of
; the descriptive name portion of the DIB.
;
; Inputs: <DIB_PTR = DIB Location
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Better be none.
;
;*******************************************************
EXPORT minor_update
minor_update PROC
;
; Change name so as not to conflict
; with any other name already built.
; All names end with '00' - '99'.
;
short
ldx #$02 ;Don't change this value.
lda [scsi_zp0]
;
; Check the length of the string. If
; it is different from the one in the
; Default DIB, then we need to assume
; that it has been renamed. In this
; case, we will crank the count up by
; 2 but not past the max count of 31
; and append the version number
; change to that.
;
cmp |default_dib+\
dib.namelen
beq @len_ok
;
; Has it already been adjusted?
;
tay
lda [scsi_zp0],y
cmp #'0'
blt @fix_it
cmp #'9'+1
bge @fix_it
dey
lda [scsi_zp0],y
cmp #'0'
blt @fix_it
cmp #'9'+1
bge @fix_it
iny
bra @name_loop
@fix_it lda [scsi_zp0]
inc a
inc a
cmp #$31
blt @set_new_len
lda #$31
@set_new_len sta [scsi_zp0]
@len_ok tay
@name_loop lda [scsi_zp0],y
inc a
cmp #'1'
blt @force_zero
cmp #'9'+1
blt @update
@force_zero lda #'0'
dex
@update sta [scsi_zp0],y
dey
dex
beq @name_loop
longmx
rts
ENDP
EJECT
;____DIB_Support_____
;*******************************************************
;
; This call is used to determine if the current DIB is
; the first device. This routine will return true
; (with the carry clear) if the device is unlinked or,
; if linked, then it will return true if this is the
; head device.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if Unit is not the first
; device or it's equivelent.
;
;*******************************************************
EXPORT chk_first_dvc
chk_first_dvc PROC
;-------------------------------------------------------------------------------
IF part_suprt = true THEN
;
; Check Linkes to see if single device.
;
ldy #dib.headlnk
lda [dib_ptr],y
bne @check_devnum
;
; Head Link = 0. Return True.
;
@true clc
rts
;
; Check to see if the Device Number and
; Head Links are the same. If not then
; we return false (carry set).
;
@check_devnum ldy #dib.devnum
cmp [dib_ptr],y
beq @true
;
; Return False.
;
sec
rts
ELSE
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; This call checks to see if the other device tied to
; it are offline. If they are then the carry will be
; clear on exit. If this device is tied to any other
; device that is currently online, then the carry will
; be set on exit.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if Unit is not the only
; device in this link that is still
; online.
;
;*******************************************************
EXPORT chk_lnk_offline
chk_lnk_offline PROC
;-------------------------------------------------------------------------------
IF part_suprt = true THEN
;
; Get the Head Pointer. Check for null.
;
clc
ldy #dib.headptr
lda [dib_ptr],y
sta <scsi_zp6
beq @over_0
sec
@over_0 ldy #dib.headptr+2
lda [dib_ptr],y
sta <scsi_zp6+2
;
; Check if <20> null.
;
bne @check_it_loop
bcs @check_it_loop
;
; It was null. This may be the head
; of the link. Check it out.
;
ldy #dib.fdvcptr
lda [dib_ptr],y
ldy #dib.fdvcptr+2
ora [dib_ptr],y
beq @no_link ;No. Return to the caller with the carry clear.
;
; Yes. This was the head of the link.
; Set our pointer to this DIB.
;
lda <dib_ptr
sta <scsi_zp6
lda <dib_ptr+2
sta <scsi_zp6+2
;
; At this point, 'scsi_zp6' points to the
; head of the link. Now walk the links
; checking for online. If a device is
; online we will need to check if it is the
; callers device. if so, then we will
; continue the search. If not, we will then
; exit with the carry set.
;
@check_it_loop ldy #dib.dvcflag
lda [scsi_zp6],y
and #dvc_online ;Is it online?
beq @not_online ;No.
lda [scsi_zp6],y ;Yes. Maybe. Check hard offline
and #dvc_hardofl ;Is it hard offline?
bne @not_online ;Yes.
lda <scsi_zp6+2 ;Is it the caller device?
cmp <dib_ptr+2
bne @linked ;No.
lda <scsi_zp6
cmp <dib_ptr
bne @linked ;No.
;
; Point to next device.
;
@not_online clc
ldy #dib.fdvcptr
lda [scsi_zp6],y
tax
beq @over_2
sec
@over_2 ldy #dib.fdvcptr+2
lda [scsi_zp6],y
bne @save_it
bcc @no_link
@save_it sta <scsi_zp6+2
stx <scsi_zp6
bra @check_it_loop
;
; Only device online.
;
@no_link clc
rts
;
; Not the only device online.
;
@linked sec
rts
ELSE
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'wake_me_up'
;
; This call walks the DIBs, slaps them in the face and
; pours cold water on them. It seems that they have
; been sleeping while P8 was running.
;
; !!!! WAAAAAAAAKE UUUUUUUP !!!!
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
EXPORT wake_me_up
wake_me_up PROC
;-------------------------------------------------------------------------------
IF warm_ss_suprt = true THEN
;
; Start with the first DIB after
; the default DIB
;
lda |default_dib
sta <first_dib
sta <next_dib
lda |default_dib+2
sta <first_dib+2
sta <next_dib+2
;
; Is he asleep?
;
@check_it_loop ldy #dib.dvcflag
lda [next_dib],y
tax
and #relaxing
beq @next_dib ;No he is not asleep
;
; Yes he is! Is he also Hard
; Offline?
;
txa
and #dvc_hardofl
beq @not_hardofl ;No. Wake him up all the way then.
txa ;Yes. Only Clear Sleep Bit.
and #relaxing--\ ;Don't set online.
$ffff
sta [next_dib],y
bra @next_dib
;
; Yes he is, wake him up.
;
@not_hardofl txa
and #relaxing--\
$ffff
ora #dvc_online
sta [next_dib],y
;
; Advance to the next one
;
@next_dib clc
ldy #dib.linkptr
lda [next_dib],y
tax
beq @over_0
sec
@over_0 ldy #dib.linkptr+2
lda [next_dib],y
sta <next_dib+2
stx <next_dib
;
; Check if <20> null.
;
bne @check_it_loop
bcs @check_it_loop
;
; That was the last one.
;
; By default of the branches above,
; the Acc. = 0 and the carry is clear.
;
rts
;-------------------------------------------------------------------------------
ELSE
;-------------------------------------------------------------------------------
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'fix_unit_1'
;
; This call walks the DIBs, ensuring that unit 1 is
; the first ProDOS Partition on the disk..
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
EXPORT fix_unit_1
fix_unit_1 PROC
;-------------------------------------------------------------------------------
IF part_suprt = true THEN
;
; Check the pdos Bit for the Unit number 1.
; This may or may not be the head device.
;
ldy #dib.headptr
lda [dib_ptr],y
sta <scsi_zp0
ldy #dib.headptr+2
lda [dib_ptr],y
sta <scsi_zp0+2
ora <scsi_zp0 ;Are we at the Head Now?
bne @check_it ;No.
;
; We were already at the head. Set pointer
; to this DIB before going on.
;
lda <dib_ptr
sta <scsi_zp0
lda <dib_ptr+2
sta <scsi_zp0+2
;
; Is this unit number 1?
;
@check_it ldy #dib.unitnum
lda [scsi_zp0],y
and #max_p_mask
beq @unit_1 ;Yes.
;
; No. It's not unit number one. Check the
; next DIB in the link if it is non-zero.
;
clc
ldy #dib.fdvcptr
lda [scsi_zp0],y
tax
beq @over_sec
sec
@over_sec ldy #dib.fdvcptr+2
lda [scsi_zp0],y
bne @set_ptr
bcc @out
@set_ptr sta <scsi_zp0+2
stx <scsi_zp0
bra @check_it
;
; This is unit 1. Is it a ProDOS Partition?
;
@unit_1 ldy #dib.dvcflag
lda [scsi_zp0],y
and #pdos_part
bne @out ;It's ProDOS, Get out of here.
;
; We're out of here.
;
@out clc
rts
;-------------------------------------------------------------------------------
ELSE ;part_suprt = true
;-------------------------------------------------------------------------------
clc
rts
ENDIF ;part_suprt = true
;-------------------------------------------------------------------------------
ENDP ;fix_unit_1
EJECT
;*******************************************************
;
; ADD_DIB_PTR
;
; This routine adds a pointer to a list of dibs to be
; installed by the 'POST_DRIVER_INSTALL' System
; Service call. Refer to the System Service ERS for
; details of how this works.
;
; Inputs: Acc = Unspecified
; Y register = High word of dib ptr
; X register = Low word of dib ptr
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: <DIB_PTR = Empty DIB if found
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None
;
;*******************************************************
EXPORT add_dib_ptr
add_dib_ptr PROC
;-------------------------------------------------------------------------------
IF part_suprt = true THEN
;
; Check to see if this has an overide
; placed on it.
;
lda |dpi_overide
bmi @exit ;Yes.
;
; Is this the first time that this has been
; called for this session. Only the
; do_post_install can clear this flag.
;
lda |new_list
beq @start_over ; Acc. = 0
;
; Inc the count of new devices to be started.
;
lda |new_dib_cnt
@start_over pha
inc a
sta |new_dib_cnt
sta |new_list
pla
;
; Calculate offset for next ptr.
;
asl a
asl a
pha
txa
plx
sta |new_dib_list,x ;Place Ptr in list
sta <scsi_zp0 ;and on zero page.
tya
sta |new_dib_list+2,x ;Place Ptr in list
sta <scsi_zp0+2 ;and on zero page.
ldy #dib.dvcflag
lda [scsi_zp0],y
ora #cold_dib ;New DIB. Do Cold Start.
sta [scsi_zp0],y
@exit clc
rts
ELSE
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; DO_POST_INSTALL
;
; This routine calls the 'POST_DRIVER_INSTALL' System
; Service call. Refer to the System Service ERS for
; details of how this works.
;
; Inputs: Acc = Unspecified
; Y register = High word of dib ptr
; X register = Low word of dib ptr
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: <DIB_PTR = Empty DIB if found
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None
;
;*******************************************************
EXPORT do_post_install
do_post_install PROC
;-------------------------------------------------------------------------------
IF part_suprt = true THEN
;
; Check to see if this has an overide
; placed on it.
;
lda |dpi_overide
bmi @exit ;Yes.
;
; Clear flag indicating that the call was
; sent.
;
stz |pdi_flag
stz |new_list
;
; Check the count of new devices to be
; started.
;
lda |new_dib_cnt
beq @exit
;
; Make the call
;
lda |gsos_dpage
tcd
ldx #new_dib_cnt
ldy #^new_dib_cnt
jsl install_driver
pha
lda |direct_page
tcd
pla
bcs @exit
dec |pdi_flag
@exit clc
rts
ELSE
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;____Part_Support____
;-------------------------------------------------------------------------------
IF part_suprt = true THEN
;*******************************************************
;
; READ_PM_BLK
;
;
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if operation could not be
; completed. Out of memory for example.
;
;*******************************************************
EXPORT read_pm_blk
read_pm_blk PROC
;
; Tell the Main Driver where
; our command structure resides.
;
lda #@read_part
sta <scsi_mdrvr
lda #^@read_part
sta <scsi_mdrvr+2
;
; And our buffer
;
lda #internal_buff
sta <buff_ptr
lda #^internal_buff
sta <buff_ptr+2
;
; Was this from the STARTUP Call?
;
bit |rebuild
bmi @over ;No.
;
; Yes, then stuff the pointer to
; the dib that we are building into
; the Direct Page locations. The
; main driver will use this to build
; the SCSI READ BLOCK Command that
; we will issue.
;
lda <next_dib
sta <dib_ptr
lda <next_dib+2
sta <dib_ptr+2
;
; And our length from the DIB's
; Block Size value
;
@over lda #block_size
sta <rqst_cnt
lda #^block_size
sta <rqst_cnt+2
;
; Was this from a Status or Control Call?
;
bit |stat_cont
bmi @block_set
;
; Set block to read for the next
; partition map block. This field
; is initialized to zero. Because
; the first partition map block is
; on block 1, this will be initialized
; to zero and then blindly incremented
; until we are finished.
;
; *** This is High --> Low format ***
;
lda |rpm_blk_num
xba
inc a
xba
sta |rpm_blk_num
;
; Set internal command flag
;
@block_set stz |stat_cont ;We used the flag, now clear it.
dec |internal
;
; Set the Partition call flag
;
dec |f_partition
;
; Set the Call Type and Issue the
; READ BLOCK Command.
;
lda #scsit_stat
sta |call_type
;
; Issue the call.
;
jsr check_532_rw
@rts rts
;
; Data for the READ BLOCK Command
;
@read_part dc.b $08
dc.b null
EXPORT rpm_blk_num
rpm_blk_num dc.w null
dcb.b 10,null
ENDP
EJECT
;*******************************************************
;
; 'write_pm_blk'
;
;
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if operation could not be
; completed. Out of memory for example.
;
;*******************************************************
EXPORT write_pm_blk
write_pm_blk PROC
;
; Tell the Main Driver where
; our command structure resides.
;
lda #@write_part
sta <scsi_mdrvr
lda #^@write_part
sta <scsi_mdrvr+2
;
; And our buffer
;
lda #internal_buff
sta <buff_ptr
lda #^internal_buff
sta <buff_ptr+2
;
; And our length from the DIB's
; Block Size value
;
lda #block_size
sta <rqst_cnt
lda #^block_size
sta <rqst_cnt+2
;
; Set internal command flag
;
dec |internal
;
; Set the Partition call flag
;
dec |f_partition
;
; Set the Call Type and Issue the
; READ BLOCK Command.
;
lda #scsit_cont
sta |call_type
;
; Issue the call.
;
jsr check_532_rw
@rts rts
;
; Data for the READ BLOCK Command
;
@write_part dc.b $0A
dc.b null
EXPORT wpm_blk_num
wpm_blk_num dc.w null
dcb.b 10,null
ENDP
EJECT
;*******************************************************
;
; CHK_MAP
;
; This routine checks the validity of the partition
; map block currently loaded in the internal buffer.
; This need only be called once per device. If the
; map is valid, then the carry will be clear. The 'Z'
; flag will also be set if this is not the first time
; that this routine was called for this device. If
; the carry is set, then the map was invalid and the
; block count at 'T_DVC_BLOCKS' has been placed in the
; DIB pointed to by 'DIB_PTR'. The starting block will
; also have been set to null.
;
; Inputs: <DIB_PTR = DIB Location
; |T_DVC_BLOCKS = Total blocks for this device
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; = Z = 0 if routine called before
; = C = 1 if not a partition map
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if 'internal_buff does not
; contain a valid Partition map entry.
;
;*******************************************************
EXPORT chk_map
chk_map PROC
;
; Valid Partition Map?
;
@over_0 inc |first_time ;Don't need to do this but once.
bne @clc ;Already done if taken.
lda |pm.Sig\
+internal_buff
cmp #Part_sig
bne no_partition ;No.
@clc clc
rts
;
; We didn't have a valid partition
; map, so this device will be
; treated as a single non-partitioned
; device.
;
EXPORT no_partition
;
; Issue the READ CAPACITY Command.
;
no_partition jsr read_capacity
bcs @over_0 ;Was there an error?
;
; Get the Block Count (Stored
; High >> Low. Must be switched
; to Low >> High). This is the last
; readable block number. Add 1 to
; it for comparison reasons.
;
lda |block.count\
+internal_buff\
+2
xba
adc #$0001
ldy #dib.blkcnt
sta [dib_ptr],y
lda |block.count\
+internal_buff
xba
adc #null
ldy #dib.blkcnt+2
sta [dib_ptr],y
@over_0 ldy #dib.start_blk
lda #null
sta [dib_ptr],y
ldy #dib.start_blk+2
sta [dib_ptr],y
;
; Set Read and Write Enables
;
lda >default_dib+dib.dvcchar ;Get Default Settings for
and #write_allow++\ ;Write Enable
read_allow++\ ;Read Enable
format_allow ;And Format Allowed
ldy #dib.dvcchar
ora [dib_ptr],y ;Combine with the current
sta [dib_ptr],y ;Settings for this device
;
; Mark DIB as online and switched.
;
ldy #dib.dvcflag
lda [dib_ptr],y
and #dvc_hardofl--\
$ffff
ora #dvc_switch++\
dvc_online
sta [dib_ptr],y
sec
rts
ENDP
EJECT
;*******************************************************
;
; 'check_map_entry'
;
; Check the Partition Map Entry currently loaded in
; the internal buffer. If it is of type
; 'Apple_partition_map', 'Apple_Driver', 'Apple_Free',
; or 'Apple_Scratch', then this routine will return
; with the carry set. Any other type will return
; with the carry clear. In addition, 'Apple_ProDOS'
; will return with the overflow flag set.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if Partition Map Entry is not
; for a valid Volume. Overflow set if the
; Partition is a ProDOS Partition.
;
;*******************************************************
EXPORT check_map_entry
check_map_entry PROC
;
; This routine runs in
; 8 bit m and x only.
;
short
;
; Is it ProDOS?
;
ldx #@p_map-@prodos-2
ldy #null
@loop lda |pm.PartType\
+internal_buff,y
beq @over_0 ;Yes or null. Either way we do the rest.
cmp @prodos,y
beq @next
eor #'A'--'a'
cmp @prodos,y
bne @over_0 ;No. Continue on.
@next iny
dex
bpl @loop
brl @pdos_part ;Skip over 8 bit code area to 16 bit area.
;
; Is it the Partition Map?
;
@over_0 ldx #32-1
ldy #null
@loop_0 lda |pm.PartType\
+internal_buff,y
beq @skip ;Yes or null. Either way we skip it.
cmp @p_map,y
beq @next_0
eor #'A'--'a'
cmp @p_map,y
bne @over_1 ;No. Continue on.
@next_0 iny
dex
bpl @loop_0
bra @skip ;Yes. Skip it.
;
; Is it a FREE Partition?
;
@over_1 ldx #32-1
ldy #null
@loop_1 lda |pm.PartType\
+internal_buff,y
beq @skip ;Yes or null. Either way we skip it.
cmp @free,y
beq @next_1
eor #'A'--'a'
cmp @free,y
bne @over_2 ;No. Continue on.
@next_1 iny
dex
bpl @loop_1
bra @skip ;Yes. Skip it.
;
; Is it a SCRATCH Partition?
;
@over_2 ldx #32-1
ldy #null
@loop_2 lda |pm.PartType\
+internal_buff,y
beq @skip ;Yes or null. Either way we skip it.
cmp @scratch,y
beq @next_2
eor #'A'--'a'
cmp @scratch,y
bne @over_3 ;No. Continue on.
@next_2 iny
dex
bpl @loop_2
;
; Is it a Driver Partition?
;
@over_3 ldx #32-1
ldy #null
@loop_3 lda |pm.PartType\
+internal_buff,y
beq @skip ;Yes or null. Either way we skip it.
cmp @driver,y
beq @next_3
eor #'A'--'a'
cmp @driver,y
bne @over_4 ;No. Continue on.
@next_3 iny
dex
bpl @loop_3
;
; At this point the Current Partition
; Type matched one of the three types
; checked for. Exit with the carry
; set.
;
@skip longmx
sec
rts
;
; At this point the Current Partition
; Type did not match one of the three
; types checked for and it was not a
; ProDOS Partition.. Exit with the
; Carry and Overflow bits clear.
;
@over_4 longmx
clc
clv
rts
;
; At this point the Current Partition
; Type is a ProDOS Partition. Exit with
; the Carry clear and Overflow set.
;
@pdos_part longmx
clc
sep #%01000000 ;Set the v flag
rts
;
; Inactive Partion Types to check for.
;
STRING ASIS
@prodos dc.b 'Apple_ProDOS'
dc.b $00
@driver dc.b 'Apple_Driver'
dc.b $00
@p_map dc.b 'Apple_partition_map'
dc.b $00
@free dc.b 'Apple_Free'
dc.b $00
@scratch dc.b 'Apple_Scratch'
dc.b $00
STRING PASCAL
ENDP
EJECT
ELSE ;part_suprt = true
;-------------------------------------------------------------------------------
EXPORT read_pm_blk
read_pm_blk PROC
EXPORT check_map_entry
check_map_entry
lda #null
clc
clv
rts
EXPORT rpm_blk_num
rpm_blk_num dc.w null
EXPORT chk_map
chk_map
;
; We can't have a valid partition
; map, so this device will be
; treated as a single non-partitioned
; device.
;
EXPORT no_partition
;
; Issue the READ CAPACITY Command.
;
no_partition jsr read_capacity
bcs @over_0 ;Was there an error?
;
; Get the Block Count (Stored
; High >> Low. Must be switched
; to Low >> High). This is the last
; readable block number. Add 1 to
; it for comparison reasons.
;
lda |block.count\
+internal_buff\
+2
xba
adc #$0001
ldy #dib.blkcnt
sta [dib_ptr],y
lda |block.count\
+internal_buff
xba
adc #null
ldy #dib.blkcnt+2
sta [dib_ptr],y
@over_0 ldy #dib.start_blk
lda #null
sta [dib_ptr],y
ldy #dib.start_blk+2
sta [dib_ptr],y
;
; Mark DIB as online and switched.
;
ldy #dib.dvcflag
lda [dib_ptr],y
and #dvc_hardofl--\
$ffff
ora #dvc_switch++\
dvc_online
sta [dib_ptr],y
sec
rts
ENDIF ;part_suprt = true
;-------------------------------------------------------------------------------
;____Cache_Suport____
;-------------------------------------------------------------------------------
IF cache_blks = true THEN
;*******************************************************
;
; 'r_all_in_cache'
;
; This routine is used to check if the entire
; requested block count is in the cache. This routine
; will check each block individually, incrementing the
; block count and subtracting the block size from the
; request count until either a block is found that is
; not in the cache (Exit Carry Set) or request count
; has been exausted (Exit Carry Clear).
;
; On entry this routine clears some values to indicate
; it's starting point for this call. As it goes
; throughthe cache looking for blocks, it will fetch
; the block from the cache and advance the starting
; point. What this does is eliminate double searches
; throughthe cache and if a read is needed, these
; blocks will not have to be transfered. When this
; routine exits, the caller will think that the first
; block that we found that was not in the cache was
; the first block requested. For this reason, any
; code that uses this call will need to preserve those
; values so that they can be restored on exit.
;
; If we receive a Read Call for 256 blocks and the
; first 128 are in the cache, then we don't want to
; re-read them, and we don't want to do a find again
; later when we fetch them. let's fetch them now and
; advance our pointers.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; <buff_ptr = Original Starting Buffer
; <rqst_cnt = Original Request Count
; <block_num = Original Starting Block
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; <buff_ptr = New Starting Buffer
; <rqst_cnt = New Request Count
; <block_num = New Starting Block
; Data Bank = Ours
;
; Errors: Acc = 0 and Carry set if 1 or more
; blocks of the request are not in the
; cache.
;
; Acc <> 0 if any other error occurs.
; Error code is in Acc.
;
;*******************************************************
EXPORT r_all_in_cache
r_all_in_cache PROC
;
; Switch Direct Pages.
;
lda |gsos_dpage
tcd
;
; Get the requested count and devide
; by block size to get the loop counter.
; If Count = 0, then exit.
;
lda <rqst_cnt
sta |divend
lda <rqst_cnt+2
sta |divend+2
ora <rqst_cnt
bne @continue
lda |direct_page
tcd
lda #null
clc
@rts rts
;
; Call divide routine.
;
@continue lda <blk_size
sta |divsor
jsr divide
bcc @over_divide
lda |direct_page
tcd
lda #drvr_bad_cnt ;non-integral rqst count.
sec
rts
;
; Count = count -1. This way we can
; use a BMI to exit with.
;
@over_divide lda |result+2
sta @blk_cnt+2
lda |result
sta @blk_cnt
bne @over_dec_high
dec @blk_cnt+2
@over_dec_high dec @blk_cnt
;
; Setup is now completed. Now we start to
; check to see if the blocks are in the
; cache.
;
@chk_blk_loop sec ;return error if not in cache. Don't add.
jsr |get_from_cache
bcc @found
lda #null ;Not there. Clear the Acc. and Exit.
bra @restore
;
; Are there any more to do?
;
@found lda @blk_cnt
bne @over_dec
dec @blk_cnt+2
bpl @over_dec
;
; No. Count is exausted.
;
lda #null
clc
bra @restore
;
; Yes. Finish the DEC and update the
; Buffer Pointer, Request Count, and
; the Block Number.
;
@over_dec dec @blk_cnt
clc ;Bump Buffer Pointer
lda <buff_ptr
adc <blk_size
sta <buff_ptr
lda <buff_ptr+2
adc #null
sta <buff_ptr+2
sec ;Back off Request Count
lda <rqst_cnt
sbc <blk_size
sta <rqst_cnt
lda <rqst_cnt+2
sbc #null
sta <rqst_cnt+2
inc <block_num ;Inc the Block Number
bne @chk_blk_loop
inc <block_num+2
bra @chk_blk_loop
;
; We are finished. We don't need to
; know if we were succesfull. We only
; need to restore the state of the
; corrupted direct page values while
; preserving the Acc. and Carry bit.
;
@restore pha
php
ldx |direct_page
lda <buff_ptr
sta >buff_ptr,x
sta |c_strt_buffer
lda <buff_ptr+2
sta >buff_ptr+2,x
sta |c_strt_buffer+2
lda <rqst_cnt
sta >rqst_cnt,x
sta |c_strt_rqst_cnt
lda <rqst_cnt+2
sta >rqst_cnt+2,x
sta |c_strt_rqst_cnt+2
lda <block_num
sta >block_num,x
sta |c_strt_blk_num
lda <block_num+2
sta >block_num+2,x
sta |c_strt_blk_num+2
;
; Validate Flag for the Read Cache
; Routines
;
dec |cache_valid
txa
tcd
plp
pla
rts
;
; Internal Data
;
@blk_cnt dc.l null
;
; The following values are used to advance
; the starting point of the cache calls.
;
EXPORT c_strt_buffer
EXPORT c_strt_rqst_cnt
EXPORT c_strt_blk_num
EXPORT cache_valid
c_strt_buffer dc.l null ;Starting Buffer Pointer
c_strt_rqst_cnt dc.l null ;Starting Request Count
c_strt_blk_num dc.l null ;Starting Block Number
cache_valid dc.w null
ENDP
EJECT
;*******************************************************
;
; 'w_update_cache'
;
; This routine is used when writting data to the disk
; with a cache priority of $0000. If the block is in
; the cache, then the cache will be updated. If not,
; then nothing will happen.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
; Carry Clear = Always place in cache
; Carry set = Only place in cache if
; already there.
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if requested block is not
; added to the cache.
;
;*******************************************************
EXPORT w_update_cache
w_update_cache PROC
;
; Zero Deferred Write failed flag.
;
stz @deferred
;
; Preserve Carry Flag for later.
;
php
;
; Set GS/OS Direct Page
;
lda |gsos_dpage
tcd
;
; Preserve Block Number and request
; count. Set count to 1 block per
; call.
;
lda <buff_ptr
sta @orig_buff
lda <rqst_cnt
sta @bytes_rem
lda <block_num
sta @start_blk
lda <buff_ptr+2
sta @orig_buff+2
lda <rqst_cnt+2
sta @bytes_rem+2
lda <block_num+2
sta @start_blk+2
;
; Is the requested block in the cache?
;
@do_cache plp
php
jsr put_in_cache
bcc @cached ;Carry set only if a deferred write
dec @deferred ;failed to go in the cache.
;
; Advance to the next block.
;
@cached inc <block_num
bne @over
inc <block_num+2
;
; Bump Buffer Pointer.
;
@over clc
lda <buff_ptr
adc <blk_size
sta <buff_ptr
bcc @over_1
inc <buff_ptr+2
;
; Any Data Left
;
@over_1 sec
lda @bytes_rem
sbc <blk_size
sta @bytes_rem
sta |temp_acc
lda @bytes_rem+2
sbc #null
sta @bytes_rem+2
ora |temp_acc
bne @do_cache ;Yes.
clc
;
; Restore Values.
;
@restore lda @orig_buff
sta <buff_ptr
lda @start_blk
sta <block_num
lda @orig_buff+2
sta <buff_ptr+2
lda @start_blk+2
sta <block_num+2
;
; Exit.
;
lda |direct_page
tcd
lda @deferred
beq @clean_out
plp ;Restore the Stack.
sec
rts
@clean_out plp ;Restore the Stack.
clc
rts
;
; Internal Data
;
@deferred dc.w null
@orig_buff dc.l null
@bytes_rem dc.l null
@start_blk dc.l null
ENDP
EJECT
;*******************************************************
;
; 'put_in_cache'
;
; This routine is used to place the current block in
; the cache. First we will check if the requested
; block is in the cache. If the block is not in the
; cache then we will add it. if there is no room and
; the driver call was issued in deferred mode then we
; will exit with the carry set. In all other cases
; we will exit Carry Clear.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
; Carry Clear = Always place in cache
; Carry set = Only place in cache if
; already there.
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if requested block is not
; added to the cache in deferred mode
; only.
;
;*******************************************************
EXPORT put_in_cache
put_in_cache PROC
;
; Is the requested block in the cache?
;
php
clc
jsl cache_find_blk
bcc @move_it_in ;Yes. It is.
plp
bcs @clc
;
; Not there. Allocate space.
;
jsl cache_add_blk
bcc @skip_fix
lda <cache_prio ;Deferred Mode?
bpl @clc ;No. Exit Carry Clear.
rts ;Yes. Exit carry set
;
; Move the block in.
;
@move_it_in plp ;Restore the stack.
@skip_fix pei <buff_ptr+2
pei <buff_ptr
pei <cache_ptr+2
pei <cache_ptr
pea $0000
pei <blk_size
pea |move_sinc_dinc
jsl move_info
;
; Exit.
;
@clc clc
@rts rts
ENDP
EJECT
;*******************************************************
;
; 'r_get_cache'
;
; This routine is used when reading data from the disk
; with a cache priority of $0000. If the block is in
; the cache, then the data will be updated. If not,
; then nothing will happen.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
; Carry Clear = Always place in cache
; Carry set = Only place in cache if
; already there.
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if requested block is not
; added to the cache.
;
;*******************************************************
EXPORT r_get_cache
r_get_cache PROC
;
; Preserve Carry Flag for later.
;
php
;
; Check Validity of the call.
;
lda |cache_valid
bmi @valid
plp
lda #drvr_bad_code
sec
rts
;
; Set GS/OS Direct Page
;
@valid lda |gsos_dpage
tcd
;
; Get Request count.
;
lda |c_strt_rqst_cnt
sta @bytes_rem
lda |c_strt_rqst_cnt+2
sta @bytes_rem+2
;
; Is the requested block in the cache?
;
@do_cache plp
php
jsr get_from_cache
;
; Advance to the next block.
;
inc <block_num
bne @over
inc <block_num+2
;
; Bump Buffer Pointer.
;
@over clc
lda <buff_ptr
adc <blk_size
sta <buff_ptr
bcc @over_1
inc <buff_ptr+2
;
; Any Data Left
;
@over_1 sec
lda @bytes_rem
sbc <blk_size
sta @bytes_rem
sta |temp_acc
lda @bytes_rem+2
sbc #null
sta @bytes_rem+2
ora |temp_acc
bne @do_cache ;Yes.
;
; Exit.
;
stz |cache_valid ;Cache Values no longer valid
lda |direct_page
tcd
plp ;Restore the Stack.
clc
rts
;
; Internal Data
;
@bytes_rem dc.l null
ENDP
EJECT
;*******************************************************
;
; 'get_from_cache'
;
; This routine is used to get the current block from
; the cache. First we will check if the requested
; block is in the cache. If the block is not in the
; cache then we will exit with the Carry Set. If it
; is found, then we will move it to the requested ram
; and exit Carry Clear.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if requested block is not
; in the cache.
;
;*******************************************************
EXPORT get_from_cache
get_from_cache PROC
;
; Is the requested block in the cache?
;
php
clc
jsl cache_find_blk
bcc @move_it_in ;Yes. It is.
plp
bcs @rts
;
; Not there. Allocate space.
;
jsl cache_add_blk
bcs @rts
pei <buff_ptr+2
pei <buff_ptr
pei <cache_ptr+2
pei <cache_ptr
pea $0000
pei <blk_size
pea |move_sinc_dinc
jsl move_info
bra @clc
;
; Move the block in.
;
@move_it_in plp ;Restore the stack.
@skip_fix pei <cache_ptr+2
pei <cache_ptr
pei <buff_ptr+2
pei <buff_ptr
pea $0000
pei <blk_size
pea |move_sinc_dinc
jsl move_info
;
; Exit.
;
@clc clc
@rts rts
ENDP
EJECT
;*******************************************************
;
; 'ko_cache'
;
; This routine is used to kill all blocks that are
; cached that are included in a device specific write
; type call.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
EXPORT ko_cache
ko_cache PROC
;
; Switch Direct Pages.
;
lda |gsos_dpage
tcd
;
; Preserve the block number on direct
; page.
;
lda <block_num
sta |scratch0
lda <block_num+2
sta |scratch1
;
; Set our starting block number on
; direct page.
;
lda |killer_blk
sta <block_num
lda |killer_blk+2
sta <block_num+2
;
; Get the requested count and devide
; by block size to get the loop counter.
; If Count = 0, then exit.
;
lda <rqst_cnt+2
sta |divend+2
lda <rqst_cnt
sta |divend
ora <rqst_cnt+2
bne @do_devide
clc
@rts bra @restore
;
; Call divide routine.
;
@do_devide lda <blk_size
sta |divsor
jsr divide
bcc @over_divide
lda #drvr_bad_cnt ;non-integral rqst count.
sec
bra @restore
;
; Count = count -1. This way we can
; use a BMI to exit with.
;
@over_divide lda |result+2
sta @blk_cnt+2
lda |result
sta @blk_cnt
bne @over_dec_high
dec @blk_cnt+2
@over_dec_high dec @blk_cnt
;
; Setup is now completed. Now we start to
; check to see if the blocks are in the
; cache.
;
@chk_blk_loop jsl cache_kil_blk
;
; Are there any more to do?
;
lda @blk_cnt
bne @over_dec
dec @blk_cnt+2
bpl @over_dec
;
; No. Count is exausted.
;
clc
bra @restore
;
; Yes. Finish the DEC and update
; the block number.
;
@over_dec dec @blk_cnt
inc <block_num
bne @chk_blk_loop
inc <block_num+2
bra @chk_blk_loop
;
; We are finished. We don't need to
; know if we were succesfull. We only
; need to restore the state of the
; corrupted direct page values while
; preserving the Acc. and Carry bit.
;
@restore pha
php
lda |scratch0
sta <block_num
lda |scratch1
sta <block_num+2
lda |direct_page
tcd
plp
pla
rts
;
; Internal Data
;
@blk_cnt dc.l null
ENDP
EJECT
ELSE
;-------------------------------------------------------------------------------
put_in_cache PROC Export
EXPORT get_from_cache
EXPORT c_strt_buffer
EXPORT c_strt_rqst_cnt
EXPORT c_strt_blk_num
EXPORT cache_valid
get_from_cache sec
rts
c_strt_buffer
c_strt_rqst_cnt
c_strt_blk_num
cache_valid dc.w null
ENDP
EJECT
ENDIF
;-------------------------------------------------------------------------------
;____Device_Tests____
;*******************************************************
;
; 'unit_state'
;
; Tests the Target Device to see if it is ready to
; accept our commands. If the device responds with a
; CHECK CONDITION, then we will find out why and
; perform the correct actions.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
; <dib_ptr = Called DIB
;
; Outputs: Acc = Error Code if any
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
; <dib_ptr = Called DIB
;
; Errors: Carry set if Unit no ready.
;
;*******************************************************
EXPORT unit_state
unit_state PROC
stz |un_formatted ;**** test Code ****
;
; This device is removable. Now we
; need to check to see if the unit
; has gone offline, (then we need to
; report that to the OS) or if the
; unit has come back online (Rebuild
; the DIBs).
;
@try_again jsr test_unit_rdy ;Is there anything that we should know about?
bcs @turdy_fail ;Yes.
@rtg brl @ready_to_go ;No.
@turdy_fail
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
cmp #$FE16
bne @not_down
brl @off_line
@not_down
ENDIF
;-------------------------------------------------------------------------------
lda |auto_sense_data+\
rqst_sens.sense_key
beq @rtg
cmp #$ffff
beq @try_again
and #$00ff
cmp #$0006
beq @do_switch
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
cmp #$0002
bne @on_line_hd
brl @off_line
@on_line_hd lda auto_sense_data+\ ;Was it a RESET?
rqst_sens.addnl_sens_code
and #$00fe
cmp #$0028 ;Checking for $28 (Medium Changed), $29 (RESET)
bne @io_error
ENDIF
;-------------------------------------------------------------------------------
IF scsi_dtype = apple_cd\
OR scsi_dtype = changer THEN
lda auto_sense_data+\ ;Was it a RESET?
rqst_sens.addnl_sens_code
and #$00ff
cmp #$00b7 ;Checking for $B7 (Media being mounted)
beq @do_switch
cmp #$00b0 ;Checking for $B0 (NO MEDIA)
bne @on_line_cd
brl @off_line
@on_line_cd and #$00fe
cmp #$0028 ;Checking for $28 (Medium Changed), $29 (RESET)
beq @no_error_cd
brl @io_error
@no_error_cd
;
; Set this location to a $B0. This will
; prevent problems if the device is reset
; durring startup while auto sensing is
; off.
;
lda #$00b0
sta auto_sense_data+\
rqst_sens.addnl_sens_code
bra unit_state
ENDIF
;-------------------------------------------------------------------------------
IF scsi_dtype = mcd_40 THEN
cmp #$0008 ;**** test Code ****
bne @err_8 ;**** test Code ****
@no_format dec |un_formatted ;**** test Code ****
brl @io_error ;**** test Code ****
@err_8 lda auto_sense_data+\ ;Was it a RESET?
rqst_sens.addnl_sens_code
and #$00ff
cmp #$00a7 ;Checking for $A7 (Load Error, UnFormatted)
beq @no_format
cmp #$0010 ;Checking for $10 (ID CRC Medium Error)
beq @no_format
cmp #$0011 ;Checking for $11 (Unrecovered Read Error)
beq @no_format
cmp #$0015 ;Checking for $15 (Seek Position Error)
beq @no_format
cmp #$0019 ;Checking for $19 (Defect List Error)
beq @no_format
cmp #$0031 ;Checking for $31 (Medium Format corrupted)
beq @no_format
cmp #$0032 ;Checking for $32 (No Defect Spare Location Avail.)
beq @no_format
cmp #$00a8 ;Checking for $A8 (Media being mounted)
beq @do_switch
cmp #$00b0 ;Checking for $B0 (NO MEDIA)
bne @chk_other
;
; No Media. Mark the DIB Hard Ofline also.
;
ldy #dib.dvcflag
lda [dib_ptr],y
and #dvc_online--\
$ffff
ora #dvc_hardofl++\
dvc_switch++\
dvc_hard_sw
sta [dib_ptr],y
brl @off_line
@chk_other cmp #$0028 ;Checking for $28 (Medium Changed), $29 (RESET)
bne @io_error
ENDIF
;-------------------------------------------------------------------------------
;
; Rebuild the DIBs.
;
@do_switch jsr test_unit_rdy ;Is it ready yet?
bcc @its_ready ;Yes.
;**** test Code ****
lda |auto_sense_data+\
rqst_sens.sense_key
and #$00ff
cmp #$0008
bne @not_err_8
@no_format2 dec |un_formatted
bra @its_ready
; brl @io_error
;**** test Code ****
@not_err_8 lda auto_sense_data+\ ;Is there media in the drive?
rqst_sens.addnl_sens_code
and #$00ff
cmp #$00a7 ;Checking for $A7 (Load Error, UnFormatted)
beq @no_format2
;-------------------------------------------------------------------------------
IF scsi_dtype = mcd_40 THEN
cmp #$0028 ;Checking for $28 (Medium Changed)
beq @do_switch ;Yes.
cmp #$0029 ;Checking for $29 (Power On/Reset)
beq @do_switch ;Yes.
cmp #$00A8 ;Checking for $A8 (Cart Loading)
beq @do_switch ;Yes.
cmp #$00b0 ;Checking for $B0 (NO MEDIA)
bne @no_format2 ;No.
ELSE
;-------------------------------------------------------------------------------
cmp #$00b0 ;Checking for $B0 (NO MEDIA)
bne @do_switch ;No.
; bcs @do_switch ;*************************
ENDIF
;-------------------------------------------------------------------------------
@its_ready
;-------------------------------------------------------------------------------
IF scsi_dtype <> mcd_40 THEN
jsr set_512_mode
ENDIF
;-------------------------------------------------------------------------------
;
; Issue the READ CAPACITY Command.
;
jsr read_capacity
bcs @over_0 ;Was there an error?
;
; Get the Block Count (Stored
; High >> Low. Must be switched
; to Low >> High). This is the last
; readable block number. Add 1 to
; it for comparison reasons.
;
lda |block.count\
+internal_buff\
+2
xba
adc #$0001
sta |t_dvc_blocks
lda |block.count\
+internal_buff
xba
adc #null
sta |t_dvc_blocks+2
@over_0 stz |trash_it ;DO NOT TRASH THIS DISK.
pei <dib_ptr
pei <dib_ptr+2
jsr rebld_dibs ;Rebuild DIBs and Issue a DISK_SW for each
tax
pla
sta <dib_ptr+2
pla
sta <dib_ptr
txa
php
;
; Restore the original Direct Page values.
;
jsr set_our_dp
plp
bcc @switch_error
jsr test_unit_rdy
bcs @io_error
;
; Disk Switched Error.
;
@switch_error lda #drvr_dsk_swch
sec
rts
;
; Some kind of I/O Error.
;
@io_error lda #drvr_io
sec
rts
;
; Are we really ready to go? If the
; dvc_hard_sw flag is set, then we
; will clear it and report the device
; as switched.
;
@ready_to_go ldy #dib.dvcflag
lda [dib_ptr],y
tax
and #dvc_hard_sw
beq @no_switch
txa
and #dvc_hard_sw--\
$ffff
sta [dib_ptr],y
@no_switch lda #null
clc
@rts rts
;
; Device is currently offline.
;
@off_line lda #drvr_off_line
sec
rts
EXPORT un_formatted ;**** test Code ****
un_formatted dc.w null ;**** test Code ****
ENDP
EJECT
;*******************************************************
;
; 'test_unit_rdy'
;
; Test the Target Device to see if it is ready to
; accept our commands. If the device responds with a
; CHECK CONDITION, then it will be up to the caller of
; this routine to find out why. This is not a test
; routine.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Error Code if any
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if Unit no ready.
;
;*******************************************************
EXPORT test_unit_rdy
test_unit_rdy PROC
;
; Set Internal Command Flag
;
dec |internal
;
; Set Parm Pointer.
;
lda #@test_unit_p
sta <scsi_mdrvr
lda #^@test_unit_p
sta <scsi_mdrvr+2
;
; It's a Status Call.
;
lda #scsit_stat
sta |call_type
;
; Issue the Call
;
jmp |main_drvr
;
; Data for this call
; TEST UNIT READY Command Packet
;
@test_unit_p dc.b $00 ;Command Number
dc.b $00 ;SCSI Command Flags
dcb.b 3,$00 ;Reserved
dc.b $00 ;Vendor Unique
dcb.b 6,$00 ;Resrved
ENDP
EJECT
;*******************************************************
;
; 'CHK_PLAY_MODE'
;
; This command is used to tell if the device is
; currently in Audio Play Mode.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Error Code if any
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set not in play mode
;
;*******************************************************
;-------------------------------------------------------------------------------
IF scsi_dtype = apple_cd\
OR scsi_dtype = changer THEN
EXPORT chk_play_mode
chk_play_mode PROC
;
; First, disable auto sensing.
;
ldy #dib.rslt_ptr
lda [dib_ptr],y
pha
lda #null
sta [dib_ptr],y
ldy #dib.rslt_ptr+2
lda [dib_ptr],y
pha
lda #null
sta [dib_ptr],y
;
; Set Internal Command Flag
;
dec |internal
;
; Set Parm length.
;
lda #$0006
sta <rqst_cnt
stz <rqst_cnt+2
;
; Set Parm Pointer.
;
lda #@chk_play
sta <scsi_mdrvr
lda #^@chk_play
sta <scsi_mdrvr+2
;
; Set Buff Pointer.
;
lda #@play_data
sta <buff_ptr
lda #^@play_data
sta <buff_ptr+2
;
; It's a Status Call.
;
lda #scsit_stat
sta |call_type
;
; Issue the Call
;
jsr |main_drvr
bcs @restore_dp ;Carry is set. Not play mode
;
; Check the Status.
;
; Status $00 = In play mode
; $01 = Pause (Play Mode)
; $02 = Muting (Play Mode)
;
; $03 = Play completed (No Play)
; $04 = Error (No Play)
; $05 = Play not requested.
;
lda @play_data
and #$0007
cmp #$0003 ; <3 = Carry Clear. =>3 = Carry set
;
; Restore the Direct Page Values.
;
@restore_dp php
jsr set_our_dp
plp
;
; Restore Auto Sensing Pointer.
;
ldy #dib.rslt_ptr+2
pla
sta [dib_ptr],y
ldy #dib.rslt_ptr
pla
sta [dib_ptr],y
rts
;
; AUDIO STATUS Command Packet
;
@chk_play dc.b $cc ;Command Number
dc.b null ;SCSI Command Flags
dcb.b 10,null ;Reserved
;
; Data received for this call
;
@play_data dcb.b 6,null
ENDP
ENDIF
;-------------------------------------------------------------------------------
EJECT
;*******************************************************
;
; The REQUEST SENSE Call is used to request any
; information from the target device that might tell
; us something about the way that the last call
; competed. This call should always be issued if any
; SCSI Command returns from the SCSI Manager with a
; CHECK CONDITION in the status result field. The
; data returned will be kept until the next REQUEST
; SENSE Command is issued. A flag however will be set
; if any other commands are received by the SCSI Driver
; indicating that this data is outdated.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Error Code if any
; |sense_data = Data returned by the device
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if Unit no ready.
;
;*******************************************************
EXPORT rqst_sense
rqst_sense PROC
;
; Set Internal Command Flag
;
dec |internal
;
; Set Parm Pointer.
;
lda #@rqst_sens_p
sta <scsi_mdrvr
lda #^@rqst_sens_p
sta <scsi_mdrvr+2
;
; Preserve the Current Direct
; Page values stored in the
; buff_ptr and rqst_cnt fields.
;
pei <buff_ptr
pei <buff_ptr+2
pei <rqst_cnt
pei <rqst_cnt+2
;
; Set our own buffer pointer
; for this call.
;
lda #sense_data
sta <buff_ptr
lda #^sense_data
sta <buff_ptr+2
;
; Set our own request count
; for this call.
;
lda #$0012
sta <rqst_cnt
stz <rqst_cnt+2
;
; It's a Status Call.
;
lda #scsit_stat
sta |call_type
;
; Issue the Call
;
stz @error
jsr |main_drvr
bcc @return_dp
;
; Save error code
;
sta @error
;
; Restore the Direct Page Values.
;
@return_dp pla
sta <rqst_cnt+2
pla
sta <rqst_cnt
pla
sta <buff_ptr+2
pla
sta <buff_ptr
;
; Restore the Acc resutl.
;
lda @error
cmp #$0001
rts
;
; Data for this call
;
@error dc.w null
;
; REQUEST SENSE Command Packet
;
@rqst_sens_p dc.b $03 ;Command Number
dc.b $00 ;SCSI Command Flags
dcb.b 3,$00 ;Reserved
dc.b $00 ;Vendor Unique
dcb.b 6,$00 ;Reserved
ENDP
EJECT
;*******************************************************
;
; 'reserve_unit'
;
; The RESERVE UNIT Call is used to reserve the target
; device for use by this host only.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Error Code if any
; |sense_data = Data returned by the device
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if Unit already reserved.
;
;*******************************************************
EXPORT reserve_unit
reserve_unit PROC
;-------------------------------------------------------------------------------
IF scsi_dtype = scanner THEN
;
; Set Internal Command Flag
;
dec |internal
;
; Set Parm Pointer.
;
lda #@rsrv_unit_p
sta <scsi_mdrvr
lda #^@rsrv_unit_p
sta <scsi_mdrvr+2
;
; It's a Control Call.
;
lda #scsit_cont
sta |call_type
;
; Issue the Call
;
jmp |main_drvr
;
; RESERVE UNIT Command Packet
;
@rsrv_unit_p dc.b $16 ;Command Number
dc.b $00 ;SCSI Command Flags
dcb.b 3,$00 ;Reserved
dc.b $00 ;Vendor Unique
dcb.b 6,$00 ;Reserved
ELSE ;scsi_dtype = scanner
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF ;scsi_dtype = scanner
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'release_unit'
;
; The RELEASE UNIT Call is used to release the target
; device from use by this host only.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Error Code if any
; |sense_data = Data returned by the device
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
EXPORT release_unit
release_unit PROC
;-------------------------------------------------------------------------------
IF scsi_dtype = scanner THEN
;
; Set Internal Command Flag
;
dec |internal
;
; Set Parm Pointer.
;
lda #@rles_unit_p
sta <scsi_mdrvr
lda #^@rles_unit_p
sta <scsi_mdrvr+2
;
; It's a Control Call.
;
lda #scsit_cont
sta |call_type
;
; Issue the Call
;
jmp |main_drvr
;
; RELEASE UNIT Command Packet
;
@rles_unit_p dc.b $17 ;Command Number
dc.b $00 ;SCSI Command Flags
dcb.b 3,$00 ;Reserved
dc.b $00 ;Vendor Unique
dcb.b 6,$00 ;Reserved
ELSE ;scsi_dtype = scanner
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF ;scsi_dtype = scanner
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'mode_sense'
;
; This routine issues an MODE SENSE call to the device.
;
; Inputs: <dib_ptr = DIB start (LONG)
; Acc = Unspecified
; Carry = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Carry = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
EXPORT mode_sense
mode_sense PROC
;
; And our buffer
;
lda #internal_buff
sta <buff_ptr
lda #^internal_buff
sta <buff_ptr+2
bra command_ptr
EXPORT mode_sense2
;
; And our buffer
;
mode_sense2 lda #sense_data
sta <buff_ptr
lda #^sense_data
sta <buff_ptr+2
;
; Tell the Main Driver where
; our command structure resides.
;
command_ptr lda #@start_mode
sta <scsi_mdrvr
lda #^@start_mode
sta <scsi_mdrvr+2
;
; And our length of $ff Bytes
;
lda #$0014
sta <rqst_cnt
stz <rqst_cnt+2
;
; Initialize the Page code to 1
;
lda #$0001
sta @page_code
;
; Set internal command flag
;
@try_again dec |internal
;
; Set the Call Type and Issue the
; MODE SENSE Command.
;
lda #scsit_stat
sta |call_type
jsr |main_drvr
bcc @rts
pha
lda @page_code
beq @pla
pla
stz @page_code
bra @try_again
@pla pla
@rts rts
;
; Data for the MODE SENSE Command
;
@start_mode dc.b $1a
dc.b $00
@page_code dc.b $01
dcb.b 9,$00
ENDP
EJECT
;*******************************************************
;
; 'read_capacity'
;
; This routine issues an READ CAPACITY call to the
; device and then uses the data returned to fill in
; some of the blank holes in the DIB.
;
; Inputs: <dib_ptr = DIB start (LONG)
; Acc = Unspecified
; Carry = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Carry = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
EXPORT read_capacity
read_capacity PROC
;-------------------------------------------------------------------------------
IF block_dvc = true\
AND character_dvc = false THEN
;
; Set internal command flag
;
dec |internal
;
; Tell the Main Driver where
; our command structure resides.
;
lda #@read_cap
sta <scsi_mdrvr
lda #^@read_cap
sta <scsi_mdrvr+2
;
; And our buffer
;
lda #internal_buff
sta <buff_ptr
lda #^internal_buff
sta <buff_ptr+2
;
; And our length of $ff Bytes
;
lda #$0008
sta <rqst_cnt
stz <rqst_cnt+2
; Set the Call Type and Issue the
; READ CAPACITY Command.
;
lda #scsit_stat
sta |call_type
jsr |main_drvr
rts
;
; Data for the READ CAPACITY Command
;
@read_cap dc.b $25
dcb.b 11,$00
ELSE
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'get_data_status'
;
; The GET DATA STATUS Call is used to find out how
; much data is available to be read from the Scanner.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Error Code if any
; |sense_data = Data returned by the device
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
EXPORT get_data_status
get_data_status PROC
;-------------------------------------------------------------------------------
IF scsi_dtype = scanner THEN
;
; Set Internal Command Flag
;
dec |internal
;
; Set Parm Pointer.
;
lda #@get_d_stat_p
sta <scsi_mdrvr
lda #^@get_d_stat_p
sta <scsi_mdrvr+2
;
; Preserve the Current Direct
; Page values stored in the
; buff_ptr and rqst_cnt fields.
;
pei <buff_ptr
pei <buff_ptr+2
pei <rqst_cnt
pei <rqst_cnt+2
;
; Set our own buffer pointer
; for this call.
;
lda #sense_data
sta <buff_ptr
lda #^sense_data
sta <buff_ptr+2
;
; Set our own request count
; for this call.
;
lda #$000C
sta <rqst_cnt
stz <rqst_cnt+2
;
; It's a Status Call.
;
lda #scsit_stat
sta |call_type
;
; Issue the Call
;
jsr |main_drvr
;
; Save error code
;
sta @error
;
; Restore the Direct Page Values.
;
@return_dp pla
sta <rqst_cnt+2
pla
sta <rqst_cnt
pla
sta <buff_ptr+2
pla
sta <buff_ptr
;
; Restore the Acc resutl.
;
lda @error
cmp #$0001
rts
;
; Data for this call
;
@error dc.w null
;
; GET DATA STATUS Command Packet
;
@get_d_stat_p dc.b $34 ;Command Number
dc.b $00 ;SCSI Command Flags
dcb.b 3,$00 ;Reserved
dc.b $00 ;Vendor Unique
dcb.b 6,$00 ;Reserved
ELSE ;scsi_dtype = scanner
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF ;scsi_dtype = scanner
;-------------------------------------------------------------------------------
ENDP
EJECT
;____Dvc_Controls____
;*******************************************************
;
; 'start_unit'
;
; Issue a START UNIT Command to the target device
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Error Code if any
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None
;
;*******************************************************
EXPORT start_unit
start_unit PROC
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
ldy #dib.dvcchar
lda [dib_ptr],y
and #removable
bne @do_it
rts
@do_it
ELSE
;-------------------------------------------------------------------------------
;
; Set Stop Flag
;
lda #$0001
tsb |stop_bit
bra start_stop
ENDIF
;-------------------------------------------------------------------------------
EJECT
;*******************************************************
;
; 'stop_unit'
;
; Issue a STOP UNIT Command to the target device
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Error Code if any
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: None
;
;*******************************************************
EXPORT stop_unit
stop_unit
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
ldy #dib.dvcchar
lda [dib_ptr],y
and #removable
bne @do_it
rts
@do_it
ELSE
;-------------------------------------------------------------------------------
;
; Set Stop Flag
;
lda #$0001
trb |stop_bit
;
; Secondary entry point for START_UNIT
; Command
;
EXPORT start_stop
start_stop
;
; Set Internal Command Flag
;
dec |internal
;
; Set Parm Pointer.
;
lda #@start_stop_p
sta <scsi_mdrvr
lda #^@start_stop_p
sta <scsi_mdrvr+2
;-------------------------------------------------------------------------------
IF scsi_dtype = mcd_40 THEN
;
; Set IMMED Bit
;
lda #immed_load
ELSE
;-------------------------------------------------------------------------------
;
; Set IMMED Bit
;
lda #immed_loc
ENDIF
;-------------------------------------------------------------------------------
tsb @immed_loc
;
; It's a Control Call.
;
lda #scsit_cont
sta |call_type
;
; Issue the Call
;
jmp |main_drvr
;
; Data for this call
; TEST UNIT READY Command Packet
;
@start_stop_p dc.b $1B ;Command Number
@immed_loc dc.b $00 ;SCSI Command Flags
dcb.b 2,$00 ;Reserved
EXPORT stop_bit
stop_bit dc.b $00 ;Start/Stop Bit
dc.b $00 ;Vendor Unique
dcb.b 6,$00 ;Resrved
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'SET 512 MODE'
;
; This command is used to tell the device to set it's
; block size to 512 mode. This is used for the CD-ROM
; Drives as well as any others that may need this
; function.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Error Code if any
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if call failed
;
;*******************************************************
EXPORT set_512_mode
set_512_mode PROC
;-------------------------------------------------------------------------------
IF block_dvc = true\
AND character_dvc = false THEN
;-------------------------------------------------------------------------------
IF scsi_dtype = mcd_40 THEN
clc
rts
ELSE ;scsi_dtype = mcd_40 THEN
;-------------------------------------------------------------------------------
;
; Set try twice flag.
;
dec @try_twice
;
; Set Internal Command Flag
;
dec |internal
;
; Set Parm length.
;
lda #end_4_512-\
data_4_512
sta <rqst_cnt
stz <rqst_cnt+2
;
; Set Parm Pointer.
;
@try_again lda #@set_512_p
sta <scsi_mdrvr
lda #^@set_512_p
sta <scsi_mdrvr+2
;
; Set Parm Pointer.
;
lda #data_4_512
sta <buff_ptr
lda #^data_4_512
sta <buff_ptr+2
;
; It's a Control Call.
;
lda #scsit_cont
sta |call_type
;
; Issue the Call
;
jsr |main_drvr
bcc @alles_klar
;
; Set Parm length.
;
lda #$0000000c
sta <rqst_cnt
stz <rqst_cnt+2
inc @try_twice
beq @try_again
;
; Restore the values we destroyed
;
@alles_klar stz @try_twice
php
pha
jsr set_our_dp
pla
plp
rts
;
; Data for this call
;
@try_twice dc.w null
;
; TEST UNIT READY Command Packet
;
@set_512_p dc.b $15 ;Command Number
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
;
; Set bit to save these values
;
dc.b $01
ENDIF ;scsi_dtype = direct_acc
;-------------------------------------------------------------------------------
IF scsi_dtype = apple_cd\
OR scsi_dtype = changer THEN
;
; Clear bit to save these values
;
dc.b $00
ENDIF ;scsi_dtype = apple_cd or changer
;-------------------------------------------------------------------------------
dcb.b 3,$00 ;Reserved
dc.b $00 ;Vendor Unique
dcb.b 6,$00 ;Resrved
;
; Data sent durring this call
;
data_4_512 dc.b null
dc.b $00
dc.b null
dc.b $08
;number of blocks
dc.b null
dc.b $00
dc.b $00
dc.b $00
;block size
dc.b null
dc.b $00
dc.b $02
dc.b $00
;-------------------------------------------------------------------------------
IF scsi_dtype = apple_cd\
OR scsi_dtype = changer THEN
end_4_512
EXPORT select_page_num
select_page_num dc.b $01 ;Page Number
dc.b $06 ;Page Length
dc.b $e4 ;Error Correction Flags
dc.b $03 ;Retry Count
dc.b $00 ;Correction Span
dc.b $00 ;Head Offset Count
dc.b $00 ;Data Strobe Offset
dc.b $00 ;Recovery Time Limit
ELSE ;scsi_dtype = apple_cd or changer
;-------------------------------------------------------------------------------
;Page 1 Data
EXPORT select_page_num
select_page_num dc.b $01 ;Page Number
dc.b $06 ;Page Length
dc.b $e4 ;Error Correction Flags
dc.b $03 ;Retry Count
dc.b $00 ;Correction Span
dc.b $00 ;Head Offset Count
dc.b $00 ;Data Strobe Offset
dc.b $00 ;Recovery Time Limit
end_4_512
ENDIF ;scsi_dtype = apple_cd or changer
;-------------------------------------------------------------------------------
ENDIF ;scsi_dtype = mcd_40
;-------------------------------------------------------------------------------
ELSE ;block_dvc = true
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF ;block_dvc = true
;-------------------------------------------------------------------------------
ENDP
EJECT
;____Evnt_Support____
;*******************************************************
;
; 'notify_me'
;
; This routine gets called by the SCSI Manager when
; some event occurs that is out of the ordinary.
;
; Inputs: None Yet
;
; Outputs: All Regs = Scrambled
;
; Errors: None Yet
;
;*******************************************************
EXPORT notify_me
notify_me PROC
;
; Vector for the SCSI Manager
; to call when an event occurs
; that affects my DIB
; structures. This could be a
; new device online or media
; insertion.
;
clc ;Add CODE LATER
rtl
ENDP
EJECT
;____Misc_Support____
;*******************************************************
;
; SET_DISK_SW
;
; This routine checks the validity of the partition
; map block currently loaded in the internal buffer.
; This need only be called once per device. If the
; map is valid, then the carry will be clear. The 'Z'
; flag will also be set if this is not the first time
; that this routine was called for this device. If
; the carry is set, then the map was invalid and the
; block count at 'T_DVC_BLOCKS' has been placed in the
; DIB pointed to by 'DIB_PTR'. The starting block will
; also have been set to null.
;
; Inputs: <DIB_PTR = DIB Location
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; = Z = 0 if routine called before
; = C = 1 if not a partition map
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Only those allowed by the System Service
; Call SET_DISKSW.
;
;*******************************************************
EXPORT set_disk_sw
set_disk_sw PROC
;-------------------------------------------------------------------------------
IF block_dvc = true\
AND character_dvc = false THEN
;
; Check the removable flag. If the media
; is removable, then we will only set the
; switch flag and let the status calls do
; their thing before this device goes
; online. If it is non-removable, then
; we need to say that the device is
; switched, but also set the online bits.
;
ldy #dib.dvcchar
lda [dib_ptr],y
and #removable
beq @non_removable
;
; Set the Disk Switched Bit in Status.
;
ldy #dib.dvcflag
lda [dib_ptr],y
ora #dvc_switch
sta [dib_ptr],y
bra @over1
;
; Set the Disk Online Bit in Status.
;
@non_removable lda leave_switched ; *** MSG 12/12/91 ***
bmi @over1 ; *** MSG 12/12/91 ***
ldy #dib.dvcflag ; *** MSG 12/12/91 ***
;@non_removable ldy #dib.dvcflag ; *** MSG 12/12/91 ***
lda [dib_ptr],y
and #$ffff--\
dvc_switch--\
dvc_hardofl
ora #dvc_online
sta [dib_ptr],y
;
; Preserve the World from this.
;
@over1 ldx |gsos_dpage
lda >dev_num,x
pha
lda >dib_ptr,x
pha
lda >dib_ptr+2,x
pha
lda <dev_num
sta >dev_num,x
lda <dib_ptr
sta >dib_ptr,x
lda <dib_ptr+2
sta >dib_ptr+2,x
txa
tcd
jsl set_disksw
tay
lda |direct_page
tcd
ldx |gsos_dpage
pla
sta >dib_ptr+2,x
pla
sta >dib_ptr,x
pla
sta >dev_num,x
stz leave_switched ;Clear this flag *** MSG 12/12/91 ***
tya
cmp #$0001
rts
;
; Data for this call
;
EXPORT leave_switched
leave_switched dc.w null ; *** MSG 12/12/91 ***
ELSE
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; TRASH_VOLUME
;
; This routine write a non-formatted pattern to block
; 2 of the volume whose DIB is [dib_ptr]. This is to
; force the OS to re-format or lay down the OS data for
; that volume rather than using what is already there
; and appears to be valid.
;
; Inputs: <DIB_PTR = DIB Location
; |trash_it = Boolean
; 0 = Trash the Volume
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Better be none.
;
;*******************************************************
EXPORT trash_volume
trash_volume PROC
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
;
; Should we really trash the volume?
;
lda |trash_it
beq @dont_trash
;
; Set Main Driver Pointer to
; our data for the command.
;
lda #@cmd_$800A
sta <scsi_mdrvr
lda #^@cmd_$800A
sta <scsi_mdrvr+2
;
; Set our buffer Pointer to
; our Code for the data for
; the command.
;
lda #trash_volume
sta <buff_ptr
lda #^trash_volume
sta <buff_ptr+2
;
; Set length of our data for the
; command.
;
lda #block_size
sta <rqst_cnt
stz <rqst_cnt+2
;
; Call Main Driver
;
lda #scsit_cont
sta |call_type
;
; Issue the call.
;
jsr check_532_rw
@dont_trash rts
;
; Command Data for this call.
;
@cmd_$800A dc.b $0A
dc.b $00
dc.b $00
dc.b $02
dc.b $01
dcb.b 7,$00
ELSE
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'SAVE_DP'
;
; This routine is only called durring the initial
; startup. It saves away the current values on GS/OS
; Direct Page so that we can use the space until we
; have our own Direct Page.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = GS/OS
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = GS/OS
; Data Bank = Ours
;
; Errors: None.
;
;*******************************************************
EXPORT save_dp
save_dp PROC
;
; Check to see if this call was
; nested.
;
lda |valid
bne @exit
;
; Calculate Source Address.
;
clc
lda |gsos_dpage
adc #start_our_zp
pea $0000
pha
;
; Destination Address of temporary
; storage
;
pushlong #saved_zp
pushlong #end_our_zp-\ ;Length of the move
start_our_zp
pushword #move_sinc_dinc
jsl move_info ;Move the data
dec |valid
@exit rts
ENDP
EJECT
;*******************************************************
;
; Move the contents of the first $20 bytes of GS/OS
; Direct Page to our Direct Page.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if Unit is not the only
; device in this linke that is still
; online.
;
;*******************************************************
EXPORT restore_dp
restore_dp PROC
;
; Check to see if this call was
; nested.
;
lda |valid
beq @exit
;
; Source Address of temporary
; storage
;
pushlong #saved_zp
;
; Calculate Destination Address.
;
clc
lda |gsos_dpage
adc #start_our_zp
pea $0000
pha
pushlong #end_our_zp-\ ;Length of the move
start_our_zp
pushword #move_sinc_dinc
jsl move_info ;Move the data
stz |valid
@exit rts
ENDP
EJECT
;*******************************************************
;
; Move the contents of the first $20 bytes of GS/OS
; Direct Page to our Direct Page.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Errors: Carry set if Unit is not the only
; device in this linke that is still
; online.
;
;*******************************************************
EXPORT set_our_dp
set_our_dp PROC
;
; Calculate Source Address.
;
clc
lda |gsos_dpage
adc #dev_num
pea $0000
pha
;
; Calculate Destination Address of
; our Direct Page that we want.
;
clc
lda |direct_page
adc #dev_num
pea $0000
pha
pushlong #dib_ptr+4 ;Length of the move
pushword #move_sinc_dinc
jsl move_info ;Move the data
rts
ENDP
EJECT
;*******************************************************
;
; 'check_532_rw'
;
; There are a few drives that have been formated with
; 512 ($214) bytes per block. These devices work
; perfectly fine in single block I/O, padding the end
; with 20 bytes in a write, or stripping them for a
; read, but when doing a multi-block transaction we
; need to account for the 20 bytes between the end of
; real data for this block and the beginning of the
; next block. Because of this, we will need to build
; a data chaining structure that will read or write
; 512 bytes of data to the users buffer, followed by
; 20 bytes read or written to ROM space. Doing this
; will hinder performance and will prevent caching
; from working. If these are important to the user,
; they can reformat their drive.
;
; This routine is called in place of calling the Main
; Driver. It decides if the call should go via the
; normal or 532 method. All Inputs, Outputs and Setup
; Structures are the same.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = GS/OS Direct Page
; Data Bank = Ours
;
; Errors: None
;
;*******************************************************
EXPORT check_532_rw
check_532_rw PROC
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
;
; Check for 532 byte block.
;
ldy #dib.blksize
lda [dib_ptr],y
cmp #$214
beq @do_532
jmp |main_drvr
@do_532 jmp munge_532
;-------------------------------------------------------------------------------
ELSE
jmp |main_drvr
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'munge_532'
;
; There are a few drives that have been formated with
; 512 ($214) bytes per block. These devices work
; perfectly fine in single block I/O, padding the end
; with 20 bytes in a write, or stripping them for a
; read, but when doing a multi-block transaction we
; need to account for the 20 bytes between the end of
; real data for this block and the beginning of the
; next block. Because of this, we will need to build
; a data chaining structure that will read or write
; 512 bytes of data to the users buffer, followed by
; 20 bytes read or written to ROM space. Doing this
; will hinder performance and will prevent caching
; from working. If these are important to the user,
; they can reformat their drive.
;
; This routine is called in place of calling the Main
; Driver. All Inputs, Outputs and Setup Structures
; are the same.
;
; Inputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = Ours
; Data Bank = Ours
;
; Outputs: Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = GS/OS Direct Page
; Data Bank = Ours
;
; Errors: None
;
;*******************************************************
EXPORT munge_532
munge_532 PROC
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
;
; Set User's buffer location
;
lda <buff_ptr
sta |@User_buff
lda <buff_ptr+2
sta |@User_buff+2
;
; Generate a block count.
;
stz @block_cnt
lda <rqst_cnt+2
sta @block_cnt+1
lda <rqst_cnt
and #$fe00
cmp <rqst_cnt
beq @equal_512
lda #drvr_bad_cnt
sec
rts
@equal_512 lsr @block_cnt+1
ror a
tsb @block_cnt-1
;
; Adjust users request cnt to reflect
; that there are 20 bytes/block more
; to transfer than the user requested.
; This is done by multiplying the block
; count that we just calculated bu $14.
;
; First preserve the old value.
;
lda <rqst_cnt+2
sta @rqst_cnt+2
lda <rqst_cnt
sta @rqst_cnt
;
; Now adjust count.
;
lda @block_cnt+2
sta |scratch0+2
lda @block_cnt
sta |scratch0
;
; x4 first
;
asl |scratch0 ;x2
asl |scratch0+2
asl |scratch0 ;x2 (x4)
asl |scratch0+2
;
; Save the x4.
;
lda |scratch0
sta |scratch2
lda |scratch0+2
sta |scratch2+2
;
; Finish the x16
;
asl |scratch0 ;x2 (x8)
asl |scratch0+2
asl |scratch0 ;x2 (x16)
asl |scratch0+2
;
; Add the x4 to x16 for x20
;
clc
lda |scratch0
adc |scratch2
sta |scratch0
lda |scratch0+2
adc |scratch2+2
sta |scratch0+2
;
; Add the x20 to the original
; request count.
;
clc
lda |scratch0
adc <rqst_cnt
sta <rqst_cnt
lda |scratch0+2
adc <rqst_cnt+2
sta <rqst_cnt+2
;
; Drop the Loop Counter by 1.
;
sec
lda @block_cnt
sbc #$0001
sta @block_cnt
lda @block_cnt+2
sbc #$0000
sta @block_cnt+2
;
; Preserve the data pointer currently
; used in the DIB and replace it with
; a pointer to our 532 byte block
; Data Chaining instructions.
;
clc
ldy #dib.trx_ptr
lda [dib_ptr],y
sta |dib_data_struct
lda #@munger_DC
sta [dib_ptr],y
ldy #dib.trx_ptr+2
lda [dib_ptr],y
sta |dib_data_struct+2
lda #^@munger_DC
sta [dib_ptr],y
;
; Preserve the Block size and replace
; it with $0214
;
lda <blk_size
sta @blk_size
lda #$0214
sta <blk_size
;
; Issue the call.
;
jsr |main_drvr
;
; Preserve the result.
;
pha
php
;
; Restore the Block size.
;
lda @blk_size
sta <blk_size
;
; Restore DIB's data pointer.
;
ldy #dib.trx_ptr
lda |dib_data_struct
sta [dib_ptr],y
ldy #dib.trx_ptr+2
lda |dib_data_struct+2
sta [dib_ptr],y
;
; Restore the original request count.
;
lda @rqst_cnt+2
sta <rqst_cnt+2
lda @rqst_cnt
sta <rqst_cnt
;
; Restore state of the call's exit.
;
plp
pla
;
; Exit.
;
rts
;
; Temp Storage area.
;
@rqst_cnt dc.l null
@blk_size dc.w null
@munger_DC ;
; Data Chaining Structure.
;
@User_buff dc.l $00000000 ;Users Buffer Space
dc.l $00000200 ;Request Count
dc.l $00000200 ;Add to buffer at each pass
dc.l $00000000 ;Reserved.
dc.l $00ff0600 ;Location for spare bytes (ROM)
dc.l $00000014 ;Number of bogus bytes
dc.l $00000000 ;Leave buffer pointer alone
dc.l $00000000 ;Reserved.
dc.l $ffffffff ;Looping Command
@block_cnt dc.l $00000000 ;Block Count
dc.l $00000000-2 ;Go Back 2 commands
dc.l $00000000 ;Reserved
dc.l $00000000 ;DCStop Command
dc.l $00000000 ;DCStop Command
dc.l $00000000 ;DCStop Command
dc.l $00000000 ;DCStop Command
dc.l $00000000 ;Safety space
;-------------------------------------------------------------------------------
ELSE
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'divide'
;
; This routine divides a 16 bit number stored in
; 'divsor' into the 32 bit number stored in 'divend'.
; The result will be in 'result' if no error occured.
; If 'divend' is not an integral multiple of 'divsor'
; an error will be returned.
;
; Inputs: 'divsor' = 16 bit number
; 'divend' = 32 bit number
; 'max_blk_cnt' = Max Result Allowed
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = GS/OS Direct Page
; Data Bank = Ours
;
; Outputs: 'result' = result of division
; Acc = Error Code if Carry Set
; 'max_blk_cnt' = null if no error
; Intact if an error is returned
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = GS/OS Direct Page
; Data Bank = Ours
;
; Errors: Carry set if non-integral result
;
;*******************************************************
EXPORT divide
divide PROC
;-------------------------------------------------------------------------------
IF block_dvc = true\
AND character_dvc = false THEN
;
; Clear Result field
;
stz |result
stz |result+2
;
; 32 bits in result.
;
ldx #32-1
;
; Make 'divsor' an odd value.
; This is nothing more than
; deviding both numbers by 16
; until 'divsor' is an odd value.
; If any bits roll out of 'divend'
; then this was an incorrect call.
;
@div16_loop lda |divsor
cmp #block_size ;Special Case /block_size
bne @not_512
lda |divend
and #block_size-1
bne @error
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
;
; Quick Divide by block_size
;
lda |divend+3
and #$00ff
lsr a
sta |result+2
lda |divend+1
ror a
sta |result
jmp @chk_rslt
ENDIF
;-------------------------------------------------------------------------------
IF scsi_dtype = apple_cd\
OR scsi_dtype = changer THEN
;
; Quick Divide by $0200
;
lda |divend+3
and #$00ff
lsr a
sta |result+2
lda |divend+1
ror a
sta |result
jmp @chk_rslt
ENDIF
;-------------------------------------------------------------------------------
IF scsi_dtype = mcd_40 THEN
;
; Quick Divide by $2000
;
stz |result
stz |result+2
lda |divend+2
sta |result
lda |divend ;$2000
asl a ;$4000
rol |result ;x2
rol |result+2 ;x2
asl a ;$8000
rol |result ;x4
rol |result+2 ;x4
asl a ;$0000
rol |result ;x8
rol |result+2 ;x8
jmp @chk_rslt
ENDIF
;-------------------------------------------------------------------------------
@not_512 and #bit_0
bne @do_divide
lsr |divsor
lsr |divend+2
ror |divend
bcc @div16_loop
bra @error
;
; Main division loop. We will
; continue to divide the divsor
; by 16 until finished. Each
; time that divsor becomes odd,
; we will subtract divend and
; roll a bit into the high end
; of result.
;
@do_divide lda |divend
and #bit_0
clc ;We must clear the carry incase
beq @do_div16 ;the branch is taken.
;
; We are odd. Do the subtraction.
;
sec
lda |divend
sbc |divsor
sta |divend
lda |divend+2
sbc #$0000
sta |divend+2
bcc @error ;If this is taken, bad data.
;
; Divide both the divsor and
; results by 16. The carry will
; roll into the reult from the
; high bit end.
;
@do_div16 php
lsr |divend+2
ror |divend
plp
@clean_up ror |result+2
ror |result
;
; Have we done 32 bits yet?
;
dex
bmi @chk_rslt ;Yes.
;
; No we havent. If divsor is
; non-zero then do next itteration.
; If it is = zero then clean up
; result and exit.
;
lda |divend+2
ora |divend
bne @do_divide
;
; Clean up Result.
;
clc
bra @clean_up
;
; Here for bcs offset.
;
@error sec
lda #drvr_bad_cnt
rts
;
; Check to see if the result is within
; the max.
;
@chk_rslt lda |max_blk_cnt
ora |max_blk_cnt+2
beq @out
sec
lda |max_blk_cnt
sbc |result
sta @temp
lda |max_blk_cnt+2
sbc |result+2
bge @clean
;
; Exit
;
sec
lda #drvr_bad_parm
rts
@clean stz |max_blk_cnt
stz |max_blk_cnt+2
@out clc
rts
;
; Data Area.
;
@temp dc.w null
ELSE
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
;*******************************************************
;
; 'calc_bytes'
;
; This routine Multiplies a 16 bit number stored in
; 'm_blk_size' by a 32 bit number stored in 'm_blk_cnt'.
; The result will be in 'm_rslt'.
;
; Inputs: 'm_blk_size'= 16 bit Block Size
; 'm_blk_cnt' = 32 bit Block Count
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = GS/OS Direct Page
; Data Bank = Ours
;
; Outputs: 'm_rslt' = result of division
; Acc = Unspecified
; Y register = Unspecified
; X register = Unspecified
; P register = 0=M=X=e
; Direct Page = GS/OS Direct Page
; Data Bank = Ours
;
; Errors: None
;
;*******************************************************
EXPORT calc_bytes
calc_bytes PROC
;-------------------------------------------------------------------------------
IF block_dvc = true\
AND character_dvc = false THEN
;
; Clear Result field
;
stz |m_rslt
stz |m_rslt+2
;
; Check 'm_blk_size' against the block
; size for this device. If it is Equal,
; the do cheap processing. Otherwise do
; full multiply logic.
;
@div16_loop lda |m_blk_size
cmp #block_size ;Special Case *block_size
bne @not_512
;-------------------------------------------------------------------------------
IF scsi_dtype = direct_acc THEN
;
; Quick Multiply by $0200
;
lda |m_blk_cnt
asl a
sta |m_rslt+1
lda |m_blk_cnt+2
and #$007f
rol a
ora |m_rslt+3 ;Watch out for that +4 value.
sta |m_rslt+3 ;It doesn't belong to us.
jmp @clean
ENDIF
;-------------------------------------------------------------------------------
IF scsi_dtype = apple_cd\
OR scsi_dtype = changer THEN
;
; Quick Multiply by $0200
;
lda |m_blk_cnt
asl a
sta |m_rslt+1
lda |m_blk_cnt+2
and #$007f
rol a
ora |m_rslt+3 ;Watch out for that +4 value.
sta |m_rslt+3 ;It doesn't belong to us.
jmp @clean
ENDIF
;-------------------------------------------------------------------------------
IF scsi_dtype = mcd_40 THEN
;
; Quick Multiply by $2000
;
lda |m_blk_cnt
sta |m_rslt+2 ;Cheap * $10000
lda |m_blk_cnt+2
lsr a
ror |m_rslt+2 ;Cheap * $8000 ( * $10000/2 )
ror |m_rslt ;Cheap * $8000 ( * $10000/2 )
lsr a
ror |m_rslt+2 ;Cheap * $4000 ( * $10000/4 )
ror |m_rslt ;Cheap * $4000 ( * $10000/4 )
lsr a
ror |m_rslt+2 ;Cheap * $2000 ( * $10000/8 )
ror |m_rslt ;Cheap * $2000 ( * $10000/8 )
jmp @clean
ENDIF
;-------------------------------------------------------------------------------
@not_512
;
; Actual Multiply Loop.
;
lsr |m_blk_size
bcc @shift_cnt ;Don't Add this one.
;
; Add in Current Byte Count.
clc
lda |m_rslt
adc |m_blk_cnt
sta |m_rslt
lda |m_rslt+2
adc |m_blk_cnt+2
sta |m_rslt+2
;
; Multiply Count by 2.
;
@shift_cnt asl |m_blk_cnt
rol |m_blk_cnt+2
lda |m_blk_size
bne @not_512 ;only do till 0 ( <16 times )
;
; Clean Exit.
;
@clean clc
rts
ELSE
;-------------------------------------------------------------------------------
lda #null
clc
rts
ENDIF
;-------------------------------------------------------------------------------
ENDP
EJECT
END