;******************************************************* ; ; 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 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 pm_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 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++\ attrnocross++\ 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 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,x lda dvc_list+2,x txa sta |direct_page sta |exit_dpage ; ; 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 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. ; jsr no_partition jsr trash_volume ;Trash the volume if caller wishes. ;See |trash_it flag in trash_volume ; ; Mark DIB as online and switched. ; @single ldy #dib.dvcflag lda [dib_ptr],y and #dvc_hardofl--\ $ffff ora #dvc_switch++\ dvc_online sta [dib_ptr],y 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 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 |pm_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 ; ; 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 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 |pm_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 ; ; 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 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: Low format *** ; lda |pm_blk_num xba inc a xba sta |pm_blk_num ; ; 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_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 pm_blk_num pm_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: 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 buff_ptr,x sta |c_strt_buffer lda buff_ptr+2,x sta |c_strt_buffer+2 lda rqst_cnt,x sta |c_strt_rqst_cnt lda rqst_cnt+2,x sta |c_strt_rqst_cnt+2 lda block_num,x sta |c_strt_blk_num lda 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 > 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 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 dev_num,x pha lda >dib_ptr,x pha lda >dib_ptr+2,x pha lda dev_num,x lda dib_ptr,x lda 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 tya cmp #$0001 rts 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: