Lots of bug fixes.
[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 #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 to meet the 250us selection abort time.\r
79         \r
80         // selFlag is required for Philips P2000C which releases it after 600ns\r
81         // without waiting for BSY.\r
82         scsiDev.selFlag = 1;\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) &&\r
392                 (SCSI_ReadPin(SCSI_In_REQ) || SCSI_ReadFilt(SCSI_Filt_ACK))\r
393                 ) {}\r
394 \r
395         int newPhase = phase > 0 ? phase : 0;\r
396         if (newPhase != SCSI_CTL_PHASE_Read())\r
397         {\r
398                 SCSI_CTL_PHASE_Write(phase > 0 ? phase : 0);\r
399                 busSettleDelay();\r
400 \r
401                 if (scsiDev.compatMode < COMPAT_SCSI2)\r
402                 {\r
403                         CyDelayUs(100);\r
404                 }\r
405         }\r
406 }\r
407 \r
408 void scsiPhyReset()\r
409 {\r
410         trace(trace_scsiPhyReset);\r
411         if (dmaInProgress)\r
412         {\r
413                 dmaInProgress = 0;\r
414                 dmaBuffer = NULL;\r
415                 dmaSentCount = 0;\r
416                 dmaTotalCount = 0;\r
417                 CyDmaChSetRequest(scsiDmaTxChan, CY_DMA_CPU_TERM_CHAIN);\r
418                 CyDmaChSetRequest(scsiDmaRxChan, CY_DMA_CPU_TERM_CHAIN);\r
419 \r
420                 // CyDmaChGetRequest returns 0 for the relevant bit once the\r
421                 // request is completed.\r
422                 trace(trace_spinDMAReset);\r
423                 while (\r
424                         (CyDmaChGetRequest(scsiDmaTxChan) & CY_DMA_CPU_TERM_CHAIN) &&\r
425                         !scsiTxDMAComplete\r
426                         )\r
427                 {}\r
428 \r
429                 while ((\r
430                         CyDmaChGetRequest(scsiDmaRxChan) & CY_DMA_CPU_TERM_CHAIN) &&\r
431                         !scsiRxDMAComplete\r
432                         )\r
433                 {}\r
434 \r
435                 CyDelayUs(1);\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                                 1, // 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                                 1, // 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         // Disable the glitch filter for ACK to improve performance.\r
509         if (scsiDev.boardCfg.flags & CONFIG_DISABLE_GLITCH)\r
510         {\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 // 1 = DBx error\r
517 // 2 = Parity error\r
518 // 4 = MSG error\r
519 // 8 = CD error\r
520 // 16 = IO error\r
521 // 32 = other error\r
522 int scsiSelfTest()\r
523 {\r
524         int result = 0;\r
525 \r
526         // TEST DBx and DBp\r
527         int i;\r
528         SCSI_Out_Ctl_Write(1); // Write bits manually.\r
529         SCSI_CTL_PHASE_Write(__scsiphase_io); // Needed for parity generation\r
530         for (i = 0; i < 256; ++i)\r
531         {\r
532                 SCSI_Out_Bits_Write(i);\r
533                 scsiDeskewDelay();\r
534                 if (scsiReadDBxPins() != (i & 0xff))\r
535                 {\r
536                         result |= 1;\r
537                 }\r
538                 if (Lookup_OddParity[i & 0xff] != SCSI_ReadPin(SCSI_In_DBP))\r
539                 {\r
540                         result |= 2;\r
541                 }\r
542         }\r
543         SCSI_Out_Ctl_Write(0); // Write bits normally.\r
544 \r
545         // TEST MSG, CD, IO\r
546         for (i = 0; i < 8; ++i)\r
547         {\r
548                 SCSI_CTL_PHASE_Write(i);\r
549                 scsiDeskewDelay();\r
550 \r
551                 if (SCSI_ReadPin(SCSI_In_MSG) != !!(i & __scsiphase_msg))\r
552                 {\r
553                         result |= 4;\r
554                 }\r
555                 if (SCSI_ReadPin(SCSI_In_CD) != !!(i & __scsiphase_cd))\r
556                 {\r
557                         result |= 8;\r
558                 }\r
559                 if (SCSI_ReadPin(SCSI_In_IO) != !!(i & __scsiphase_io))\r
560                 {\r
561                         result |= 16;\r
562                 }\r
563         }\r
564         SCSI_CTL_PHASE_Write(0);\r
565 \r
566         uint32_t signalsOut[] = { SCSI_Out_ATN, SCSI_Out_BSY, SCSI_Out_RST, SCSI_Out_SEL };\r
567         uint32_t signalsIn[] = { SCSI_Filt_ATN, SCSI_Filt_BSY, SCSI_Filt_RST, SCSI_Filt_SEL };\r
568 \r
569         for (i = 0; i < 4; ++i)\r
570         {\r
571                 SCSI_SetPin(signalsOut[i]);\r
572                 scsiDeskewDelay();\r
573 \r
574                 int j;\r
575                 for (j = 0; j < 4; ++j)\r
576                 {\r
577                         if (i == j)\r
578                         {\r
579                                 if (! SCSI_ReadFilt(signalsIn[j]))\r
580                                 {\r
581                                         result |= 32;\r
582                                 }\r
583                         }\r
584                         else\r
585                         {\r
586                                 if (SCSI_ReadFilt(signalsIn[j]))\r
587                                 {\r
588                                         result |= 32;\r
589                                 }\r
590                         }\r
591                 }\r
592                 SCSI_ClearPin(signalsOut[i]);\r
593         }\r
594         return result;\r
595 }\r
596 \r
597 \r
598 #pragma GCC pop_options\r