; Script for the NCR (or symbios) 53c700 and 53c700-66 chip
;
; Copyright (C) 2001 James.Bottomley@HansenPartnership.com
;;-----------------------------------------------------------------------------
;;  
;;  This program is free software; you can redistribute it and/or modify
;;  it under the terms of the GNU General Public License as published by
;;  the Free Software Foundation; either version 2 of the License, or
;;  (at your option) any later version.
;;
;;  This program is distributed in the hope that it will be useful,
;;  but WITHOUT ANY WARRANTY; without even the implied warranty of
;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;  GNU General Public License for more details.
;;
;;  You should have received a copy of the GNU General Public License
;;  along with this program; if not, write to the Free Software
;;  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;;
;;-----------------------------------------------------------------------------
;
; This script is designed to be modified for the particular command in
; operation.  The particular variables pertaining to the commands are:
;
ABSOLUTE	Device_ID = 0		; ID of target for command
ABSOLUTE	MessageCount = 0	; Number of bytes in message
ABSOLUTE	MessageLocation = 0	; Addr of message
ABSOLUTE	CommandCount = 0	; Number of bytes in command
ABSOLUTE	CommandAddress = 0	; Addr of Command
ABSOLUTE	StatusAddress = 0	; Addr to receive status return
ABSOLUTE	ReceiveMsgAddress = 0	; Addr to receive msg
;
; This is the magic component for handling scatter-gather.  Each of the
; SG components is preceded by a script fragment which moves the
; necessary amount of data and jumps to the next SG segment.  The final
; SG segment jumps back to .  However, this address is the first SG script
; segment.
;
ABSOLUTE	SGScriptStartAddress = 0

; The following represent status interrupts we use 3 hex digits for
; this: 0xPRS where 

; P:
ABSOLUTE	AFTER_SELECTION 	= 0x100
ABSOLUTE	BEFORE_CMD 		= 0x200
ABSOLUTE	AFTER_CMD 		= 0x300
ABSOLUTE	AFTER_STATUS 		= 0x400
ABSOLUTE	AFTER_DATA_IN		= 0x500
ABSOLUTE	AFTER_DATA_OUT		= 0x600
ABSOLUTE	DURING_DATA_IN		= 0x700

; R:
ABSOLUTE	NOT_MSG_OUT 		= 0x10
ABSOLUTE	UNEXPECTED_PHASE 	= 0x20
ABSOLUTE	NOT_MSG_IN 		= 0x30
ABSOLUTE	UNEXPECTED_MSG		= 0x40
ABSOLUTE	MSG_IN			= 0x50
ABSOLUTE	SDTR_MSG_R		= 0x60
ABSOLUTE	REJECT_MSG_R		= 0x70
ABSOLUTE	DISCONNECT		= 0x80
ABSOLUTE	MSG_OUT			= 0x90
ABSOLUTE	WDTR_MSG_R		= 0xA0

; S:
ABSOLUTE	GOOD_STATUS 		= 0x1

; Combinations, since the script assembler can't process |
ABSOLUTE	NOT_MSG_OUT_AFTER_SELECTION = 0x110
ABSOLUTE	UNEXPECTED_PHASE_BEFORE_CMD = 0x220
ABSOLUTE	UNEXPECTED_PHASE_AFTER_CMD = 0x320
ABSOLUTE	NOT_MSG_IN_AFTER_STATUS = 0x430
ABSOLUTE	GOOD_STATUS_AFTER_STATUS = 0x401
ABSOLUTE	UNEXPECTED_PHASE_AFTER_DATA_IN = 0x520
ABSOLUTE	UNEXPECTED_PHASE_AFTER_DATA_OUT = 0x620
ABSOLUTE	UNEXPECTED_MSG_BEFORE_CMD = 0x240
ABSOLUTE	MSG_IN_BEFORE_CMD = 0x250
ABSOLUTE	MSG_IN_AFTER_CMD = 0x350
ABSOLUTE	SDTR_MSG_BEFORE_CMD = 0x260
ABSOLUTE	REJECT_MSG_BEFORE_CMD = 0x270
ABSOLUTE	DISCONNECT_AFTER_CMD = 0x380
ABSOLUTE	SDTR_MSG_AFTER_CMD = 0x360
ABSOLUTE	WDTR_MSG_AFTER_CMD = 0x3A0
ABSOLUTE	MSG_IN_AFTER_STATUS = 0x440
ABSOLUTE	DISCONNECT_AFTER_DATA = 0x580
ABSOLUTE	MSG_IN_AFTER_DATA_IN = 0x550
ABSOLUTE	MSG_IN_AFTER_DATA_OUT = 0x650
ABSOLUTE	MSG_OUT_AFTER_DATA_IN = 0x590
ABSOLUTE	DATA_IN_AFTER_DATA_IN = 0x5a0
ABSOLUTE	MSG_IN_DURING_DATA_IN = 0x750
ABSOLUTE	DISCONNECT_DURING_DATA = 0x780

