Sym8xxScript.ss   [plain text]



; Copyright (c) 1997-1999 Apple Computer, Inc. All rights reserved.
;
; @APPLE_LICENSE_HEADER_START@
; 
; The contents of this file constitute Original Code as defined in and
; are subject to the Apple Public Source License Version 1.1 (the
; "License").  You may not use this file except in compliance with the
; License.  Please obtain a copy of the License at
; http://www.apple.com/publicsource and read it before using this file.
; 
; This Original Code and all software distributed under the License are
; distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
; EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
; INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
; License for the specific language governing rights and limitations
; under the License.
; 
; @APPLE_LICENSE_HEADER_END@
;
;   File Ownership:
;
;       DRI:            Mike Johnson
;
;       Other Contact:  Russ Berkoff
;
;       Technology:     SCSI
;
;   Writers:
;
;       (MLJ)   Mike Johnson
;       (RRA)   Rick Auricchio


;       NCR Errata Listing 125 Item 1 : Clear the SCNTL0 start bit
;       when jump to reselect during select (try_reselect)
;
;       NCR Errata Listing 117 Item 4 : Bad parity if odd bytes during
;       wide transfer. Only for DATA OUT in Initiator mode.
;       (Confirm by Manfred Eierle 3rd June 93 not during DATA IN)

    ARCH     825A                ;specifically for 825a and 875 (new instructions)


        ;*****************************************************************
        ;
        ;   Phase codes - These values represent which action is being handled
        ;
        ;*****************************************************************

    ABSOLUTE kphase_DATA_OUT                = 0x00
    ABSOLUTE kphase_DATA_IN                 = 0x01
    ABSOLUTE kphase_COMMAND                 = 0x02
    ABSOLUTE kphase_STATUS                  = 0x03
    ABSOLUTE kphase_MSG_OUT                 = 0x06
    ABSOLUTE kphase_MSG_IN                  = 0x07
    ABSOLUTE kphase_SELECT                  = 0x08
    ABSOLUTE kphase_RESELECT                = 0x09
    ABSOLUTE kphase_ABORT_CURRENT           = 0x0A
    ABSOLUTE kphase_ABORT_MAILBOX           = 0x0B
    ABSOLUTE kphase_CMD_COMPLETE            = 0x0C
    ABSOLUTE kphase_DISCONNECT              = 0x0D
    ABSOLUTE kphase_saveDataPointer         = 0x0E  ; ??? driver work to be done
    ABSOLUTE kphase_restoreDataPointer      = 0x0F  ; ??? driver work to be done


        ;*****************************************************************
        ;   interrupt codes
        ;*****************************************************************

    ABSOLUTE unknown_phase              = 0x00  ; A spurious phase on SCSI Bus
    ABSOLUTE status_error               = 0x01  ; IO completes, but with status error
    ABSOLUTE unexpected_msg             = 0x02  ; An 'unknown' message is in ld_message var
    ABSOLUTE unexpected_ext_msg         = 0x03  ; An 'unknown' extended message in ld_message
    ABSOLUTE wide_32_not_supported      = 0x04  ; The device wants 32 bits data phase
    ABSOLUTE no_msgin_after_reselect    = 0x05  ; No message-in after reselection
    ABSOLUTE reqack_too_large           = 0x06  ; The device answer ReqAck offset is greater than 8
    ABSOLUTE unknown_reselect           = 0x07  ; The valid bit in SFBR reg not set
    ABSOLUTE unallocated_nexus          = 0x08  ; nexus index -> 0xFFFFFFFF
    ABSOLUTE abort_mailbox              = 0x09  ; Abort/BDR mailbox completed
    ABSOLUTE abort_current              = 0x0A  ; Abort/BDR current op completed
    ABSOLUTE unknown_message_out        = 0x0B  ; Unknown phase before message out
    ABSOLUTE unknown_msg_reject         = 0x0C  ; Unknown message reject
    ABSOLUTE negotiateSDTR              = 0x0D  ; Sync negotiation rx'd
    ABSOLUTE negotiateWDTR              = 0x0E  ; Wide negotiation rx'd
    ABSOLUTE sglist_complete            = 0x0F  ; SGList complete


        ;*****************************************************************
        ;
        ; Data structure for T/L/Q Nexus:
        ;
        ;*****************************************************************

    ABSOLUTE TLQ_SCSI_ID    =  0    ;  4 SCSI ID et al for SELECT instruction
    ABSOLUTE TLQ_xferAdr    =  4    ;  4 Physical address of CHMOV instructions
    ABSOLUTE TLQ_MSGOp      =  8    ;  8 Byte count, data adr   -> TLQ_MSGO
    ABSOLUTE TLQ_CDBp       = 16    ;  8 Byte count, data adr   -> TLQ_CDB
    ABSOLUTE TLQ_CDP        = 24    ;  4 Current Data Pointer
    ABSOLUTE TLQ_SDP        = 28    ;  4 Saved   Data Pointer
    ABSOLUTE TLQ_index      = 32    ;  1 index into nexus array
    ABSOLUTE TLQ_xferStarted= 33    ;  1 transfer started flag
    ABSOLUTE TLQ_IWR        = 34    ;  1 flag to Ignore Wide Residue
    ABSOLUTE TLQ_pad        = 35    ;  1 pad byte


        ;*****************************************************************
        ;
        ; ENTRY declarations - Declare entry points for driver
        ;
        ;*****************************************************************

    ENTRY   select_phase
    ENTRY   phase_handler
    ENTRY   issueMessageOut         ; for negotiation and Reject messages
    ENTRY   issueAbort_BDR          ; to immediately Abort or Bus-Device-Reset
    ENTRY   clearACK                ; MsgIn done - clr ACK, jump to phase handler


        ;*****************************************************************
        ;
        ; Define local data structure at start of SCRIPTS.
        ; This structure is allocated by the following nops.
        ;
        ;*****************************************************************
        ;

    RELATIVE local_data     \
        ld_AbortCode                = 4{??}\    ; 1 byte code to Abort or BDR
        ld_zeroes                   = 4{??}\    ; 4 bytes of 0 to clear registers
        ld_status                   = 4{??}\    ; Status byte from target
        ld_counter                  = 4{??}\    ; index into mailbox array
           ld_AbortBdr_mailbox      = 4{??}\    ; Abort/BusDeviceReset mailbox
           ld_IOdone_mailbox        = 4{??}\    ; [ nexus 0 0 semaphore ]
           ld_sched_mlbx_base_adr   = 4{??}\    ; base addr of mailbox array
           ld_mailboxp              = 4{??}\    ; address of current mailbox
        ld_scsi_id                  = 4{??}\    ; ptr to current mailbox
        ld_nexus_array_base         = 4{??}\    ; base address of Nexus pointers
        ld_nexus_index              = 4{??}\    ; index to Nexus pointer
        ld_nexus                    = 4{??}\    ; address of Nexus
           ld_phase_flag            = 4{??}\    ; for debugging
           ld_device_table_base_adr = 4{??}\    ; device configuration table
           ld_scratch               = 4{??}\    ; scratch memory
           ld_unused                = 4{??}\    ; unused
        ld_message                  = 4{??}\    ; buffer for MsgIn bytes
        ld_message4                 = 4{??}\    ; buffer continuation
        ld_pad                      = 4{??}\    ; padding
        ld_size                     = 4{??}     ; size of this structure


