1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - library - dsp
3   File:     dsp_if.c
4 
5   Copyright 2007-2009 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2010-01-12#$
14   $Rev: 11277 $
15   $Author: kakemizu_hironori $
16  *---------------------------------------------------------------------------*/
17 #include <twl.h>
18 #include <twl/dsp.h>
19 
20 #include <dsp_coff.h>
21 #include "dsp_process.h"
22 
23 /*---------------------------------------------------------------------------*
24     Constant definitions
25  *---------------------------------------------------------------------------*/
26 
27 #define reg_CFG_DSP_RST             *(vu8*)REG_RST_ADDR
28 
29 #define REG_DSP_PCFG_RRIE_MASK      (REG_DSP_PCFG_PRIE0_MASK | REG_DSP_PCFG_PRIE1_MASK | REG_DSP_PCFG_PRIE2_MASK)
30 #define REG_DSP_PCFG_RRIE_SHIFT     REG_DSP_PCFG_PRIE0_SHIFT
31 #define REG_DSP_PSTS_RCOMIM_SHIFT   REG_DSP_PSTS_RCOMIM0_SHIFT
32 #define REG_DSP_PSTS_RRI_SHIFT      REG_DSP_PSTS_RRI0_SHIFT
33 
34 
35 #define DSP_DPRINTF(...)    ((void) 0)
36 //#define DSP_DPRINTF OS_TPrintf
37 
38 /*---------------------------------------------------------------------------*
39     Type Definitions
40  *---------------------------------------------------------------------------*/
41 typedef struct DSPData
42 {
43     u16 send;
44     u16 reserve1;
45     u16 recv;
46     u16 reserve2;
47 }
48 DSPData;
49 
50 
51 /*---------------------------------------------------------------------------*
52     Static Variable Definitions
53  *---------------------------------------------------------------------------*/
54 static volatile DSPData *const dspData = (DSPData*)REG_COM0_ADDR;
55 
56 /*---------------------------------------------------------------------------*
57     Internal Function Definitions
58  *---------------------------------------------------------------------------*/
59 
60 /*---------------------------------------------------------------------------*
61   Name:         DSP_PowerOn
62 
63   Description:  Turns on the DSP and configures it to be in the reset state.
64                 You must call DSP_ResetOff() to start the DSP.
65 
66   Arguments:    None.
67 
68   Returns:      None.
69  *---------------------------------------------------------------------------*/
DSP_PowerOnCore(void)70 void DSP_PowerOnCore(void)  // DSP_Init
71 {
72     SCFG_ResetDSP();                                // Confirms reset of DSP block
73     SCFG_SupplyClockToDSP(TRUE);                    // Power on for DSP block
74     OS_SpinWaitSysCycles(2);                        // Wait 8 cycles @ 134 MHz
75     SCFG_ReleaseResetDSP();                         // Release reset of DSP block
76     DSP_ResetOnCore();                                  // Set reset of DSP core
77 }
78 
79 /*---------------------------------------------------------------------------*
80   Name:         DSP_PowerOff
81 
82   Description:  Turns off the DSP.
83 
84   Arguments:    None.
85 
86   Returns:      None.
87  *---------------------------------------------------------------------------*/
DSP_PowerOffCore(void)88 void DSP_PowerOffCore(void) // DSP_End
89 {
90     SCFG_ResetDSP();                                // Sets reset of DSP block
91     SCFG_SupplyClockToDSP(FALSE);                   // Power off for DSP block
92 }
93 
94 /*---------------------------------------------------------------------------*
95   Name:         DSP_ResetOn
96 
97   Description:  Resets the DSP.
98 
99   Arguments:    None.
100 
101   Returns:      None.
102  *---------------------------------------------------------------------------*/
DSP_ResetOnCore(void)103 void DSP_ResetOnCore(void)
104 {
105     OS_SpinWaitSysCycles(4);
106     if ((reg_DSP_PCFG & REG_DSP_PCFG_DSPR_MASK) == 0)
107     {
108         reg_DSP_PCFG |= REG_DSP_PCFG_DSPR_MASK;
109         OS_SpinWaitSysCycles(4);
110         while ( reg_DSP_PSTS & REG_DSP_PSTS_PRST_MASK )
111         {
112         }
113     }
114 }
115 
116 /*---------------------------------------------------------------------------*
117   Name:         DSP_ResetOff
118 
119   Description:  Starts the DSP from the reset state.
120 
121   Arguments:    None.
122 
123   Returns:      None.
124  *---------------------------------------------------------------------------*/
DSP_ResetOffCore(void)125 void DSP_ResetOffCore(void)
126 {
127     OS_SpinWaitSysCycles(4);
128     while ( reg_DSP_PSTS & REG_DSP_PSTS_PRST_MASK )
129     {
130     }
131     DSP_ResetInterfaceCore();   // Initialize DSP-A9IF
132     OS_SpinWaitSysCycles(4);
133     reg_DSP_PCFG &= ~REG_DSP_PCFG_DSPR_MASK;
134 }
135 
136 /*---------------------------------------------------------------------------*
137   Name:         DSP_ResetOffEx
138 
139   Description:  Starts the DSP from the reset state.
140 
141   Arguments:    bitmap: Bitmap of the reply registers and semaphores that allow interrupts from the DSP to the ARM9
142 
143   Returns:      None.
144  *---------------------------------------------------------------------------*/
DSP_ResetOffExCore(u16 bitmap)145 void DSP_ResetOffExCore(u16 bitmap)
146 {
147     SDK_ASSERT(bitmap >= 0 && bitmap <= 7);
148 
149     OS_SpinWaitSysCycles(4);
150     while ( reg_DSP_PSTS & REG_DSP_PSTS_PRST_MASK )
151     {
152     }
153     DSP_ResetInterfaceCore();   // Initialize DSP-A9IF
154     OS_SpinWaitSysCycles(4);
155     reg_DSP_PCFG |= (bitmap) << REG_DSP_PCFG_RRIE_SHIFT;
156     OS_SpinWaitSysCycles(4);
157     reg_DSP_PCFG &= ~REG_DSP_PCFG_DSPR_MASK;
158 }
159 
160 /*---------------------------------------------------------------------------*
161   Name:         DSP_ResetInterface
162 
163   Description:  Initializes the registers.
164                 You must call this function when the DSP is reset.
165 
166   Arguments:    None.
167 
168   Returns:      None.
169  *---------------------------------------------------------------------------*/
DSP_ResetInterfaceCore(void)170 void DSP_ResetInterfaceCore(void)
171 {
172     OS_SpinWaitSysCycles(4);
173     if (reg_DSP_PCFG & REG_DSP_PCFG_DSPR_MASK)
174     {
175         u16 dummy;
176         reg_DSP_PCFG &= ~REG_DSP_PCFG_RRIE_MASK;
177         reg_DSP_PSEM = 0;
178         reg_DSP_PCLEAR = 0xFFFF;
179         dummy = dspData[0].recv;
180         dummy = dspData[1].recv;
181         dummy = dspData[2].recv;
182     }
183 }
184 
185 /*---------------------------------------------------------------------------*
186   Name:         DSP_EnableRecvDataInterrupt
187 
188   Description:  Enables interrupts for the specified reply register.
189 
190   Arguments:    dataNo: Reply register number (0-2)
191 
192   Returns:      None.
193  *---------------------------------------------------------------------------*/
DSP_EnableRecvDataInterruptCore(u32 dataNo)194 void DSP_EnableRecvDataInterruptCore(u32 dataNo)
195 {
196     SDK_ASSERT(dataNo >= 0 && dataNo <= 2);
197     OS_SpinWaitSysCycles(4);
198     reg_DSP_PCFG |= (1 << dataNo) << REG_DSP_PCFG_RRIE_SHIFT;
199 }
200 
201 /*---------------------------------------------------------------------------*
202   Name:         DSP_DisableRecvDataInterrupt
203 
204   Description:  Disables interrupts for the specified reply register.
205 
206   Arguments:    dataNo: Reply register number (0-2)
207 
208   Returns:      None.
209  *---------------------------------------------------------------------------*/
DSP_DisableRecvDataInterruptCore(u32 dataNo)210 void DSP_DisableRecvDataInterruptCore(u32 dataNo)
211 {
212     SDK_ASSERT(dataNo >= 0 && dataNo <= 2);
213     OS_SpinWaitSysCycles(4);
214     reg_DSP_PCFG &= ~((1 << dataNo) << REG_DSP_PCFG_RRIE_SHIFT);
215 }
216 
217 /*---------------------------------------------------------------------------*
218   Name:         DSP_SendDataIsEmpty
219 
220   Description:  Checks whether the DSP has received data for the specified command register.
221 
222   Arguments:    dataNo: Command register number (0-2)
223 
224   Returns:      TRUE if the DSP has already received data.
225  *---------------------------------------------------------------------------*/
DSP_SendDataIsEmptyCore(u32 dataNo)226 BOOL DSP_SendDataIsEmptyCore(u32 dataNo)
227 {
228     SDK_ASSERT(dataNo >= 0 && dataNo <= 2);
229     OS_SpinWaitSysCycles(4);
230     return (reg_DSP_PSTS & ((1 << dataNo) << REG_DSP_PSTS_RCOMIM_SHIFT)) ? FALSE : TRUE;
231 }
232 
233 /*---------------------------------------------------------------------------*
234   Name:         DSP_RecvDataIsReady
235 
236   Description:  Checks whether data was sent from the DSP to the specified reply register.
237 
238   Arguments:    dataNo: Reply register number (0-2)
239 
240   Returns:      TRUE if the DSP has sent data.
241  *---------------------------------------------------------------------------*/
DSP_RecvDataIsReadyCore(u32 dataNo)242 BOOL DSP_RecvDataIsReadyCore(u32 dataNo)
243 {
244     SDK_ASSERT(dataNo >= 0 && dataNo <= 2);
245     OS_SpinWaitSysCycles(4);
246     return (reg_DSP_PSTS & ((1 << dataNo) << REG_DSP_PSTS_RRI_SHIFT)) ? TRUE : FALSE;
247 }
248 
249 /*---------------------------------------------------------------------------*
250   Name:         DSP_SendData
251 
252   Description:  Sends data to the DSP.
253 
254   Arguments:    dataNo: Command register number (0-2)
255                 data: Data to send
256 
257   Returns:      None.
258  *---------------------------------------------------------------------------*/
DSP_SendDataCore(u32 dataNo,u16 data)259 void DSP_SendDataCore(u32 dataNo, u16 data)
260 {
261     SDK_ASSERT(dataNo >= 0 && dataNo <= 2);
262     OS_SpinWaitSysCycles(4);
263     while (reg_DSP_PSTS & ((1 << dataNo) << REG_DSP_PSTS_RCOMIM_SHIFT))
264     {
265     }
266     dspData[dataNo].send = data;
267 }
268 
269 /*---------------------------------------------------------------------------*
270   Name:         DSP_RecvData
271 
272   Description:  Receives data from the DSP.
273 
274   Arguments:    dataNo: Reply register number (0-2)
275 
276   Returns:      The received data.
277  *---------------------------------------------------------------------------*/
DSP_RecvDataCore(u32 dataNo)278 u16 DSP_RecvDataCore(u32 dataNo)
279 {
280     SDK_ASSERT(dataNo >= 0 && dataNo <= 2);
281     OS_SpinWaitSysCycles(4);
282     while (!(reg_DSP_PSTS & ((1 << dataNo) << REG_DSP_PSTS_RRI_SHIFT)))
283     {
284     }
285     return dspData[dataNo].recv;
286 }
287 
288 /*---------------------------------------------------------------------------*
289   Name:         DSP_EnableFifoInterrupt
290 
291   Description:  Enables FIFO interrupts.
292 
293   Arguments:    type: Cause of the FIFO interrupt
294 
295   Returns:      None.
296  *---------------------------------------------------------------------------*/
DSP_EnableFifoInterruptCore(DSPFifoIntr type)297 void DSP_EnableFifoInterruptCore(DSPFifoIntr type)
298 {
299     OS_SpinWaitSysCycles(4);
300     reg_DSP_PCFG |= type;
301 }
302 
303 /*---------------------------------------------------------------------------*
304   Name:         DSP_DisableFifoInterrupt
305 
306   Description:  Disables FIFO interrupts.
307 
308   Arguments:    type: Cause of the FIFO interrupt
309 
310   Returns:      None.
311  *---------------------------------------------------------------------------*/
DSP_DisableFifoInterruptCore(DSPFifoIntr type)312 void DSP_DisableFifoInterruptCore(DSPFifoIntr type)
313 {
314     OS_SpinWaitSysCycles(4);
315     reg_DSP_PCFG &= ~type;
316 }
317 
318 /*---------------------------------------------------------------------------*
319   Name:         DSP_SendFifoEx
320 
321   Description:  Writes data in the DSP's memory space.
322 
323   Arguments:    memsel: Memory space in which to write data
324                 dest: Address to write to (half-word).
325                          If the most-significant 16 bits are set, values must be set in the DMA register in advance.
326 
327                 src: Data to write
328                 size: Length of the data to write (half-word)
329                 flags: You can select a write mode other than DSP_FIFO_FLAG_RECV_UNIT_* from DSPFifoFlag
330 
331 
332   Returns:      None.
333  *---------------------------------------------------------------------------*/
DSP_SendFifoExCore(DSPFifoMemSel memsel,u16 dest,const u16 * src,int size,u16 flags)334 void DSP_SendFifoExCore(DSPFifoMemSel memsel, u16 dest, const u16 *src, int size, u16 flags)
335 {
336     OSIntrMode  bak = OS_DisableInterrupts();
337     u16 incmode = (u16)((flags & DSP_FIFO_FLAG_DEST_FIX) ? 0 : REG_DSP_PCFG_AIM_MASK);
338 
339     OS_SpinWaitSysCycles(4);
340     reg_DSP_PCFG = (u16)((reg_DSP_PCFG & ~(REG_DSP_PCFG_MEMSEL_MASK|REG_DSP_PCFG_AIM_MASK))
341                         | memsel | incmode);
342     reg_DSP_PADR = dest;
343 
344     if (flags & DSP_FIFO_FLAG_SRC_FIX)
345     {
346         while (size-- > 0)
347         {
348             OS_SpinWaitSysCycles(4);
349             while (reg_DSP_PSTS & REG_DSP_PSTS_WFFI_MASK)
350             {
351             }
352             reg_DSP_PDATA = *src;
353         }
354     }
355     else
356     {
357         while (size-- > 0)
358         {
359             OS_SpinWaitSysCycles(4);
360             while (reg_DSP_PSTS & REG_DSP_PSTS_WFFI_MASK)
361             {
362             }
363             reg_DSP_PDATA = *src++;
364         }
365     }
366     (void)OS_RestoreInterrupts(bak);
367 }
368 
369 /*---------------------------------------------------------------------------*
370   Name:         DSP_RecvFifoEx
371 
372   Description:  Reads data from the DSP's memory space.
373 
374   Arguments:    memsel: Memory space to read data from (other than program memory)
375                 dest: Address at which to receive data
376                 src: Address from which the data was received (half-word)
377                          If the most-significant 16 bits are set, values must be set in the DMA register in advance.
378 
379                 size: Data size to read (half-word)
380                 flags: Select the read mode from DSPFifoFlag
381 
382   Returns:      None.
383  *---------------------------------------------------------------------------*/
DSP_RecvFifoExCore(DSPFifoMemSel memsel,u16 * dest,u16 src,int size,u16 flags)384 void DSP_RecvFifoExCore(DSPFifoMemSel memsel, u16* dest, u16 src, int size, u16 flags)
385 {
386     OSIntrMode  bak = OS_DisableInterrupts();
387     DSPFifoRecvLength len;
388     u16 incmode = (u16)((flags & DSP_FIFO_FLAG_SRC_FIX) ? 0 : REG_DSP_PCFG_AIM_MASK);
389 
390     SDK_ASSERT(memsel != DSP_FIFO_MEMSEL_PROGRAM);
391 
392     switch (flags & DSP_FIFO_FLAG_RECV_MASK)
393     {
394     case DSP_FIFO_FLAG_RECV_UNIT_2B:
395         len = DSP_FIFO_RECV_2B;
396         size = 1;
397         break;
398     case DSP_FIFO_FLAG_RECV_UNIT_16B:
399         len = DSP_FIFO_RECV_16B;
400         size = 8;
401         break;
402     case DSP_FIFO_FLAG_RECV_UNIT_32B:
403         len = DSP_FIFO_RECV_32B;
404         size = 16;
405         break;
406     default:
407         len = DSP_FIFO_RECV_CONTINUOUS;
408         break;
409     }
410 
411     reg_DSP_PADR = src;
412     OS_SpinWaitSysCycles(4);
413     reg_DSP_PCFG = (u16)((reg_DSP_PCFG & ~(REG_DSP_PCFG_MEMSEL_MASK|REG_DSP_PCFG_DRS_MASK|REG_DSP_PCFG_AIM_MASK))
414                         | memsel | len | incmode | REG_DSP_PCFG_RS_MASK);
415 
416     if (flags & DSP_FIFO_FLAG_DEST_FIX)
417     {
418         while (size-- > 0)
419         {
420             OS_SpinWaitSysCycles(4);
421             while ((reg_DSP_PSTS & REG_DSP_PSTS_RFNEI_MASK) == 0)
422             {
423             }
424             *dest = reg_DSP_PDATA;
425         }
426     }
427     else
428     {
429         while (size-- > 0)
430         {
431             OS_SpinWaitSysCycles(4);
432             while ((reg_DSP_PSTS & REG_DSP_PSTS_RFNEI_MASK) == 0)
433             {
434             }
435             *dest++ = reg_DSP_PDATA;
436         }
437     }
438     OS_SpinWaitSysCycles(4);
439     reg_DSP_PCFG &= ~REG_DSP_PCFG_RS_MASK;
440     (void)OS_RestoreInterrupts(bak);
441 }
442 
443 /*---------------------------------------------------------------------------*
444   Name:         DSP_SetCommandReg
445 
446   Description:  Writes a value to a command register.
447 
448   Arguments:    regNo: Command register number (0-2)
449                 data: Data
450 
451   Returns:      None.
452  *---------------------------------------------------------------------------*/
DSP_SetCommandRegCore(u32 regNo,u16 data)453 void DSP_SetCommandRegCore(u32 regNo, u16 data)
454 {
455     SDK_ASSERT(regNo >= 0 && regNo <= 2);
456     OS_SpinWaitSysCycles(4);
457     dspData[regNo].send = data;
458 }
459 
460 /*---------------------------------------------------------------------------*
461   Name:         DSP_GetReplyReg
462 
463   Description:  Reads a value from a reply register.
464 
465   Arguments:    regNo: Reply register number (0-2)
466 
467   Returns:      The data read.
468  *---------------------------------------------------------------------------*/
DSP_GetReplyRegCore(u32 regNo)469 u16 DSP_GetReplyRegCore(u32 regNo)
470 {
471     SDK_ASSERT(regNo >= 0 && regNo <= 2);
472     OS_SpinWaitSysCycles(4);
473     return dspData[regNo].recv;
474 }
475 
476 /*---------------------------------------------------------------------------*
477   Name:         DSP_SetSemaphore
478 
479   Description:  Writes a value to the semaphore register used to send notifications from the ARM to the DSP.
480 
481   Arguments:    mask: Value to set
482 
483   Returns:      None.
484  *---------------------------------------------------------------------------*/
DSP_SetSemaphoreCore(u16 mask)485 void DSP_SetSemaphoreCore(u16 mask)
486 {
487     reg_DSP_PSEM = mask;
488 }
489 
490 /*---------------------------------------------------------------------------*
491   Name:         DSP_GetSemaphore
492 
493   Description:  Reads the value of the semaphore register for sending notifications from the DSP to an ARM processor.
494 
495   Arguments:    None.
496 
497   Returns:      The value written to the semaphore by the DSP.
498  *---------------------------------------------------------------------------*/
DSP_GetSemaphoreCore(void)499 u16 DSP_GetSemaphoreCore(void)
500 {
501     OS_SpinWaitSysCycles(4);
502     return reg_DSP_SEM;
503 }
504 
505 /*---------------------------------------------------------------------------*
506   Name:         DSP_ClearSemaphore
507 
508   Description:  Clears the value of the semaphore register for sending notifications from the DSP to an ARM processor.
509 
510   Arguments:    mask: Bits to clear
511 
512   Returns:      None.
513  *---------------------------------------------------------------------------*/
DSP_ClearSemaphoreCore(u16 mask)514 void DSP_ClearSemaphoreCore(u16 mask)
515 {
516     reg_DSP_PCLEAR = mask;
517 }
518 
519 /*---------------------------------------------------------------------------*
520   Name:         DSP_MaskSemaphore
521 
522   Description:  Sets bits that correspond to interrupts to disable in the semaphore register for sending notifications from the DSP to an ARM processor.
523 
524   Arguments:    mask: Bits corresponding to interrupts to disable
525 
526   Returns:      None.
527  *---------------------------------------------------------------------------*/
DSP_MaskSemaphoreCore(u16 mask)528 void DSP_MaskSemaphoreCore(u16 mask)
529 {
530     reg_DSP_PMASK = mask;
531 }
532 
533 /*---------------------------------------------------------------------------*
534   Name:         DSP_CheckSemaphoreRequest
535 
536   Description:  Checks whether there is an interrupt request from the semaphore register.
537 
538   Arguments:    None.
539 
540   Returns:      TRUE if there is a request.
541  *---------------------------------------------------------------------------*/
DSP_CheckSemaphoreRequestCore(void)542 BOOL DSP_CheckSemaphoreRequestCore(void)
543 {
544     return (reg_DSP_PSTS & REG_DSP_PSTS_PSEMI_MASK) >> REG_DSP_PSTS_PSEMI_SHIFT;
545 }
546 
547 
548 #if defined(DSP_SUPPORT_OBSOLETE_LOADER)
549 /*---------------------------------------------------------------------------*
550  * The following shows candidate interfaces for termination because they are considered not to be currently in use
551  *---------------------------------------------------------------------------*/
552 
553 /*---------------------------------------------------------------------------*
554  * Below is the old interface using a straight-mapping method
555  *---------------------------------------------------------------------------*/
556 
557 /*---------------------------------------------------------------------------*
558   Name:         DSPi_MapProcessSlotAsStraight
559 
560   Description:  Initializes the slot map so the segment number and the WRAM slot number match.
561                 (This method is used only with the initial load function that does not have Ex applied.)
562 
563   Arguments:    context: DSPProcessContext structure
564                 slotB: WRAM-B allowed for use for code memory
565                 slotC: WRAM-C allowed for use for data memory
566 
567   Returns:      None.
568  *---------------------------------------------------------------------------*/
DSPi_MapProcessSlotAsStraight(DSPProcessContext * context,int slotB,int slotC)569 static BOOL DSPi_MapProcessSlotAsStraight(DSPProcessContext *context, int slotB, int slotC)
570 {
571     int     segment;
572     for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment)
573     {
574         if (context->segmentCode & (1 << segment) != 0)
575         {
576             int     slot = segment;
577             if ((slotB & (1 << slot)) == 0)
578             {
579                 return FALSE;
580             }
581             context->slotOfSegmentCode[segment] = slot;
582         }
583     }
584     for (segment = 0; segment < MI_WRAM_C_MAX_NUM; ++segment)
585     {
586         if (context->segmentData & (1 << segment) != 0)
587         {
588             int     slot = segment;
589             if ((slotC & (1 << slot)) == 0)
590             {
591                 return FALSE;
592             }
593             context->slotOfSegmentData[segment] = slot;
594         }
595     }
596     return TRUE;
597 }
598 
599 /*---------------------------------------------------------------------------*
600   Name:         DSP_LoadFileAuto
601 
602   Description:  Loads a COFF format DSP program and assigns the necessary WRAM to DSP.
603 
604   Arguments:    image: COFF file
605 
606   Returns:      TRUE on success.
607  *---------------------------------------------------------------------------*/
DSP_LoadFileAutoCore(const void * image)608 BOOL DSP_LoadFileAutoCore(const void *image)
609 {
610     // Temporarily convert to a memory file
611     FSFile  memfile[1];
612     if(DSPi_CreateMemoryFile(memfile, image))
613     {
614         DSPProcessContext   context[1];
615         DSP_InitProcessContext(context, NULL);
616         return DSP_StartupProcess(context, memfile, 0xFF, 0xFF, DSPi_MapProcessSlotAsStraight);
617     }
618     return FALSE;
619 }
620 
621 
622 #endif
623 
624 /*---------------------------------------------------------------------------*
625   End of file
626  *---------------------------------------------------------------------------*/
627