;
; Other interrupt conditions
; 
ABSOLUTE	RESELECTED_DURING_SELECTION = 0x1000
ABSOLUTE	COMPLETED_SELECTION_AS_TARGET = 0x1001
ABSOLUTE	RESELECTION_IDENTIFIED = 0x1003
;
; Fatal interrupt conditions.  If you add to this, also add to the
; array of corresponding messages
;
ABSOLUTE	FATAL = 0x2000
ABSOLUTE	FATAL_UNEXPECTED_RESELECTION_MSG = 0x2000
ABSOLUTE	FATAL_SEND_MSG = 0x2001
ABSOLUTE	FATAL_NOT_MSG_IN_AFTER_SELECTION = 0x2002
ABSOLUTE	FATAL_ILLEGAL_MSG_LENGTH = 0x2003

ABSOLUTE	DEBUG_INTERRUPT	= 0x3000
ABSOLUTE	DEBUG_INTERRUPT1 = 0x3001
ABSOLUTE	DEBUG_INTERRUPT2 = 0x3002
ABSOLUTE	DEBUG_INTERRUPT3 = 0x3003
ABSOLUTE	DEBUG_INTERRUPT4 = 0x3004
ABSOLUTE	DEBUG_INTERRUPT5 = 0x3005
ABSOLUTE	DEBUG_INTERRUPT6 = 0x3006


;
; SCSI Messages we interpret in the script
;
ABSOLUTE	COMMAND_COMPLETE_MSG	= 0x00
ABSOLUTE	EXTENDED_MSG		= 0x01
ABSOLUTE	SDTR_MSG		= 0x01
ABSOLUTE	SAVE_DATA_PTRS_MSG	= 0x02
ABSOLUTE	RESTORE_DATA_PTRS_MSG	= 0x03
ABSOLUTE	WDTR_MSG		= 0x03
ABSOLUTE	DISCONNECT_MSG		= 0x04
ABSOLUTE	REJECT_MSG		= 0x07
ABSOLUTE	PARITY_ERROR_MSG	= 0x09
ABSOLUTE	SIMPLE_TAG_MSG		= 0x20
ABSOLUTE	IDENTIFY_MSG		= 0x80
ABSOLUTE	IDENTIFY_MSG_MASK	= 0x7F
ABSOLUTE	TWO_BYTE_MSG		= 0x20
ABSOLUTE	TWO_BYTE_MSG_MASK	= 0x0F

; This is where the script begins

ENTRY	StartUp

StartUp:
	SELECT	ATN Device_ID, Reselect
	JUMP	Finish, WHEN STATUS
	JUMP	SendIdentifyMsg, IF MSG_OUT
	INT	NOT_MSG_OUT_AFTER_SELECTION

Reselect:
	WAIT	RESELECT SelectedAsTarget
	INT	RESELECTED_DURING_SELECTION, WHEN MSG_IN
	INT	FATAL_NOT_MSG_IN_AFTER_SELECTION

	ENTRY	GetReselectionData
GetReselectionData:
	MOVE	1, ReceiveMsgAddress, WHEN MSG_IN
	INT	RESELECTION_IDENTIFIED

	ENTRY	GetReselectionWithTag
GetReselectionWithTag:
	MOVE	3, ReceiveMsgAddress, WHEN MSG_IN
	INT	RESELECTION_IDENTIFIED
	
	ENTRY	SelectedAsTarget
SelectedAsTarget:
; Basically tell the selecting device that there's nothing here
	SET	TARGET
	DISCONNECT
	CLEAR	TARGET
	INT	COMPLETED_SELECTION_AS_TARGET
;
; These are the messaging entries
;
; Send a message.  Message count should be correctly patched
	ENTRY	SendMessage
SendMessage:
	MOVE	MessageCount, MessageLocation, WHEN MSG_OUT
ResumeSendMessage:
	RETURN,	WHEN NOT MSG_OUT
	INT	FATAL_SEND_MSG

	ENTRY	SendMessagePhaseMismatch