PROC BSC_SCRIPT:

        ; *** These NOPs must be at address 0.                          ***
        ; *** This is reserved space for the structure "local_data".    ***
        ; *** The driver inits this area to zero.                       ***

    nop 0       ; ld_AbortCode,             ld_zeroes
    nop 0       ; ld_status,                ld_counter

    nop 0       ; ld_AbortBdr_mailbox,      ld_IOdone_mailbox
    nop 0       ; ld_sched_mlbx_base_adr,   ld_mailboxp

    nop 0       ; ld_scsi_id,               ld_nexus_array_base
    nop 0       ; ld_nexus_index,           ld_nexus

    nop 0       ; ld_phase_flag,            ld_device_table_base_adr
    nop 0       ; ld_scratch,               ld_unused

    nop 0       ; ld_message,               ld_message4
    nop ld_size ; ld_pad,                   ld_size     (Use ld_size or lose it)

    nop sglist_complete     ; use sglist_complete or lose it from gen'd output file

    ;****************************************************************************
    ;
    ; findNexusFromIndex - load DSA with pointer to Nexus given a Nexus index:
    ;
    ;****************************************************************************

findNexusFromIndex:

    load SCRATCHA0, 4, ld_nexus_index       ; load index and leading zeroes
    clear CARRY
    move SCRATCHA0 SHL 0 to SCRATCHA0       ; double the index
    move SCRATCHA1 SHL 0 to SCRATCHA1
    move SCRATCHA0 SHL 0 to SCRATCHA0       ; double again
    move SCRATCHA1 SHL 0 to SCRATCHA1       ; A0 now has index to 4-byte address
    store SCRATCHA0, 4, patchArrayOffset+4  ; *** patch the code
    
    load DSA0, 4, ld_nexus_array_base       ; load base address of array of Nexus pointers
