1#undef DEBUG
2#undef EVENTS
3; NCR 53c810 driver, main script
4; Sponsored by
5;	iX Multiuser Multitasking Magazine
6;	hm@ix.de
7;
8; Copyright 1993, 1994, 1995 Drew Eckhardt
9;      Visionary Computing
10;      (Unix and Linux consulting and custom programming)
11;      drew@PoohSticks.ORG
12;      +1 (303) 786-7975
13;
14; TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
15;
16; PRE-ALPHA
17;
18; For more information, please consult
19;
20; NCR 53C810
21; PCI-SCSI I/O Processor
22; Data Manual
23;
24; NCR 53C710
25; SCSI I/O Processor
26; Programmers Guide
27;
28; NCR Microelectronics
29; 1635 Aeroplaza Drive
30; Colorado Springs, CO 80916
31; 1+ (719) 578-3400
32;
33; Toll free literature number
34; +1 (800) 334-5454
35;
36; IMPORTANT : This code is self modifying due to the limitations of
37;	the NCR53c7,8xx series chips.  Persons debugging this code with
38;	the remote debugger should take this into account, and NOT set
39;	breakpoints in modified instructions.
40;
41; Design:
42; The NCR53c7,8xx family of SCSI chips are busmasters with an onboard
43; microcontroller using a simple instruction set.
44;
45; So, to minimize the effects of interrupt latency, and to maximize
46; throughput, this driver offloads the practical maximum amount
47; of processing to the SCSI chip while still maintaining a common
48; structure.
49;
50; Where tradeoffs were needed between efficiency on the older
51; chips and the newer NCR53c800 series, the NCR53c800 series
52; was chosen.
53;
54; While the NCR53c700 and NCR53c700-66 lacked the facilities to fully
55; automate SCSI transfers without host processor intervention, this
56; isn't the case with the NCR53c710 and newer chips which allow
57;
58; - reads and writes to the internal registers from within the SCSI
59; 	scripts, allowing the SCSI SCRIPTS(tm) code to save processor
60; 	state so that multiple threads of execution are possible, and also
61; 	provide an ALU for loop control, etc.
62;
63; - table indirect addressing for some instructions. This allows
64;	pointers to be located relative to the DSA ((Data Structure
65;	Address) register.
66;
67; These features make it possible to implement a mailbox style interface,
68; where the same piece of code is run to handle I/O for multiple threads
69; at once minimizing our need to relocate code.  Since the NCR53c700/
70; NCR53c800 series have a unique combination of features, making a
71; a standard ingoing/outgoing mailbox system, costly, I've modified it.
72;
73; - Mailboxes are a mixture of code and data.  This lets us greatly
74; 	simplify the NCR53c810 code and do things that would otherwise
75;	not be possible.
76;
77; The saved data pointer is now implemented as follows :
78;
79; 	Control flow has been architected such that if control reaches
80;	munge_save_data_pointer, on a restore pointers message or
81;	reconnection, a jump to the address formerly in the TEMP register
82;	will allow the SCSI command to resume execution.
83;
84
85;
86; Note : the DSA structures must be aligned on 32 bit boundaries,
87; since the source and destination of MOVE MEMORY instructions
88; must share the same alignment and this is the alignment of the
89; NCR registers.
90;
91
92ABSOLUTE dsa_temp_lun = 0		; Patch to lun for current dsa
93ABSOLUTE dsa_temp_next = 0		; Patch to dsa next for current dsa
94ABSOLUTE dsa_temp_addr_next = 0		; Patch to address of dsa next address
95					; 	for current dsa
96ABSOLUTE dsa_temp_sync = 0		; Patch to address of per-target
97					;	sync routine
98ABSOLUTE dsa_temp_target = 0		; Patch to id for current dsa
99ABSOLUTE dsa_temp_addr_saved_pointer = 0; Patch to address of per-command
100					; 	saved data pointer
101ABSOLUTE dsa_temp_addr_residual = 0	; Patch to address of per-command
102					;	current residual code
103ABSOLUTE dsa_temp_addr_saved_residual = 0; Patch to address of per-command
104					; saved residual code
105ABSOLUTE dsa_temp_addr_new_value = 0	; Address of value for JUMP operand
106ABSOLUTE dsa_temp_addr_array_value = 0 	; Address to copy to
107ABSOLUTE dsa_temp_addr_dsa_value = 0	; Address of this DSA value
108
109;
110; Once a device has initiated reselection, we need to compare it
111; against the singly linked list of commands which have disconnected
112; and are pending reselection.  These commands are maintained in
113; an unordered singly linked list of DSA structures, through the
114; DSA pointers at their 'centers' headed by the reconnect_dsa_head
115; pointer.
116;
117; To avoid complications in removing commands from the list,
118; I minimize the amount of expensive (at eight operations per
119; addition @ 500-600ns each) pointer operations which must
120; be done in the NCR driver by precomputing them on the
121; host processor during dsa structure generation.
122;
123; The fixed-up per DSA code knows how to recognize the nexus
124; associated with the corresponding SCSI command, and modifies
125; the source and destination pointers for the MOVE MEMORY
126; instruction which is executed when reselected_ok is called
127; to remove the command from the list.  Similarly, DSA is
128; loaded with the address of the next DSA structure and
129; reselected_check_next is called if a failure occurs.
130;
131; Perhaps more concisely, the net effect of the mess is
132;
133; for (dsa = reconnect_dsa_head, dest = &reconnect_dsa_head,
134;     src = NULL; dsa; dest = &dsa->next, dsa = dsa->next) {
135; 	src = &dsa->next;
136; 	if (target_id == dsa->id && target_lun == dsa->lun) {
137; 		*dest = *src;
138; 		break;
139;         }
140; }
141;
142; if (!dsa)
143;           error (int_err_unexpected_reselect);
144; else
145;     longjmp (dsa->jump_resume, 0);
146;
147;
148
149#if (CHIP != 700) && (CHIP != 70066)
150; Define DSA structure used for mailboxes
151ENTRY dsa_code_template
152dsa_code_template:
153ENTRY dsa_code_begin
154dsa_code_begin:
155	MOVE dmode_memory_to_ncr TO DMODE
156	MOVE MEMORY 4, dsa_temp_addr_dsa_value, addr_scratch
157	MOVE dmode_memory_to_memory TO DMODE
158	CALL scratch_to_dsa
159	CALL select
160; Handle the phase mismatch which may have resulted from the
161; MOVE FROM dsa_msgout if we returned here.  The CLEAR ATN
162; may or may not be necessary, and we should update script_asm.pl
163; to handle multiple pieces.
164    CLEAR ATN
165    CLEAR ACK
166
167; Replace second operand with address of JUMP instruction dest operand
168; in schedule table for this DSA.  Becomes dsa_jump_dest in 53c7,8xx.c.
169ENTRY dsa_code_fix_jump
170dsa_code_fix_jump:
171	MOVE MEMORY 4, NOP_insn, 0
172	JUMP select_done
173
174; wrong_dsa loads the DSA register with the value of the dsa_next
175; field.
176;
177wrong_dsa:
178;		Patch the MOVE MEMORY INSTRUCTION such that
179;		the destination address is the address of the OLD
180;		next pointer.
181;
182	MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok + 8
183	MOVE dmode_memory_to_ncr TO DMODE
184;
185; 	Move the _contents_ of the next pointer into the DSA register as
186;	the next I_T_L or I_T_L_Q tupple to check against the established
187;	nexus.
188;
189	MOVE MEMORY 4, dsa_temp_next, addr_scratch
190	MOVE dmode_memory_to_memory TO DMODE
191	CALL scratch_to_dsa
192	JUMP reselected_check_next
193
194ABSOLUTE dsa_save_data_pointer = 0
195ENTRY dsa_code_save_data_pointer
196dsa_code_save_data_pointer:
197    	MOVE dmode_ncr_to_memory TO DMODE
198    	MOVE MEMORY 4, addr_temp, dsa_temp_addr_saved_pointer
199    	MOVE dmode_memory_to_memory TO DMODE
200; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
201    	MOVE MEMORY 24, dsa_temp_addr_residual, dsa_temp_addr_saved_residual
202        CLEAR ACK
203#ifdef DEBUG
204        INT int_debug_saved
205#endif
206    	RETURN
207ABSOLUTE dsa_restore_pointers = 0
208ENTRY dsa_code_restore_pointers
209dsa_code_restore_pointers:
210    	MOVE dmode_memory_to_ncr TO DMODE
211    	MOVE MEMORY 4, dsa_temp_addr_saved_pointer, addr_temp
212    	MOVE dmode_memory_to_memory TO DMODE
213; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
214    	MOVE MEMORY 24, dsa_temp_addr_saved_residual, dsa_temp_addr_residual
215        CLEAR ACK
216#ifdef DEBUG
217        INT int_debug_restored
218#endif
219    	RETURN
220
221ABSOLUTE dsa_check_reselect = 0
222; dsa_check_reselect determines whether or not the current target and
223; lun match the current DSA
224ENTRY dsa_code_check_reselect
225dsa_code_check_reselect:
226	MOVE SSID TO SFBR		; SSID contains 3 bit target ID
227; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips
228	JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 0xf8
229;
230; Hack - move to scratch first, since SFBR is not writeable
231; 	via the CPU and hence a MOVE MEMORY instruction.
232;
233	MOVE dmode_memory_to_ncr TO DMODE
234	MOVE MEMORY 1, reselected_identify, addr_scratch
235	MOVE dmode_memory_to_memory TO DMODE
236	MOVE SCRATCH0 TO SFBR
237; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips
238	JUMP REL (wrong_dsa), IF NOT dsa_temp_lun, AND MASK 0xf8
239;		Patch the MOVE MEMORY INSTRUCTION such that
240;		the source address is the address of this dsa's
241;		next pointer.
242	MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok + 4
243	CALL reselected_ok
244	CALL dsa_temp_sync
245; Release ACK on the IDENTIFY message _after_ we've set the synchronous
246; transfer parameters!
247	CLEAR ACK
248; Implicitly restore pointers on reselection, so a RETURN
249; will transfer control back to the right spot.
250    	CALL REL (dsa_code_restore_pointers)
251    	RETURN
252ENTRY dsa_zero
253dsa_zero:
254ENTRY dsa_code_template_end
255dsa_code_template_end:
256
257; Perform sanity check for dsa_fields_start == dsa_code_template_end -
258; dsa_zero, puke.
259
260ABSOLUTE dsa_fields_start =  0	; Sanity marker
261				; 	pad 48 bytes (fix this RSN)
262ABSOLUTE dsa_next = 48		; len 4 Next DSA
263 				; del 4 Previous DSA address
264ABSOLUTE dsa_cmnd = 56		; len 4 Scsi_Cmnd * for this thread.
265ABSOLUTE dsa_select = 60	; len 4 Device ID, Period, Offset for
266			 	;	table indirect select
267ABSOLUTE dsa_msgout = 64	; len 8 table indirect move parameter for
268				;       select message
269ABSOLUTE dsa_cmdout = 72	; len 8 table indirect move parameter for
270				;	command
271ABSOLUTE dsa_dataout = 80	; len 4 code pointer for dataout
272ABSOLUTE dsa_datain = 84	; len 4 code pointer for datain
273ABSOLUTE dsa_msgin = 88		; len 8 table indirect move for msgin
274ABSOLUTE dsa_status = 96 	; len 8 table indirect move for status byte
275ABSOLUTE dsa_msgout_other = 104	; len 8 table indirect for normal message out
276				; (Synchronous transfer negotiation, etc).
277ABSOLUTE dsa_end = 112
278
279ABSOLUTE schedule = 0 		; Array of JUMP dsa_begin or JUMP (next),
280				; terminated by a call to JUMP wait_reselect
281
282; Linked lists of DSA structures
283ABSOLUTE reconnect_dsa_head = 0	; Link list of DSAs which can reconnect
284ABSOLUTE addr_reconnect_dsa_head = 0 ; Address of variable containing
285				; address of reconnect_dsa_head
286
287; These select the source and destination of a MOVE MEMORY instruction
288ABSOLUTE dmode_memory_to_memory = 0x0
289ABSOLUTE dmode_memory_to_ncr = 0x0
290ABSOLUTE dmode_ncr_to_memory = 0x0
291
292ABSOLUTE addr_scratch = 0x0
293ABSOLUTE addr_temp = 0x0
294#endif /* CHIP != 700 && CHIP != 70066 */
295
296; Interrupts -
297; MSB indicates type
298; 0	handle error condition
299; 1 	handle message
300; 2 	handle normal condition
301; 3	debugging interrupt
302; 4 	testing interrupt
303; Next byte indicates specific error
304
305; XXX not yet implemented, I'm not sure if I want to -
306; Next byte indicates the routine the error occurred in
307; The LSB indicates the specific place the error occurred
308
309ABSOLUTE int_err_unexpected_phase = 0x00000000	; Unexpected phase encountered
310ABSOLUTE int_err_selected = 0x00010000		; SELECTED (nee RESELECTED)
311ABSOLUTE int_err_unexpected_reselect = 0x00020000
312ABSOLUTE int_err_check_condition = 0x00030000
313ABSOLUTE int_err_no_phase = 0x00040000
314ABSOLUTE int_msg_wdtr = 0x01000000		; WDTR message received
315ABSOLUTE int_msg_sdtr = 0x01010000		; SDTR received
316ABSOLUTE int_msg_1 = 0x01020000			; single byte special message
317						; received
318
319ABSOLUTE int_norm_select_complete = 0x02000000	; Select complete, reprogram
320						; registers.
321ABSOLUTE int_norm_reselect_complete = 0x02010000	; Nexus established
322ABSOLUTE int_norm_command_complete = 0x02020000 ; Command complete
323ABSOLUTE int_norm_disconnected = 0x02030000	; Disconnected
324ABSOLUTE int_norm_aborted =0x02040000		; Aborted *dsa
325ABSOLUTE int_norm_reset = 0x02050000		; Generated BUS reset.
326ABSOLUTE int_debug_break = 0x03000000		; Break point
327#ifdef DEBUG
328ABSOLUTE int_debug_scheduled = 0x03010000	; new I/O scheduled
329ABSOLUTE int_debug_idle = 0x03020000		; scheduler is idle
330ABSOLUTE int_debug_dsa_loaded = 0x03030000	; dsa reloaded
331ABSOLUTE int_debug_reselected = 0x03040000	; NCR reselected
332ABSOLUTE int_debug_head = 0x03050000		; issue head overwritten
333ABSOLUTE int_debug_disconnected = 0x03060000	; disconnected
334ABSOLUTE int_debug_disconnect_msg = 0x03070000	; got message to disconnect
335ABSOLUTE int_debug_dsa_schedule = 0x03080000	; in dsa_schedule
336ABSOLUTE int_debug_reselect_check = 0x03090000  ; Check for reselection of DSA
337ABSOLUTE int_debug_reselected_ok = 0x030a0000 	; Reselection accepted
338#endif
339ABSOLUTE int_debug_panic = 0x030b0000		; Panic driver
340#ifdef DEBUG
341ABSOLUTE int_debug_saved = 0x030c0000 		; save/restore pointers
342ABSOLUTE int_debug_restored = 0x030d0000
343ABSOLUTE int_debug_sync = 0x030e0000		; Sanity check synchronous
344						; parameters.
345ABSOLUTE int_debug_datain = 0x030f0000		; going into data in phase
346						; now.
347ABSOLUTE int_debug_check_dsa = 0x03100000	; Sanity check DSA against
348						; SDID.
349#endif
350
351ABSOLUTE int_test_1 = 0x04000000		; Test 1 complete
352ABSOLUTE int_test_2 = 0x04010000		; Test 2 complete
353ABSOLUTE int_test_3 = 0x04020000		; Test 3 complete
354
355
356; These should start with 0x05000000, with low bits incrementing for
357; each one.
358
359#ifdef EVENTS
360ABSOLUTE int_EVENT_SELECT = 0
361ABSOLUTE int_EVENT_DISCONNECT = 0
362ABSOLUTE int_EVENT_RESELECT = 0
363ABSOLUTE int_EVENT_COMPLETE = 0
364ABSOLUTE int_EVENT_IDLE = 0
365ABSOLUTE int_EVENT_SELECT_FAILED = 0
366ABSOLUTE int_EVENT_BEFORE_SELECT = 0
367ABSOLUTE int_EVENT_RESELECT_FAILED = 0
368#endif
369
370ABSOLUTE NCR53c7xx_msg_abort = 0	; Pointer to abort message
371ABSOLUTE NCR53c7xx_msg_reject = 0       ; Pointer to reject message
372ABSOLUTE NCR53c7xx_zero	= 0		; long with zero in it, use for source
373ABSOLUTE NCR53c7xx_sink = 0		; long to dump worthless data in
374ABSOLUTE NOP_insn = 0			; NOP instruction
375
376; Pointer to message, potentially multi-byte
377ABSOLUTE msg_buf = 0
378
379; Pointer to holding area for reselection information
380ABSOLUTE reselected_identify = 0
381ABSOLUTE reselected_tag = 0
382
383; Request sense command pointer, it's a 6 byte command, should
384; be constant for all commands since we always want 16 bytes of
385; sense and we don't need to change any fields as we did under
386; SCSI-I when we actually cared about the LUN field.
387;EXTERNAL NCR53c7xx_sense		; Request sense command
388
389#if (CHIP != 700) && (CHIP != 70066)
390; dsa_schedule
391; PURPOSE : after a DISCONNECT message has been received, and pointers
392;	saved, insert the current DSA structure at the head of the
393; 	disconnected queue and fall through to the scheduler.
394;
395; CALLS : OK
396;
397; INPUTS : dsa - current DSA structure, reconnect_dsa_head - list
398;	of disconnected commands
399;
400; MODIFIES : SCRATCH, reconnect_dsa_head
401;
402; EXITS : always passes control to schedule
403
404ENTRY dsa_schedule
405dsa_schedule:
406#if 0
407    INT int_debug_dsa_schedule
408#endif
409
410;
411; Calculate the address of the next pointer within the DSA
412; structure of the command that is currently disconnecting
413;
414    CALL dsa_to_scratch
415    MOVE SCRATCH0 + dsa_next TO SCRATCH0
416    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
417    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
418    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
419
420; Point the next field of this DSA structure at the current disconnected
421; list
422    MOVE dmode_ncr_to_memory TO DMODE
423    MOVE MEMORY 4, addr_scratch, dsa_schedule_insert + 8
424    MOVE dmode_memory_to_memory TO DMODE
425dsa_schedule_insert:
426    MOVE MEMORY 4, reconnect_dsa_head, 0
427
428; And update the head pointer.
429    CALL dsa_to_scratch
430    MOVE dmode_ncr_to_memory TO DMODE
431    MOVE MEMORY 4, addr_scratch, reconnect_dsa_head
432    MOVE dmode_memory_to_memory TO DMODE
433/* Temporarily, see what happens. */
434#ifndef ORIGINAL
435    MOVE SCNTL2 & 0x7f TO SCNTL2
436    CLEAR ACK
437#endif
438    WAIT DISCONNECT
439#ifdef EVENTS
440    INT int_EVENT_DISCONNECT;
441#endif
442#if 0
443    INT int_debug_disconnected
444#endif
445    JUMP schedule
446#endif
447
448;
449; select
450;
451; PURPOSE : establish a nexus for the SCSI command referenced by DSA.
452;	On success, the current DSA structure is removed from the issue
453;	queue.  Usually, this is entered as a fall-through from schedule,
454;	although the contingent allegiance handling code will write
455;	the select entry address to the DSP to restart a command as a
456;	REQUEST SENSE.  A message is sent (usually IDENTIFY, although
457;	additional SDTR or WDTR messages may be sent).  COMMAND OUT
458;	is handled.
459;
460; INPUTS : DSA - SCSI command, issue_dsa_head
461;
462; CALLS : NOT OK
463;
464; MODIFIES : SCRATCH, issue_dsa_head
465;
466; EXITS : on reselection or selection, go to select_failed
467;	otherwise, RETURN so control is passed back to
468;	dsa_begin.
469;
470
471ENTRY select
472select:
473
474#if 0
475#ifdef EVENTS
476    INT int_EVENT_BEFORE_SELECT
477#endif
478#endif
479
480#if 0
481#ifdef DEBUG
482    INT int_debug_scheduled
483#endif
484#endif
485    CLEAR TARGET
486
487; XXX
488;
489; In effect, SELECTION operations are backgrounded, with execution
490; continuing until code which waits for REQ or a fatal interrupt is
491; encountered.
492;
493; So, for more performance, we could overlap the code which removes
494; the command from the NCRs issue queue with the selection, but
495; at this point I don't want to deal with the error recovery.
496;
497
498#if (CHIP != 700) && (CHIP != 70066)
499    SELECT ATN FROM dsa_select, select_failed
500    JUMP select_msgout, WHEN MSG_OUT
501ENTRY select_msgout
502select_msgout:
503    MOVE FROM dsa_msgout, WHEN MSG_OUT
504#else
505ENTRY select_msgout
506    SELECT ATN 0, select_failed
507select_msgout:
508    MOVE 0, 0, WHEN MSGOUT
509#endif
510
511#ifdef EVENTS
512   INT int_EVENT_SELECT
513#endif
514   RETURN
515
516;
517; select_done
518;
519; PURPOSE: continue on to normal data transfer; called as the exit
520;	point from dsa_begin.
521;
522; INPUTS: dsa
523;
524; CALLS: OK
525;
526;
527
528select_done:
529
530#ifdef DEBUG
531ENTRY select_check_dsa
532select_check_dsa:
533    INT int_debug_check_dsa
534#endif
535
536; After a successful selection, we should get either a CMD phase or
537; some transfer request negotiation message.
538
539    JUMP cmdout, WHEN CMD
540    INT int_err_unexpected_phase, WHEN NOT MSG_IN
541
542select_msg_in:
543    CALL msg_in, WHEN MSG_IN
544    JUMP select_msg_in, WHEN MSG_IN
545
546cmdout:
547    INT int_err_unexpected_phase, WHEN NOT CMD
548#if (CHIP == 700)
549    INT int_norm_selected
550#endif
551ENTRY cmdout_cmdout
552cmdout_cmdout:
553#if (CHIP != 700) && (CHIP != 70066)
554    MOVE FROM dsa_cmdout, WHEN CMD
555#else
556    MOVE 0, 0, WHEN CMD
557#endif /* (CHIP != 700) && (CHIP != 70066) */
558
559;
560; data_transfer
561; other_out
562; other_in
563; other_transfer
564;
565; PURPOSE : handle the main data transfer for a SCSI command in
566;	several parts.  In the first part, data_transfer, DATA_IN
567;	and DATA_OUT phases are allowed, with the user provided
568;	code (usually dynamically generated based on the scatter/gather
569;	list associated with a SCSI command) called to handle these
570;	phases.
571;
572;	After control has passed to one of the user provided
573;	DATA_IN or DATA_OUT routines, back calls are made to
574;	other_transfer_in or other_transfer_out to handle non-DATA IN
575;	and DATA OUT phases respectively, with the state of the active
576;	data pointer being preserved in TEMP.
577;
578;	On completion, the user code passes control to other_transfer
579;	which causes DATA_IN and DATA_OUT to result in unexpected_phase
580;	interrupts so that data overruns may be trapped.
581;
582; INPUTS : DSA - SCSI command
583;
584; CALLS : OK in data_transfer_start, not ok in other_out and other_in, ok in
585;	other_transfer
586;
587; MODIFIES : SCRATCH
588;
589; EXITS : if STATUS IN is detected, signifying command completion,
590;	the NCR jumps to command_complete.  If MSG IN occurs, a
591;	CALL is made to msg_in.  Otherwise, other_transfer runs in
592;	an infinite loop.
593;
594
595ENTRY data_transfer
596data_transfer:
597    JUMP cmdout_cmdout, WHEN CMD
598    CALL msg_in, WHEN MSG_IN
599    INT int_err_unexpected_phase, WHEN MSG_OUT
600    JUMP do_dataout, WHEN DATA_OUT
601    JUMP do_datain, WHEN DATA_IN
602    JUMP command_complete, WHEN STATUS
603    JUMP data_transfer
604ENTRY end_data_transfer
605end_data_transfer:
606
607;
608; FIXME: On NCR53c700 and NCR53c700-66 chips, do_dataout/do_datain
609; should be fixed up whenever the nexus changes so it can point to the
610; correct routine for that command.
611;
612
613#if (CHIP != 700) && (CHIP != 70066)
614; Nasty jump to dsa->dataout
615do_dataout:
616    CALL dsa_to_scratch
617    MOVE SCRATCH0 + dsa_dataout TO SCRATCH0
618    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
619    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
620    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
621    MOVE dmode_ncr_to_memory TO DMODE
622    MOVE MEMORY 4, addr_scratch, dataout_to_jump + 4
623    MOVE dmode_memory_to_memory TO DMODE
624dataout_to_jump:
625    MOVE MEMORY 4, 0, dataout_jump + 4
626dataout_jump:
627    JUMP 0
628
629; Nasty jump to dsa->dsain
630do_datain:
631    CALL dsa_to_scratch
632    MOVE SCRATCH0 + dsa_datain TO SCRATCH0
633    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
634    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
635    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
636    MOVE dmode_ncr_to_memory TO DMODE
637    MOVE MEMORY 4, addr_scratch, datain_to_jump + 4
638    MOVE dmode_memory_to_memory TO DMODE
639ENTRY datain_to_jump
640datain_to_jump:
641    MOVE MEMORY 4, 0, datain_jump + 4
642#if 0
643    INT int_debug_datain
644#endif
645datain_jump:
646    JUMP 0
647#endif /* (CHIP != 700) && (CHIP != 70066) */
648
649
650; Note that other_out and other_in loop until a non-data phase
651; is discovered, so we only execute return statements when we
652; can go on to the next data phase block move statement.
653
654ENTRY other_out
655other_out:
656#if 0
657    INT 0x03ffdead
658#endif
659    INT int_err_unexpected_phase, WHEN CMD
660    JUMP msg_in_restart, WHEN MSG_IN
661    INT int_err_unexpected_phase, WHEN MSG_OUT
662    INT int_err_unexpected_phase, WHEN DATA_IN
663    JUMP command_complete, WHEN STATUS
664    JUMP other_out, WHEN NOT DATA_OUT
665    RETURN
666
667ENTRY other_in
668other_in:
669#if 0
670    INT 0x03ffdead
671#endif
672    INT int_err_unexpected_phase, WHEN CMD
673    JUMP msg_in_restart, WHEN MSG_IN
674    INT int_err_unexpected_phase, WHEN MSG_OUT
675    INT int_err_unexpected_phase, WHEN DATA_OUT
676    JUMP command_complete, WHEN STATUS
677    JUMP other_in, WHEN NOT DATA_IN
678    RETURN
679
680
681ENTRY other_transfer
682other_transfer:
683    INT int_err_unexpected_phase, WHEN CMD
684    CALL msg_in, WHEN MSG_IN
685    INT int_err_unexpected_phase, WHEN MSG_OUT
686    INT int_err_unexpected_phase, WHEN DATA_OUT
687    INT int_err_unexpected_phase, WHEN DATA_IN
688    JUMP command_complete, WHEN STATUS
689    JUMP other_transfer
690
691;
692; msg_in_restart
693; msg_in
694; munge_msg
695;
696; PURPOSE : process messages from a target.  msg_in is called when the
697;	caller hasn't read the first byte of the message.  munge_message
698;	is called when the caller has read the first byte of the message,
699;	and left it in SFBR.  msg_in_restart is called when the caller
700;	hasn't read the first byte of the message, and wishes RETURN
701;	to transfer control back to the address of the conditional
702;	CALL instruction rather than to the instruction after it.
703;
704;	Various int_* interrupts are generated when the host system
705;	needs to intervene, as is the case with SDTR, WDTR, and
706;	INITIATE RECOVERY messages.
707;
708;	When the host system handles one of these interrupts,
709;	it can respond by reentering at reject_message,
710;	which rejects the message and returns control to
711;	the caller of msg_in or munge_msg, accept_message
712;	which clears ACK and returns control, or reply_message
713;	which sends the message pointed to by the DSA
714;	msgout_other table indirect field.
715;
716;	DISCONNECT messages are handled by moving the command
717;	to the reconnect_dsa_queue.
718;
719; INPUTS : DSA - SCSI COMMAND, SFBR - first byte of message (munge_msg
720;	only)
721;
722; CALLS : NO.  The TEMP register isn't backed up to allow nested calls.
723;
724; MODIFIES : SCRATCH, DSA on DISCONNECT
725;
726; EXITS : On receipt of SAVE DATA POINTER, RESTORE POINTERS,
727;	and normal return from message handlers running under
728;	Linux, control is returned to the caller.  Receipt
729;	of DISCONNECT messages pass control to dsa_schedule.
730;
731ENTRY msg_in_restart
732msg_in_restart:
733; XXX - hackish
734;
735; Since it's easier to debug changes to the statically
736; compiled code, rather than the dynamically generated
737; stuff, such as
738;
739; 	MOVE x, y, WHEN data_phase
740; 	CALL other_z, WHEN NOT data_phase
741; 	MOVE x, y, WHEN data_phase
742;
743; I'd like to have certain routines (notably the message handler)
744; restart on the conditional call rather than the next instruction.
745;
746; So, subtract 8 from the return address
747
748    MOVE TEMP0 + 0xf8 TO TEMP0
749    MOVE TEMP1 + 0xff TO TEMP1 WITH CARRY
750    MOVE TEMP2 + 0xff TO TEMP2 WITH CARRY
751    MOVE TEMP3 + 0xff TO TEMP3 WITH CARRY
752
753ENTRY msg_in
754msg_in:
755    MOVE 1, msg_buf, WHEN MSG_IN
756
757munge_msg:
758    JUMP munge_extended, IF 0x01		; EXTENDED MESSAGE
759    JUMP munge_2, IF 0x20, AND MASK 0xdf	; two byte message
760;
761; XXX - I've seen a handful of broken SCSI devices which fail to issue
762; 	a SAVE POINTERS message before disconnecting in the middle of
763; 	a transfer, assuming that the DATA POINTER will be implicitly
764; 	restored.
765;
766; Historically, I've often done an implicit save when the DISCONNECT
767; message is processed.  We may want to consider having the option of
768; doing that here.
769;
770    JUMP munge_save_data_pointer, IF 0x02	; SAVE DATA POINTER
771    JUMP munge_restore_pointers, IF 0x03	; RESTORE POINTERS
772    JUMP munge_disconnect, IF 0x04		; DISCONNECT
773    INT int_msg_1, IF 0x07			; MESSAGE REJECT
774    INT int_msg_1, IF 0x0f			; INITIATE RECOVERY
775#ifdef EVENTS
776    INT int_EVENT_SELECT_FAILED
777#endif
778    JUMP reject_message
779
780munge_2:
781    JUMP reject_message
782;
783; The SCSI standard allows targets to recover from transient
784; error conditions by backing up the data pointer with a
785; RESTORE POINTERS message.
786;
787; So, we must save and restore the _residual_ code as well as
788; the current instruction pointer.  Because of this messiness,
789; it is simpler to put dynamic code in the dsa for this and to
790; just do a simple jump down there.
791;
792
793munge_save_data_pointer:
794    MOVE DSA0 + dsa_save_data_pointer TO SFBR
795    MOVE SFBR TO SCRATCH0
796    MOVE DSA1 + 0xff TO SFBR WITH CARRY
797    MOVE SFBR TO SCRATCH1
798    MOVE DSA2 + 0xff TO SFBR WITH CARRY
799    MOVE SFBR TO SCRATCH2
800    MOVE DSA3 + 0xff TO SFBR WITH CARRY
801    MOVE SFBR TO SCRATCH3
802
803    MOVE dmode_ncr_to_memory TO DMODE
804    MOVE MEMORY 4, addr_scratch, jump_dsa_save + 4
805    MOVE dmode_memory_to_memory TO DMODE
806jump_dsa_save:
807    JUMP 0
808
809munge_restore_pointers:
810    MOVE DSA0 + dsa_restore_pointers TO SFBR
811    MOVE SFBR TO SCRATCH0
812    MOVE DSA1 + 0xff TO SFBR WITH CARRY
813    MOVE SFBR TO SCRATCH1
814    MOVE DSA2 + 0xff TO SFBR WITH CARRY
815    MOVE SFBR TO SCRATCH2
816    MOVE DSA3 + 0xff TO SFBR WITH CARRY
817    MOVE SFBR TO SCRATCH3
818
819    MOVE dmode_ncr_to_memory TO DMODE
820    MOVE MEMORY 4, addr_scratch, jump_dsa_restore + 4
821    MOVE dmode_memory_to_memory TO DMODE
822jump_dsa_restore:
823    JUMP 0
824
825
826munge_disconnect:
827#if 0
828    INT int_debug_disconnect_msg
829#endif
830
831/*
832 * Before, we overlapped processing with waiting for disconnect, but
833 * debugging was beginning to appear messy.  Temporarily move things
834 * to just before the WAIT DISCONNECT.
835 */
836
837#ifdef ORIGINAL
838    MOVE SCNTL2 & 0x7f TO SCNTL2
839    CLEAR ACK
840#endif
841
842#if (CHIP != 700) && (CHIP != 70066)
843    JUMP dsa_schedule
844#else
845    WAIT DISCONNECT
846    INT int_norm_disconnected
847#endif
848
849munge_extended:
850    CLEAR ACK
851    INT int_err_unexpected_phase, WHEN NOT MSG_IN
852    MOVE 1, msg_buf + 1, WHEN MSG_IN
853    JUMP munge_extended_2, IF 0x02
854    JUMP munge_extended_3, IF 0x03
855    JUMP reject_message
856
857munge_extended_2:
858    CLEAR ACK
859    MOVE 1, msg_buf + 2, WHEN MSG_IN
860    JUMP reject_message, IF NOT 0x02	; Must be WDTR
861    CLEAR ACK
862    MOVE 1, msg_buf + 3, WHEN MSG_IN
863    INT int_msg_wdtr
864
865munge_extended_3:
866    CLEAR ACK
867    MOVE 1, msg_buf + 2, WHEN MSG_IN
868    JUMP reject_message, IF NOT 0x01	; Must be SDTR
869    CLEAR ACK
870    MOVE 2, msg_buf + 3, WHEN MSG_IN
871    INT int_msg_sdtr
872
873ENTRY reject_message
874reject_message:
875    SET ATN
876    CLEAR ACK
877    MOVE 1, NCR53c7xx_msg_reject, WHEN MSG_OUT
878    RETURN
879
880ENTRY accept_message
881accept_message:
882    CLEAR ATN
883    CLEAR ACK
884    RETURN
885
886ENTRY respond_message
887respond_message:
888    SET ATN
889    CLEAR ACK
890    MOVE FROM dsa_msgout_other, WHEN MSG_OUT
891    RETURN
892
893;
894; command_complete
895;
896; PURPOSE : handle command termination when STATUS IN is detected by reading
897;	a status byte followed by a command termination message.
898;
899;	Normal termination results in an INTFLY instruction, and
900;	the host system can pick out which command terminated by
901;	examining the MESSAGE and STATUS buffers of all currently
902;	executing commands;
903;
904;	Abnormal (CHECK_CONDITION) termination results in an
905;	int_err_check_condition interrupt so that a REQUEST SENSE
906;	command can be issued out-of-order so that no other command
907;	clears the contingent allegiance condition.
908;
909;
910; INPUTS : DSA - command
911;
912; CALLS : OK
913;
914; EXITS : On successful termination, control is passed to schedule.
915;	On abnormal termination, the user will usually modify the
916;	DSA fields and corresponding buffers and return control
917;	to select.
918;
919
920ENTRY command_complete
921command_complete:
922    MOVE FROM dsa_status, WHEN STATUS
923#if (CHIP != 700) && (CHIP != 70066)
924    MOVE SFBR TO SCRATCH0		; Save status
925#endif /* (CHIP != 700) && (CHIP != 70066) */
926ENTRY command_complete_msgin
927command_complete_msgin:
928    MOVE FROM dsa_msgin, WHEN MSG_IN
929; Indicate that we should be expecting a disconnect
930    MOVE SCNTL2 & 0x7f TO SCNTL2
931    CLEAR ACK
932#if (CHIP != 700) && (CHIP != 70066)
933    WAIT DISCONNECT
934
935;
936; The SCSI specification states that when a UNIT ATTENTION condition
937; is pending, as indicated by a CHECK CONDITION status message,
938; the target shall revert to asynchronous transfers.  Since
939; synchronous transfers parameters are maintained on a per INITIATOR/TARGET
940; basis, and returning control to our scheduler could work on a command
941; running on another lun on that target using the old parameters, we must
942; interrupt the host processor to get them changed, or change them ourselves.
943;
944; Once SCSI-II tagged queueing is implemented, things will be even more
945; hairy, since contingent allegiance conditions exist on a per-target/lun
946; basis, and issuing a new command with a different tag would clear it.
947; In these cases, we must interrupt the host processor to get a request
948; added to the HEAD of the queue with the request sense command, or we
949; must automatically issue the request sense command.
950
951#if 0
952    MOVE SCRATCH0 TO SFBR
953    JUMP command_failed, IF 0x02
954#endif
955    INTFLY
956#endif /* (CHIP != 700) && (CHIP != 70066) */
957#ifdef EVENTS
958    INT int_EVENT_COMPLETE
959#endif
960#if (CHIP != 700) && (CHIP != 70066)
961    JUMP schedule
962command_failed:
963    INT int_err_check_condition
964#else
965    INT int_norm_command_complete
966#endif
967
968;
969; wait_reselect
970;
971; PURPOSE : This is essentially the idle routine, where control lands
972;	when there are no new processes to schedule.  wait_reselect
973;	waits for reselection, selection, and new commands.
974;
975;	When a successful reselection occurs, with the aid
976;	of fixed up code in each DSA, wait_reselect walks the
977;	reconnect_dsa_queue, asking each dsa if the target ID
978;	and LUN match its.
979;
980;	If a match is found, a call is made back to reselected_ok,
981;	which through the miracles of self modifying code, extracts
982;	the found DSA from the reconnect_dsa_queue and then
983;	returns control to the DSAs thread of execution.
984;
985; INPUTS : NONE
986;
987; CALLS : OK
988;
989; MODIFIES : DSA,
990;
991; EXITS : On successful reselection, control is returned to the
992;	DSA which called reselected_ok.  If the WAIT RESELECT
993;	was interrupted by a new commands arrival signaled by
994;	SIG_P, control is passed to schedule.  If the NCR is
995;	selected, the host system is interrupted with an
996;	int_err_selected which is usually responded to by
997;	setting DSP to the target_abort address.
998
999ENTRY wait_reselect
1000wait_reselect:
1001#ifdef EVENTS
1002    int int_EVENT_IDLE
1003#endif
1004#if 0
1005    int int_debug_idle
1006#endif
1007    WAIT RESELECT wait_reselect_failed
1008
1009reselected:
1010#ifdef EVENTS
1011    int int_EVENT_RESELECT
1012#endif
1013    CLEAR TARGET
1014    MOVE dmode_memory_to_memory TO DMODE
1015    ; Read all data needed to reestablish the nexus -
1016    MOVE 1, reselected_identify, WHEN MSG_IN
1017    ; We used to CLEAR ACK here.
1018#if (CHIP != 700) && (CHIP != 70066)
1019#if 0
1020    int int_debug_reselected
1021#endif
1022
1023    ; Point DSA at the current head of the disconnected queue.
1024    MOVE dmode_memory_to_ncr  TO DMODE
1025    MOVE MEMORY 4, reconnect_dsa_head, addr_scratch
1026    MOVE dmode_memory_to_memory TO DMODE
1027    CALL scratch_to_dsa
1028
1029    ; Fix the update-next pointer so that the reconnect_dsa_head
1030    ; pointer is the one that will be updated if this DSA is a hit
1031    ; and we remove it from the queue.
1032
1033    MOVE MEMORY 4, addr_reconnect_dsa_head, reselected_ok + 8
1034
1035ENTRY reselected_check_next
1036reselected_check_next:
1037#if 0
1038    INT int_debug_reselect_check
1039#endif
1040    ; Check for a NULL pointer.
1041    MOVE DSA0 TO SFBR
1042    JUMP reselected_not_end, IF NOT 0
1043    MOVE DSA1 TO SFBR
1044    JUMP reselected_not_end, IF NOT 0
1045    MOVE DSA2 TO SFBR
1046    JUMP reselected_not_end, IF NOT 0
1047    MOVE DSA3 TO SFBR
1048    JUMP reselected_not_end, IF NOT 0
1049    INT int_err_unexpected_reselect
1050
1051reselected_not_end:
1052    ;
1053    ; XXX the ALU is only eight bits wide, and the assembler
1054    ; wont do the dirt work for us.  As long as dsa_check_reselect
1055    ; is negative, we need to sign extend with 1 bits to the full
1056    ; 32 bit width of the address.
1057    ;
1058    ; A potential work around would be to have a known alignment
1059    ; of the DSA structure such that the base address plus
1060    ; dsa_check_reselect doesn't require carrying from bytes
1061    ; higher than the LSB.
1062    ;
1063
1064    MOVE DSA0 TO SFBR
1065    MOVE SFBR + dsa_check_reselect TO SCRATCH0
1066    MOVE DSA1 TO SFBR
1067    MOVE SFBR + 0xff TO SCRATCH1 WITH CARRY
1068    MOVE DSA2 TO SFBR
1069    MOVE SFBR + 0xff TO SCRATCH2 WITH CARRY
1070    MOVE DSA3 TO SFBR
1071    MOVE SFBR + 0xff TO SCRATCH3 WITH CARRY
1072
1073    MOVE dmode_ncr_to_memory TO DMODE
1074    MOVE MEMORY 4, addr_scratch, reselected_check + 4
1075    MOVE dmode_memory_to_memory TO DMODE
1076reselected_check:
1077    JUMP 0
1078
1079
1080;
1081;
1082ENTRY reselected_ok
1083reselected_ok:
1084    MOVE MEMORY 4, 0, 0				; Patched : first word
1085						; 	is address of
1086						;       successful dsa_next
1087						; Second word is last
1088						;	unsuccessful dsa_next,
1089						;	starting with
1090						;       dsa_reconnect_head
1091    ; We used to CLEAR ACK here.
1092#if 0
1093    INT int_debug_reselected_ok
1094#endif
1095#ifdef DEBUG
1096    INT int_debug_check_dsa
1097#endif
1098    RETURN					; Return control to where
1099#else
1100    INT int_norm_reselected
1101#endif /* (CHIP != 700) && (CHIP != 70066) */
1102
1103selected:
1104    INT int_err_selected;
1105
1106;
1107; A select or reselect failure can be caused by one of two conditions :
1108; 1.  SIG_P was set.  This will be the case if the user has written
1109;	a new value to a previously NULL head of the issue queue.
1110;
1111; 2.  The NCR53c810 was selected or reselected by another device.
1112;
1113; 3.  The bus was already busy since we were selected or reselected
1114;	before starting the command.
1115
1116wait_reselect_failed:
1117#ifdef EVENTS
1118	INT int_EVENT_RESELECT_FAILED
1119#endif
1120; Check selected bit.
1121    MOVE SIST0 & 0x20 TO SFBR
1122    JUMP selected, IF 0x20
1123; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
1124    MOVE CTEST2 & 0x40 TO SFBR
1125    JUMP schedule, IF 0x40
1126; Check connected bit.
1127; FIXME: this needs to change if we support target mode
1128    MOVE ISTAT & 0x08 TO SFBR
1129    JUMP reselected, IF 0x08
1130; FIXME : Something bogus happened, and we shouldn't fail silently.
1131#if 0
1132    JUMP schedule
1133#else
1134    INT int_debug_panic
1135#endif
1136
1137
1138select_failed:
1139#ifdef EVENTS
1140  int int_EVENT_SELECT_FAILED
1141#endif
1142; Otherwise, mask the selected and reselected bits off SIST0
1143    MOVE SIST0 & 0x30 TO SFBR
1144    JUMP selected, IF 0x20
1145    JUMP reselected, IF 0x10
1146; If SIGP is set, the user just gave us another command, and
1147; we should restart or return to the scheduler.
1148; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
1149    MOVE CTEST2 & 0x40 TO SFBR
1150    JUMP select, IF 0x40
1151; Check connected bit.
1152; FIXME: this needs to change if we support target mode
1153; FIXME: is this really necessary?
1154    MOVE ISTAT & 0x08 TO SFBR
1155    JUMP reselected, IF 0x08
1156; FIXME : Something bogus happened, and we shouldn't fail silently.
1157#if 0
1158    JUMP schedule
1159#else
1160    INT int_debug_panic
1161#endif
1162
1163;
1164; test_1
1165; test_2
1166;
1167; PURPOSE : run some verification tests on the NCR.  test_1
1168;	copies test_src to test_dest and interrupts the host
1169;	processor, testing for cache coherency and interrupt
1170; 	problems in the processes.
1171;
1172;	test_2 runs a command with offsets relative to the
1173;	DSA on entry, and is useful for miscellaneous experimentation.
1174;
1175
1176; Verify that interrupts are working correctly and that we don't
1177; have a cache invalidation problem.
1178
1179ABSOLUTE test_src = 0, test_dest = 0
1180ENTRY test_1
1181test_1:
1182    MOVE MEMORY 4, test_src, test_dest
1183    INT int_test_1
1184
1185;
1186; Run arbitrary commands, with test code establishing a DSA
1187;
1188
1189ENTRY test_2
1190test_2:
1191    CLEAR TARGET
1192    SELECT ATN FROM 0, test_2_fail
1193    JUMP test_2_msgout, WHEN MSG_OUT
1194ENTRY test_2_msgout
1195test_2_msgout:
1196    MOVE FROM 8, WHEN MSG_OUT
1197    MOVE FROM 16, WHEN CMD
1198    MOVE FROM 24, WHEN DATA_IN
1199    MOVE FROM 32, WHEN STATUS
1200    MOVE FROM 40, WHEN MSG_IN
1201    MOVE SCNTL2 & 0x7f TO SCNTL2
1202    CLEAR ACK
1203    WAIT DISCONNECT
1204test_2_fail:
1205    INT int_test_2
1206
1207ENTRY debug_break
1208debug_break:
1209    INT int_debug_break
1210
1211;
1212; initiator_abort
1213; target_abort
1214;
1215; PURPOSE : Abort the currently established nexus from with initiator
1216;	or target mode.
1217;
1218;
1219
1220ENTRY target_abort
1221target_abort:
1222    SET TARGET
1223    DISCONNECT
1224    CLEAR TARGET
1225    JUMP schedule
1226
1227ENTRY initiator_abort
1228initiator_abort:
1229    SET ATN
1230;
1231; The SCSI-I specification says that targets may go into MSG out at
1232; their leisure upon receipt of the ATN single.  On all versions of the
1233; specification, we can't change phases until REQ transitions true->false,
1234; so we need to sink/source one byte of data to allow the transition.
1235;
1236; For the sake of safety, we'll only source one byte of data in all
1237; cases, but to accommodate the SCSI-I dain bramage, we'll sink an
1238; arbitrary number of bytes.
1239    JUMP spew_cmd, WHEN CMD
1240    JUMP eat_msgin, WHEN MSG_IN
1241    JUMP eat_datain, WHEN DATA_IN
1242    JUMP eat_status, WHEN STATUS
1243    JUMP spew_dataout, WHEN DATA_OUT
1244    JUMP sated
1245spew_cmd:
1246    MOVE 1, NCR53c7xx_zero, WHEN CMD
1247    JUMP sated
1248eat_msgin:
1249    MOVE 1, NCR53c7xx_sink, WHEN MSG_IN
1250    JUMP eat_msgin, WHEN MSG_IN
1251    JUMP sated
1252eat_status:
1253    MOVE 1, NCR53c7xx_sink, WHEN STATUS
1254    JUMP eat_status, WHEN STATUS
1255    JUMP sated
1256eat_datain:
1257    MOVE 1, NCR53c7xx_sink, WHEN DATA_IN
1258    JUMP eat_datain, WHEN DATA_IN
1259    JUMP sated
1260spew_dataout:
1261    MOVE 1, NCR53c7xx_zero, WHEN DATA_OUT
1262sated:
1263    MOVE SCNTL2 & 0x7f TO SCNTL2
1264    MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT
1265    WAIT DISCONNECT
1266    INT int_norm_aborted
1267
1268;
1269; dsa_to_scratch
1270; scratch_to_dsa
1271;
1272; PURPOSE :
1273; 	The NCR chips cannot do a move memory instruction with the DSA register
1274; 	as the source or destination.  So, we provide a couple of subroutines
1275; 	that let us switch between the DSA register and scratch register.
1276;
1277; 	Memory moves to/from the DSPS  register also don't work, but we
1278; 	don't use them.
1279;
1280;
1281
1282
1283dsa_to_scratch:
1284    MOVE DSA0 TO SFBR
1285    MOVE SFBR TO SCRATCH0
1286    MOVE DSA1 TO SFBR
1287    MOVE SFBR TO SCRATCH1
1288    MOVE DSA2 TO SFBR
1289    MOVE SFBR TO SCRATCH2
1290    MOVE DSA3 TO SFBR
1291    MOVE SFBR TO SCRATCH3
1292    RETURN
1293
1294scratch_to_dsa:
1295    MOVE SCRATCH0 TO SFBR
1296    MOVE SFBR TO DSA0
1297    MOVE SCRATCH1 TO SFBR
1298    MOVE SFBR TO DSA1
1299    MOVE SCRATCH2 TO SFBR
1300    MOVE SFBR TO DSA2
1301    MOVE SCRATCH3 TO SFBR
1302    MOVE SFBR TO DSA3
1303    RETURN
1304
1305