SendMessagePhaseMismatch:
	CLEAR	ACK
	JUMP	ResumeSendMessage
;
; Receive a message.  Need to identify the message to
; receive it correctly
	ENTRY	ReceiveMessage
ReceiveMessage:
	MOVE	1, ReceiveMsgAddress, WHEN MSG_IN
;
; Use this entry if we've just tried to look at the first byte
; of the message and want to process it further
ProcessReceiveMessage:
	JUMP	ReceiveExtendedMessage, IF EXTENDED_MSG
	RETURN,	IF NOT TWO_BYTE_MSG, AND MASK TWO_BYTE_MSG_MASK
	CLEAR	ACK
	MOVE	1, ReceiveMsgAddress + 1, WHEN MSG_IN
	RETURN
ReceiveExtendedMessage:
	CLEAR	ACK
	MOVE	1, ReceiveMsgAddress + 1, WHEN MSG_IN
	JUMP	Receive1Byte, IF 0x01
	JUMP	Receive2Byte, IF 0x02
	JUMP	Receive3Byte, IF 0x03
	JUMP	Receive4Byte, IF 0x04
	JUMP	Receive5Byte, IF 0x05
	INT	FATAL_ILLEGAL_MSG_LENGTH
Receive1Byte:
	CLEAR	ACK
	MOVE	1, ReceiveMsgAddress + 2, WHEN MSG_IN
	RETURN
Receive2Byte:
	CLEAR	ACK
	MOVE	2, ReceiveMsgAddress + 2, WHEN MSG_IN
	RETURN
Receive3Byte:
	CLEAR	ACK
	MOVE	3, ReceiveMsgAddress + 2, WHEN MSG_IN
	RETURN
Receive4Byte:
	CLEAR	ACK
	MOVE	4, ReceiveMsgAddress + 2, WHEN MSG_IN
	RETURN
Receive5Byte:
	CLEAR	ACK
	MOVE	5, ReceiveMsgAddress + 2, WHEN MSG_IN
	RETURN
;
; Come here from the message processor to ignore the message
;
	ENTRY	IgnoreMessage
IgnoreMessage:
	CLEAR	ACK
	RETURN
;
; Come here to send a reply to a message
;
	ENTRY	SendMessageWithATN
SendMessageWithATN:
	SET	ATN
	CLEAR	ACK
	JUMP	SendMessage

SendIdentifyMsg:
	CALL	SendMessage
	CLEAR	ATN

IgnoreMsgBeforeCommand:
	CLEAR	ACK
	ENTRY	SendCommand
SendCommand:
	JUMP	Finish, WHEN STATUS
	JUMP	MsgInBeforeCommand, IF MSG_IN
	INT	UNEXPECTED_PHASE_BEFORE_CMD, IF NOT CMD
	MOVE	CommandCount, CommandAddress, WHEN CMD
ResumeSendCommand:
	JUMP	Finish, WHEN STATUS
	JUMP	MsgInAfterCmd, IF MSG_IN
	JUMP	DataIn, IF DATA_IN
	JUMP	DataOut, IF DATA_OUT
	INT	UNEXPECTED_PHASE_AFTER_CMD

IgnoreMsgDuringData:
	CLEAR	ACK
	; fall through to MsgInDuringData

Entry MsgInDuringData
MsgInDuringData:
;
; Could be we have nothing more to transfer
;
	JUMP	Finish, WHEN STATUS
	MOVE	1, ReceiveMsgAddress, WHEN MSG_IN
	JUMP	DisconnectDuringDataIn, IF DISCONNECT_MSG
	JUMP	IgnoreMsgDuringData, IF SAVE_DATA_PTRS_MSG
	JUMP	IgnoreMsgDuringData, IF RESTORE_DATA_PTRS_MSG
	INT	MSG_IN_DURING_DATA_IN

MsgInAfterCmd:
	MOVE	1, ReceiveMsgAddress, WHEN MSG_IN
	JUMP	DisconnectAfterCmd, IF DISCONNECT_MSG
	JUMP	IgnoreMsgInAfterCmd, IF SAVE_DATA_PTRS_MSG
	JUMP	IgnoreMsgInAfterCmd, IF RESTORE_DATA_PTRS_MSG
	CALL	ProcessReceiveMessage
	INT	MSG_IN_AFTER_CMD
	CLEAR	ACK
	JUMP	ResumeSendCommand

IgnoreMsgInAfterCmd:
	CLEAR	ACK
	JUMP	ResumeSendCommand

