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