3 #undef NO_SELECTION_TIMEOUT
6 ; 53c710 driver. Modified from Drew Eckhardts driver
7 ; for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk]
9 ; I have left the script for the 53c8xx family in here, as it is likely
10 ; to be useful to see what I changed when bug hunting.
12 ; NCR 53c810 driver, main script
14 ; iX Multiuser Multitasking Magazine
17 ; Copyright 1993, 1994, 1995 Drew Eckhardt
19 ; (Unix and Linux consulting and custom programming)
23 ; TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
27 ; For more information, please consult
30 ; PCI-SCSI I/O Processor
37 ; NCR Microelectronics
38 ; 1635 Aeroplaza Drive
39 ; Colorado Springs, CO 80916
42 ; Toll free literature number
45 ; IMPORTANT : This code is self modifying due to the limitations of
46 ; the NCR53c7,8xx series chips. Persons debugging this code with
47 ; the remote debugger should take this into account, and NOT set
48 ; breakpoints in modified instructions.
51 ; The NCR53c7,8xx family of SCSI chips are busmasters with an onboard
52 ; microcontroller using a simple instruction set.
54 ; So, to minimize the effects of interrupt latency, and to maximize
55 ; throughput, this driver offloads the practical maximum amount
56 ; of processing to the SCSI chip while still maintaining a common
59 ; Where tradeoffs were needed between efficiency on the older
60 ; chips and the newer NCR53c800 series, the NCR53c800 series
63 ; While the NCR53c700 and NCR53c700-66 lacked the facilities to fully
64 ; automate SCSI transfers without host processor intervention, this
65 ; isn't the case with the NCR53c710 and newer chips which allow
67 ; - reads and writes to the internal registers from within the SCSI
68 ; scripts, allowing the SCSI SCRIPTS(tm) code to save processor
69 ; state so that multiple threads of execution are possible, and also
70 ; provide an ALU for loop control, etc.
72 ; - table indirect addressing for some instructions. This allows
73 ; pointers to be located relative to the DSA ((Data Structure
76 ; These features make it possible to implement a mailbox style interface,
77 ; where the same piece of code is run to handle I/O for multiple threads
78 ; at once minimizing our need to relocate code. Since the NCR53c700/
79 ; NCR53c800 series have a unique combination of features, making a
80 ; a standard ingoing/outgoing mailbox system, costly, I've modified it.
82 ; - Mailboxes are a mixture of code and data. This lets us greatly
83 ; simplify the NCR53c810 code and do things that would otherwise
86 ; The saved data pointer is now implemented as follows :
88 ; Control flow has been architected such that if control reaches
89 ; munge_save_data_pointer, on a restore pointers message or
90 ; reconnection, a jump to the address formerly in the TEMP register
91 ; will allow the SCSI command to resume execution.
95 ; Note : the DSA structures must be aligned on 32 bit boundaries,
96 ; since the source and destination of MOVE MEMORY instructions
97 ; must share the same alignment and this is the alignment of the
101 ; For some systems (MVME166, for example) dmode is always the same, so don't
102 ; waste time writing it
105 #define DMODE_MEMORY_TO_NCR
106 #define DMODE_MEMORY_TO_MEMORY
107 #define DMODE_NCR_TO_MEMORY
109 #define DMODE_MEMORY_TO_NCR MOVE dmode_memory_to_ncr TO DMODE
110 #define DMODE_MEMORY_TO_MEMORY MOVE dmode_memory_to_memory TO DMODE
111 #define DMODE_NCR_TO_MEMORY MOVE dmode_ncr_to_memory TO DMODE
114 ABSOLUTE dsa_temp_lun = 0 ; Patch to lun for current dsa
115 ABSOLUTE dsa_temp_next = 0 ; Patch to dsa next for current dsa
116 ABSOLUTE dsa_temp_addr_next = 0 ; Patch to address of dsa next address
118 ABSOLUTE dsa_temp_sync = 0 ; Patch to address of per-target
120 ABSOLUTE dsa_sscf_710 = 0 ; Patch to address of per-target
121 ; sscf value (53c710)
122 ABSOLUTE dsa_temp_target = 0 ; Patch to id for current dsa
123 ABSOLUTE dsa_temp_addr_saved_pointer = 0; Patch to address of per-command
125 ABSOLUTE dsa_temp_addr_residual = 0 ; Patch to address of per-command
126 ; current residual code
127 ABSOLUTE dsa_temp_addr_saved_residual = 0; Patch to address of per-command
128 ; saved residual code
129 ABSOLUTE dsa_temp_addr_new_value = 0 ; Address of value for JUMP operand
130 ABSOLUTE dsa_temp_addr_array_value = 0 ; Address to copy to
131 ABSOLUTE dsa_temp_addr_dsa_value = 0 ; Address of this DSA value
134 ; Once a device has initiated reselection, we need to compare it
135 ; against the singly linked list of commands which have disconnected
136 ; and are pending reselection. These commands are maintained in
137 ; an unordered singly linked list of DSA structures, through the
138 ; DSA pointers at their 'centers' headed by the reconnect_dsa_head
141 ; To avoid complications in removing commands from the list,
142 ; I minimize the amount of expensive (at eight operations per
143 ; addition @ 500-600ns each) pointer operations which must
144 ; be done in the NCR driver by precomputing them on the
145 ; host processor during dsa structure generation.
147 ; The fixed-up per DSA code knows how to recognize the nexus
148 ; associated with the corresponding SCSI command, and modifies
149 ; the source and destination pointers for the MOVE MEMORY
150 ; instruction which is executed when reselected_ok is called
151 ; to remove the command from the list. Similarly, DSA is
152 ; loaded with the address of the next DSA structure and
153 ; reselected_check_next is called if a failure occurs.
155 ; Perhaps more concisely, the net effect of the mess is
157 ; for (dsa = reconnect_dsa_head, dest = &reconnect_dsa_head,
158 ; src = NULL; dsa; dest = &dsa->next, dsa = dsa->next) {
160 ; if (target_id == dsa->id && target_lun == dsa->lun) {
167 ; error (int_err_unexpected_reselect);
169 ; longjmp (dsa->jump_resume, 0);
173 #if (CHIP != 700) && (CHIP != 70066)
174 ; Define DSA structure used for mailboxes
175 ENTRY dsa_code_template
179 ; RGH: Don't care about TEMP and DSA here
181 MOVE MEMORY 4, dsa_temp_addr_dsa_value, addr_scratch
182 DMODE_MEMORY_TO_MEMORY
184 MOVE MEMORY 4, addr_scratch, saved_dsa
185 ; We are about to go and select the device, so must set SSCF bits
186 MOVE MEMORY 4, dsa_sscf_710, addr_scratch
188 MOVE SCRATCH3 TO SFBR
190 MOVE SCRATCH0 TO SFBR
193 MOVE MEMORY 4, saved_dsa, addr_dsa
198 ; Handle the phase mismatch which may have resulted from the
199 ; MOVE FROM dsa_msgout if we returned here. The CLEAR ATN
200 ; may or may not be necessary, and we should update script_asm.pl
201 ; to handle multiple pieces.
205 ; Replace second operand with address of JUMP instruction dest operand
206 ; in schedule table for this DSA. Becomes dsa_jump_dest in 53c7,8xx.c.
207 ENTRY dsa_code_fix_jump
209 MOVE MEMORY 4, NOP_insn, 0
212 ; wrong_dsa loads the DSA register with the value of the dsa_next
217 ; NOTE DSA is corrupt when we arrive here!
219 ; Patch the MOVE MEMORY INSTRUCTION such that
220 ; the destination address is the address of the OLD
223 MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok_patch + 8
226 ; Move the _contents_ of the next pointer into the DSA register as
227 ; the next I_T_L or I_T_L_Q tupple to check against the established
230 MOVE MEMORY 4, dsa_temp_next, addr_scratch
231 DMODE_MEMORY_TO_MEMORY
233 MOVE MEMORY 4, addr_scratch, saved_dsa
234 MOVE MEMORY 4, saved_dsa, addr_dsa
238 JUMP reselected_check_next
240 ABSOLUTE dsa_save_data_pointer = 0
241 ENTRY dsa_code_save_data_pointer
242 dsa_code_save_data_pointer:
244 ; When we get here, TEMP has been saved in jump_temp+4, DSA is corrupt
245 ; We MUST return with DSA correct
246 MOVE MEMORY 4, jump_temp+4, dsa_temp_addr_saved_pointer
247 ; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
248 MOVE MEMORY 24, dsa_temp_addr_residual, dsa_temp_addr_saved_residual
253 MOVE MEMORY 4, saved_dsa, addr_dsa
257 MOVE MEMORY 4, addr_temp, dsa_temp_addr_saved_pointer
258 DMODE_MEMORY_TO_MEMORY
259 ; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
260 MOVE MEMORY 24, dsa_temp_addr_residual, dsa_temp_addr_saved_residual
267 ABSOLUTE dsa_restore_pointers = 0
268 ENTRY dsa_code_restore_pointers
269 dsa_code_restore_pointers:
271 ; TEMP and DSA are corrupt when we get here, but who cares!
272 MOVE MEMORY 4, dsa_temp_addr_saved_pointer, jump_temp + 4
273 ; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
274 MOVE MEMORY 24, dsa_temp_addr_saved_residual, dsa_temp_addr_residual
276 ; Restore DSA, note we don't care about TEMP
277 MOVE MEMORY 4, saved_dsa, addr_dsa
279 INT int_debug_restored
284 MOVE MEMORY 4, dsa_temp_addr_saved_pointer, addr_temp
285 DMODE_MEMORY_TO_MEMORY
286 ; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
287 MOVE MEMORY 24, dsa_temp_addr_saved_residual, dsa_temp_addr_residual
290 INT int_debug_restored
295 ABSOLUTE dsa_check_reselect = 0
296 ; dsa_check_reselect determines whether or not the current target and
297 ; lun match the current DSA
298 ENTRY dsa_code_check_reselect
299 dsa_code_check_reselect:
301 /* Arrives here with DSA correct */
302 /* Assumes we are always ID 7 */
303 MOVE LCRC TO SFBR ; LCRC has our ID and his ID bits set
304 JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 0x80
306 MOVE SSID TO SFBR ; SSID contains 3 bit target ID
307 ; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips
308 JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 0xf8
311 ; Hack - move to scratch first, since SFBR is not writeable
312 ; via the CPU and hence a MOVE MEMORY instruction.
315 MOVE MEMORY 1, reselected_identify, addr_scratch
316 DMODE_MEMORY_TO_MEMORY
318 ; BIG ENDIAN ON MVME16x
319 MOVE SCRATCH3 TO SFBR
321 MOVE SCRATCH0 TO SFBR
323 ; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips
324 ; Are you sure about that? richard@sleepie.demon.co.uk
325 JUMP REL (wrong_dsa), IF NOT dsa_temp_lun, AND MASK 0xf8
326 ; Patch the MOVE MEMORY INSTRUCTION such that
327 ; the source address is the address of this dsa's
329 MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok_patch + 4
332 ; Restore DSA following memory moves in reselected_ok
333 ; dsa_temp_sync doesn't really care about DSA, but it has an
334 ; optional debug INT so a valid DSA is a good idea.
335 MOVE MEMORY 4, saved_dsa, addr_dsa
338 ; Release ACK on the IDENTIFY message _after_ we've set the synchronous
339 ; transfer parameters!
341 ; Implicitly restore pointers on reselection, so a RETURN
342 ; will transfer control back to the right spot.
343 CALL REL (dsa_code_restore_pointers)
347 ENTRY dsa_code_template_end
348 dsa_code_template_end:
350 ; Perform sanity check for dsa_fields_start == dsa_code_template_end -
353 ABSOLUTE dsa_fields_start = 0 ; Sanity marker
354 ; pad 48 bytes (fix this RSN)
355 ABSOLUTE dsa_next = 48 ; len 4 Next DSA
356 ; del 4 Previous DSA address
357 ABSOLUTE dsa_cmnd = 56 ; len 4 Scsi_Cmnd * for this thread.
358 ABSOLUTE dsa_select = 60 ; len 4 Device ID, Period, Offset for
359 ; table indirect select
360 ABSOLUTE dsa_msgout = 64 ; len 8 table indirect move parameter for
362 ABSOLUTE dsa_cmdout = 72 ; len 8 table indirect move parameter for
364 ABSOLUTE dsa_dataout = 80 ; len 4 code pointer for dataout
365 ABSOLUTE dsa_datain = 84 ; len 4 code pointer for datain
366 ABSOLUTE dsa_msgin = 88 ; len 8 table indirect move for msgin
367 ABSOLUTE dsa_status = 96 ; len 8 table indirect move for status byte
368 ABSOLUTE dsa_msgout_other = 104 ; len 8 table indirect for normal message out
369 ; (Synchronous transfer negotiation, etc).
370 ABSOLUTE dsa_end = 112
372 ABSOLUTE schedule = 0 ; Array of JUMP dsa_begin or JUMP (next),
373 ; terminated by a call to JUMP wait_reselect
375 ; Linked lists of DSA structures
376 ABSOLUTE reconnect_dsa_head = 0 ; Link list of DSAs which can reconnect
377 ABSOLUTE addr_reconnect_dsa_head = 0 ; Address of variable containing
378 ; address of reconnect_dsa_head
380 ; These select the source and destination of a MOVE MEMORY instruction
381 ABSOLUTE dmode_memory_to_memory = 0x0
382 ABSOLUTE dmode_memory_to_ncr = 0x0
383 ABSOLUTE dmode_ncr_to_memory = 0x0
385 ABSOLUTE addr_scratch = 0x0
386 ABSOLUTE addr_temp = 0x0
388 ABSOLUTE saved_dsa = 0x0
389 ABSOLUTE emulfly = 0x0
390 ABSOLUTE addr_dsa = 0x0
392 #endif /* CHIP != 700 && CHIP != 70066 */
396 ; 0 handle error condition
398 ; 2 handle normal condition
399 ; 3 debugging interrupt
400 ; 4 testing interrupt
401 ; Next byte indicates specific error
403 ; XXX not yet implemented, I'm not sure if I want to -
404 ; Next byte indicates the routine the error occurred in
405 ; The LSB indicates the specific place the error occurred
407 ABSOLUTE int_err_unexpected_phase = 0x00000000 ; Unexpected phase encountered
408 ABSOLUTE int_err_selected = 0x00010000 ; SELECTED (nee RESELECTED)
409 ABSOLUTE int_err_unexpected_reselect = 0x00020000
410 ABSOLUTE int_err_check_condition = 0x00030000
411 ABSOLUTE int_err_no_phase = 0x00040000
412 ABSOLUTE int_msg_wdtr = 0x01000000 ; WDTR message received
413 ABSOLUTE int_msg_sdtr = 0x01010000 ; SDTR received
414 ABSOLUTE int_msg_1 = 0x01020000 ; single byte special message
417 ABSOLUTE int_norm_select_complete = 0x02000000 ; Select complete, reprogram
419 ABSOLUTE int_norm_reselect_complete = 0x02010000 ; Nexus established
420 ABSOLUTE int_norm_command_complete = 0x02020000 ; Command complete
421 ABSOLUTE int_norm_disconnected = 0x02030000 ; Disconnected
422 ABSOLUTE int_norm_aborted =0x02040000 ; Aborted *dsa
423 ABSOLUTE int_norm_reset = 0x02050000 ; Generated BUS reset.
424 ABSOLUTE int_norm_emulateintfly = 0x02060000 ; 53C710 Emulated intfly
425 ABSOLUTE int_debug_break = 0x03000000 ; Break point
427 ABSOLUTE int_debug_scheduled = 0x03010000 ; new I/O scheduled
428 ABSOLUTE int_debug_idle = 0x03020000 ; scheduler is idle
429 ABSOLUTE int_debug_dsa_loaded = 0x03030000 ; dsa reloaded
430 ABSOLUTE int_debug_reselected = 0x03040000 ; NCR reselected
431 ABSOLUTE int_debug_head = 0x03050000 ; issue head overwritten
432 ABSOLUTE int_debug_disconnected = 0x03060000 ; disconnected
433 ABSOLUTE int_debug_disconnect_msg = 0x03070000 ; got message to disconnect
434 ABSOLUTE int_debug_dsa_schedule = 0x03080000 ; in dsa_schedule
435 ABSOLUTE int_debug_reselect_check = 0x03090000 ; Check for reselection of DSA
436 ABSOLUTE int_debug_reselected_ok = 0x030a0000 ; Reselection accepted
438 ABSOLUTE int_debug_panic = 0x030b0000 ; Panic driver
440 ABSOLUTE int_debug_saved = 0x030c0000 ; save/restore pointers
441 ABSOLUTE int_debug_restored = 0x030d0000
442 ABSOLUTE int_debug_sync = 0x030e0000 ; Sanity check synchronous
444 ABSOLUTE int_debug_datain = 0x030f0000 ; going into data in phase
446 ABSOLUTE int_debug_check_dsa = 0x03100000 ; Sanity check DSA against
450 ABSOLUTE int_test_1 = 0x04000000 ; Test 1 complete
451 ABSOLUTE int_test_2 = 0x04010000 ; Test 2 complete
452 ABSOLUTE int_test_3 = 0x04020000 ; Test 3 complete
455 ; These should start with 0x05000000, with low bits incrementing for
459 ABSOLUTE int_EVENT_SELECT = 0
460 ABSOLUTE int_EVENT_DISCONNECT = 0
461 ABSOLUTE int_EVENT_RESELECT = 0
462 ABSOLUTE int_EVENT_COMPLETE = 0
463 ABSOLUTE int_EVENT_IDLE = 0
464 ABSOLUTE int_EVENT_SELECT_FAILED = 0
465 ABSOLUTE int_EVENT_BEFORE_SELECT = 0
466 ABSOLUTE int_EVENT_RESELECT_FAILED = 0
469 ABSOLUTE NCR53c7xx_msg_abort = 0 ; Pointer to abort message
470 ABSOLUTE NCR53c7xx_msg_reject = 0 ; Pointer to reject message
471 ABSOLUTE NCR53c7xx_zero = 0 ; long with zero in it, use for source
472 ABSOLUTE NCR53c7xx_sink = 0 ; long to dump worthless data in
473 ABSOLUTE NOP_insn = 0 ; NOP instruction
475 ; Pointer to message, potentially multi-byte
478 ; Pointer to holding area for reselection information
479 ABSOLUTE reselected_identify = 0
480 ABSOLUTE reselected_tag = 0
482 ; Request sense command pointer, it's a 6 byte command, should
483 ; be constant for all commands since we always want 16 bytes of
484 ; sense and we don't need to change any fields as we did under
485 ; SCSI-I when we actually cared about the LUN field.
486 ;EXTERNAL NCR53c7xx_sense ; Request sense command
488 #if (CHIP != 700) && (CHIP != 70066)
490 ; PURPOSE : after a DISCONNECT message has been received, and pointers
491 ; saved, insert the current DSA structure at the head of the
492 ; disconnected queue and fall through to the scheduler.
496 ; INPUTS : dsa - current DSA structure, reconnect_dsa_head - list
497 ; of disconnected commands
499 ; MODIFIES : SCRATCH, reconnect_dsa_head
501 ; EXITS : always passes control to schedule
506 INT int_debug_dsa_schedule
510 ; Calculate the address of the next pointer within the DSA
511 ; structure of the command that is currently disconnecting
514 ; Read what should be the current DSA from memory - actual DSA
515 ; register is probably corrupt
516 MOVE MEMORY 4, saved_dsa, addr_scratch
520 MOVE SCRATCH0 + dsa_next TO SCRATCH0
521 MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
522 MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
523 MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
525 ; Point the next field of this DSA structure at the current disconnected
528 MOVE MEMORY 4, addr_scratch, dsa_schedule_insert + 8
529 DMODE_MEMORY_TO_MEMORY
531 MOVE MEMORY 4, reconnect_dsa_head, 0
533 ; And update the head pointer.
535 ; Read what should be the current DSA from memory - actual DSA
536 ; register is probably corrupt
537 MOVE MEMORY 4, saved_dsa, addr_scratch
542 MOVE MEMORY 4, addr_scratch, reconnect_dsa_head
543 DMODE_MEMORY_TO_MEMORY
544 /* Temporarily, see what happens. */
547 MOVE SCNTL2 & 0x7f TO SCNTL2
552 ; Time to correct DSA following memory move
553 MOVE MEMORY 4, saved_dsa, addr_dsa
557 INT int_EVENT_DISCONNECT;
560 INT int_debug_disconnected
568 ; PURPOSE : establish a nexus for the SCSI command referenced by DSA.
569 ; On success, the current DSA structure is removed from the issue
570 ; queue. Usually, this is entered as a fall-through from schedule,
571 ; although the contingent allegiance handling code will write
572 ; the select entry address to the DSP to restart a command as a
573 ; REQUEST SENSE. A message is sent (usually IDENTIFY, although
574 ; additional SDTR or WDTR messages may be sent). COMMAND OUT
577 ; INPUTS : DSA - SCSI command, issue_dsa_head
581 ; MODIFIES : SCRATCH, issue_dsa_head
583 ; EXITS : on reselection or selection, go to select_failed
584 ; otherwise, RETURN so control is passed back to
592 INT int_EVENT_BEFORE_SELECT
596 INT int_debug_scheduled
602 ; In effect, SELECTION operations are backgrounded, with execution
603 ; continuing until code which waits for REQ or a fatal interrupt is
606 ; So, for more performance, we could overlap the code which removes
607 ; the command from the NCRs issue queue with the selection, but
608 ; at this point I don't want to deal with the error recovery.
611 #if (CHIP != 700) && (CHIP != 70066)
613 ; Enable selection timer
614 #ifdef NO_SELECTION_TIMEOUT
615 MOVE CTEST7 & 0xff TO CTEST7
617 MOVE CTEST7 & 0xef TO CTEST7
620 SELECT ATN FROM dsa_select, select_failed
621 JUMP select_msgout, WHEN MSG_OUT
625 ; Disable selection timer
626 MOVE CTEST7 | 0x10 TO CTEST7
628 MOVE FROM dsa_msgout, WHEN MSG_OUT
631 SELECT ATN 0, select_failed
633 MOVE 0, 0, WHEN MSGOUT
644 ; PURPOSE: continue on to normal data transfer; called as the exit
645 ; point from dsa_begin.
655 ; NOTE DSA is corrupt when we arrive here!
656 MOVE MEMORY 4, saved_dsa, addr_dsa
660 ENTRY select_check_dsa
662 INT int_debug_check_dsa
665 ; After a successful selection, we should get either a CMD phase or
666 ; some transfer request negotiation message.
668 JUMP cmdout, WHEN CMD
669 INT int_err_unexpected_phase, WHEN NOT MSG_IN
672 CALL msg_in, WHEN MSG_IN
673 JUMP select_msg_in, WHEN MSG_IN
676 INT int_err_unexpected_phase, WHEN NOT CMD
678 INT int_norm_selected
682 #if (CHIP != 700) && (CHIP != 70066)
683 MOVE FROM dsa_cmdout, WHEN CMD
686 #endif /* (CHIP != 700) && (CHIP != 70066) */
694 ; PURPOSE : handle the main data transfer for a SCSI command in
695 ; several parts. In the first part, data_transfer, DATA_IN
696 ; and DATA_OUT phases are allowed, with the user provided
697 ; code (usually dynamically generated based on the scatter/gather
698 ; list associated with a SCSI command) called to handle these
701 ; After control has passed to one of the user provided
702 ; DATA_IN or DATA_OUT routines, back calls are made to
703 ; other_transfer_in or other_transfer_out to handle non-DATA IN
704 ; and DATA OUT phases respectively, with the state of the active
705 ; data pointer being preserved in TEMP.
707 ; On completion, the user code passes control to other_transfer
708 ; which causes DATA_IN and DATA_OUT to result in unexpected_phase
709 ; interrupts so that data overruns may be trapped.
711 ; INPUTS : DSA - SCSI command
713 ; CALLS : OK in data_transfer_start, not ok in other_out and other_in, ok in
718 ; EXITS : if STATUS IN is detected, signifying command completion,
719 ; the NCR jumps to command_complete. If MSG IN occurs, a
720 ; CALL is made to msg_in. Otherwise, other_transfer runs in
726 JUMP cmdout_cmdout, WHEN CMD
727 CALL msg_in, WHEN MSG_IN
728 INT int_err_unexpected_phase, WHEN MSG_OUT
729 JUMP do_dataout, WHEN DATA_OUT
730 JUMP do_datain, WHEN DATA_IN
731 JUMP command_complete, WHEN STATUS
733 ENTRY end_data_transfer
737 ; FIXME: On NCR53c700 and NCR53c700-66 chips, do_dataout/do_datain
738 ; should be fixed up whenever the nexus changes so it can point to the
739 ; correct routine for that command.
742 #if (CHIP != 700) && (CHIP != 70066)
743 ; Nasty jump to dsa->dataout
746 MOVE MEMORY 4, saved_dsa, addr_scratch
750 MOVE SCRATCH0 + dsa_dataout TO SCRATCH0
751 MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
752 MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
753 MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
755 MOVE MEMORY 4, addr_scratch, dataout_to_jump + 4
756 DMODE_MEMORY_TO_MEMORY
758 MOVE MEMORY 4, 0, dataout_jump + 4
760 ; Time to correct DSA following memory move
761 MOVE MEMORY 4, saved_dsa, addr_dsa
766 ; Nasty jump to dsa->dsain
769 MOVE MEMORY 4, saved_dsa, addr_scratch
773 MOVE SCRATCH0 + dsa_datain TO SCRATCH0
774 MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
775 MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
776 MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
778 MOVE MEMORY 4, addr_scratch, datain_to_jump + 4
779 DMODE_MEMORY_TO_MEMORY
782 MOVE MEMORY 4, 0, datain_jump + 4
784 ; Time to correct DSA following memory move
785 MOVE MEMORY 4, saved_dsa, addr_dsa
792 #endif /* (CHIP != 700) && (CHIP != 70066) */
795 ; Note that other_out and other_in loop until a non-data phase
796 ; is discovered, so we only execute return statements when we
797 ; can go on to the next data phase block move statement.
804 INT int_err_unexpected_phase, WHEN CMD
805 JUMP msg_in_restart, WHEN MSG_IN
806 INT int_err_unexpected_phase, WHEN MSG_OUT
807 INT int_err_unexpected_phase, WHEN DATA_IN
808 JUMP command_complete, WHEN STATUS
809 JUMP other_out, WHEN NOT DATA_OUT
811 ; TEMP should be OK, as we got here from a call in the user dataout code.
820 INT int_err_unexpected_phase, WHEN CMD
821 JUMP msg_in_restart, WHEN MSG_IN
822 INT int_err_unexpected_phase, WHEN MSG_OUT
823 INT int_err_unexpected_phase, WHEN DATA_OUT
824 JUMP command_complete, WHEN STATUS
825 JUMP other_in, WHEN NOT DATA_IN
827 ; TEMP should be OK, as we got here from a call in the user datain code.
834 INT int_err_unexpected_phase, WHEN CMD
835 CALL msg_in, WHEN MSG_IN
836 INT int_err_unexpected_phase, WHEN MSG_OUT
837 INT int_err_unexpected_phase, WHEN DATA_OUT
838 INT int_err_unexpected_phase, WHEN DATA_IN
839 JUMP command_complete, WHEN STATUS
847 ; PURPOSE : process messages from a target. msg_in is called when the
848 ; caller hasn't read the first byte of the message. munge_message
849 ; is called when the caller has read the first byte of the message,
850 ; and left it in SFBR. msg_in_restart is called when the caller
851 ; hasn't read the first byte of the message, and wishes RETURN
852 ; to transfer control back to the address of the conditional
853 ; CALL instruction rather than to the instruction after it.
855 ; Various int_* interrupts are generated when the host system
856 ; needs to intervene, as is the case with SDTR, WDTR, and
857 ; INITIATE RECOVERY messages.
859 ; When the host system handles one of these interrupts,
860 ; it can respond by reentering at reject_message,
861 ; which rejects the message and returns control to
862 ; the caller of msg_in or munge_msg, accept_message
863 ; which clears ACK and returns control, or reply_message
864 ; which sends the message pointed to by the DSA
865 ; msgout_other table indirect field.
867 ; DISCONNECT messages are handled by moving the command
868 ; to the reconnect_dsa_queue.
870 ; NOTE: DSA should be valid when we get here - we cannot save both it
871 ; and TEMP in this routine.
874 ; INPUTS : DSA - SCSI COMMAND, SFBR - first byte of message (munge_msg
877 ; CALLS : NO. The TEMP register isn't backed up to allow nested calls.
879 ; MODIFIES : SCRATCH, DSA on DISCONNECT
881 ; EXITS : On receipt of SAVE DATA POINTER, RESTORE POINTERS,
882 ; and normal return from message handlers running under
883 ; Linux, control is returned to the caller. Receipt
884 ; of DISCONNECT messages pass control to dsa_schedule.
890 ; Since it's easier to debug changes to the statically
891 ; compiled code, rather than the dynamically generated
894 ; MOVE x, y, WHEN data_phase
895 ; CALL other_z, WHEN NOT data_phase
896 ; MOVE x, y, WHEN data_phase
898 ; I'd like to have certain routines (notably the message handler)
899 ; restart on the conditional call rather than the next instruction.
901 ; So, subtract 8 from the return address
903 MOVE TEMP0 + 0xf8 TO TEMP0
904 MOVE TEMP1 + 0xff TO TEMP1 WITH CARRY
905 MOVE TEMP2 + 0xff TO TEMP2 WITH CARRY
906 MOVE TEMP3 + 0xff TO TEMP3 WITH CARRY
910 MOVE 1, msg_buf, WHEN MSG_IN
913 JUMP munge_extended, IF 0x01 ; EXTENDED MESSAGE
914 JUMP munge_2, IF 0x20, AND MASK 0xdf ; two byte message
916 ; XXX - I've seen a handful of broken SCSI devices which fail to issue
917 ; a SAVE POINTERS message before disconnecting in the middle of
918 ; a transfer, assuming that the DATA POINTER will be implicitly
921 ; Historically, I've often done an implicit save when the DISCONNECT
922 ; message is processed. We may want to consider having the option of
925 JUMP munge_save_data_pointer, IF 0x02 ; SAVE DATA POINTER
926 JUMP munge_restore_pointers, IF 0x03 ; RESTORE POINTERS
927 JUMP munge_disconnect, IF 0x04 ; DISCONNECT
928 INT int_msg_1, IF 0x07 ; MESSAGE REJECT
929 INT int_msg_1, IF 0x0f ; INITIATE RECOVERY
931 INT int_EVENT_SELECT_FAILED
938 ; The SCSI standard allows targets to recover from transient
939 ; error conditions by backing up the data pointer with a
940 ; RESTORE POINTERS message.
942 ; So, we must save and restore the _residual_ code as well as
943 ; the current instruction pointer. Because of this messiness,
944 ; it is simpler to put dynamic code in the dsa for this and to
945 ; just do a simple jump down there.
948 munge_save_data_pointer:
950 ; We have something in TEMP here, so first we must save that
952 MOVE SFBR TO SCRATCH0
954 MOVE SFBR TO SCRATCH1
956 MOVE SFBR TO SCRATCH2
958 MOVE SFBR TO SCRATCH3
959 MOVE MEMORY 4, addr_scratch, jump_temp + 4
961 MOVE MEMORY 4, saved_dsa, addr_dsa
963 MOVE DSA0 + dsa_save_data_pointer TO SFBR
964 MOVE SFBR TO SCRATCH0
965 MOVE DSA1 + 0xff TO SFBR WITH CARRY
966 MOVE SFBR TO SCRATCH1
967 MOVE DSA2 + 0xff TO SFBR WITH CARRY
968 MOVE SFBR TO SCRATCH2
969 MOVE DSA3 + 0xff TO SFBR WITH CARRY
970 MOVE SFBR TO SCRATCH3
973 MOVE MEMORY 4, addr_scratch, jump_dsa_save + 4
974 DMODE_MEMORY_TO_MEMORY
978 munge_restore_pointers:
980 ; The code at dsa_restore_pointers will RETURN, but we don't care
981 ; about TEMP here, as it will overwrite it anyway.
983 MOVE DSA0 + dsa_restore_pointers TO SFBR
984 MOVE SFBR TO SCRATCH0
985 MOVE DSA1 + 0xff TO SFBR WITH CARRY
986 MOVE SFBR TO SCRATCH1
987 MOVE DSA2 + 0xff TO SFBR WITH CARRY
988 MOVE SFBR TO SCRATCH2
989 MOVE DSA3 + 0xff TO SFBR WITH CARRY
990 MOVE SFBR TO SCRATCH3
993 MOVE MEMORY 4, addr_scratch, jump_dsa_restore + 4
994 DMODE_MEMORY_TO_MEMORY
1001 INT int_debug_disconnect_msg
1005 * Before, we overlapped processing with waiting for disconnect, but
1006 * debugging was beginning to appear messy. Temporarily move things
1007 * to just before the WAIT DISCONNECT.
1012 ; Following clears Unexpected Disconnect bit. What do we do?
1014 MOVE SCNTL2 & 0x7f TO SCNTL2
1019 #if (CHIP != 700) && (CHIP != 70066)
1023 INT int_norm_disconnected
1028 INT int_err_unexpected_phase, WHEN NOT MSG_IN
1029 MOVE 1, msg_buf + 1, WHEN MSG_IN
1030 JUMP munge_extended_2, IF 0x02
1031 JUMP munge_extended_3, IF 0x03
1036 MOVE 1, msg_buf + 2, WHEN MSG_IN
1037 JUMP reject_message, IF NOT 0x02 ; Must be WDTR
1039 MOVE 1, msg_buf + 3, WHEN MSG_IN
1044 MOVE 1, msg_buf + 2, WHEN MSG_IN
1045 JUMP reject_message, IF NOT 0x01 ; Must be SDTR
1047 MOVE 2, msg_buf + 3, WHEN MSG_IN
1050 ENTRY reject_message
1054 MOVE 1, NCR53c7xx_msg_reject, WHEN MSG_OUT
1057 ENTRY accept_message
1063 ENTRY respond_message
1067 MOVE FROM dsa_msgout_other, WHEN MSG_OUT
1073 ; PURPOSE : handle command termination when STATUS IN is detected by reading
1074 ; a status byte followed by a command termination message.
1076 ; Normal termination results in an INTFLY instruction, and
1077 ; the host system can pick out which command terminated by
1078 ; examining the MESSAGE and STATUS buffers of all currently
1079 ; executing commands;
1081 ; Abnormal (CHECK_CONDITION) termination results in an
1082 ; int_err_check_condition interrupt so that a REQUEST SENSE
1083 ; command can be issued out-of-order so that no other command
1084 ; clears the contingent allegiance condition.
1087 ; INPUTS : DSA - command
1091 ; EXITS : On successful termination, control is passed to schedule.
1092 ; On abnormal termination, the user will usually modify the
1093 ; DSA fields and corresponding buffers and return control
1097 ENTRY command_complete
1099 MOVE FROM dsa_status, WHEN STATUS
1100 #if (CHIP != 700) && (CHIP != 70066)
1101 MOVE SFBR TO SCRATCH0 ; Save status
1102 #endif /* (CHIP != 700) && (CHIP != 70066) */
1103 ENTRY command_complete_msgin
1104 command_complete_msgin:
1105 MOVE FROM dsa_msgin, WHEN MSG_IN
1106 ; Indicate that we should be expecting a disconnect
1108 MOVE SCNTL2 & 0x7f TO SCNTL2
1110 ; Above code cleared the Unexpected Disconnect bit, what do we do?
1113 #if (CHIP != 700) && (CHIP != 70066)
1117 ; The SCSI specification states that when a UNIT ATTENTION condition
1118 ; is pending, as indicated by a CHECK CONDITION status message,
1119 ; the target shall revert to asynchronous transfers. Since
1120 ; synchronous transfers parameters are maintained on a per INITIATOR/TARGET
1121 ; basis, and returning control to our scheduler could work on a command
1122 ; running on another lun on that target using the old parameters, we must
1123 ; interrupt the host processor to get them changed, or change them ourselves.
1125 ; Once SCSI-II tagged queueing is implemented, things will be even more
1126 ; hairy, since contingent allegiance conditions exist on a per-target/lun
1127 ; basis, and issuing a new command with a different tag would clear it.
1128 ; In these cases, we must interrupt the host processor to get a request
1129 ; added to the HEAD of the queue with the request sense command, or we
1130 ; must automatically issue the request sense command.
1133 MOVE SCRATCH0 TO SFBR
1134 JUMP command_failed, IF 0x02
1137 #if defined(MVME16x_INTFLY)
1138 ; For MVME16x (ie CHIP=710) we will force an INTFLY by triggering a software
1139 ; interrupt (SW7). We can use SCRATCH, as we are about to jump to
1140 ; schedule, which corrupts it anyway. Will probably remove this later,
1141 ; but want to check performance effects first.
1143 #define INTFLY_ADDR 0xfff40070
1146 MOVE 0x80 TO SCRATCH1
1149 MOVE MEMORY 4, addr_scratch, INTFLY_ADDR
1151 INT int_norm_emulateintfly
1156 #endif /* (CHIP != 700) && (CHIP != 70066) */
1158 ; Time to correct DSA following memory move
1159 MOVE MEMORY 4, saved_dsa, addr_dsa
1162 INT int_EVENT_COMPLETE
1164 #if (CHIP != 700) && (CHIP != 70066)
1167 INT int_err_check_condition
1169 INT int_norm_command_complete
1175 ; PURPOSE : This is essentially the idle routine, where control lands
1176 ; when there are no new processes to schedule. wait_reselect
1177 ; waits for reselection, selection, and new commands.
1179 ; When a successful reselection occurs, with the aid
1180 ; of fixed up code in each DSA, wait_reselect walks the
1181 ; reconnect_dsa_queue, asking each dsa if the target ID
1182 ; and LUN match its.
1184 ; If a match is found, a call is made back to reselected_ok,
1185 ; which through the miracles of self modifying code, extracts
1186 ; the found DSA from the reconnect_dsa_queue and then
1187 ; returns control to the DSAs thread of execution.
1195 ; EXITS : On successful reselection, control is returned to the
1196 ; DSA which called reselected_ok. If the WAIT RESELECT
1197 ; was interrupted by a new commands arrival signaled by
1198 ; SIG_P, control is passed to schedule. If the NCR is
1199 ; selected, the host system is interrupted with an
1200 ; int_err_selected which is usually responded to by
1201 ; setting DSP to the target_abort address.
1211 WAIT RESELECT wait_reselect_failed
1215 int int_EVENT_RESELECT
1218 DMODE_MEMORY_TO_MEMORY
1219 ; Read all data needed to reestablish the nexus -
1220 MOVE 1, reselected_identify, WHEN MSG_IN
1221 ; We used to CLEAR ACK here.
1222 #if (CHIP != 700) && (CHIP != 70066)
1224 int int_debug_reselected
1227 ; Point DSA at the current head of the disconnected queue.
1229 MOVE MEMORY 4, reconnect_dsa_head, addr_scratch
1230 DMODE_MEMORY_TO_MEMORY
1232 MOVE MEMORY 4, addr_scratch, saved_dsa
1237 ; Fix the update-next pointer so that the reconnect_dsa_head
1238 ; pointer is the one that will be updated if this DSA is a hit
1239 ; and we remove it from the queue.
1241 MOVE MEMORY 4, addr_reconnect_dsa_head, reselected_ok_patch + 8
1243 ; Time to correct DSA following memory move
1244 MOVE MEMORY 4, saved_dsa, addr_dsa
1247 ENTRY reselected_check_next
1248 reselected_check_next:
1250 INT int_debug_reselect_check
1252 ; Check for a NULL pointer.
1254 JUMP reselected_not_end, IF NOT 0
1256 JUMP reselected_not_end, IF NOT 0
1258 JUMP reselected_not_end, IF NOT 0
1260 JUMP reselected_not_end, IF NOT 0
1261 INT int_err_unexpected_reselect
1265 ; XXX the ALU is only eight bits wide, and the assembler
1266 ; wont do the dirt work for us. As long as dsa_check_reselect
1267 ; is negative, we need to sign extend with 1 bits to the full
1268 ; 32 bit width of the address.
1270 ; A potential work around would be to have a known alignment
1271 ; of the DSA structure such that the base address plus
1272 ; dsa_check_reselect doesn't require carrying from bytes
1273 ; higher than the LSB.
1277 MOVE SFBR + dsa_check_reselect TO SCRATCH0
1279 MOVE SFBR + 0xff TO SCRATCH1 WITH CARRY
1281 MOVE SFBR + 0xff TO SCRATCH2 WITH CARRY
1283 MOVE SFBR + 0xff TO SCRATCH3 WITH CARRY
1286 MOVE MEMORY 4, addr_scratch, reselected_check + 4
1287 DMODE_MEMORY_TO_MEMORY
1289 ; Time to correct DSA following memory move
1290 MOVE MEMORY 4, saved_dsa, addr_dsa
1299 ; We have problems here - the memory move corrupts TEMP and DSA. This
1300 ; routine is called from DSA code, and patched from many places. Scratch
1301 ; is probably free when it is called.
1303 ; copy temp to scratch, one byte at a time
1304 ; write scratch to patch a jump in place of the return
1305 ; do the move memory
1306 ; jump to the patched in return address
1307 ; DSA is corrupt when we get here, and can be left corrupt
1312 MOVE SFBR TO SCRATCH0
1314 MOVE SFBR TO SCRATCH1
1316 MOVE SFBR TO SCRATCH2
1318 MOVE SFBR TO SCRATCH3
1319 MOVE MEMORY 4, addr_scratch, reselected_ok_jump + 4
1320 reselected_ok_patch:
1327 reselected_ok_patch:
1328 MOVE MEMORY 4, 0, 0 ; Patched : first word
1330 ; successful dsa_next
1331 ; Second word is last
1332 ; unsuccessful dsa_next,
1334 ; dsa_reconnect_head
1335 ; We used to CLEAR ACK here.
1337 INT int_debug_reselected_ok
1340 INT int_debug_check_dsa
1342 RETURN ; Return control to where
1345 INT int_norm_reselected
1346 #endif /* (CHIP != 700) && (CHIP != 70066) */
1349 INT int_err_selected;
1352 ; A select or reselect failure can be caused by one of two conditions :
1353 ; 1. SIG_P was set. This will be the case if the user has written
1354 ; a new value to a previously NULL head of the issue queue.
1356 ; 2. The NCR53c810 was selected or reselected by another device.
1358 ; 3. The bus was already busy since we were selected or reselected
1359 ; before starting the command.
1361 wait_reselect_failed:
1363 INT int_EVENT_RESELECT_FAILED
1365 ; Check selected bit.
1367 ; Must work out how to tell if we are selected....
1369 MOVE SIST0 & 0x20 TO SFBR
1370 JUMP selected, IF 0x20
1372 ; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
1373 MOVE CTEST2 & 0x40 TO SFBR
1374 JUMP schedule, IF 0x40
1375 ; Check connected bit.
1376 ; FIXME: this needs to change if we support target mode
1377 MOVE ISTAT & 0x08 TO SFBR
1378 JUMP reselected, IF 0x08
1379 ; FIXME : Something bogus happened, and we shouldn't fail silently.
1389 ; Disable selection timer
1390 MOVE CTEST7 | 0x10 TO CTEST7
1393 int int_EVENT_SELECT_FAILED
1395 ; Otherwise, mask the selected and reselected bits off SIST0
1397 ; Let's assume we don't get selected for now
1398 MOVE SSTAT0 & 0x10 TO SFBR
1400 MOVE SIST0 & 0x30 TO SFBR
1401 JUMP selected, IF 0x20
1403 JUMP reselected, IF 0x10
1404 ; If SIGP is set, the user just gave us another command, and
1405 ; we should restart or return to the scheduler.
1406 ; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
1407 MOVE CTEST2 & 0x40 TO SFBR
1408 JUMP select, IF 0x40
1409 ; Check connected bit.
1410 ; FIXME: this needs to change if we support target mode
1411 ; FIXME: is this really necessary?
1412 MOVE ISTAT & 0x08 TO SFBR
1413 JUMP reselected, IF 0x08
1414 ; FIXME : Something bogus happened, and we shouldn't fail silently.
1425 ; PURPOSE : run some verification tests on the NCR. test_1
1426 ; copies test_src to test_dest and interrupts the host
1427 ; processor, testing for cache coherency and interrupt
1428 ; problems in the processes.
1430 ; test_2 runs a command with offsets relative to the
1431 ; DSA on entry, and is useful for miscellaneous experimentation.
1434 ; Verify that interrupts are working correctly and that we don't
1435 ; have a cache invalidation problem.
1437 ABSOLUTE test_src = 0, test_dest = 0
1440 MOVE MEMORY 4, test_src, test_dest
1444 ; Run arbitrary commands, with test code establishing a DSA
1451 ; Enable selection timer
1452 #ifdef NO_SELECTION_TIMEOUT
1453 MOVE CTEST7 & 0xff TO CTEST7
1455 MOVE CTEST7 & 0xef TO CTEST7
1458 SELECT ATN FROM 0, test_2_fail
1459 JUMP test_2_msgout, WHEN MSG_OUT
1463 ; Disable selection timer
1464 MOVE CTEST7 | 0x10 TO CTEST7
1466 MOVE FROM 8, WHEN MSG_OUT
1467 MOVE FROM 16, WHEN CMD
1468 MOVE FROM 24, WHEN DATA_IN
1469 MOVE FROM 32, WHEN STATUS
1470 MOVE FROM 40, WHEN MSG_IN
1472 MOVE SCNTL2 & 0x7f TO SCNTL2
1478 ; Disable selection timer
1479 MOVE CTEST7 | 0x10 TO CTEST7
1491 ; PURPOSE : Abort the currently established nexus from with initiator
1503 ENTRY initiator_abort
1507 ; The SCSI-I specification says that targets may go into MSG out at
1508 ; their leisure upon receipt of the ATN single. On all versions of the
1509 ; specification, we can't change phases until REQ transitions true->false,
1510 ; so we need to sink/source one byte of data to allow the transition.
1512 ; For the sake of safety, we'll only source one byte of data in all
1513 ; cases, but to accommodate the SCSI-I dain bramage, we'll sink an
1514 ; arbitrary number of bytes.
1515 JUMP spew_cmd, WHEN CMD
1516 JUMP eat_msgin, WHEN MSG_IN
1517 JUMP eat_datain, WHEN DATA_IN
1518 JUMP eat_status, WHEN STATUS
1519 JUMP spew_dataout, WHEN DATA_OUT
1522 MOVE 1, NCR53c7xx_zero, WHEN CMD
1525 MOVE 1, NCR53c7xx_sink, WHEN MSG_IN
1526 JUMP eat_msgin, WHEN MSG_IN
1529 MOVE 1, NCR53c7xx_sink, WHEN STATUS
1530 JUMP eat_status, WHEN STATUS
1533 MOVE 1, NCR53c7xx_sink, WHEN DATA_IN
1534 JUMP eat_datain, WHEN DATA_IN
1537 MOVE 1, NCR53c7xx_zero, WHEN DATA_OUT
1540 MOVE SCNTL2 & 0x7f TO SCNTL2
1542 MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT
1544 INT int_norm_aborted
1552 ; The NCR chips cannot do a move memory instruction with the DSA register
1553 ; as the source or destination. So, we provide a couple of subroutines
1554 ; that let us switch between the DSA register and scratch register.
1556 ; Memory moves to/from the DSPS register also don't work, but we
1564 MOVE SFBR TO SCRATCH0
1566 MOVE SFBR TO SCRATCH1
1568 MOVE SFBR TO SCRATCH2
1570 MOVE SFBR TO SCRATCH3
1574 MOVE SCRATCH0 TO SFBR
1576 MOVE SCRATCH1 TO SFBR
1578 MOVE SCRATCH2 TO SFBR
1580 MOVE SCRATCH3 TO SFBR
1586 ; Little patched jump, used to overcome problems with TEMP getting
1587 ; corrupted on memory moves.