Improve XEBEC controller support
[SCSI2SD.git] / software / SCSI2SD / src / scsiPhy.c
1 //      Copyright (C) 2013 Michael McMaster <michael@codesrc.com>\r
2 //\r
3 //      This file is part of SCSI2SD.\r
4 //\r
5 //      SCSI2SD is free software: you can redistribute it and/or modify\r
6 //      it under the terms of the GNU General Public License as published by\r
7 //      the Free Software Foundation, either version 3 of the License, or\r
8 //      (at your option) any later version.\r
9 //\r
10 //      SCSI2SD is distributed in the hope that it will be useful,\r
11 //      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 //      GNU General Public License for more details.\r
14 //\r
15 //      You should have received a copy of the GNU General Public License\r
16 //      along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.\r
17 #include "device.h"\r
18 #include "scsi.h"\r
19 #include "scsiPhy.h"\r
20 #include "bits.h"\r
21 #include "trace.h"\r
22 \r
23 #define scsiTarget_AUX_CTL (* (reg8 *) scsiTarget_datapath__DP_AUX_CTL_REG)\r
24 \r
25 // DMA controller can't handle any more than 4095 bytes,\r
26 // but we round down to nearest multiple of 4 bytes..\r
27 #define MAX_DMA_BYTES 4088\r
28 \r
29 // Private DMA variables.\r
30 static int dmaInProgress = 0;\r
31 // used when transferring > MAX_DMA_BYTES.\r
32 static uint8_t* dmaBuffer = NULL;\r
33 static uint32_t dmaSentCount = 0;\r
34 static uint32_t dmaTotalCount = 0;\r
35 \r
36 static uint8 scsiDmaRxChan = CY_DMA_INVALID_CHANNEL;\r
37 static uint8 scsiDmaTxChan = CY_DMA_INVALID_CHANNEL;\r
38 \r
39 // DMA descriptors\r
40 static uint8 scsiDmaRxTd[1] = { CY_DMA_INVALID_TD };\r
41 static uint8 scsiDmaTxTd[1] = { CY_DMA_INVALID_TD };\r
42 \r
43 // Source of dummy bytes for DMA reads\r
44 static uint8 dummyBuffer = 0xFF;\r
45 \r
46 volatile uint8_t scsiRxDMAComplete;\r
47 volatile uint8_t scsiTxDMAComplete;\r
48 \r
49 CY_ISR_PROTO(scsiRxCompleteISR);\r
50 CY_ISR(scsiRxCompleteISR)\r
51 {\r
52         traceIrq(trace_scsiRxCompleteISR);\r
53         scsiRxDMAComplete = 1;\r
54 }\r
55 \r
56 CY_ISR_PROTO(scsiTxCompleteISR);\r
57 CY_ISR(scsiTxCompleteISR)\r
58 {\r
59         traceIrq(trace_scsiTxCompleteISR);\r
60         scsiTxDMAComplete = 1;\r
61 }\r
62 \r
63 CY_ISR_PROTO(scsiResetISR);\r
64 CY_ISR(scsiResetISR)\r
65 {\r
66         traceIrq(trace_scsiResetISR);\r
67         scsiDev.resetFlag = 1;\r
68 }\r
69 \r
70 CY_ISR_PROTO(scsiSelectionISR);\r
71 CY_ISR(scsiSelectionISR)\r
72 {\r
73         // The SEL signal ISR ensures we wake up from a _WFI() (wait-for-interrupt)\r
74         // call in the main loop without waiting for our 1ms timer to\r
75         // expire. This is done to meet the 250us selection abort time.\r
76         \r
77         // selFlag is required for Philips P2000C which releases it after 600ns\r
78         // without waiting for BSY.\r
79         scsiDev.selFlag = 1;\r
80 \r
81         // Required for some early Mac Plus roms\r
82         scsiDev.selDBX = scsiReadDBxPins();\r
83 }\r
84 \r
85 uint8_t\r
86 scsiReadDBxPins()\r
87 {\r
88         return\r
89                 (SCSI_ReadPin(SCSI_In_DBx_DB7) << 7) |\r
90                 (SCSI_ReadPin(SCSI_In_DBx_DB6) << 6) |\r
91                 (SCSI_ReadPin(SCSI_In_DBx_DB5) << 5) |\r
92                 (SCSI_ReadPin(SCSI_In_DBx_DB4) << 4) |\r
93                 (SCSI_ReadPin(SCSI_In_DBx_DB3) << 3) |\r
94                 (SCSI_ReadPin(SCSI_In_DBx_DB2) << 2) |\r
95                 (SCSI_ReadPin(SCSI_In_DBx_DB1) << 1) |\r
96                 SCSI_ReadPin(SCSI_In_DBx_DB0);\r
97 }\r
98 \r
99 uint8_t\r
100 scsiReadByte(void)\r
101 {\r
102         trace(trace_spinPhyTxFifo);\r
103         while (unlikely(scsiPhyTxFifoFull()) && likely(!scsiDev.resetFlag)) {}\r
104         scsiPhyTx(0);\r
105 \r
106         trace(trace_spinPhyRxFifo);\r
107         while (scsiPhyRxFifoEmpty() && likely(!scsiDev.resetFlag)) {}\r
108         uint8_t val = scsiPhyRx();\r
109         scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
110 \r
111         return val;\r
112 }\r
113 \r
114 static void\r
115 scsiReadPIO(uint8* data, uint32 count)\r
116 {\r
117         int prep = 0;\r
118         int i = 0;\r
119 \r
120         while (i < count && likely(!scsiDev.resetFlag))\r
121         {\r
122                 uint8_t status = scsiPhyStatus();\r
123 \r
124                 if (prep < count && (status & SCSI_PHY_TX_FIFO_NOT_FULL))\r
125                 {\r
126                         scsiPhyTx(0);\r
127                         ++prep;\r
128                 }\r
129                 if (status & SCSI_PHY_RX_FIFO_NOT_EMPTY)\r
130                 {\r
131                         data[i] = scsiPhyRx();\r
132                         ++i;\r
133                 }\r
134         }\r
135         scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
136 }\r
137 \r
138 static void\r
139 doRxSingleDMA(uint8* data, uint32 count)\r
140 {\r
141         // Prepare DMA transfer\r
142         dmaInProgress = 1;\r
143         trace(trace_doRxSingleDMA);\r
144 \r
145         CyDmaTdSetConfiguration(\r
146                 scsiDmaTxTd[0],\r
147                 count,\r
148                 CY_DMA_DISABLE_TD, // Disable the DMA channel when TD completes count bytes\r
149                 SCSI_TX_DMA__TD_TERMOUT_EN // Trigger interrupt when complete\r
150                 );\r
151         CyDmaTdSetConfiguration(\r
152                 scsiDmaRxTd[0],\r
153                 count,\r
154                 CY_DMA_DISABLE_TD, // Disable the DMA channel when TD completes count bytes\r
155                 TD_INC_DST_ADR |\r
156                         SCSI_RX_DMA__TD_TERMOUT_EN // Trigger interrupt when complete\r
157                 );\r
158 \r
159         CyDmaTdSetAddress(\r
160                 scsiDmaTxTd[0],\r
161                 LO16((uint32)&dummyBuffer),\r
162                 LO16((uint32)scsiTarget_datapath__F0_REG));\r
163         CyDmaTdSetAddress(\r
164                 scsiDmaRxTd[0],\r
165                 LO16((uint32)scsiTarget_datapath__F1_REG),\r
166                 LO16((uint32)data)\r
167                 );\r
168 \r
169         CyDmaChSetInitialTd(scsiDmaTxChan, scsiDmaTxTd[0]);\r
170         CyDmaChSetInitialTd(scsiDmaRxChan, scsiDmaRxTd[0]);\r
171 \r
172         // The DMA controller is a bit trigger-happy. It will retain\r
173         // a drq request that was triggered while the channel was\r
174         // disabled.\r
175         CyDmaClearPendingDrq(scsiDmaTxChan);\r
176         CyDmaClearPendingDrq(scsiDmaRxChan);\r
177 \r
178         scsiTxDMAComplete = 0;\r
179         scsiRxDMAComplete = 0;\r
180 \r
181         CyDmaChEnable(scsiDmaRxChan, 1);\r
182         CyDmaChEnable(scsiDmaTxChan, 1);\r
183 }\r
184 \r
185 void\r
186 scsiReadDMA(uint8* data, uint32 count)\r
187 {\r
188         dmaSentCount = 0;\r
189         dmaTotalCount = count;\r
190         dmaBuffer = data;\r
191 \r
192         uint32_t singleCount = (count > MAX_DMA_BYTES) ? MAX_DMA_BYTES : count;\r
193         doRxSingleDMA(data, singleCount);\r
194         dmaSentCount += count;\r
195 }\r
196 \r
197 int\r
198 scsiReadDMAPoll()\r
199 {\r
200         if (scsiTxDMAComplete && scsiRxDMAComplete)\r
201         {\r
202                 if (likely(dmaSentCount == dmaTotalCount))\r
203                 {\r
204                         dmaInProgress = 0;\r
205                         scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
206                         return 1;\r
207                 }\r
208                 else\r
209                 {\r
210                         // Transfer was too large for a single DMA transfer. Continue\r
211                         // to send remaining bytes.\r
212                         uint32_t count = dmaTotalCount - dmaSentCount;\r
213                         if (unlikely(count > MAX_DMA_BYTES)) count = MAX_DMA_BYTES;\r
214                         doRxSingleDMA(dmaBuffer + dmaSentCount, count);\r
215                         dmaSentCount += count;\r
216                         return 0;\r
217                 }\r
218         }\r
219         else\r
220         {\r
221                 return 0;\r
222         }\r
223 }\r
224 \r
225 void\r
226 scsiRead(uint8_t* data, uint32_t count)\r
227 {\r
228         if (count < 12)\r
229         {\r
230                 scsiReadPIO(data, count);\r
231         }\r
232         else\r
233         {\r
234                 scsiReadDMA(data, count);\r
235 \r
236                 // Wait for the next DMA interrupt (or the 1ms systick)\r
237                 // It's beneficial to halt the processor to\r
238                 // give the DMA controller more memory bandwidth to work with.\r
239                 __WFI();\r
240 \r
241                 trace(trace_spinReadDMAPoll);\r
242                 while (!scsiReadDMAPoll() && likely(!scsiDev.resetFlag))\r
243                 {\r
244                         __WFI();\r
245                 };\r
246         }\r
247 }\r
248 \r
249 void\r
250 scsiWriteByte(uint8 value)\r
251 {\r
252         trace(trace_spinPhyTxFifo);\r
253         while (unlikely(scsiPhyTxFifoFull()) && likely(!scsiDev.resetFlag)) {}\r
254         scsiPhyTx(value);\r
255 \r
256         trace(trace_spinTxComplete);\r
257         while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}\r
258         scsiPhyRx();\r
259 }\r
260 \r
261 static void\r
262 scsiWritePIO(const uint8_t* data, uint32_t count)\r
263 {\r
264         int i = 0;\r
265 \r
266         while (i < count && likely(!scsiDev.resetFlag))\r
267         {\r
268                 if (!scsiPhyTxFifoFull())\r
269                 {\r
270                         scsiPhyTx(data[i]);\r
271                         ++i;\r
272                 }\r
273         }\r
274 \r
275         trace(trace_spinTxComplete);\r
276         while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}\r
277         scsiPhyRxFifoClear();\r
278 }\r
279 \r
280 static void\r
281 doTxSingleDMA(const uint8* data, uint32 count)\r
282 {\r
283         // Prepare DMA transfer\r
284         dmaInProgress = 1;\r
285         trace(trace_doTxSingleDMA);\r
286 \r
287         CyDmaTdSetConfiguration(\r
288                 scsiDmaTxTd[0],\r
289                 count,\r
290                 CY_DMA_DISABLE_TD, // Disable the DMA channel when TD completes count bytes\r
291                 TD_INC_SRC_ADR |\r
292                         SCSI_TX_DMA__TD_TERMOUT_EN // Trigger interrupt when complete\r
293                 );\r
294         CyDmaTdSetAddress(\r
295                 scsiDmaTxTd[0],\r
296                 LO16((uint32)data),\r
297                 LO16((uint32)scsiTarget_datapath__F0_REG));\r
298         CyDmaChSetInitialTd(scsiDmaTxChan, scsiDmaTxTd[0]);\r
299 \r
300         // The DMA controller is a bit trigger-happy. It will retain\r
301         // a drq request that was triggered while the channel was\r
302         // disabled.\r
303         CyDmaClearPendingDrq(scsiDmaTxChan);\r
304 \r
305         scsiTxDMAComplete = 0;\r
306         scsiRxDMAComplete = 1;\r
307 \r
308         CyDmaChEnable(scsiDmaTxChan, 1);\r
309 }\r
310 \r
311 void\r
312 scsiWriteDMA(const uint8* data, uint32 count)\r
313 {\r
314         dmaSentCount = 0;\r
315         dmaTotalCount = count;\r
316         dmaBuffer = data;\r
317 \r
318         uint32_t singleCount = (count > MAX_DMA_BYTES) ? MAX_DMA_BYTES : count;\r
319         doTxSingleDMA(data, singleCount);\r
320         dmaSentCount += count;\r
321 }\r
322 \r
323 int\r
324 scsiWriteDMAPoll()\r
325 {\r
326         if (scsiTxDMAComplete)\r
327         {\r
328                 // Wait until our scsi signals are consistent. This should only be\r
329                 // a few cycles.\r
330                 trace(trace_spinTxComplete);\r
331                 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE)) {}\r
332 \r
333                 if (likely(dmaSentCount == dmaTotalCount))\r
334                 {\r
335                         scsiPhyRxFifoClear();\r
336                         dmaInProgress = 0;\r
337                         return 1;\r
338                 }\r
339                 else\r
340                 {\r
341                         // Transfer was too large for a single DMA transfer. Continue\r
342                         // to send remaining bytes.\r
343                         uint32_t count = dmaTotalCount - dmaSentCount;\r
344                         if (unlikely(count > MAX_DMA_BYTES)) count = MAX_DMA_BYTES;\r
345                         doTxSingleDMA(dmaBuffer + dmaSentCount, count);\r
346                         dmaSentCount += count;\r
347                         return 0;\r
348                 }\r
349         }\r
350         else\r
351         {\r
352                 return 0;\r
353         }\r
354 }\r
355 \r
356 void\r
357 scsiWrite(const uint8_t* data, uint32_t count)\r
358 {\r
359         if (count < 12)\r
360         {\r
361                 scsiWritePIO(data, count);\r
362         }\r
363         else\r
364         {\r
365                 scsiWriteDMA(data, count);\r
366 \r
367                 // Wait for the next DMA interrupt (or the 1ms systick)\r
368                 // It's beneficial to halt the processor to\r
369                 // give the DMA controller more memory bandwidth to work with.\r
370                 __WFI();\r
371 \r
372                 trace(trace_spinWriteDMAPoll);\r
373                 while (!scsiWriteDMAPoll() && likely(!scsiDev.resetFlag))\r
374                 {\r
375                         __WFI();\r
376                 };\r
377         }\r
378 }\r
379 \r
380 static inline void busSettleDelay(void)\r
381 {\r
382         // Data Release time (switching IO) = 400ns\r
383         // + Bus Settle time (switching phase) = 400ns.\r
384         CyDelayUs(1); // Close enough.\r
385 }\r
386 \r
387 void scsiEnterPhase(int phase)\r
388 {\r
389         // ANSI INCITS 362-2002 SPI-3 10.7.1:\r
390         // Phase changes are not allowed while REQ or ACK is asserted.\r
391         while (likely(!scsiDev.resetFlag) && SCSI_ReadFilt(SCSI_Filt_ACK))\r
392         {}\r
393 \r
394         int newPhase = phase > 0 ? phase : 0;\r
395         if (newPhase != SCSI_CTL_PHASE_Read())\r
396         {\r
397                 SCSI_CTL_PHASE_Write(phase > 0 ? phase : 0);\r
398                 busSettleDelay();\r
399 \r
400                 if (scsiDev.compatMode < COMPAT_SCSI2)\r
401                 {\r
402                         // XEBEC S1410 manual (old SASI controller) gives 10uSec delay\r
403                         // between phase bits and REQ.\r
404                         CyDelayUs(10);\r
405                 }\r
406         }\r
407 }\r
408 \r
409 void scsiPhyReset()\r
410 {\r
411         trace(trace_scsiPhyReset);\r
412         if (dmaInProgress)\r
413         {\r
414                 dmaInProgress = 0;\r
415                 dmaBuffer = NULL;\r
416                 dmaSentCount = 0;\r
417                 dmaTotalCount = 0;\r
418                 CyDmaChSetRequest(scsiDmaTxChan, CY_DMA_CPU_TERM_CHAIN);\r
419                 CyDmaChSetRequest(scsiDmaRxChan, CY_DMA_CPU_TERM_CHAIN);\r
420 \r
421                 // CyDmaChGetRequest returns 0 for the relevant bit once the\r
422                 // request is completed.\r
423                 trace(trace_spinDMAReset);\r
424                 while (\r
425                         (CyDmaChGetRequest(scsiDmaTxChan) & CY_DMA_CPU_TERM_CHAIN) &&\r
426                         !scsiTxDMAComplete\r
427                         )\r
428                 {}\r
429 \r
430                 while ((\r
431                         CyDmaChGetRequest(scsiDmaRxChan) & CY_DMA_CPU_TERM_CHAIN) &&\r
432                         !scsiRxDMAComplete\r
433                         )\r
434                 {}\r
435 \r
436                 CyDelayUs(1);\r
437 \r
438                 CyDmaChDisable(scsiDmaTxChan);\r
439                 CyDmaChDisable(scsiDmaRxChan);\r
440         }\r
441 \r
442         // Set the Clear bits for both SCSI device FIFOs\r
443         scsiTarget_AUX_CTL = scsiTarget_AUX_CTL | 0x03;\r
444 \r
445         // Trigger RST outselves.  It is connected to the datapath and will\r
446         // ensure it returns to the idle state.  The datapath runs at the BUS clk\r
447         // speed (ie. same as the CPU), so we can be sure it is active for a sufficient\r
448         // duration.\r
449         SCSI_RST_ISR_Disable();\r
450         SCSI_SetPin(SCSI_Out_RST);\r
451 \r
452         SCSI_CTL_PHASE_Write(0);\r
453         #ifdef SCSI_Out_ATN\r
454         SCSI_ClearPin(SCSI_Out_ATN);\r
455         #endif\r
456         SCSI_ClearPin(SCSI_Out_BSY);\r
457         #ifdef SCSI_Out_ACK\r
458         SCSI_ClearPin(SCSI_Out_ACK);\r
459         #endif\r
460         SCSI_ClearPin(SCSI_Out_SEL);\r
461         SCSI_ClearPin(SCSI_Out_REQ);\r
462 \r
463         // Allow the FIFOs to fill up again.\r
464         SCSI_ClearPin(SCSI_Out_RST);\r
465         SCSI_RST_ISR_Enable();\r
466         scsiTarget_AUX_CTL = scsiTarget_AUX_CTL & ~(0x03);\r
467 \r
468         SCSI_Parity_Error_Read(); // clear sticky bits\r
469 }\r
470 \r
471 static void scsiPhyInitDMA()\r
472 {\r
473         // One-time init only.\r
474         if (scsiDmaTxChan == CY_DMA_INVALID_CHANNEL)\r
475         {\r
476                 scsiDmaRxChan =\r
477                         SCSI_RX_DMA_DmaInitialize(\r
478                                 1, // Bytes per burst\r
479                                 1, // request per burst\r
480                                 HI16(CYDEV_PERIPH_BASE),\r
481                                 HI16(CYDEV_SRAM_BASE)\r
482                                 );\r
483 \r
484                 scsiDmaTxChan =\r
485                         SCSI_TX_DMA_DmaInitialize(\r
486                                 1, // Bytes per burst\r
487                                 1, // request per burst\r
488                                 HI16(CYDEV_SRAM_BASE),\r
489                                 HI16(CYDEV_PERIPH_BASE)\r
490                                 );\r
491 \r
492                 CyDmaChDisable(scsiDmaRxChan);\r
493                 CyDmaChDisable(scsiDmaTxChan);\r
494 \r
495                 scsiDmaRxTd[0] = CyDmaTdAllocate();\r
496                 scsiDmaTxTd[0] = CyDmaTdAllocate();\r
497 \r
498                 SCSI_RX_DMA_COMPLETE_StartEx(scsiRxCompleteISR);\r
499                 SCSI_TX_DMA_COMPLETE_StartEx(scsiTxCompleteISR);\r
500         }\r
501 }\r
502 \r
503 \r
504 void scsiPhyInit()\r
505 {\r
506         scsiPhyInitDMA();\r
507 \r
508         SCSI_RST_ISR_StartEx(scsiResetISR);\r
509 \r
510         SCSI_SEL_ISR_StartEx(scsiSelectionISR);\r
511 }\r
512 \r
513 void scsiPhyConfig()\r
514 {\r
515         // Disable the glitch filter for ACK to improve performance.\r
516         if (scsiDev.boardCfg.flags & CONFIG_DISABLE_GLITCH)\r
517         {\r
518                 SCSI_Glitch_Ctl_Write(1);\r
519 \r
520                 // Reduce deskew time to 1. (deskew init + 0)\r
521                 CY_SET_REG8(scsiTarget_datapath__D0_REG, 0);\r
522         }\r
523         if ((scsiDev.target->cfg->quirks & CONFIG_QUIRKS_XEBEC) ||\r
524                 (scsiDev.boardCfg.scsiSpeed == CONFIG_SPEED_ASYNC_15))\r
525         {\r
526                 // 125ns to 250ns deskew time = 3.125 clocks\r
527                 // - 1 (valid during DESKEW INIT)\r
528                 // = 2.125. Default is 1.\r
529                 // Round down because it's going to be doubled anyway due to clock\r
530                 // divider change below.\r
531                 CY_SET_REG8(scsiTarget_datapath__D0_REG, 2);\r
532 \r
533                 // Half the SCSI clock as a way to extend the glitch filter.\r
534                 // This also helps meet the 250ns delays between ACK and reading\r
535                 // data, or release ack and reassert req.\r
536 \r
537                 // The register contains (divider - 1)\r
538                 uint16_t clkDiv25MHz =  SCSI_CLK_GetDividerRegister();\r
539                 SCSI_CLK_SetDivider(((clkDiv25MHz + 1) * 2) - 1);\r
540                 // Wait for the clock to settle.\r
541                 CyDelayUs(1);\r
542         }\r
543 \r
544         #ifdef TERM_EN_0\r
545         TERM_EN_Write((scsiDev.boardCfg.flags6 & S2S_CFG_ENABLE_TERMINATOR) ? 0 : 1);\r
546         #endif\r
547 }\r
548 \r
549 // 1 = DBx error\r
550 // 2 = Parity error\r
551 // 4 = MSG error\r
552 // 8 = CD error\r
553 // 16 = IO error\r
554 // 32 = other error\r
555 int scsiSelfTest()\r
556 {\r
557         int result = 0;\r
558 \r
559         // TEST DBx and DBp\r
560         int i;\r
561         SCSI_Out_Ctl_Write(1); // Write bits manually.\r
562         SCSI_CTL_PHASE_Write(__scsiphase_io); // Needed for parity generation\r
563         for (i = 0; i < 256; ++i)\r
564         {\r
565                 SCSI_Out_Bits_Write(i);\r
566                 scsiDeskewDelay();\r
567                 if (scsiReadDBxPins() != (i & 0xff))\r
568                 {\r
569                         result |= 1;\r
570                 }\r
571                 if (Lookup_OddParity[i & 0xff] != SCSI_ReadPin(SCSI_In_DBP))\r
572                 {\r
573                         result |= 2;\r
574                 }\r
575         }\r
576         SCSI_Out_Ctl_Write(0); // Write bits normally.\r
577 \r
578         // TEST MSG, CD, IO\r
579         #ifdef SCSI_In_MSG\r
580         for (i = 0; i < 8; ++i)\r
581         {\r
582                 SCSI_CTL_PHASE_Write(i);\r
583                 scsiDeskewDelay();\r
584 \r
585                 if (SCSI_ReadPin(SCSI_In_MSG) != !!(i & __scsiphase_msg))\r
586                 {\r
587                         result |= 4;\r
588                 }\r
589                 if (SCSI_ReadPin(SCSI_In_CD) != !!(i & __scsiphase_cd))\r
590                 {\r
591                         result |= 8;\r
592                 }\r
593                 if (SCSI_ReadPin(SCSI_In_IO) != !!(i & __scsiphase_io))\r
594                 {\r
595                         result |= 16;\r
596                 }\r
597         }\r
598         #endif\r
599         SCSI_CTL_PHASE_Write(0);\r
600 \r
601         uint32_t signalsOut[] = { SCSI_Out_BSY, SCSI_Out_RST, SCSI_Out_SEL };\r
602         uint32_t signalsIn[] = { SCSI_Filt_BSY, SCSI_Filt_RST, SCSI_Filt_SEL };\r
603 \r
604         for (i = 0; i < 3; ++i)\r
605         {\r
606                 SCSI_SetPin(signalsOut[i]);\r
607                 scsiDeskewDelay();\r
608 \r
609                 int j;\r
610                 for (j = 0; j < 3; ++j)\r
611                 {\r
612                         if (i == j)\r
613                         {\r
614                                 if (! SCSI_ReadFilt(signalsIn[j]))\r
615                                 {\r
616                                         result |= 32;\r
617                                 }\r
618                         }\r
619                         else\r
620                         {\r
621                                 if (SCSI_ReadFilt(signalsIn[j]))\r
622                                 {\r
623                                         result |= 32;\r
624                                 }\r
625                         }\r
626                 }\r
627                 SCSI_ClearPin(signalsOut[i]);\r
628         }\r
629         return result;\r
630 }\r
631 \r
632 \r