patchArrayOffset:
    load DSA0, 4, DSAREL( 0 )               ; *** patched offset. Load pointer.

    move DSA0 to SFBR                       ; Ensure pointer is not 0xFFFFFFFF
    int unallocated_nexus, if 0xFF          ; Interrupt if NFG

    store DSA0, 4, ld_nexus                 ; Store the Nexus pointer
    return                                  ; end findNexusFromIndex


    ;****************************************************************************
    ;
    ; initContext - Initialize the registers for Sync and Wide using
    ;   values stored in the device configuration table.
    ;   Return with values in SCRATCHB for Select code.
    ;
    ;****************************************************************************

initContext:

    load SCRATCHB0, 4, ld_scsi_id                   ; load 4-bit SCSI ID and zeroes
    clear CARRY
    move SCRATCHB0 SHL SCRATCHB0                    ; * 2
    move SCRATCHB0 SHL SCRATCHB0                    ; * 2 -> UInt32 index
    store SCRATCHB0, 4, patchGetDevConfigOffset+4   ; *** Patch load  code

    load DSA0, 4, ld_device_table_base_adr          ; load base physical addr of tables

patchGetDevConfigOffset:
    load  SCRATCHB0, 4, DSAREL( 0 )                 ; *** Patched table offset ***

        ; SCRATCHB0 = 0
        ; SCRATCHB1 = TP,MO (SXFER bits7-5 bits3-0)
        ; SCRATCHB2 = 0 (position for SCSI ID)
        ; SCRATCHB3 = SCCF,EWS (SCNTL3 bits6-4 bit 3)

    move SCRATCHB1 to SFBR                          ; init SXFER from B1
    move SFBR to SXFER
                                                    ; Init SCNTL3 from B3
    move SCRATCHB3 to SFBR
    move SFBR to SCNTL3
    return                                          ; return with SCRATCHB intact.


    ;*****************************************************************
    ;
    ; Select_phase:
    ;       Clear the SIGP bit.
    ;       Check if any Abort/BusDeviceReset request waiting.
    ;       Nexus is found in the list of 256 mailboxes.
    ;       If current mailbox is empty, jump to reselect_phase.
    ;       SCRIPTS tries to select device.
    ;       If select fails due to reselect, jump to reselect_phase
    ;       Select Timeout handled by driver.
    ;       If select succeeds, clear the mailbox entry
    ;       and increment the mailbox counter.
    ;       Jump to the phase_handler (hopefully for MSG_OUT)
    ;
    ;*****************************************************************

select_phase:

    move CTEST2 | 0x00 to CTEST2           ; Clear SIGP bit from ISTAT reg

        ; Check abort mailbox:

    load SCRATCHA0, 4, ld_AbortBdr_mailbox ; Get AbortBdr mailbox
        ; The Identify byte in byte 0 is also the semaphore
        ; A0 = Identify byte (0xC0 + LUN  N.B. Disconnect allowed)
        ; A1 = Tag, if any
        ; A2 = SCSI ID
        ; A3 = Abort code   Abort=0x06; Abort Tag=0D; Bus Device Reset=0x0C
    move SCRATCHA0 to SFBR                  ; test the semaphore/Identify
    jump rel( AbortMailbox ), if not 0      ; jump if aborting


        ; Get the next IO nexus in the mailboxes circular list.
        ; Calculate current mailbox address as so:
        ;   counter byte index * 4  to get mailbox index
        ;   add base physical address of mailboxes giving current mailbox address

    load SCRATCHA0, 4, ld_counter           ; get 1-byte mailbox counter & 0s
    clear CARRY
    move SCRATCHA0 SHL 0 to SCRATCHA0       ; double it
    move SCRATCHA1 SHL 0 to SCRATCHA1
    move SCRATCHA0 SHL 0 to SCRATCHA0       ; double it again
    move SCRATCHA1 SHL 0 to SCRATCHA1       ; now have a UInt32 index
    store SCRATCHA0, 4, fetchMailbox+4      ; *** patch the load  DSA instruction
    store SCRATCHA0, 4, clear_mailbox+4     ; *** patch the store DSA instruction

    load DSA0, 4, ld_sched_mlbx_base_adr    ; load base physical address of mailboxes

fetchMailbox:
    load  DSA0, 4, DSAREL( 0 )              ; *** Patched offset. Load Nexus address
    store DSA0, 4, ld_nexus                 ; save pointer to current Nexus
    load SCRATCHA0, 4, ld_nexus             ; copy to A0

    move SCRATCHA0 to SFBR                  ; 
    jump rel( next_mailbox ), if 1          ; if low-byte == 0x01 then cancelled mailbox

    move SCRATCHA1 | SFBR to SFBR           ; if non-zero, have implicit semaphore
    move SCRATCHA2 | SFBR to SFBR
    move SCRATCHA3 | SFBR to SFBR
    jump rel( reselect_phase ), if 0        ; go to reselect_phase if empty

        ;*****************************************************************
        ;
        ; Something in mailbox: we have work to do
        ;
        ;*****************************************************************

    move kphase_SELECT to SCRATCHB0             ; set phase indicator
    store SCRATCHB0, 1, ld_phase_flag

    load  SCRATCHB0, 4, ld_zeroes               ; clr the invalid-nexus-index flag
    load  SCRATCHB0, 1, DSAREL( TLQ_index )     ; get index byte from nexus
    store SCRATCHB0, 4, ld_nexus_index          ; save it in local data

    load DSA0, 4, ld_nexus                      ; restore DSA register
    load SCRATCHB2, 1, DSAREL( TLQ_SCSI_ID+2 )  ; get Target's SCSI ID
    move SCRATCHB2 to SFBR
    move SFBR to SCRATCHB0                      ; position it
    store SCRATCHB0, 1, ld_scsi_id              ; save it
    call rel( initContext )                     ; setup Sync/Wide regs in SCRATCHB
    load DSA0, 4, ld_nexus                      ; restore DSA register
    store SCRATCHB1, 1, DSAREL( TLQ_SCSI_ID+1 ) ; SXFER
    store SCRATCHB3, 1, DSAREL( TLQ_SCSI_ID+3 ) ; SCNTL3

        ;********************** select the device ********************************
    SELECT ATN from TLQ_SCSI_ID, rel( try_reselect )    ; ************************
        ;*************************************************************************

        ; looking good - clear the mailbox:

next_mailbox:
    load SCRATCHA0, 4, ld_zeroes            ; zero out scratch register A
    load DSA0, 4, ld_sched_mlbx_base_adr    ; load base physical address of mailboxes
clear_mailbox:
    store SCRATCHA0, 4, DSAREL( 0 )         ; *** Patched offset. Zero the mailbox

        ; Update the index to the mailbox circular list:
    load SCRATCHB0, 1, ld_counter           ; get counter (mailbox index)
    move SCRATCHB0 + 1 to SCRATCHB0         ; add 1
    store SCRATCHB0, 1, ld_counter  ; put it back

    load SCRATCHB0, 1, ld_nexus             ; if low-byte == 0x01 then cancelled mailbox
    move SCRATCHB0 to SFBR
    jump rel( select_phase ), if 1        

;   *** FALL THROUGH TO phase_handler ***


    ;*****************************************************************
    ;
    ;   Phase_handler
    ;       The phase handler script is a dispatcher function of SCSI phase
    ;
    ;*****************************************************************

phase_handler:
    load DSA0, 4, ld_nexus                          ; reload DSA
    jump rel( command_phase ),        when CMD      ; wait for REQ
    jump rel( data_out_phase ),       if DATA_OUT   ; already latched REQ signal
    jump rel( message_out_phase ),    if MSG_OUT
    jump rel( data_in_phase ),        if DATA_IN
    jump rel( status_phase ),         if STATUS
    jump rel( message_in_phase ),     if MSG_IN
    int     unknown_phase


    ;*****************************************************************
    ;
    ; Message-Out phase
    ;
    ;*****************************************************************

message_out_phase:
    move kphase_MSG_OUT to SCRATCHB0                ; Set phase indicator
    store SCRATCHB0, 1, ld_phase_flag

    move from TLQ_MSGOp, when MSG_OUT               ; put out the message(s)
    jump rel( phase_handler )


        ; issueMessageOut - Driver entry point for Sync/Wide negotiation and
        ; to issue message Reject:

issueMessageOut:
    set     ATN                                     ; tell Target we have something to say
    clear   ACK
    jump rel( message_out_phase ),  when MSG_OUT    ; wait for REQ. Jump if msg-out phase.
    jump rel( phase_handler ),      if not MSG_IN   ; jump if weird phase
    move 1, ld_scratch+1,           when   MSG_IN   ; dump the msg byte
    clear ACK                                       ; accept Target's last msg-in byte
    jump rel( issueMessageOut )


    ;*****************************************************************
    ;
    ;   Command phase
    ;
    ;*****************************************************************

command_phase:
    move kphase_COMMAND to SCRATCHB0    ; Set phase indicator
    store SCRATCHB0, 1, ld_phase_flag

    clear ATN                           ; In case we missed the sending nego
    move FROM TLQ_CDBp, when CMD        ; issue the CDB
    jump rel( phase_handler )


    ;*****************************************************************
    ;
    ; Data_out_phase
    ;
    ;*****************************************************************

data_out_phase:
    move kphase_DATA_OUT to SCRATCHB0       ; Set phase indicator
    store SCRATCHB0, 1, ld_phase_flag

    call rel( driverXfer )                  ; call driver-built CHMOV instructions
    jump rel( phase_handler )               ; if all data xfer'd, get next phase

driverXfer:                                 ; get here from data-in code also
    load  SCRATCHA0, 4, DSAREL( TLQ_xferAdr )
    store SCRATCHA0, 4, doItPatch+4         ; *** patch the JUMP address
    move 0xFF to SCRATCHA1
    store SCRATCHA1, 1, DSAREL( TLQ_xferStarted )

doItPatch:
    jump 0x0333                             ; *** patched address



    ;*****************************************************************
    ;
    ; Data_in_phase
    ;   875 sets ATN if bad parity detected.
    ;   Use of CHMOV instructions assures that we properly handle
    ;   a leftover wide byte in the SWIDE or SODL register, depending
    ;   on the data direction. This can happen in either of two conditions:
    ;     1. The Target disconnects at an odd boundary. This is
    ;       extremely unlikely with disk devices.
    ;     2. The client passes either an odd buffer address or
    ;       an odd transfer count. When the Target disconnects (at
    ;       an even boundary, we end up with the extra wide
    ;       byte in SWIDE or SODL. MacOS does this with VM on.
    ;
    ;*****************************************************************

data_in_phase:
    move kphase_DATA_IN to SCRATCHB0        ; Set phase indicator
    store SCRATCHB0, 1, ld_phase_flag

    call rel( driverXfer )                  ; call driver-built CHMOV instructions

    ; The driver gets interrupted if a phase mismatch occurs as when
    ; the Target goes MSG-IN with a Disconnect.
    ; The driver codes either a RETURN if the Scatter/Gather list is complete or
    ; an INT if more Scatter/Gather elements need to be generated.
    ; On the Macintosh, client programs expect extra incoming data to be dumped.
    ; For example, during boot the ROM reads 512 bytes from a 2K-byte-sector CD.

bucket_loop:
    jump rel( phase_handler ), when not DATA_IN ; wait for phase, exit if changed
    CHMOV 1, ld_status, when DATA_IN            ; eat a byte
    jump rel( bucket_loop );                    ; keep dumping bytes


    ;*****************************************************************
    ;
    ; Status phase
    ;
    ;*****************************************************************

status_phase:
    move kphase_STATUS to SCRATCHB0             ; Set phase indicator
    store SCRATCHB0, 1, ld_phase_flag

    move 1, ld_status, when STATUS              ; Read Status byte from bus
    jump rel( phase_handler )


    ;*****************************************************************
    ;
    ; Message-In phase
    ;
    ;*****************************************************************

message_in_phase:
    move kphase_MSG_IN to SCRATCHB0             ; Set phase indicator
    store SCRATCHB0, 1, ld_phase_flag

    move 1, ld_message, when MSG_IN             ; Read byte from bus

    jump rel( cmdComplete ),        if 0x00     ; Command Complete
    jump rel( saveDataPointer ),    if 0x02     ; Save Data Pointer
    jump rel( disconnect_msg ),     if 0x04     ; Disconnect
    jump rel( ignoreWideResidue ),  if 0x23     ; Ignore Wide Residue
    jump rel( restoreDataPointer ), if 0x03     ; Restore Data Pointer
    jump rel( extended_msg ),       if 0x01     ; Extended message
    jump rel( msg_reject ),         if 0x07     ; Message Reject
        ; Identify,                 if 0x80-FF  ; Identify + LUN
        ; simple_queue_tag,         if 0x20     ; Simple Queue Tag
        ; initiate_recovery,        if 0x0F     ; Initiate Recovery
        ; linked_cde_complete,      if 0x0A/0x0B
    int unexpected_msg                          ; unknown

msg_reject:
    int  unknown_msg_reject

clearACK:                                       ; ENTRY point to end negotiation
    clear ACK
    jump rel( phase_handler )



    ;*****************************************************************
    ;
    ; Ignore Wide Residue
    ;
    ;*****************************************************************

ignoreWideResidue:      ; this is a two byte message so snag the 2nd byte here
    clear ACK
    move 1, ld_message+1, when MSG_IN       ; save residue count
    move SFBR to SCRATCHB2                  ; byte is still in SFBR. Position it.
    store SCRATCHB2, 1, DSAREL( TLQ_IWR )   ; Store residue count in Nexus for driver.
    clear ACK
    jump rel( phase_handler )


    ;*****************************************************************
    ;
    ; Extended message
    ;       Accept Wide and Synchronous Data Transfer messages
    ;
    ;*****************************************************************

extended_msg:
    clear ACK
    move 1, ld_message+1, when MSG_IN       ; read msg length byte from bus
    clear ACK
    move 1, ld_message+2, when MSG_IN       ; read ext msg code from bus
    clear ACK
        ; extended_identify,   IF 0x02
        ; modify_data_pointer, if 0x00
    jump rel( sdtr ),   if 0x01             ; jump if SDTR, sync negotiation msg
    jump rel( wdtr ),   if 0x03             ; jump if WDTR, wide negotiation msg
    int unexpected_ext_msg                  ; let driver deal with unknown


    ;*****************************************************************
    ;
    ; Command complete
    ;       The Command-Complete message is sent to indicate that the
    ;       IO operation has completed and valid status has been sent.
    ;       The Target should then disconnect.
    ;       SCRIPTS must spin until the IOdone mailbox is empty.
    ;       Then it sets the IOdone mailbox with the current Nexus.
    ;       The status message is analyzed.
    ;       If status is good, INTF the driver and jump to select_phase.
    ;       If status is NG, save it in the NEXUS and INT the driver.
    ;
    ;*****************************************************************

cmdComplete:
    move kphase_CMD_COMPLETE to SCRATCHB0       ; Set phase indicator
    store SCRATCHB0, 1, ld_phase_flag

    move SCNTL2 & 0X7F to SCNTL2                ; Clr SDU: SCSI Disconnect Unexpected
    clear ACK
    WAIT DISCONNECT

testMbxLp:                                      ; loop until IOdone mailbox empty
    load SCRATCHA0, 4, ld_IOdone_mailbox
    move SCRATCHA3 to SFBR                      ; A3 = semaphore
    jump rel( testMbxLp ), if not 0

        ; Fill in the IOdone mailbox with the following:
        ;   A0 = index to Nexus
        ;   A1 = Status
        ;   A2 = 0
        ;   A3 = semaphore (FF = set)
    load SCRATCHA0, 1, ld_nexus_index       ; A0 = index to Nexus
    load SCRATCHB0, 1, ld_status
    move SCRATCHB0 to SFBR
    move SFBR to SCRATCHA1                  ; A1 = Status
    move 0x00 to SCRATCHA2                  ; A2 = 0
    move 0xFF to SCRATCHA3                  ; A3 = semaphore IOdone mailbox
    store SCRATCHA0, 4, ld_IOdone_mailbox

    move SCRATCHA1 to SFBR                  ; Test the Status of this IO
        ; SFBR = status msg
        ; Test status - If good, Interrupt on the fly and jump to select phase
    intfly 0xFF, if 0 and mask 0xC1         ; mask off reserved bits
    jump rel( select_phase ), if 0 and mask 0xC1
    int     status_error                    ; Status err. Interrupt driver & stop


    ;*****************************************************************
    ;
    ; Disconnect
    ;       The 8xx Accepts the disconnection and jumps to the select_phase
    ;       to check for another IO
    ;
    ;*****************************************************************

disconnect_msg:
    load SCRATCHB0, 1, ld_phase_flag
    move SCRATCHB0 to SFBR
        ; If we got here from reselect just bailout since ld_nexus is
        ; not setup and the code using it is not needed anyway (no data xfer)
    jump rel( bailout ), if kphase_RESELECT

    move kphase_DISCONNECT to SCRATCHB0
    store SCRATCHB0, 1, ld_phase_flag

bailout:
    move 0xFF to SCRATCHB3                  ; invalidate nexus index for driver
    store SCRATCHB3, 1, ld_nexus_index+3
    move SCNTL2 & 0x7F to SCNTL2            ; Clr SDU: SCSI Disconnect Unexpected
    clear ACK
    WAIT DISCONNECT                         ; wait for bus-free
    jump rel( select_phase )                ; go see if more to do


    ;******************************************************************
    ;
    ; ??? mlj - saveDataPointer and restoreDataPointer are incorrect.
    ; ??? They basically do nothing.
    ; Save Data Pointer
    ;
    ;*****************************************************************

saveDataPointer:
    move kphase_saveDataPointer to SCRATCHB0
    store SCRATCHB0, 1, ld_phase_flag
    clear ACK
    jump rel( phase_handler )


    ;******************************************************************
    ;
    ; ??? mlj - saveDataPointer and restoreDataPointer are incorrect.
    ; ??? They basically do nothing.
    ; Restore Data Pointer
    ;       The local values still blocks, still bytes and data address
    ;       must be loaded from the corresponding NEXUS data set.
    ;       This message should followed an IDE (parity error)
    ;
    ;*****************************************************************

restoreDataPointer:
    move kphase_restoreDataPointer to SCRATCHB0
    store SCRATCHB0, 1, ld_phase_flag
    clear ACK
    jump rel( phase_handler )


    ;*****************************************************************
    ;
    ; Synchronous data transfer request or response
    ;
    ;*****************************************************************
sdtr:
    move 2, ld_message+3, when MSG_IN       ; Read period & offset from bus
    int negotiateSDTR


    ;***************************************************************************
    ;
    ; Wide Data Transfer request or response
    ;
    ;***************************************************************************
wdtr:
    move 1, ld_message+3, when MSG_IN       ; get Transfer Width Exponent fm bus
    int  negotiateWDTR


    ;*****************************************************************
    ;
    ; Reselect phase
    ;       The chip waits here either for a Reselection from a Target or
    ;       a SIGP from the driver indicating something in the mailbox.
    ;       If reselected, the script uses the Nexus value which is either
    ;       a Tag or a SCSI ID/LUN combo to lookup the Nexus.
    ;       Then init the SXFER and SCNTL3 registers from the device config table.
    ;
    ;*****************************************************************

try_reselect:                               ; Select failed - probably reselecting
                                            ; Cf NCR Errata Listing 117 Item 1:
    move SCNTL0 & 0xDF to SCNTL0            ; clr Start bit
    move CTEST2 | 0x00 to CTEST2            ; Clear SIGP bit from ISTAT reg

reselect_phase:
    move kphase_RESELECT to SCRATCHB0       ; Set phase indicator
    store SCRATCHB0, 1, ld_phase_flag

    move 0xFF to SCRATCHB3                  ; invalidate nexus index for driver
    store SCRATCHB3, 1, ld_nexus_index+3

        ; wait here for reselect from a Target
        ; or SIGP from the driver

    WAIT RESELECT REL( select_phase )       ; jump if SIGP

        ; Reselected:

    move SSID to SFBR                       ; SSID = [ Valxxx Scsi_id ]
    int unknown_reselect, if 0 and mask 0x7F; Interrupt if VAL bit not set
    move SFBR & 0x0F to SCRATCHB0           ; B0 = Target ID
    store SCRATCHB0, 1, ld_scsi_id          ; save it

    call rel( initContext )                 ; setup sync regs here

    int no_msgin_after_reselect, when not MSG_IN

    move 1, ld_message, when MSG_IN         ; Read Identify byte from bus

        ; if another REQ is asserted, a SimpleQueueTag message should be next

    clear ACK                               ; notify Target: msg byte rx'd
    jump rel( getNextMsg ), when MSG_IN     ; jump if SimpleQueueTag coming

        ; untagged operation:

    move SFBR & 0x07 to SCRATCHA0           ; isolate LUN from Identify byte

    load SCRATCHB0, 1, ld_scsi_id           ; B0 = Target ID
    clear CARRY
    move SCRATCHB0 SHL SFBR                 ; shift left #1
    move SFBR SHL SCRATCHB0                 ; shift left #2
    move SCRATCHB0 SHL SFBR                 ; shift left #3
    move SCRATCHA0 | SFBR to SCRATCHA0      ; form Nexus index = 0b0TTTTLLL

    store SCRATCHA0, 1, ld_nexus_index      ; store as index to Nexus
    jump rel( haveNexusIndex )

        ; should be tagged operation:

getNextMsg:
    move 1, ld_message,  when MSG_IN        ; read message byte from bus
    jump rel( disconnect_msg ), if 0x04     ; if Disconnect, oh well.
    clear ACK
    jump rel( phase_handler ), if not 0x20; Branch if not Queue tag code
        ; get the Queue Tag and save as the nexus index
    move 1, ld_nexus_index, when MSG_IN     ; Nexus index <- Tag from bus
    clear ACK                               ; acknowledge it

haveNexusIndex:
    move 0x00 to SCRATCHB3                  ; clear invalid-nexus-index flag
    store SCRATCHB3, 1, ld_nexus_index+3 
    call rel( findNexusFromIndex )          ; set DSA <- Nexus pointer
    jump rel( phase_handler )               ; start handling phases.


        ;*****************************************************************
        ;
        ; AbortMailbox - Abort (or BusDeviceReset) the mailbox entry.
        ; This is a queued operation - not an immediate
        ; operation as is issueAbort_BDR.
        ;   The Abort message clears all IO processes for the
        ;   selecting Initiator on the specified LUN.
        ;
        ;   The Bus Device Reset message clears all IO processes for
        ;   all Initiators on all LUNs of selected Target.
        ;   It forces a hard reset condition to the selected SCSI device.
        ;
        ;   A0 = Identify byte (0xC0 + LUN  N.B. Disconnect allowed)
        ;   A1 = Tag, if any
        ;   A2 = SCSI ID
        ;   A3 = Abort code Abort=0x06; Abort Tag=0D; Bus Device Reset=0x0C
        ;
        ;   Mailbox not cleared by SCRIPTS so that driver can find SCSI ID when done
        ;   N.B.: Device is Async and Narrow after BDR!!!
        ;   Driver must set the device config table values accordingly.
        ;*****************************************************************

AbortMailbox:
    move kphase_ABORT_MAILBOX to SCRATCHB0      ; Set phase code
    store SCRATCHB0, 1, ld_phase_flag
        
    move 0xFF to SCRATCHB3                      ; invalidate nexus index for driver
    store SCRATCHB3, 1, ld_nexus_index+3

    load  SCRATCHB2, 1, ld_AbortBdr_mailbox+2   ; get SCSI ID
    store SCRATCHB2, 1, AbortSelect+2           ; *** Patch the Select/ATN instruction

AbortSelect:
    SELECT ATN 0, REL( try_reselect )           ; *** Patched SCSI ID

    move SCRATCHA1 to SFBR                      ; check for Tag
    jump rel( taggedAbort ) if not 0x00         ; jump if tagged abort

        ; untagged Abort or BusDeviceReset:

    move SCRATCHA3 to SFBR                      ; position the abort code
    move SFBR to SCRATCHA1
    store SCRATCHA0, 2, ld_scratch              ; Store Identify and Abort msgs
    move 0x00 to SCNTL2                         ; Clr SDU SCSI Disconnect Unexpected
    move 2, ld_scratch , when MSG_OUT           ; emit Identify and Abort messages
    WAIT DISCONNECT
    int abort_mailbox

        ; AbortTag:

taggedAbort:
    move SCRATCHA1 to SFBR                      ; position the Tag
    move SFBR to SCRATCHA2
    move 0x20 to SCRATCHA1                      ; gen SimpleQueueTag code
    store SCRATCHA0, 4, ld_scratch              ; store Identify, SQT, Tag, AbortTag
    move 0x00 to SCNTL2                         ; Clr SDU SCSI Disconnect Unexpected
    move 4, ld_scratch, when MSG_OUT            ; emit all 4 bytes
    WAIT DISCONNECT
    int abort_mailbox


        ;*****************************************************************
        ;
        ; issueAbort_BDR - Abort (or BusDeviceReset) the current operation.
        ; This is an immediate operation - not a queued operation
        ; as is AbortMailbox.
        ;   The Abort message clears all IO processes for the
        ;   selecting Initiator on the specified LUN.
        ;
        ;   The Bus Device Reset message clears all IO processes for
        ;   all Initiators on all LUNs of selected Target.
        ;   It forces a hard reset condition to the selected SCSI device.
        ;
        ;*****************************************************************

issueAbort_BDR:
    move kphase_ABORT_CURRENT to SCRATCHB0      ; Set phase code
    store SCRATCHB0, 1, ld_phase_flag

    move ISTAT & 0x08 to SFBR                   ; see if Target connected to bus
    int abort_current, if 0                     ; interrupt driver if not connected

    SET ATN                                     ; get Target's attention
    load DSA0, 4, ld_nexus                      ; load pointer to Nexus

bucketLoop:
    clear ACK
    jump rel( sendAbortBDR ),   when MSG_OUT    ; wait for REQ. Jump if OK.

    jump rel( BucketInStatus ), if STATUS       ; bit bucket in
    jump rel( BucketInMsg ),    if MSG_IN       ; bit bucket in
    jump rel( BucketInData ),   if DATA_IN      ; bit bucket in

    move 0xAD to SCRATCHA0
    jump rel( BucketOutData ),  if DATA_OUT     ; bit bucket out
    jump rel( BucketOutCmd ),   if CMD          ; bit bucket out
    int unknown_phase                           ; back to driver for harsher measures


BucketInStatus:
    move 1, ld_scratch, when STATUS             ; eat the Status byte
    jump rel( bucketLoop );                     ; keep bit-bucketing bytes

BucketInMsg:
    move 1, ld_scratch, when MSG_IN             ; eat a message byte
    jump rel( bucketLoop );                     ; keep bit-bucketing bytes

BucketInData:
    move 1, ld_scratch, when DATA_IN            ; eat a data byte
    jump rel( bucketLoop );                     ; keep bit-bucketing bytes

BucketOutData:
    move SCRATCHA0 xor 0x73 to SCRATCHA0        ; gen 0xDEAD ...
    store SCRATCHA0, 1, ld_scratch
    move 1, ld_scratch, when DATA_OUT           ; pad a byte out
    jump rel( bucketLoop );                     ; keep bit-bucketing bytes

BucketOutCmd:
    move 0x00 to SCRATCHA0                      ; load Null, TestUnitReady, whatever
    store SCRATCHA0, 1, ld_scratch
    move 1, ld_scratch, when CMD                ; pad a byte out
    jump rel( bucketLoop );                     ; keep bit-bucketing bytes


sendAbortBDR:
    move 0x00 to SCNTL2                         ; Clr SDU SCSI Disconnect Unexpected
    move  1, ld_AbortCode, when MSG_OUT         ; Send Abort(06) or BDR(0C) message
    load  SCRATCHA0, 4, ld_zeroes               ; load 0's
    store SCRATCHA0, 4, ld_AbortCode            ; clear the Abort code
    WAIT DISCONNECT
    int abort_current                           ; went BusFree - tell Driver