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

1 line
163 KiB
Plaintext
Raw 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