Performance fixes, scsi2sd-util crash fixes, scsi2 config option.
[SCSI2SD-V6.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 #pragma GCC push_options\r
18 #pragma GCC optimize("-flto")\r
19 \r
20 #include "device.h"\r
21 #include "scsi.h"\r
22 #include "scsiPhy.h"\r
23 #include "bits.h"\r
24 #include "trace.h"\r
25 \r
26 #define scsiTarget_AUX_CTL (* (reg8 *) scsiTarget_datapath__DP_AUX_CTL_REG)\r
27 \r
28 // DMA controller can't handle any more than 4095 bytes,\r
29 // but we round down to nearest multiple of 4 bytes..\r
30 #define MAX_DMA_BYTES 4088\r
31 \r
32 // Private DMA variables.\r
33 static int dmaInProgress = 0;\r
34 // used when transferring > MAX_DMA_BYTES.\r
35 static uint8_t* dmaBuffer = NULL;\r
36 static uint32_t dmaSentCount = 0;\r
37 static uint32_t dmaTotalCount = 0;\r
38 \r
39 static uint8 scsiDmaRxChan = CY_DMA_INVALID_CHANNEL;\r
40 static uint8 scsiDmaTxChan = CY_DMA_INVALID_CHANNEL;\r
41 \r
42 // DMA descriptors\r
43 static uint8 scsiDmaRxTd[1] = { CY_DMA_INVALID_TD };\r
44 static uint8 scsiDmaTxTd[1] = { CY_DMA_INVALID_TD };\r
45 \r
46 // Source of dummy bytes for DMA reads\r
47 static uint8 dummyBuffer = 0xFF;\r
48 \r
49 volatile uint8_t scsiRxDMAComplete;\r
50 volatile uint8_t scsiTxDMAComplete;\r
51 \r
52 CY_ISR_PROTO(scsiRxCompleteISR);\r
53 CY_ISR(scsiRxCompleteISR)\r
54 {\r
55         traceIrq(trace_scsiRxCompleteISR);\r
56         scsiRxDMAComplete = 1;\r
57 }\r
58 \r
59 CY_ISR_PROTO(scsiTxCompleteISR);\r
60 CY_ISR(scsiTxCompleteISR)\r
61 {\r
62         traceIrq(trace_scsiTxCompleteISR);\r
63         scsiTxDMAComplete = 1;\r
64 }\r
65 \r
66 CY_ISR_PROTO(scsiResetISR);\r
67 CY_ISR(scsiResetISR)\r
68 {\r
69         traceIrq(trace_scsiResetISR);\r
70         scsiDev.resetFlag = 1;\r
71 }\r
72 \r
73 CY_ISR_PROTO(scsiSelectionISR);\r
74 CY_ISR(scsiSelectionISR)\r
75 {\r
76         // The SEL signal ISR ensures we wake up from a _WFI() (wait-for-interrupt)\r
77         // call in the main loop without waiting for our 1ms timer to\r
78         // expire. This is done for performance reasons only.\r
79 }\r
80 \r
81 uint8_t\r
82 scsiReadDBxPins()\r
83 {\r
84         return\r
85                 (SCSI_ReadPin(SCSI_In_DBx_DB7) << 7) |\r
86                 (SCSI_ReadPin(SCSI_In_DBx_DB6) << 6) |\r
87                 (SCSI_ReadPin(SCSI_In_DBx_DB5) << 5) |\r
88                 (SCSI_ReadPin(SCSI_In_DBx_DB4) << 4) |\r
89                 (SCSI_ReadPin(SCSI_In_DBx_DB3) << 3) |\r
90                 (SCSI_ReadPin(SCSI_In_DBx_DB2) << 2) |\r
91                 (SCSI_ReadPin(SCSI_In_DBx_DB1) << 1) |\r
92                 SCSI_ReadPin(SCSI_In_DBx_DB0);\r
93 }\r
94 \r
95 uint8_t\r
96 scsiReadByte(void)\r
97 {\r
98         trace(trace_spinPhyTxFifo);\r
99         while (unlikely(scsiPhyTxFifoFull()) && likely(!scsiDev.resetFlag)) {}\r
100         scsiPhyTx(0);\r
101 \r
102         trace(trace_spinPhyRxFifo);\r
103         while (scsiPhyRxFifoEmpty() && likely(!scsiDev.resetFlag)) {}\r
104         uint8_t val = scsiPhyRx();\r
105         scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
106 \r
107         trace(trace_spinTxComplete);\r
108         while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}\r
109 \r
110         return val;\r
111 }\r
112 \r
113 static void\r
114 scsiReadPIO(uint8* data, uint32 count)\r
115 {\r
116         int prep = 0;\r
117         int i = 0;\r
118 \r
119         while (i < count && likely(!scsiDev.resetFlag))\r
120         {\r
121                 uint8_t status = scsiPhyStatus();\r
122 \r
123                 if (prep < count && (status & SCSI_PHY_TX_FIFO_NOT_FULL))\r
124                 {\r
125                         scsiPhyTx(0);\r
126                         ++prep;\r
127                 }\r
128                 if (status & SCSI_PHY_RX_FIFO_NOT_EMPTY)\r
129                 {\r
130                         data[i] = scsiPhyRx();\r
131                         ++i;\r
132                 }\r
133         }\r
134         scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
135         while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}\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                 // Wait until our scsi signals are consistent. This should only be\r
203                 // a few cycles.\r
204                 trace(trace_spinTxComplete);\r
205                 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE)) {}\r
206 \r
207                 if (likely(dmaSentCount == dmaTotalCount))\r
208                 {\r
209                         dmaInProgress = 0;\r
210                         scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
211                         return 1;\r
212                 }\r
213                 else\r
214                 {\r
215                         // Transfer was too large for a single DMA transfer. Continue\r
216                         // to send remaining bytes.\r
217                         uint32_t count = dmaTotalCount - dmaSentCount;\r
218                         if (unlikely(count > MAX_DMA_BYTES)) count = MAX_DMA_BYTES;\r
219                         doRxSingleDMA(dmaBuffer + dmaSentCount, count);\r
220                         dmaSentCount += count;\r
221                         return 0;\r
222                 }\r
223         }\r
224         else\r
225         {\r
226                 return 0;\r
227         }\r
228 }\r
229 \r
230 void\r
231 scsiRead(uint8_t* data, uint32_t count)\r
232 {\r
233         if (count < 12)\r
234         {\r
235                 scsiReadPIO(data, count);\r
236         }\r
237         else\r
238         {\r
239                 uint32_t alignedCount = count & 0xFFFFFFF8;\r
240                 scsiReadDMA(data, alignedCount);\r
241 \r
242                 // Wait for the next DMA interrupt (or the 1ms systick)\r
243                 // It's beneficial to halt the processor to\r
244                 // give the DMA controller more memory bandwidth to work with.\r
245                 __WFI();\r
246 \r
247                 trace(trace_spinReadDMAPoll);\r
248                 while (!scsiReadDMAPoll() && likely(!scsiDev.resetFlag))\r
249                 {\r
250                         __WFI();\r
251                 };\r
252 \r
253                 if (count > alignedCount)\r
254                 {\r
255                         scsiReadPIO(data + alignedCount, count - alignedCount);\r
256                 }\r
257         }\r
258 }\r
259 \r
260 void\r
261 scsiWriteByte(uint8 value)\r
262 {\r
263         trace(trace_spinPhyTxFifo);\r
264         while (unlikely(scsiPhyTxFifoFull()) && likely(!scsiDev.resetFlag)) {}\r
265         scsiPhyTx(value);\r
266 \r
267         trace(trace_spinTxComplete);\r
268         while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}\r
269         scsiPhyRxFifoClear();\r
270 }\r
271 \r
272 static void\r
273 scsiWritePIO(const uint8_t* data, uint32_t count)\r
274 {\r
275         int i = 0;\r
276 \r
277         while (i < count && likely(!scsiDev.resetFlag))\r
278         {\r
279                 if (!scsiPhyTxFifoFull())\r
280                 {\r
281                         scsiPhyTx(data[i]);\r
282                         ++i;\r
283                 }\r
284         }\r
285 \r
286         trace(trace_spinTxComplete);\r
287         while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}\r
288         scsiPhyRxFifoClear();\r
289 }\r
290 \r
291 static void\r
292 doTxSingleDMA(const uint8* data, uint32 count)\r
293 {\r
294         // Prepare DMA transfer\r
295         dmaInProgress = 1;\r
296         trace(trace_doTxSingleDMA);\r
297 \r
298         CyDmaTdSetConfiguration(\r
299                 scsiDmaTxTd[0],\r
300                 count,\r
301                 CY_DMA_DISABLE_TD, // Disable the DMA channel when TD completes count bytes\r
302                 TD_INC_SRC_ADR |\r
303                         SCSI_TX_DMA__TD_TERMOUT_EN // Trigger interrupt when complete\r
304                 );\r
305         CyDmaTdSetAddress(\r
306                 scsiDmaTxTd[0],\r
307                 LO16((uint32)data),\r
308                 LO16((uint32)scsiTarget_datapath__F0_REG));\r
309         CyDmaChSetInitialTd(scsiDmaTxChan, scsiDmaTxTd[0]);\r
310 \r
311         // The DMA controller is a bit trigger-happy. It will retain\r
312         // a drq request that was triggered while the channel was\r
313         // disabled.\r
314         CyDmaClearPendingDrq(scsiDmaTxChan);\r
315 \r
316         scsiTxDMAComplete = 0;\r
317         scsiRxDMAComplete = 1;\r
318 \r
319         CyDmaChEnable(scsiDmaTxChan, 1);\r
320 }\r
321 \r
322 void\r
323 scsiWriteDMA(const uint8* data, uint32 count)\r
324 {\r
325         dmaSentCount = 0;\r
326         dmaTotalCount = count;\r
327         dmaBuffer = data;\r
328 \r
329         uint32_t singleCount = (count > MAX_DMA_BYTES) ? MAX_DMA_BYTES : count;\r
330         doTxSingleDMA(data, singleCount);\r
331         dmaSentCount += count;\r
332 }\r
333 \r
334 int\r
335 scsiWriteDMAPoll()\r
336 {\r
337         if (scsiTxDMAComplete)\r
338         {\r
339                 // Wait until our scsi signals are consistent. This should only be\r
340                 // a few cycles.\r
341                 trace(trace_spinTxComplete);\r
342                 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE)) {}\r
343 \r
344                 if (likely(dmaSentCount == dmaTotalCount))\r
345                 {\r
346                         scsiPhyRxFifoClear();\r
347                         dmaInProgress = 0;\r
348                         return 1;\r
349                 }\r
350                 else\r
351                 {\r
352                         // Transfer was too large for a single DMA transfer. Continue\r
353                         // to send remaining bytes.\r
354                         uint32_t count = dmaTotalCount - dmaSentCount;\r
355                         if (unlikely(count > MAX_DMA_BYTES)) count = MAX_DMA_BYTES;\r
356                         doTxSingleDMA(dmaBuffer + dmaSentCount, count);\r
357                         dmaSentCount += count;\r
358                         return 0;\r
359                 }\r
360         }\r
361         else\r
362         {\r
363                 return 0;\r
364         }\r
365 }\r
366 \r
367 void\r
368 scsiWrite(const uint8_t* data, uint32_t count)\r
369 {\r
370         if (count < 12)\r
371         {\r
372                 scsiWritePIO(data, count);\r
373         }\r
374         else\r
375         {\r
376                 uint32_t alignedCount = count & 0xFFFFFFF8;\r
377                 scsiWriteDMA(data, alignedCount);\r
378 \r
379                 // Wait for the next DMA interrupt (or the 1ms systick)\r
380                 // It's beneficial to halt the processor to\r
381                 // give the DMA controller more memory bandwidth to work with.\r
382                 __WFI();\r
383 \r
384                 trace(trace_spinWriteDMAPoll);\r
385                 while (!scsiWriteDMAPoll() && likely(!scsiDev.resetFlag))\r
386                 {\r
387                         __WFI();\r
388                 };\r
389                 \r
390                 if (count > alignedCount)\r
391                 {\r
392                         scsiWritePIO(data + alignedCount, count - alignedCount);\r
393                 }\r
394         }\r
395 }\r
396 \r
397 static inline void busSettleDelay(void)\r
398 {\r
399         // Data Release time (switching IO) = 400ns\r
400         // + Bus Settle time (switching phase) = 400ns.\r
401         CyDelayUs(1); // Close enough.\r
402 }\r
403 \r
404 void scsiEnterPhase(int phase)\r
405 {\r
406         int newPhase = phase > 0 ? phase : 0;\r
407         if (newPhase != SCSI_CTL_PHASE_Read())\r
408         {\r
409                 SCSI_CTL_PHASE_Write(phase > 0 ? phase : 0);\r
410                 busSettleDelay();\r
411 \r
412                 if (scsiDev.compatMode < COMPAT_SCSI2)\r
413                 {\r
414                         CyDelayUs(100);\r
415                 }\r
416         }\r
417 }\r
418 \r
419 void scsiPhyReset()\r
420 {\r
421         trace(trace_scsiPhyReset);\r
422         if (dmaInProgress)\r
423         {\r
424                 dmaInProgress = 0;\r
425                 dmaBuffer = NULL;\r
426                 dmaSentCount = 0;\r
427                 dmaTotalCount = 0;\r
428                 CyDmaChSetRequest(scsiDmaTxChan, CY_DMA_CPU_TERM_CHAIN);\r
429                 CyDmaChSetRequest(scsiDmaRxChan, CY_DMA_CPU_TERM_CHAIN);\r
430                 \r
431                 // CyDmaChGetRequest returns 0 for the relevant bit once the\r
432                 // request is completed.\r
433                 trace(trace_spinDMAReset);\r
434                 while (CyDmaChGetRequest(scsiDmaTxChan) & CY_DMA_CPU_TERM_CHAIN) {}\r
435                 while (CyDmaChGetRequest(scsiDmaRxChan) & CY_DMA_CPU_TERM_CHAIN) {}\r
436 \r
437                 CyDmaChDisable(scsiDmaTxChan);\r
438                 CyDmaChDisable(scsiDmaRxChan);\r
439         }\r
440 \r
441         // Set the Clear bits for both SCSI device FIFOs\r
442         scsiTarget_AUX_CTL = scsiTarget_AUX_CTL | 0x03;\r
443 \r
444         // Trigger RST outselves.  It is connected to the datapath and will\r
445         // ensure it returns to the idle state.  The datapath runs at the BUS clk\r
446         // speed (ie. same as the CPU), so we can be sure it is active for a sufficient\r
447         // duration.\r
448         SCSI_RST_ISR_Disable();\r
449         SCSI_SetPin(SCSI_Out_RST);\r
450 \r
451         SCSI_CTL_PHASE_Write(0);\r
452         SCSI_ClearPin(SCSI_Out_ATN);\r
453         SCSI_ClearPin(SCSI_Out_BSY);\r
454         SCSI_ClearPin(SCSI_Out_ACK);\r
455         SCSI_ClearPin(SCSI_Out_RST);\r
456         SCSI_ClearPin(SCSI_Out_SEL);\r
457         SCSI_ClearPin(SCSI_Out_REQ);\r
458 \r
459         // Allow the FIFOs to fill up again.\r
460         SCSI_ClearPin(SCSI_Out_RST);\r
461         SCSI_RST_ISR_Enable();\r
462         scsiTarget_AUX_CTL = scsiTarget_AUX_CTL & ~(0x03);\r
463 \r
464         SCSI_Parity_Error_Read(); // clear sticky bits\r
465 }\r
466 \r
467 static void scsiPhyInitDMA()\r
468 {\r
469         // One-time init only.\r
470         if (scsiDmaTxChan == CY_DMA_INVALID_CHANNEL)\r
471         {\r
472                 scsiDmaRxChan =\r
473                         SCSI_RX_DMA_DmaInitialize(\r
474                                 4, // Bytes per burst\r
475                                 1, // request per burst\r
476                                 HI16(CYDEV_PERIPH_BASE),\r
477                                 HI16(CYDEV_SRAM_BASE)\r
478                                 );\r
479 \r
480                 scsiDmaTxChan =\r
481                         SCSI_TX_DMA_DmaInitialize(\r
482                                 4, // Bytes per burst\r
483                                 1, // request per burst\r
484                                 HI16(CYDEV_SRAM_BASE),\r
485                                 HI16(CYDEV_PERIPH_BASE)\r
486                                 );\r
487                 \r
488                 CyDmaChDisable(scsiDmaRxChan);\r
489                 CyDmaChDisable(scsiDmaTxChan);\r
490 \r
491                 scsiDmaRxTd[0] = CyDmaTdAllocate();\r
492                 scsiDmaTxTd[0] = CyDmaTdAllocate();\r
493 \r
494                 SCSI_RX_DMA_COMPLETE_StartEx(scsiRxCompleteISR);\r
495                 SCSI_TX_DMA_COMPLETE_StartEx(scsiTxCompleteISR);\r
496         }\r
497 }\r
498 \r
499 \r
500 void scsiPhyInit()\r
501 {\r
502         scsiPhyInitDMA();\r
503 \r
504         SCSI_RST_ISR_StartEx(scsiResetISR);\r
505 \r
506         SCSI_SEL_ISR_StartEx(scsiSelectionISR);\r
507 \r
508 /*\r
509         // Disable the glitch filter for ACK to improve performance.\r
510         // TODO NEED SOME CONFIG\r
511         SCSI_Glitch_Ctl_Write(1);\r
512         CY_SET_REG8(scsiTarget_datapath__D0_REG, 0);\r
513 */\r
514 \r
515 }\r
516 \r
517 // 1 = DBx error\r
518 // 2 = Parity error\r
519 // 4 = MSG error\r
520 // 8 = CD error\r
521 // 16 = IO error\r
522 // 32 = other error\r
523 int scsiSelfTest()\r
524 {\r
525         int result = 0;\r
526 \r
527         // TEST DBx and DBp\r
528         int i;\r
529         SCSI_Out_Ctl_Write(1); // Write bits manually.\r
530         SCSI_CTL_PHASE_Write(__scsiphase_io); // Needed for parity generation\r
531         for (i = 0; i < 256; ++i)\r
532         {\r
533                 SCSI_Out_Bits_Write(i);\r
534                 scsiDeskewDelay();\r
535                 if (scsiReadDBxPins() != (i & 0xff))\r
536                 {\r
537                         result |= 1;\r
538                 }\r
539                 if (Lookup_OddParity[i & 0xff] != SCSI_ReadPin(SCSI_In_DBP))\r
540                 {\r
541                         result |= 2;\r
542                 }\r
543         }\r
544         SCSI_Out_Ctl_Write(0); // Write bits normally.\r
545 \r
546         // TEST MSG, CD, IO\r
547         for (i = 0; i < 8; ++i)\r
548         {\r
549                 SCSI_CTL_PHASE_Write(i);\r
550                 scsiDeskewDelay();\r
551 \r
552                 if (SCSI_ReadPin(SCSI_In_MSG) != !!(i & __scsiphase_msg))\r
553                 {\r
554                         result |= 4;\r
555                 }\r
556                 if (SCSI_ReadPin(SCSI_In_CD) != !!(i & __scsiphase_cd))\r
557                 {\r
558                         result |= 8;\r
559                 }\r
560                 if (SCSI_ReadPin(SCSI_In_IO) != !!(i & __scsiphase_io))\r
561                 {\r
562                         result |= 16;\r
563                 }\r
564         }\r
565         SCSI_CTL_PHASE_Write(0);\r
566 \r
567         uint32_t signalsOut[] = { SCSI_Out_ATN, SCSI_Out_BSY, SCSI_Out_RST, SCSI_Out_SEL };\r
568         uint32_t signalsIn[] = { SCSI_Filt_ATN, SCSI_Filt_BSY, SCSI_Filt_RST, SCSI_Filt_SEL };\r
569 \r
570         for (i = 0; i < 4; ++i)\r
571         {\r
572                 SCSI_SetPin(signalsOut[i]);\r
573                 scsiDeskewDelay();\r
574 \r
575                 int j;\r
576                 for (j = 0; j < 4; ++j)\r
577                 {\r
578                         if (i == j)\r
579                         {\r
580                                 if (! SCSI_ReadFilt(signalsIn[j]))\r
581                                 {\r
582                                         result |= 32;\r
583                                 }\r
584                         }\r
585                         else\r
586                         {\r
587                                 if (SCSI_ReadFilt(signalsIn[j]))\r
588                                 {\r
589                                         result |= 32;\r
590                                 }\r
591                         }\r
592                 }\r
593                 SCSI_ClearPin(signalsOut[i]);\r
594         }\r
595         return result;\r
596 }\r
597 \r
598 \r
599 #pragma GCC pop_options\r