DisconnectAfterCmd:
	CLEAR	ACK
	WAIT	DISCONNECT
	ENTRY	Disconnect1
Disconnect1:
	INT	DISCONNECT_AFTER_CMD
	ENTRY	Disconnect2
Disconnect2:
; We return here after a reselection
	CLEAR	ACK
	JUMP	ResumeSendCommand

MsgInBeforeCommand:
	MOVE	1, ReceiveMsgAddress, WHEN MSG_IN
	JUMP	IgnoreMsgBeforeCommand, IF SAVE_DATA_PTRS_MSG
	JUMP	IgnoreMsgBeforeCommand, IF RESTORE_DATA_PTRS_MSG
	CALL	ProcessReceiveMessage
	INT	MSG_IN_BEFORE_CMD
	CLEAR	ACK
	JUMP	SendCommand

DataIn:
	CALL	SGScriptStartAddress
ResumeDataIn:
	JUMP	Finish, WHEN STATUS
	JUMP	MsgInAfterDataIn, IF MSG_IN
	JUMP	DataInAfterDataIn, if DATA_IN
	INT	MSG_OUT_AFTER_DATA_IN, if MSG_OUT
	INT	UNEXPECTED_PHASE_AFTER_DATA_IN

DataInAfterDataIn:
	INT	DATA_IN_AFTER_DATA_IN
	JUMP	ResumeDataIn

DataOut:
	CALL	SGScriptStartAddress
ResumeDataOut:
	JUMP	Finish, WHEN STATUS
	JUMP	MsgInAfterDataOut, IF MSG_IN
	INT	UNEXPECTED_PHASE_AFTER_DATA_OUT

MsgInAfterDataIn:
	MOVE	1, ReceiveMsgAddress, WHEN MSG_IN
	JUMP	DisconnectAfterDataIn, IF DISCONNECT_MSG
	JUMP	IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG
	JUMP	IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG
	CALL	ProcessReceiveMessage
	INT	MSG_IN_AFTER_DATA_IN
	CLEAR	ACK
	JUMP	ResumeDataIn

DisconnectDuringDataIn:
	CLEAR	ACK
	WAIT	DISCONNECT
	ENTRY	Disconnect3
Disconnect3:
	INT	DISCONNECT_DURING_DATA
	ENTRY	Disconnect4
Disconnect4:
; we return here after a reselection
	CLEAR	ACK
	JUMP	ResumeSendCommand


DisconnectAfterDataIn:
	CLEAR	ACK
	WAIT	DISCONNECT
	ENTRY	Disconnect5
Disconnect5:
	INT	DISCONNECT_AFTER_DATA
	ENTRY	Disconnect6
Disconnect6:
; we return here after a reselection
	CLEAR	ACK
	JUMP	ResumeDataIn

MsgInAfterDataOut:
	MOVE	1, ReceiveMsgAddress, WHEN MSG_IN
	JUMP	DisconnectAfterDataOut, if DISCONNECT_MSG
	JUMP	IgnoreMsgAfterData, IF SAVE_DATA_PTRS_MSG
	JUMP	IgnoreMsgAfterData, IF RESTORE_DATA_PTRS_MSG
	CALL	ProcessReceiveMessage
	INT	MSG_IN_AFTER_DATA_OUT
	CLEAR	ACK
	JUMP	ResumeDataOut

IgnoreMsgAfterData:
	CLEAR	ACK
; Data in and out do the same thing on resume, so pick one
	JUMP	ResumeDataIn

DisconnectAfterDataOut:
	CLEAR	ACK
	WAIT	DISCONNECT
	ENTRY	Disconnect7
Disconnect7:
	INT	DISCONNECT_AFTER_DATA
	ENTRY	Disconnect8
Disconnect8:
; we return here after a reselection
	CLEAR	ACK
	JUMP	ResumeDataOut

Finish:
	MOVE	1, StatusAddress, WHEN STATUS
	INT	NOT_MSG_IN_AFTER_STATUS, WHEN NOT MSG_IN
	MOVE	1, ReceiveMsgAddress, WHEN MSG_IN
	JUMP	FinishCommandComplete, IF COMMAND_COMPLETE_MSG
	CALL	ProcessReceiveMessage
	INT	MSG_IN_AFTER_STATUS
	ENTRY	FinishCommandComplete
FinishCommandComplete:
	CLEAR	ACK
	WAIT	DISCONNECT
	ENTRY	Finish1
Finish1:
	INT	GOOD_STATUS_AFTER_STATUS
	ENTRY	Finish2
Finish2: