;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Generic SCRIPTS for 53C875 chip ; ; ; ; This script is designed to minimise host CPU intervention ; ; ; ; Unfortunately, CPU intervention is required: ; ; 1) If a target wants to disconnect in the middle of a transfer ; ; 2) On just about any error condition ; ; ; ; In the absense of errors, the CPU queues requests to the ; ; scsi_id_table[scsi_id].queue_head and sets the SIGP bit in the ISTAT reg. ; ; It gets an 'INTFLY' as each request completes. This script runs ; ; continuously waiting for requests to process. ; ; ; ; If an error occurrs, this chip halts and the host CPU gets an interrupt. ; ; The host CPU will attempt to recover the condition and restart the chip. ; ; The host CPU may decide the best recovery is to software reset this chip ; ; and perform a SCSI reset, at which point it will re-load this code and re- ; ; start this chip. ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ARCH 875 ; chip is an 53C875 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; List of entrypoints that the host can start us at ; ENTRY select_timedout ; jump here after 'SELECT' timeout ENTRY startup ; initialization ENTRY transfer_data_done ; the list of rp_datamovs JUMP's here when done ENTRY transfer_data_mismatch ; re-enter here if phase mismatch during data transfer ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Transfer requested negotiation options ; ABSOLUTE REQ_ACK_OFFSET = 16 ; can send up to 16 unacknowleged data at a time ABSOLUTE XFER_PERIOD_FACT = 12 ; 12=50ns (20MHz) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Scsi id table entry definitions ; ; There is one entry per possible scsi id. These are stored in the internal RAM. ; The table must be aligned on a 256-byte boundary. Each element can have up to ; 16 bytes. ; ; se_queue_head[32]: pointer to first request in queue ; se0_state[8]: state the current request is in (init to STATE_EMPTY) ; gets changed in real-time to the current state ; (used for debugging only) ; se1_sxfer[8]: negotiated value (default startup value 0x00) ; se2_unused[8]: unused ; se3_scntl3[8]: negotiated value (default startup value 0x55) ; se_saved_pointer[32]: saved data offset ; ABSOLUTE se_queue_head = 0 ABSOLUTE se0_state = 4 ABSOLUTE SE_STATE_IDLE = 0 ABSOLUTE SE_STATE_GETTING_MESSAGE = 1 ABSOLUTE SE_STATE_GETTING_STATUS = 2 ABSOLUTE SE_STATE_MESSAGE_OUT = 3 ABSOLUTE SE_STATE_CHECKING_TARGET = 4 ABSOLUTE SE_STATE_SELECTED = 5 ABSOLUTE SE_STATE_SELECTING = 6 ABSOLUTE SE_STATE_SENDING_COMMAND = 7 ABSOLUTE SE_STATE_TRANSFERRING_DATA = 8 ABSOLUTE SE_STATE_WAITING_FOR_RESELECT = 9 ABSOLUTE se1_sxfer = 5 ABSOLUTE se3_scntl3 = 7 ABSOLUTE se_saved_pointer = 8 ; ; Initialization value for longword at offset 4 ; ABSOLUTE INIT_SCSI_TABLE_SCNTL3 = 0x55 ABSOLUTE INIT_SCSI_TABLE_SXFER = 0x00 ABSOLUTE INIT_SCSI_TABLE_4 = SE_STATE_IDLE | (INIT_SCSI_TABLE_SXFER << 8) | (INIT_SCSI_TABLE_SCNTL3 << 24) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Request packet definitions. There is one per outstanding I/O request. These ; are stored in the host CPU's general memory. ; ; rp_this_va[32]: host's virtual address of this packet ; rp_next_pa[32]: pointer to next request in queue ; (0 if end of queue, else address with low bit set) ; rp_datamov_pa[32]: pointer to data transfer CHMOV's ; low bit set means there either aren't any ; ... or they have all been executed ; rp0_flags[8]: flag bits: ; <0> : 0=have already done identify message ; 1=have yet to do identify message (this is the initial state) ; <1> : 0=don't do width negotiation ; 1=do width negotiation ; <2> : 0=don't do synchronous negotiation ; 1=do sync negotiation ; <3> : 0=disconnect not allowed ; 1=disconnect allowed ; <4> : 0=haven't received status yet ; 1=status byte has been received ; <5> : 0=request still pending ; 1=request completed ; rp1_abort[8]: 0=host wants request executed as is ; else=host wants request aborted asap (set RP_FLAG_DONE and unhook abort complete) ; rp2_status[8]: final scsi status byte ; rp3_cmdlen[8]: number of bytes in command (1..??) ; rp_command[??]: command bytes ; ABSOLUTE rp_this_va = 0 ABSOLUTE rp_next_pa = 4 ABSOLUTE rp_datamov_pa = 8 ABSOLUTE rp0_flags = 12 ABSOLUTE RP_FLAG_NEEDTOIDENT = 0x01 ABSOLUTE RP_FLAG_NEGWIDTH = 0x02 ABSOLUTE RP_FLAG_NEGSYNCH = 0x04 ABSOLUTE RP_FLAG_GOTSTATUS = 0x08 ABSOLUTE RP_FLAG_DONE = 0x10 ABSOLUTE rp1_abort = 13 ABSOLUTE rp2_status = 14 ABSOLUTE rp3_cmdlen = 15 ABSOLUTE rp_command = 16 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Scratch registers ; ; next_scsi_index = SCRATCHJ3 ; next scsi index to be processed ; scsi_index = SCRATCHJ2 ; scsi index being processed ; doing_data_xfer = SCRATCHJ1 ; used to inform host CPU when we expect that an exception might occur ; request_packet = SCRATCHI ; base address of request being processed ; request_packetv = SCRATCHH ; host virt address of request being processed ; (only valid when SCRATCHJ1 = 1) ; scsi_id_entry = SCRATCHG ; scsi_id_table entry ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Flags that go in SCRATCHJ1. The host cpu driver uses these values when it ; gets an interrupt to see if it should do anything special to process it. ; ABSOLUTE SCRATCHJ1_CHMOVS = 1 ABSOLUTE SCRATCHJ1_SELECT = 2 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Internal memory ; ; - this should be first as it must be on a 256-byte boundary ; scsi_id_table: CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT ; table of 256 bytes CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT CHMOV 0, INIT_SCSI_TABLE_4, WHEN DATA_OUT CHMOV 0, 0, WHEN DATA_OUT ; scsi_id_ptr: CHMOV 0, scsi_id_table, WHEN DATA_OUT ; point to scsi_id_table ; msg_buf: CHMOV 0, 0, WHEN DATA_OUT ; 8-byte temp message-in or -out buffer ; ; Predefined messages ; abort_task: CHMOV 0x06, 0, WHEN DATA_OUT ; ABORT TASK (0x06) message_reject: CHMOV 0x07, 0, WHEN DATA_OUT ; MESSAGE REJECT (0x07) noop_message: CHMOV 0x08, 0, WHEN DATA_OUT ; NO-OP (0x08) message_ident_width: CHMOV 0x0201C0, 1, WHEN STATUS ; IDENTIFY AND ALLOW DISCONNECT (0xC0), ; NEGOTIATE WIDE DATA TRANSFER (0x01,0x02,0x03,1) message_ident_synch: CHMOV 0x0301C0, XFER_PERIOD_FACT+(REQ_ACK_OFFSET<<8), WHEN DATA_IN ; IDENTIFY AND ALLOW DISCONNECT (0xC0), ; NEGOTIATE SYNCHRONOUS TRANSFER (0x01,0x03,0x01,XFER_PERIOD_FACT,REQ_ACK_OFFSET) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This is the initial start entrypoint ; startup: MOVE 0 TO SCRATCHJ1 ; we are not doing anything special ; - so if host gets an interrupt (other than INTFLY), ; it should consider it a fatal error MOVE 0 TO SCRATCHJ3 ; start looking at device 0 LOAD SCRATCHG0, 4, scsi_id_ptr+4 ; set scsi_id_table entry pointer ; ; See if there is any new request to start ; ; New requests will have the low bit set in scsi_id_table[scsi_id].queue_head ; When the queue is empty, scsi_id_table[scsi_id].queue_head will be zero ; When the request is in progress, scsi_id_table[scsi_id].queue_head will be non-zero but will have low bit clear ; select_timedout: mainloop: MOVE ISTAT & 0xDF TO ISTAT ; about to scan loop, so clear SIGP flag bit ; if host queues something while we're scanning, ; ... it will set this bit again and we won't ; ... hang in the WAIT RESELECT instruction MOVE 0xF0 TO SCRATCHA0 ; max of 16 devices to scan new_req_scanloop: MOVE SCRATCHJ3 TO SFBR ; get which device to scan for MOVE SFBR TO SCRATCHJ2 MOVE SCRATCHJ3 + 1 TO SCRATCHJ3 ; set up id of next device to check MOVE SCRATCHJ3 & 15 TO SCRATCHJ3 CALL REL (get_request_packet) ; point to request packet MOVE SCRATCHI0 TO SFBR ; test low bit of request_packet JUMP REL (got_new_req), IF 1 AND MASK 0xFE ; break out if we got something new MOVE SCRATCHA0 + 1 TO SCRATCHA0 ; nothing new there, try next device JUMP REL (new_req_scanloop), IF NOT CARRY ; ; Check for some target trying to reselect ; Presumably, it is ready for data or status transfer ; check_reselect: MOVE 0 TO SCRATCHJ1 ; we are no longer doing the SELECT instruction WAIT RESELECT REL (not_reselect) ; wait for reselection ; - this will jump to 'not_reselect' if either ISTAT is set ; (meaning there are new requests to process) or some dumbell is ; trying to select me ; ; Something is trying to re-select, resume processing the target ; MOVE SSID TO SFBR ; see who is calling us JUMP REL (bad_reselect_scsi_id), IF 0 AND MASK 0x7F ; bad if the 'valid' bit is not set MOVE SFBR & 0x0F TO SCRATCHJ2 ; save the scsi id in the scsi_index scratch register CALL REL (get_request_packet) ; set request_packet = first queue entry for the scsi_index MOVE SCRATCHI0 TO SFBR ; queue entry not active if low bit is set JUMP REL (bad_reselect_scsi_id), IF 1 AND MASK 0xFE MOVE SCRATCHI1 | SFBR TO SFBR ; queue entry zero means not active also MOVE SCRATCHI2 | SFBR TO SFBR MOVE SCRATCHI3 | SFBR TO SFBR JUMP REL (check_target_state), IF NOT 0 ; go see what target wants bad_reselect_scsi_id: CALL REL (set_atn) ; tell target we have something to tell it MOVE 1, abort_task, WHEN MSG_OUT ; reselect from unknown source, tell it to abort what it is trying to do WAIT DISCONNECT ; wait for target to disconnect from scsi bus JUMP REL (mainloop) ; go find something else to do ; ; Not being reselected, maybe some idiot is trying to select me ; not_reselect: WAIT SELECT REL (mainloop) ; see if someone is trying to select me ; - this will jump to 'mainloop' if either ISTAT is set ; (meaning there are new requests to process) or some target ; is trying to reselect me MOVE 1, abort_task, WITH MSG_OUT DISCONNECT CLEAR TARGET JUMP REL (mainloop) ; ; New request found, decrement the base address to eliminate the low bit being set ; then try to select the device. If another target tries to reselect us in the ; mean time, forget about this for now and go process it ; ; Registers: ; ; SCRATCHJ2 (scsi_index) = scsi-id of device that has a new request ; DSA = points to scsi_id_table entry for the device ; SCRATCHI (request_packet) = points to the request packet (but it has its low bit set) ; got_new_req: MOVE SCRATCHI0 & 0xFE TO SCRATCHI0 ; clear low flag bit CALL REL (request_packet_to_dsa) ; see if host CPU wants this request aborted LOAD SCRATCHA1, 1, DSAREL (rp1_abort) MOVE SCRATCHA1 TO SFBR JUMP REL (req_complete2), IF NOT 0 ; if so, go post it as completed CALL REL (scsi_id_entry_to_dsa) ; set the state to 'SELECTING' MOVE SE_STATE_SELECTING TO SFBR STORE NOFLUSH SFBR, 1, DSAREL (se0_state) MOVE SCRATCHJ1_SELECT TO SCRATCHJ1 ; tell host CPU we are about to to the SELECT instruction STORE SCRATCHJ2, 1, select_instr+2 ; store scsi_id in select instruction ; - this is the one instance where we DO need it to flush! select_instr: SELECT ATN 0, REL (check_reselect) ; (this instr is modified with the scsi_id register contents) ; a jump is made to 'check_reselect' if someone is trying to (re)select ; - note that SIGP is probably still clear so the WAIT instructions should ; do their job. If SIGP was set by the host in the mean time, this idiot ; computer skips the WAIT instructions and thus we should end up right ; back here very quickly only to do it all over again (but hopefully with ; SIGP cleared) ; if this times out, host CPU will jump to 'select_timedout' ; - the host aborts all requests from the queue first, however ; - the jump goes back to clear SIGP and re-scan for more requests to start MOVE 0 TO SCRATCHJ1 ; we are no longer doing the SELECT instruction ; ; We have selected the target, mark the queue entry as being 'in progress' ; Also, clear the low bit in queue_head entry so we won't think we need to select again ; MOVE SE_STATE_SELECTED TO SFBR ; set the state to 'SELECTED' STORE NOFLUSH SFBR, 1, DSAREL (se0_state) STORE NOFLUSH SCRATCHI0, 1, DSAREL (se_queue_head) ; store the request_packet without the low bit ; ; Now see what target wants to do ; ; Registers: ; ; SCRATCHJ2 (scsi_index) = the scsi-id of the device we are processing ; SCRATCHG (scsi_id_entry) = points to entry in the scsi_id_table that we are processing ; SCRATCHI (request_packet) = points to the request packet for the device ; check_target_state: CALL REL (scsi_id_entry_to_dsa) ; set state to 'checking target' MOVE SE_STATE_CHECKING_TARGET TO SFBR STORE NOFLUSH SFBR, 1, DSAREL (se0_state) JUMP REL (transfer_message_out), WHEN MSG_OUT ; now see what target wants to do JUMP REL (transfer_command), IF CMD JUMP REL (transfer_data_in), IF DATA_IN JUMP REL (transfer_data_out), IF DATA_OUT JUMP REL (transfer_status), IF STATUS JUMP REL (transfer_message_in), IF MSG_IN INT 0xA1 ; ?? don't know what to do - target must have disconnected ?? ; ; Target is ready to accept a message from us ; We generally have three messages to send it (in this order): ; 1) Identify (always) ; 2) Negotiate width (optional) ; 3) Negotiate speed (optional) ; transfer_message_out: MOVE SE_STATE_MESSAGE_OUT TO SFBR STORE NOFLUSH SFBR, 1, DSAREL (se0_state) CALL REL (request_packet_to_dsa) ; see what we have yet to send LOAD SCRATCHA0, 1, DSAREL (rp0_flags) MOVE SCRATCHA0 TO SFBR JUMP REL (send_message_ident), IF RP_FLAG_NEEDTOIDENT AND MASK 0xFF - (RP_FLAG_NEGSYNCH | RP_FLAG_NEGWIDTH | RP_FLAG_NEEDTOIDENT) JUMP REL (send_message_width), IF RP_FLAG_NEGWIDTH AND MASK 0xFF - ( RP_FLAG_NEGWIDTH | RP_FLAG_NEEDTOIDENT) JUMP REL (send_message_ident_width), IF RP_FLAG_NEGWIDTH | RP_FLAG_NEEDTOIDENT AND MASK 0xFF - ( RP_FLAG_NEGWIDTH | RP_FLAG_NEEDTOIDENT) JUMP REL (send_message_synch), IF RP_FLAG_NEGSYNCH AND MASK 0xFF - (RP_FLAG_NEGSYNCH | RP_FLAG_NEGWIDTH | RP_FLAG_NEEDTOIDENT) JUMP REL (send_message_ident_synch), IF RP_FLAG_NEGSYNCH | RP_FLAG_NEEDTOIDENT AND MASK 0xFF - (RP_FLAG_NEGSYNCH | RP_FLAG_NEGWIDTH | RP_FLAG_NEEDTOIDENT) MOVE 1, noop_message, WHEN MSG_OUT ; we have nothing to send, so send a noop (this also clears the ATN bit) JUMP REL (check_target_state) send_message_ident: MOVE SFBR & 0xFF - RP_FLAG_NEEDTOIDENT TO SFBR ; clear flag bit STORE SFBR, 1, DSAREL (rp0_flags) MOVE 1, message_ident_width, WHEN MSG_OUT ; send ident message (this also clears the ATN bit) JUMP REL (check_target_state) send_message_ident_width: MOVE SFBR & 0xFF - (RP_FLAG_NEEDTOIDENT | RP_FLAG_NEGWIDTH) TO SFBR ; clear flag bits STORE SFBR, 1, DSAREL (rp0_flags) MOVE 5, message_ident_width, WHEN MSG_OUT ; send ident and width messages (this also clears the ATN bit) JUMP REL (proc_width_reply) send_message_width: MOVE SFBR & 0xFF - RP_FLAG_NEGWIDTH TO SFBR ; clear flag bit STORE SFBR, 1, DSAREL (rp0_flags) MOVE 4, message_ident_width+1, WHEN MSG_OUT ; send width message (this also clears the ATN bit) proc_width_reply: MOVE 1, msg_buf, WHEN MSG_IN ; read the reply, byte-by-byte JUMP REL (bad_nego_mess), IF NOT 0x01 ; - it must be an extended message CLEAR ACK MOVE 1, msg_buf, WHEN MSG_IN JUMP REL (bad_nego_mess_reject), IF NOT 0x02 ; - it must have length 2 CLEAR ACK MOVE 1, msg_buf, WHEN MSG_IN JUMP REL (bad_nego_mess_reject), IF NOT 0x03 ; - it must be a 'wide data transfer' message CLEAR ACK MOVE 1, msg_buf, WHEN MSG_IN ; ok, get the resultant width (either 8 or 16 bits) CALL REL (save_transfer_width) ; save it in memory CALL REL (request_packet_to_dsa) ; see if we have to send synchronous negotiation message, too LOAD SCRATCHA0, 1, DSAREL (rp0_flags) MOVE SCRATCHA0 TO SFBR JUMP REL (proc_width_reply_clear_ack), IF 0 AND MASK 0xFF - RP_FLAG_NEGSYNCH SET ATN ; need to do synch negotiation, tell target we have another message proc_width_reply_clear_ack: CLEAR ACK ; anyway, clear ack to indicate we got all the incoming message JUMP REL (check_target_state) ; and check to see what target wants now send_message_ident_synch: MOVE SFBR & 0xFF - (RP_FLAG_NEEDTOIDENT | RP_FLAG_NEGSYNCH) TO SFBR ; clear flag bits STORE SFBR, 1, DSAREL (rp0_flags) MOVE 6, message_ident_synch, WHEN MSG_OUT ; send ident and synch messages (this also clears the ATN bit) JUMP REL (proc_synch_reply) send_message_synch: MOVE SFBR & 0xFF - RP_FLAG_NEGSYNCH TO SFBR ; clear flag bit STORE SFBR, 1, DSAREL (rp0_flags) MOVE 5, message_ident_synch+1, WHEN MSG_OUT ; send synch message (this also clears the ATN bit) proc_synch_reply: MOVE 1, msg_buf, WHEN MSG_IN ; read the reply, byte-by-byte JUMP REL (bad_nego_mess), IF NOT 0x01 ; - it must be an extended message CLEAR ACK MOVE 1, msg_buf, WHEN MSG_IN JUMP REL (bad_nego_mess_reject), IF NOT 0x03 ; - it must have length 3 CLEAR ACK MOVE 1, msg_buf, WHEN MSG_IN JUMP REL (bad_nego_mess_reject), IF NOT 0x01 ; - it must be a 'synchronous data transfer' message CLEAR ACK MOVE 1, msg_buf, WHEN MSG_IN ; ok, get the resultant tranfer period factor CALL REL (save_transfer_period) ; save it in memory CLEAR ACK MOVE 1, msg_buf, WHEN MSG_IN ; get the resultant req/ack offset CALL REL (save_req_ack_offset) ; save it in memory CLEAR ACK ; clear ack to indicate we got all the incoming message JUMP REL (check_target_state) ; ; Target is ready to accept the command from us ; transfer_command: MOVE SE_STATE_SENDING_COMMAND TO SFBR ; set state 'sending command' STORE NOFLUSH SFBR, 1, DSAREL (se0_state) CALL REL (request_packet_to_dsa) ; point to the request packet LOAD SCRATCHA3, 1, DSAREL (rp3_cmdlen) ; get length of command MOVE SCRATCHA3 TO SFBR STORE NOFLUSH SFBR, 1, command_move+0 ; store in the 'command_move' instruction MOVE DSA0 + rp_command TO DSA0 ; point to the command bytes MOVE DSA1 + 0 TO DSA1 WITH CARRY MOVE DSA2 + 0 TO DSA2 WITH CARRY MOVE DSA3 + 0 TO DSA3 WITH CARRY STORE DSA0, 4, command_move+4 command_move: MOVE 0, 0, WHEN CMD ; transfer the command bytes to the target JUMP REL (check_target_state) ; now see what target wants to do ; ; Target is ready to transfer data ; transfer_data_in: transfer_data_out: MOVE SE_STATE_TRANSFERRING_DATA TO SFBR ; set state 'transferring data' STORE NOFLUSH SFBR, 1, DSAREL (se0_state) LOAD SCNTL3, 1, DSAREL (se3_scntl3) ; set up transfer speed and width LOAD SXFER, 1, DSAREL (se1_sxfer) CALL REL (request_packet_to_dsa) ; point to the request packet LOAD TEMP0, 4, DSAREL (rp_datamov_pa) ; get where the CHMOV's are MOVE TEMP0 TO SFBR ; low bit says we're all done moving data JUMP REL (transfer_no_data), IF 0x01 AND MASK 0xFE LOAD SCRATCHH0, 4, DSAREL (rp_this_va) ; get virt address of request packet MOVE SCRATCHJ1_CHMOVS TO SCRATCHJ1 ; set the 'CHMOV in progress' flag RETURN ; execute the CHMOV's (jumps to address in TEMP) ; code with jump to 'transfer_data_done' if it gets to the end ok ; otherwise, if a phase mismatch occurrs, the host cpu will modify ; the 'rp_datamov_pa' pointer accordingly then jump to ; 'transfer_data_mismatch' ; transfer_data_done: MOVE 1 TO SFBR ; data has all transferred, mark it done STORE NOFLUSH SFBR, 1, DSAREL (rp_datamov_pa) ; - by storing a value with the low bit set transfer_data_mismatch: MOVE 0 TO SCRATCHJ1 ; clear the 'CHMOV in progress' flag JUMP REL (check_target_state) ; go see what target wants now ; transfer_no_data: CALL REL (set_atn) ; target wants more data but there isn't any MOVE 1, abort_task, WHEN MSG_OUT ; - sent it a nasty message JUMP REL (check_target_state) ; - then hopefully it will disconnect ; ; Target has status byte ready for us ; transfer_status: MOVE SE_STATE_GETTING_STATUS TO SFBR ; set state 'getting status' STORE NOFLUSH SFBR, 1, DSAREL (se0_state) MOVE 1, msg_buf, WHEN STATUS ; read the status byte CALL REL (request_packet_to_dsa) ; store in request packet struct MOVE SFBR TO SCRATCHA2 STORE NOFLUSH SCRATCHA2, 1, DSAREL (rp2_status) LOAD TEMP0, 1, DSAREL (rp0_flags) ; ... and say that we got it MOVE TEMP0 | RP_FLAG_GOTSTATUS TO TEMP0 STORE TEMP0, 1, DSAREL (rp0_flags) JUMP REL (check_target_state) ; ; Target has an unsolicited message for us ; transfer_message_in: MOVE SE_STATE_GETTING_MESSAGE TO SFBR ; set state 'getting message' STORE NOFLUSH SFBR, 1, DSAREL (se0_state) MOVE 1, msg_buf, WHEN MSG_IN ; see what the target wants JUMP REL (req_complete), IF 0x00 ; 00 means the request is now complete JUMP REL (got_extended_message), IF 0x01 ; check for 'extended' messages ;; JUMP REL (save_data_pointer), IF 0x02 ; 02 means to save data transfer pointer ;; JUMP REL (restore_pointers), IF 0x03 ; 03 means to restore transfer pointers JUMP REL (disconnecting), IF 0x04 ; 04 means it is disconnecting and will reselect us later JUMP REL (ignore_message), IF 0x07 ; skip over reject's JUMP REL (ignore_message), IF 0x08 ; skip over nop's JUMP REL (ignore_message), IF 0x80 AND MASK 0x7F ; ignore all identify messages from target reject_message: CALL REL (set_atn) ; tell target we are rejecting its message MOVE 1, message_reject, WHEN MSG_OUT ; send reject message, clear ATN JUMP REL (check_target_state) ignore_message: CLEAR ACK ; acknowledge it JUMP REL (check_target_state) ; go see what target wants now ;;save_data_pointer: ;; CLEAR ACK ; acknowledge it ;; CALL REL (request_packet_to_dsa) ; get current pointer ;; LOAD SCRATCHA0, 4, DSAREL (rp_data_offset) ;; CALL scsi_id_entry_to_dsa ; store in saved pointer ;; STORE NOFLUSH SCRATCHA0, 4, DSAREL (se_saved_pointer) ;; JUMP REL (check_target_state) ;;restore_pointers: ;; CLEAR ACK ; acknowledge it ;; LOAD SCRATCHA0, 4, DSAREL (se_saved_pointer) ; get saved pointer ;; CALL REL (request_packet_to_dsa) ; store in current pointer ;; STORE NOFLUSH SCRATCHA0, 4, DSAREL (rp_data_offset) ;; JUMP REL (check_target_state) got_extended_message: CLEAR ACK MOVE 2, msg_buf, WHEN MSG_IN ; get extended message length and code LOAD SCRATCHA0, 2, msg_buf ; get length in A0, code in A1 MOVE SCRATCHA1 TO SFBR ; check out the code JUMP REL (got_sync_data_xfer_msg), IF 0x01 JUMP REL (got_wide_data_xfer_msg), IF 0x03 JUMP REL (reject_message) got_wide_data_xfer_msg: MOVE SCRATCHA0 TO SFBR ; make sure the length is 2 JUMP REL (bad_nego_mess_reject), IF NOT 0x02 CLEAR ACK MOVE 1, msg_buf, WHEN MSG_IN ; get the requested width factor CLEAR ACK ; tell target we got the last byte of message MOVE MEMORY NOFLUSH 8, message_ident_width, msg_buf ; copy a template message JUMP REL (width_ok), IF 0 ; width 0 (8-bits) is ok as is MOVE 1 TO SFBR ; if else, do width 1 (16-bits) width_ok: STORE NOFLUSH SFBR, 1, msg_buf+4 ; save it in outgoing message buffer CALL REL (scsi_id_entry_to_dsa) ; save negotiated width in scsi_id_table entry CALL REL (save_transfer_width) MOVE 4, msg_buf+1, WHEN MSG_OUT ; send reply message JUMP REL (check_target_state) ; now go see what target wants got_sync_data_xfer_msg: MOVE SCRATCHA0 to SFBR ; make sure the length is 3 JUMP REL (bad_nego_mess_reject), IF NOT 0x03 CLEAR ACK MOVE 2, msg_buf, WHEN MSG_IN ; get the transfer period factor and req/ack offset bytes CLEAR ACK ; tell target we got the last bytes of message LOAD SCRATCHA3, 1, msg_buf+1 ; save req/ack offset factor MOVE MEMORY NOFLUSH 5, message_ident_synch+1, msg_buf+1 ; set up a template message CALL REL (scsi_id_entry_to_dsa) CALL REL (save_transfer_period) ; process negotiated speed setting STORE NOFLUSH SCRATCHA2, 1, msg_buf+4 ; save it in reply message buffer MOVE SCRATCHA3 TO SFBR ; get requested req/ack offset CALL REL (save_req_ack_offset) ; process it STORE NOFLUSH SCRATCHA2, 1, msg_buf+5 ; save negotiated value in reply message MOVE 5, msg_buf+1, WHEN MSG_OUT ; send reply message JUMP REL (check_target_state) ; now go see what target wants ; ; Target is disconnecting, remember the state and go do something else in the mean time ; disconnecting: CLEAR ACK ; acknowledge it MOVE SE_STATE_WAITING_FOR_RESELECT TO SFBR ; remember we're waiting for a reselect from it STORE NOFLUSH SFBR, 1, DSAREL (se0_state) WAIT DISCONNECT ; wait for target to disconnect from scsi bus JUMP REL (mainloop) ; go find something else to do ; ; The request is complete ; req_complete: CLEAR ACK ; acknowledge it WAIT DISCONNECT ; wait for target to disconnect from scsi bus req_complete2: MOVE SE_STATE_IDLE TO SFBR ; remember this device isn't doing anything now STORE NOFLUSH SFBR, 1, DSAREL (se0_state) CALL REL (request_packet_to_dsa) MOVE ISTAT | 0x10 TO ISTAT ; set the SEM bit to let host know were modifying queue and RP_FLAG_DONE LOAD SCRATCHA0, 4, DSAREL (rp_next_pa) ; find the next item in the list (it is either zero or has the low bit set) CALL REL (scsi_id_entry_to_dsa) STORE NOFLUSH SCRATCHA0, 4, DSAREL (se_queue_head) ; unlink request from queue CALL REL (request_packet_to_dsa) LOAD SCRATCHA0, 1, DSAREL (rp0_flags) ; flag the request as 'done' MOVE SCRATCHA0 | RP_FLAG_DONE TO SCRATCHA0 STORE SCRATCHA0, 1, DSAREL (rp0_flags) MOVE ISTAT & 0xEF TO ISTAT ; clear SEM bit to let host know were done with mods INTFLY ; tell the host computer that a request completed JUMP REL (mainloop) ; go find something to do ; ; Something was bad about the negotiation reply, send reject then set up async mode ; bad_nego_mess: JUMP REL (bad_nego_mess_async), IF 0x07 ; maybe it is rejecting my request bad_nego_mess_reject: CALL REL (set_atn) ; something else bad with message, reject it MOVE 1, message_reject, WHEN MSG_OUT bad_nego_mess_async: CLEAR ACK CALL REL (scsi_id_entry_to_dsa) MOVE INIT_SCSI_TABLE_SCNTL3 TO SCRATCHA3 ; set up async transfer mode STORE NOFLUSH SCRATCHA3, 1, DSAREL (se3_scntl3) MOVE INIT_SCSI_TABLE_SXFER TO SCRATCHA1 STORE NOFLUSH SCRATCHA1, 1, DSAREL (se1_sxfer) JUMP REL (check_target_state) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Get pointer to first request packet queued to a scsi device ; ; Input: ; ; SCRATCHJ2 (scsi_index) = scsi id to get packet for ; ; Output: ; ; SCRATCHI (request_packet) = 0 : nothing is queued ; odd : address of new packet queued + 1 ; even : address of previously queued packet ; SCRATCHG (scsi_id_entry) = DSA = points to scsi_id_table entry ; get_request_packet: CLEAR CARRY MOVE SCRATCHJ2 TO SFBR ; multiply scsi_index by 16 to get offset in scsi_id_table MOVE SFBR TO SCRATCHG0 MOVE SCRATCHG0 SHL SCRATCHG0 MOVE SCRATCHG0 SHL SCRATCHG0 MOVE SCRATCHG0 SHL SCRATCHG0 MOVE SCRATCHG0 SHL SCRATCHG0 MOVE SCRATCHG0 TO SFBR ; point the DSA at the scsi_id_table entry for the device MOVE SFBR TO DSA0 MOVE SCRATCHG1 TO SFBR MOVE SFBR TO DSA1 MOVE SCRATCHG2 TO SFBR MOVE SFBR TO DSA2 MOVE SCRATCHG3 TO SFBR MOVE SFBR TO DSA3 LOAD SCRATCHI0, 4, DSAREL (se_queue_head) ; load the scsi_id_table[scsi_index].queue_head entry into 'request_packet' RETURN ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Copy the scsi_id_entry register to the DSA ; scsi_id_entry_to_dsa: MOVE SCRATCHG0 TO SFBR MOVE SFBR TO DSA0 MOVE SCRATCHG1 TO SFBR MOVE SFBR TO DSA1 MOVE SCRATCHG2 TO SFBR MOVE SFBR TO DSA2 MOVE SCRATCHG3 TO SFBR MOVE SFBR TO DSA3 RETURN ; ; Copy the request_packet register to the DSA ; request_packet_to_dsa: MOVE SCRATCHI0 TO SFBR MOVE SFBR TO DSA0 MOVE SCRATCHI1 TO SFBR MOVE SFBR TO DSA1 MOVE SCRATCHI2 TO SFBR MOVE SFBR TO DSA2 MOVE SCRATCHI3 TO SFBR MOVE SFBR TO DSA3 RETURN ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Set ATN and wait for target to enter MSG_OUT phase ; ; Output: ; ; target now in MSG_OUT phase ; ; Scratch: ; ; SFBR, msg_buf[0] ; set_atn: SET ATN ; tell target we have something to say set_atn_clrack: CLEAR ACK ; in case last thing was a MSG_IN set_atn_loop: RETURN, WHEN MSG_OUT ; if target is in MSG_OUT, return to caller JUMP REL (set_atn_data_in), WHEN DATA_IN ; if target is in DATA_IN, read (& ignore) a byte JUMP REL (set_atn_data_out), WHEN DATA_OUT ; if target is in DATA_OUT, send it a null byte JUMP REL (set_atn_msg_in), WHEN MSG_IN ; if target is in MSG_IN, read (& ignore) a byte JUMP REL (set_atn_command), WHEN CMD ; if target is in CMD, send it a null byte JUMP REL (set_atn_status), WHEN STATUS ; if target is in STATUS, read (& ignore) a byte INT 0xA2 ; don't know what state target is in, barf set_atn_data_in: MOVE 1, msg_buf, WHEN DATA_IN JUMP REL (set_atn_loop) set_atn_data_out: MOVE 0 TO SFBR STORE SFBR, 1, msg_buf MOVE 1, msg_buf, WHEN DATA_OUT JUMP REL (set_atn_loop) set_atn_msg_in: MOVE 1, msg_buf, WHEN MSG_IN JUMP REL (set_atn_clrack) set_atn_command: MOVE 0 TO SFBR STORE SFBR, 1, msg_buf MOVE 1, msg_buf, WHEN COMMAND JUMP REL (set_atn_loop) set_atn_status: MOVE 1, msg_buf, WHEN STATUS JUMP REL (set_atn_loop) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Clock and width routines ; ; These routines take the results of negotiation messages and updates the ; se3_scntl3 and se1_sxfer locations accordingly ; ; scntl3 consists of the following bits ; ; <7> = 0 : normal de-glitching ; 1 : ultra synchronous de-glitching ; this gets set only for 20MHz synchronous mode ; <4:6> = SCF divider (sets synchronous receive rate = 80MHz/4/SCF) ; 000 = /3 ; 001 = /1 ; 010 = /1.5 ; 011 = /2 ; 100 = /3 ; 101 = /4 ; <3> = 0 : 8-bit transfers ; 1 : 16-bit transfers ; <0:2> = CCF divider (sets asynchronous clock = 80MHz/CCF) (must not exceed 25MHz) ; 101 = /4 ; ; sxfer consists of these bits: ; ; <5:7> = TP (synchronous clock = 80MHz/SCF/TP) ; 000 = /4 ; 001 = /5 ; 010 = /6 ; 011 = /7 ; 100 = /8 ; 101 = /9 ; 110 = /10 ; 111 = /11 ; <0:4> = req/ack offset ; 0 = async ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Save transfer width ; ; Input: ; ; SFBR = as defined in scsi standard for the negotiation value ; 0: 8-bit transfers ; 1: 16-bit transfers ; DSA = points to scsi_id_table entry ; ; Output: ; ; se3_scntl3 = modified ; ; Scratch: ; ; SCRATCHA3 ; save_transfer_width: LOAD SCRATCHA3, 1, DSAREL (se3_scntl3) ; get what was in there before MOVE SCRATCHA3 & 0xF7 TO SCRATCHA3 ; clear the 'wide' bit JUMP REL (save_transfer_width_ok), IF 0 MOVE SCRATCHA3 | 0x08 TO SCRATCHA3 ; ok, set it then save_transfer_width_ok: STORE NOFLUSH SCRATCHA3, 1, DSAREL (se3_scntl3) RETURN ; ; This is the rate that synchronous transfers will happen at ; ; Input: ; ; SFBR = as defined in scsi standard for the negotiation value ; ... 10=25ns period, 11=30.3ns, 12=50ns, 13=52nS, 14=56nS, 15=60nS, ... ; ; Output: ; ; se3_scntl3 = modified accordingly ; SCRATCHA2 = resultant negotiation value ; ; Scratch: ; ; SFBR, SCRATCHA1, SCRATCHA3 ; ; Note: ; ; requested we do giving SCF ; <= 50ns (12) 20MHz /1 (001) ; <=100ns (25) 10MHz /2 (011) ; else..... 5MHz /4 (101) ; save_transfer_period: LOAD SCRATCHA3, 1, DSAREL (se3_scntl3) ; get what's in se3_scntl3 MOVE SCRATCHA3 & 0x8F TO SCRATCHA3 ; clear out the SCF bits MOVE SCRATCHA3 | 0x50 TO SCRATCHA3 ; set SCF = 101 (/4) for 5MHz rate MOVE 50 TO SCRATCHA2 ; set up negotiation value for 5MHz MOVE SFBR + (0xFF - 25) TO SFBR ; sets carry iff sfbr > 25 JUMP REL (save_transfer_period_ok), IF CARRY MOVE SCRATCHA3 - 0x20 TO SCRATCHA3 ; set SCF = 011 (/2) for 10MHz rate MOVE 25 TO SCRATCHA2 ; set up negotiation value for 10MHz MOVE SFBR + 13 TO SFBR ; sets carry iff original sfbr > 12 JUMP REL (save_transfer_period_ok), IF NOT CARRY MOVE 12 TO SCRATCHA2 ; set up negotiation value for 20MHz MOVE SCRATCHA3 - 0x20 TO SCRATCHA3 ; set SCF = 101 (/4) for 5MHz rate save_transfer_period_ok: STORE NOFLUSH SCRATCHA3, 1, DSAREL (se3_scntl3) LOAD SCRATCHA1, 1, DSAREL (se1_sxfer) JUMP REL (check_ultra_enable) ; ; This is the maximum number of req's than can be sent out without having ; received the corresponding ack's for a synchronous transfer. ; ; Zero means use asynchronous transfer (the default case) ; ; Input: ; ; SFBR = as defined in scsi standard for the negotiation value ; range 0..16 ; DSA = points to scsi_id_table entry ; ; Output: ; ; se1_sxfer = modified accordingly ; SCRATCHA2 = resultant negotiated value ; ; Scratch: ; ; SFBR, SCRATCHA1, SCRATCHA3 ; save_req_ack_offset: MOVE SFBR TO SCRATCHA2 ; assume sfbr value is ok as is MOVE SFBR + (0xFF - 16) TO SFBR ; sets carry iff sfbr > 16 JUMP REL (save_req_ack_offset_ok), IF NOT CARRY MOVE 16 TO SCRATCHA2 ; if too big, just use 16 save_req_ack_offset_ok: MOVE SCRATCHA2 TO SFBR LOAD SCRATCHA1, 1, DSAREL (se1_sxfer) ; get what is there MOVE SCRATCHA1 & 0xE0 TO SCRATCHA1 ; save the existing rate info MOVE SCRATCHA1 | SFBR TO SCRATCHA1 ; put in the new offset info STORE NOFLUSH SCRATCHA1, 1, DSAREL (se1_sxfer) LOAD SCRATCHA3, 1, DSAREL (se3_scntl3) ; ; Set the ULTRA enable bit in SCNTL3 iff SXFER indicates max speed synchronous ; ; Input: ; ; SCRATCHA1 = se1_sxfer contents ; SCRATCHA3 = se3_scntl3 contents ; check_ultra_enable: MOVE SCRATCHA3 & 0x7F TO SCRATCHA3 ; clear ultra enable MOVE SCRATCHA1 TO SFBR JUMP REL (check_ultra_enable_ok), IF 0x00 AND MASK 0xE0 ; don't bother setting it if async mode MOVE SCRATCHA3 TO SFBR JUMP REL (check_ultra_enable_ok), IF NOT 0x10 AND MASK 0x8F ; don't bother setting if not max speed MOVE SCRATCHA3 | 0x80 TO SCRATCHA3 ; synchronous and max speed, set ultra enable bit check_ultra_enable_ok: STORE SCRATCHA3, 1, DSAREL (se3_scntl3) RETURN ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;