1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - library - dsp
3   File:     dsp_process.c
4 
5   Copyright 2008 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:: 2008-11-21#$
14   $Rev: 9387 $
15   $Author: kitase_hirotake $
16  *---------------------------------------------------------------------------*/
17 
18 
19 #include <twl.h>
20 #include <twl/dsp.h>
21 #include <twl/dsp/common/pipe.h>
22 
23 #include "dsp_coff.h"
24 
25 
26 /*---------------------------------------------------------------------------*/
27 /* Constants */
28 
29 #define DSP_DPRINTF(...)    ((void) 0)
30 //#define DSP_DPRINTF OS_TPrintf
31 
32 // Section enumeration callback in the process image
33 typedef BOOL (*DSPSectionEnumCallback)(DSPProcessContext *, const COFFFileHeader *, const COFFSectionTable *);
34 
35 
36 /*---------------------------------------------------------------------------*/
37 /* Variables */
38 
39 // Process context currently running
40 static DSPProcessContext   *DSPiCurrentComponent = NULL;
41 static PMExitCallbackInfo   DSPiShutdownCallbackInfo[1];
42 static BOOL                 DSPiShutdownCallbackIsRegistered = FALSE;
43 static PMSleepCallbackInfo  DSPiPreSleepCallbackInfo[1];
44 static BOOL                 DSPiPreSleepCallbackIsRegistered = FALSE;
45 
46 
47 /*---------------------------------------------------------------------------*/
48 /* Functions */
49 
50 /*---------------------------------------------------------------------------*
51   Name:         DSP_EnumSections
52 
53   Description:  Runs the predetermined processes by enumerating sections in the process image
54 
55   Arguments:    context: DSPProcessContext structure passed to the callback function
56                 callback: Callback called for each section
57 
58   Returns:      TRUE if all sections are enumerated; FALSE if ended prematurely.
59  *---------------------------------------------------------------------------*/
60 BOOL DSP_EnumSections(DSPProcessContext *context, DSPSectionEnumCallback callback);
61 
62 /*---------------------------------------------------------------------------*
63   Name:         DSP_StopDSPComponent
64 
65   Description:  Prepares to end DSP.
66                 Currently, only when stopping the DMA of DSP
67 
68   Arguments:    None.
69 
70   Returns:      None.
71  *---------------------------------------------------------------------------*/
DSP_StopDSPComponent(void)72 void DSP_StopDSPComponent(void)
73 {
74     DSPProcessContext  *context = DSPiCurrentComponent;
75     context->hookFactors = 0;
76     DSP_SendData(DSP_PIPE_COMMAND_REGISTER, DSP_PIPE_FLAG_EXIT_OS);
77     (void)DSP_RecvData(DSP_PIPE_COMMAND_REGISTER);
78 }
79 
80 /*---------------------------------------------------------------------------*
81   Name:         DSPi_ShutdownCallback
82 
83   Description:  Forced system shutdown termination callback.
84 
85   Arguments:    name: Process name
86                        Search for processes registered last if NULL is specified
87 
88   Returns:      DSPProcessContext structure that matches the specified name
89  *---------------------------------------------------------------------------*/
DSPi_ShutdownCallback(void * arg)90 static void DSPi_ShutdownCallback(void *arg)
91 {
92     (void)arg;
93     for (;;)
94     {
95         DSPProcessContext  *context = DSP_FindProcess(NULL);
96         if (!context)
97         {
98             break;
99         }
100 //        OS_TWarning("force-exit %s component.\n", context->name);
101         DSP_QuitProcess(context);
102     }
103 }
104 
105 /*---------------------------------------------------------------------------*
106   Name:         DSPi_PreSleepCallback
107 
108   Description:  Sleep warning callback.
109 
110   Arguments:    None.
111 
112   Returns:      None.
113  *---------------------------------------------------------------------------*/
DSPi_PreSleepCallback(void * arg)114 static void DSPi_PreSleepCallback(void *arg)
115 {
116 #pragma unused( arg )
117     OS_Panic("Could not sleep while DSP is processing.\n");
118 }
119 
120 /*---------------------------------------------------------------------------*
121   Name:         DSPi_MasterInterrupts
122 
123   Description:  DSP interrupt process.
124 
125   Arguments:    None.
126 
127   Returns:      None.
128  *---------------------------------------------------------------------------*/
DSPi_MasterInterrupts(void)129 static void DSPi_MasterInterrupts(void)
130 {
131     DSPProcessContext  *context = DSPiCurrentComponent;
132     if (context)
133     {
134         // An interrupt cause flag (IF) is only generated to the OS for the instance that either of the DSP interrupt causes first occurs. If there is the possibility that new DSP interrupt causes can occur while multiple DSP interrupt causes are being sequentially processed, it is necessary to repeat at that time without relying on the interrupt handler.
135         //
136         //
137         //
138         //
139         for (;;)
140         {
141             // Batch determine messages that can be causes of DSP interrupts
142             int     ready = (reg_DSP_SEM | (((reg_DSP_PSTS >> REG_DSP_PSTS_RRI0_SHIFT) & 7) << 16));
143             int     factors = (ready & context->hookFactors);
144             if (factors == 0)
145             {
146                 break;
147             }
148             else
149             {
150                 // Process corresponding hook process in order
151                 // If group-registered, notify together one time only
152                 while (factors != 0)
153                 {
154                     int     index = (int)MATH_CTZ((u32)factors);
155                     factors &= ~context->hookGroup[index];
156                     (*context->hookFunction[index])(context->hookUserdata[index]);
157                 }
158             }
159         }
160     }
161     OS_SetIrqCheckFlag(OS_IE_DSP);
162 }
163 
164 /*---------------------------------------------------------------------------*
165   Name:         DSPi_ReadProcessImage
166 
167   Description:  Read data from the process image.
168 
169   Arguments:    context: DSPProcessContext structure
170                 offset: Offset in image
171                 buffer: The buffer to transfer to
172                 length: Transfer size
173 
174   Returns:      TRUE, if specified size is correctly read
175  *---------------------------------------------------------------------------*/
DSPi_ReadProcessImage(DSPProcessContext * context,int offset,void * buffer,int length)176 static BOOL DSPi_ReadProcessImage(DSPProcessContext *context, int offset, void *buffer, int length)
177 {
178     return FS_SeekFile(context->image, offset, FS_SEEK_SET) &&
179            (FS_ReadFile(context->image, buffer, length) == length);
180 }
181 
182 /*---------------------------------------------------------------------------*
183   Name:         DSPi_CommitWram
184 
185   Description:  Assign specified segment to specific processor.
186 
187   Arguments:    context: DSPProcessContext structure
188                 wram: Code/Data
189                 segment: Segment number to switch
190                 to: Processor to switch
191 
192   Returns:      TRUE, if all slots are correctly switched
193  *---------------------------------------------------------------------------*/
DSPi_CommitWram(DSPProcessContext * context,MIWramPos wram,int segment,MIWramProc to)194 static BOOL DSPi_CommitWram(DSPProcessContext *context, MIWramPos wram, int segment, MIWramProc to)
195 {
196     BOOL    retval = FALSE;
197     int     slot = DSP_GetProcessSlotFromSegment(context, wram, segment);
198     // Disconnect from currently assigned processor
199     if (!MI_IsWramSlotUsed(wram, slot) ||
200         MI_FreeWramSlot(wram, slot, MI_WRAM_SIZE_32KB, MI_GetWramBankMaster(wram, slot)) > 0)
201     {
202         // Assign to specified processor
203         void   *physicalAddr = (void *)MI_AllocWramSlot(wram, slot, MI_WRAM_SIZE_32KB, to);
204         if (physicalAddr != 0)
205         {
206             // Move to predetermined offset for each processor
207             vu8    *bank = &((vu8*)((wram == MI_WRAM_B) ? REG_MBK_B0_ADDR  : REG_MBK_C0_ADDR))[slot];
208             if (to == MI_WRAM_ARM9)
209             {
210                 *bank = (u8)((*bank & ~MI_WRAM_OFFSET_MASK_B) | (slot << MI_WRAM_OFFSET_SHIFT_B));
211             }
212             else if (to == MI_WRAM_DSP)
213             {
214                 *bank = (u8)((*bank & ~MI_WRAM_OFFSET_MASK_B) | (segment << MI_WRAM_OFFSET_SHIFT_B));
215             }
216             retval = TRUE;
217         }
218     }
219     return retval;
220 }
221 
222 
223 /*---------------------------------------------------------------------------*
224   Name:         DSPi_MapAndLoadProcessImageCallback
225 
226   Description:  Callback to map and load with 1 pass of the process image.
227 
228   Arguments:    context: DSPProcessContext structure
229                 header: COFF file header information
230                 section: Enumerated section
231 
232   Returns:      TRUE, if enumeration is continued; FALSE, if acceptable to end
233  *---------------------------------------------------------------------------*/
DSPi_MapAndLoadProcessImageCallback(DSPProcessContext * context,const COFFFileHeader * header,const COFFSectionTable * section)234 static BOOL DSPi_MapAndLoadProcessImageCallback(DSPProcessContext *context,
235                                                 const COFFFileHeader *header,
236                                                 const COFFSectionTable *section)
237 {
238     BOOL        retval = TRUE;
239 
240     // Because there is possibility that single sections can be located at a maximum of four locations for code and data.
241     //
242     enum
243     {
244         placement_kind_max = 2, // { CODE, DATA }
245         placement_code_page_max = 2,
246         placement_data_page_max = 2,
247         placement_max = placement_code_page_max + placement_data_page_max
248     };
249     MIWramPos   wrams[placement_max];
250     int         addrs[placement_max];
251     BOOL        nolds[placement_max];
252     int         placement = 0;
253 
254     // Determine the page number to locate using the suffix of the section name
255     // If the name field directly represents the string, it is located in only one position, but if it is a long name that must be referenced from the string table , it is possible to be located in multiple places.
256     //
257     const char *name = (const char *)section->Name;
258     char        longname[128];
259     if (*(u32*)name == 0)
260     {
261         u32     stringtable = header->PointerToSymbolTable + 0x12 * header->NumberOfSymbols;
262         if(!DSPi_ReadProcessImage(context,
263                               (int)(stringtable + *(u32*)&section->Name[4]),
264                               longname, sizeof(longname)))
265         {
266             retval = FALSE;
267             return retval;
268         }
269         name = longname;
270     }
271 
272     // Determine existence of section here that is a special landmark
273     if (STD_CompareString(name, "SDK_USING_OS@d0") == 0)
274     {
275         context->flags |= DSP_PROCESS_FLAG_USING_OS;
276     }
277 
278     // CODE section is WRAM-B
279     if ((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_CODE_MEMORY) != 0)
280     {
281         int         baseaddr = (int)(section->s_paddr * 2);
282         BOOL        noload = ((section->s_flags & COFF_SECTION_ATTR_NOLOAD_FOR_CODE_MEMORY) != 0);
283         if (STD_StrStr(name, "@c0") != NULL)
284         {
285             wrams[placement] = MI_WRAM_B;
286             addrs[placement] = baseaddr + DSP_WRAM_SLOT_SIZE * 4 * 0;
287             nolds[placement] = noload;
288             ++placement;
289         }
290         if (STD_StrStr(name, "@c1") != NULL)
291         {
292             wrams[placement] = MI_WRAM_B;
293             addrs[placement] = baseaddr + DSP_WRAM_SLOT_SIZE * 4 * 1;
294             nolds[placement] = noload;
295             ++placement;
296         }
297     }
298 
299     // DATA section is WRAM-C
300     if ((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_DATA_MEMORY) != 0)
301     {
302         int         baseaddr = (int)(section->s_vaddr * 2);
303         BOOL        noload = ((section->s_flags & COFF_SECTION_ATTR_NOLOAD_FOR_DATA_MEMORY) != 0);
304         if (STD_StrStr(name, "@d0") != NULL)
305         {
306             wrams[placement] = MI_WRAM_C;
307             addrs[placement] = baseaddr + DSP_WRAM_SLOT_SIZE * 4 * 0;
308             nolds[placement] = noload;
309             ++placement;
310         }
311         if (STD_StrStr(name, "@d1") != NULL)
312         {
313             wrams[placement] = MI_WRAM_C;
314             addrs[placement] = baseaddr + DSP_WRAM_SLOT_SIZE * 4 * 1;
315             nolds[placement] = noload;
316             ++placement;
317         }
318     }
319 
320     // Location destination information was listed up, so locate in each address
321     {
322         int     i;
323         for (i = 0; i < placement; ++i)
324         {
325             MIWramPos   wram = wrams[i];
326             int     dstofs = addrs[i];
327             int     length = (int)section->s_size;
328             int     srcofs = (int)section->s_scnptr;
329             // Split load so as not to overlap the slot boundary
330             while (length > 0)
331             {
332                 // Clip the size at the slot boundary
333                 int     ceil = MATH_ROUNDUP(dstofs + 1, DSP_WRAM_SLOT_SIZE);
334                 int     curlen = MATH_MIN(length, ceil - dstofs);
335                 BOOL    newmapped = FALSE;
336                 // If the corresponding segment is not yet mapped, map and initialize here
337                 if (DSP_GetProcessSlotFromSegment(context, wram, dstofs / DSP_WRAM_SLOT_SIZE) == -1)
338                 {
339                     int     segment = (dstofs / DSP_WRAM_SLOT_SIZE);
340                     u16    *slots = (wram == MI_WRAM_B) ? &context->slotB : &context->slotC;
341                     int    *segbits = (wram == MI_WRAM_B) ? &context->segmentCode : &context->segmentData;
342                     int    *map = (wram == MI_WRAM_B) ? context->slotOfSegmentCode : context->slotOfSegmentData;
343                     int     slot = (int)MATH_CountTrailingZeros((u32)*slots);
344                     if (slot >= MI_WRAM_B_MAX_NUM)
345                     {
346                         retval = FALSE;
347                         break;
348                     }
349                     else
350                     {
351                         newmapped = TRUE;
352                         map[segment] = slot;
353                         *slots &= ~(1 << slot);
354                         *segbits |= (1 << segment);
355                         if (!DSPi_CommitWram(context, wram, segment, MI_WRAM_ARM9))
356                         {
357                             retval = FALSE;
358                             break;
359                         }
360                     }
361                     MI_CpuFillFast(DSP_ConvertProcessAddressFromDSP(context, wram,
362                                                                     segment * (DSP_WRAM_SLOT_SIZE / 2)),
363                                    0, DSP_WRAM_SLOT_SIZE);
364                 }
365                 // If noload is specified, ignore
366                 if (nolds[i])
367                 {
368                     DSP_DPRINTF("$%04X (noload)\n", dstofs);
369                 }
370                 else
371                 {
372                     // Calculate the corresponding offset in this time's transfer range
373                     u8     *dstbuf = (u8*)DSP_ConvertProcessAddressFromDSP(context, wram, dstofs / 2);
374                     if (!DSPi_ReadProcessImage(context, srcofs, dstbuf, curlen))
375                     {
376                         retval = FALSE;
377                         break;
378                     }
379                     DSP_DPRINTF("$%04X -> mem:%08X\n", dstofs, dstbuf);
380                 }
381                 srcofs += curlen;
382                 dstofs += curlen;
383                 length -= curlen;
384             }
385         }
386     }
387 
388     return retval;
389 }
390 
391 /*---------------------------------------------------------------------------*
392   Name:         DSP_InitProcessContext
393 
394   Description:  Initializes the process information structure.
395 
396   Arguments:    context: DSPProcessContext structure
397                 name: Process name or NULL
398 
399   Returns:      None.
400  *---------------------------------------------------------------------------*/
DSP_InitProcessContext(DSPProcessContext * context,const char * name)401 void DSP_InitProcessContext(DSPProcessContext *context, const char *name)
402 {
403     int     i;
404     int     segment;
405     MI_CpuFill8(context, 0, sizeof(*context));
406     context->next = NULL;
407     context->flags = 0;
408     (void)STD_CopyString(context->name, name ? name : "");
409     context->image = NULL;
410     // Make the segment map empty
411     context->segmentCode = 0;
412     context->segmentData = 0;
413     // Make the slot map empty
414     for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment)
415     {
416         context->slotOfSegmentCode[segment] = -1;
417     }
418     for (segment = 0; segment < MI_WRAM_C_MAX_NUM; ++segment)
419     {
420         context->slotOfSegmentData[segment] = -1;
421     }
422     // Make the hook registration state empty
423     context->hookFactors = 0;
424     for (i = 0; i < DSP_HOOK_MAX; ++i)
425     {
426         context->hookFunction[i] = NULL;
427         context->hookUserdata[i] = NULL;
428         context->hookGroup[i] = 0;
429     }
430 }
431 
432 /*---------------------------------------------------------------------------*
433   Name:         DSP_EnumSections
434 
435   Description:  Runs the predetermined processes by enumerating sections in the process image.
436 
437   Arguments:    context: DSPProcessContext structure passed to the callback function
438                 callback: Callback called for each section
439 
440   Returns:      TRUE if all sections are enumerated; FALSE if ended prematurely.
441  *---------------------------------------------------------------------------*/
DSP_EnumSections(DSPProcessContext * context,DSPSectionEnumCallback callback)442 BOOL DSP_EnumSections(DSPProcessContext *context, DSPSectionEnumCallback callback)
443 {
444     BOOL            retval = FALSE;
445     // Copy once to temporary, considering the memory alignment of the image buffer
446     COFFFileHeader  header[1];
447     if (DSPi_ReadProcessImage(context, 0, header, sizeof(header)))
448     {
449         int     base = (int)(sizeof(header) + header->SizeOfOptionalHeader);
450         int     index;
451         for (index = 0; index < header->NumberOfSections; ++index)
452         {
453             COFFSectionTable    section[1];
454             if (!DSPi_ReadProcessImage(context, (int)(base + index * sizeof(section)), section, (int)sizeof(section)))
455             {
456                 break;
457             }
458             // Sort only sections with a size over 1 and no BLOCK-HEADER attribute
459             if (((section->s_flags & COFF_SECTION_ATTR_BLOCK_HEADER) == 0) && (section->s_size != 0))
460             {
461                 if (callback && !(*callback)(context, header, section))
462                 {
463                     break;
464                 }
465             }
466         }
467         retval = (index >= header->NumberOfSections);
468     }
469     return retval;
470 }
471 
472 /*---------------------------------------------------------------------------*
473   Name:         DSP_AttachProcessMemory
474 
475   Description:  Assigns memory space continuously to unused regions of the process.
476 
477   Arguments:    context: DSPProcessContext structure
478                 wram: Memory space to assign (MI_WRAM_B/MI_WRAM_C)
479                 slots: WRAM slot to assign
480 
481   Returns:      Header address of the assigned DSP memory space, or 0
482  *---------------------------------------------------------------------------*/
DSP_AttachProcessMemory(DSPProcessContext * context,MIWramPos wram,int slots)483 u32 DSP_AttachProcessMemory(DSPProcessContext *context, MIWramPos wram, int slots)
484 {
485     u32     retval = 0;
486     int    *segbits = (wram == MI_WRAM_B) ? &context->segmentCode : &context->segmentData;
487     int    *map = (wram == MI_WRAM_B) ? context->slotOfSegmentCode : context->slotOfSegmentData;
488     // Search for continuous unusued regions only for the necessary amount
489     int     need = (int)MATH_CountPopulation((u32)slots);
490     u32     region = (u32)((1 << need) - 1);
491     u32     space = (u32)(~*segbits & 0xFF);
492     int     segment = 0;
493     for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment)
494     {
495         // There are various restrictions in the DSP page boundary (4 segments) so be careful to allocate without overlap
496         //
497         if ((((segment ^ (segment + need - 1)) & 4) == 0) &&
498             (((space >> segment) & region) == region))
499         {
500             // If there is an adequate unused region existing, assign slots from a lower order
501             retval = (u32)(DSP_ADDR_TO_DSP(DSP_WRAM_SLOT_SIZE) * segment);
502             while (slots)
503             {
504                 int     slot = (int)MATH_CountTrailingZeros((u32)slots);
505                 map[segment] = slot;
506                 slots &= ~(1 << slot);
507                 *segbits |= (1 << segment);
508                 segment += 1;
509             }
510             break;
511         }
512     }
513     return retval;
514 }
515 
516 /*---------------------------------------------------------------------------*
517   Name:         DSP_DetachProcessMemory
518 
519   Description:  Deallocates the WRAM slots assigned to the regions used by the process.
520 
521   Arguments:    context: DSPProcessContext structure
522                 slots: Assigned WRAM slot
523 
524   Returns:      None.
525  *---------------------------------------------------------------------------*/
DSP_DetachProcessMemory(DSPProcessContext * context,MIWramPos wram,int slots)526 void DSP_DetachProcessMemory(DSPProcessContext *context, MIWramPos wram, int slots)
527 {
528     int    *segbits = (wram == MI_WRAM_B) ? &context->segmentCode : &context->segmentData;
529     int    *map = (wram == MI_WRAM_B) ? context->slotOfSegmentCode : context->slotOfSegmentData;
530     int     segment;
531     for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment)
532     {
533         if ((((1 << segment) & *segbits) != 0) && (((1 << map[segment]) & slots) != 0))
534         {
535             *segbits &= ~(1 << segment);
536             map[segment] = -1;
537         }
538     }
539 }
540 
541 /*---------------------------------------------------------------------------*
542   Name:         DSP_SwitchProcessMemory
543 
544   Description:  Switches DSP address management being used by a specified process.
545 
546   Arguments:    context: DSPProcessContext structure
547                 wram: Memory space to switch (MI_WRAM_B/MI_WRAM_C)
548                 address: Header address to switch (Word units of the DSP memory space)
549                 length: Memory size to switch (Word units of the DSP memory space)
550                 to: New master processor
551 
552   Returns:      TRUE, if all slots are correctly switched
553  *---------------------------------------------------------------------------*/
DSP_SwitchProcessMemory(DSPProcessContext * context,MIWramPos wram,u32 address,u32 length,MIWramProc to)554 BOOL DSP_SwitchProcessMemory(DSPProcessContext *context, MIWramPos wram, u32 address, u32 length, MIWramProc to)
555 {
556     address = DSP_ADDR_TO_ARM(address);
557     length = DSP_ADDR_TO_ARM(MATH_MAX(length, 1));
558     {
559         int    *segbits = (wram == MI_WRAM_B) ? &context->segmentCode : &context->segmentData;
560         int     lower = (int)(address / DSP_WRAM_SLOT_SIZE);
561         int     upper = (int)((address + length - 1) / DSP_WRAM_SLOT_SIZE);
562         int     segment;
563         for (segment = lower; segment <= upper; ++segment)
564         {
565             if ((*segbits & (1 << segment)) != 0)
566             {
567                 if (!DSPi_CommitWram(context, wram, segment, to))
568                 {
569                     return FALSE;
570                 }
571             }
572         }
573     }
574     return TRUE;
575 }
576 
577 /*---------------------------------------------------------------------------*
578   Name:         DSP_MapAndLoadProcessImage
579 
580   Description:  Maps and loads with one pass of the specified process image.
581                 Segment map does not need to be calculated
582 
583   Arguments:    context: DSPProcessContext structure
584                 image: File handle that specifies the process image
585                           Referenced only in this function
586                 slotB: WRAM-B allowed for use for code memory
587                 slotC: WRAM-C allowed for use for data memory
588 
589   Returns:      TRUE if all images are correctly loaded
590  *---------------------------------------------------------------------------*/
DSP_MapAndLoadProcessImage(DSPProcessContext * context,FSFile * image,int slotB,int slotC)591 static BOOL DSP_MapAndLoadProcessImage(DSPProcessContext *context, FSFile *image, int slotB, int slotC)
592 {
593     BOOL    retval = FALSE;
594     const u32   dspMemSize = DSP_ADDR_TO_DSP(DSP_WRAM_SLOT_SIZE) * MI_WRAM_B_MAX_NUM;
595     // Map WRAM while actually loading the process image
596     context->image = image;
597     context->slotB = (u16)slotB;
598     context->slotC = (u16)slotC;
599 
600     if (DSP_EnumSections(context, DSPi_MapAndLoadProcessImageCallback))
601     {
602         DC_FlushRange((const void*)MI_GetWramMapStart_B(), MI_WRAM_B_SIZE);
603         DC_FlushRange((const void*)MI_GetWramMapStart_C(), MI_WRAM_C_SIZE);
604         // Switch all assigned WRAM slots to DSP
605         if (DSP_SwitchProcessMemory(context, MI_WRAM_B, 0, dspMemSize, MI_WRAM_DSP) &&
606             DSP_SwitchProcessMemory(context, MI_WRAM_C, 0, dspMemSize, MI_WRAM_DSP))
607         {
608             retval = TRUE;
609         }
610     }
611     return retval;
612 }
613 
614 /*---------------------------------------------------------------------------*
615   Name:         DSP_SetProcessHook
616 
617   Description:  Sets the hook for DSP interrupt cause to the process.
618 
619   Arguments:    context: DSPProcessContext structure
620                 factors: Bit set of the specified interrupt cause
621                            Bits 0 - 15 are semaphore; bits 16 - 18 are reply
622                 function: Hook function to call
623                 userdata: Arbitrary user-defined argument given to the hook function
624 
625   Returns:      None.
626  *---------------------------------------------------------------------------*/
DSP_SetProcessHook(DSPProcessContext * context,int factors,DSPHookFunction function,void * userdata)627 void DSP_SetProcessHook(DSPProcessContext *context, int factors, DSPHookFunction function, void *userdata)
628 {
629     OSIntrMode  bak_cpsr = OS_DisableInterrupts();
630     int     i;
631     for (i = 0; i < DSP_HOOK_MAX; ++i)
632     {
633         int     bit = (1 << i);
634         if ((bit & factors) != 0)
635         {
636             context->hookFunction[i] = function;
637             context->hookUserdata[i] = userdata;
638             context->hookGroup[i] = factors;
639         }
640     }
641     {
642         // Change the interrupt cause
643         int         modrep = (((factors >> 16) & 0x7) << REG_DSP_PCFG_PRIE0_SHIFT);
644         int         modsem = ((factors >> 0) & 0xFFFF);
645         int         currep = reg_DSP_PCFG;
646         int         cursem = reg_DSP_PMASK;
647         if (function != NULL)
648         {
649             reg_DSP_PCFG = (u16)(currep | modrep);
650             reg_DSP_PMASK = (u16)(cursem & ~modsem);
651             context->hookFactors |= factors;
652         }
653         else
654         {
655             reg_DSP_PCFG = (u16)(currep & ~modrep);
656             reg_DSP_PMASK = (u16)(cursem | modsem);
657             context->hookFactors &= ~factors;
658         }
659     }
660     (void)OS_RestoreInterrupts(bak_cpsr);
661 }
662 
663 /*---------------------------------------------------------------------------*
664   Name:         DSP_HookPostStartProcess
665 
666   Description:  Hooks immediately after loading the DSP process image.
667                 It is necessary for the DSP component developer to start the debugger
668 
669   Arguments:    None.
670 
671   Returns:      None.
672  *---------------------------------------------------------------------------*/
DSP_HookPostStartProcess(void)673 SDK_WEAK_SYMBOL void DSP_HookPostStartProcess(void)
674 {
675 }
676 
677 /*---------------------------------------------------------------------------*
678   Name:         DSP_ExecuteProcess
679 
680   Description:  Loads the DSP process image and starts.
681 
682   Arguments:    context: DSPProcessContext structure used in status management
683                           Referenced using the system until the process is destroyed
684                 image: File handle that specifies the process image
685                           Referenced only in this function
686                 slotB: WRAM-B allowed for use for code memory
687                 slotC: WRAM-C allowed for use for data memory
688 
689   Returns:      None.
690  *---------------------------------------------------------------------------*/
DSP_ExecuteProcess(DSPProcessContext * context,FSFile * image,int slotB,int slotC)691 BOOL DSP_ExecuteProcess(DSPProcessContext *context, FSFile *image, int slotB, int slotC)
692 {
693     BOOL    retval = FALSE;
694     if (!FS_IsAvailable())
695     {
696         OS_TWarning("FS is not initialized yet.\n");
697         FS_Init(FS_DMA_NOT_USE);
698     }
699     // Initialize DSP systems common to all components
700     DSP_InitPipe();
701     OS_SetIrqFunction(OS_IE_DSP, DSPi_MasterInterrupts);
702     DSP_SetProcessHook(context,
703                        DSP_HOOK_SEMAPHORE_(15) | DSP_HOOK_REPLY_(2),
704                        (DSPHookFunction)DSP_HookPipeNotification, NULL);
705     (void)OS_EnableIrqMask(OS_IE_DSP);
706     // Map the process image to memory and load
707     if (!DSP_MapAndLoadProcessImage(context, image, slotB, slotC))
708     {
709         OS_TWarning("you should check wram\n");
710     }
711     else
712     {
713         OSIntrMode  bak_cpsr = OS_DisableInterrupts();
714         // Register the context to the process list
715         DSPProcessContext  **pp = &DSPiCurrentComponent;
716         for (pp = &DSPiCurrentComponent; *pp && (*pp != context); pp = &(*pp)->next)
717         {
718         }
719         *pp = context;
720         context->image = NULL;
721         // Set the shutdown forced termination callback
722         if (!DSPiShutdownCallbackIsRegistered)
723         {
724             PM_SetExitCallbackInfo(DSPiShutdownCallbackInfo, DSPi_ShutdownCallback, NULL);
725             PMi_InsertPostExitCallbackEx(DSPiShutdownCallbackInfo, PM_CALLBACK_PRIORITY_DSP);
726             DSPiShutdownCallbackIsRegistered = TRUE;
727         }
728         // Set the sleep warning callback
729         if (!DSPiPreSleepCallbackIsRegistered)
730         {
731             PM_SetSleepCallbackInfo(DSPiPreSleepCallbackInfo, DSPi_PreSleepCallback, NULL);
732             PMi_InsertPreSleepCallbackEx(DSPiPreSleepCallbackInfo, PM_CALLBACK_PRIORITY_DSP);
733             DSPiPreSleepCallbackIsRegistered = TRUE;
734         }
735         // Set only the necessary interrupt cause as early as possible after starting up
736         DSP_PowerOn();
737         DSP_ResetOffEx((u16)(context->hookFactors >> 16));
738         DSP_MaskSemaphore((u16)~(context->hookFactors >> 0));
739         // If the ARM side is paused immediately after startup, it is possible to reload and trace the same component
740         //
741         DSP_HookPostStartProcess();
742         // For components that spontaneously send commands from the DSP side without waiting for command from the ARM9 processor, if reloaded by the DSP debugger, the command value will be initialized to 0 and unread bits will remain on the ARM9 processor.
743         //
744         //
745         //
746         // This 0 is read and discarded in the library
747         if ((context->flags & DSP_PROCESS_FLAG_USING_OS) != 0)
748         {
749             u16     id;
750             for (id = 0; id < 3; ++id)
751             {
752                 vu16    dummy;
753                 while (dummy = DSP_RecvDataCore(id), dummy != 1)
754                 {
755                 }
756             }
757         }
758         // Just in case, check whether there is an interrupt that was not taken in the time from startup until setting
759         DSPi_MasterInterrupts();
760         retval = TRUE;
761         (void)OS_RestoreInterrupts(bak_cpsr);
762     }
763     return retval;
764 }
765 
766 /*---------------------------------------------------------------------------*
767   Name:         DSP_QuitProcess
768 
769   Description:  Deallocates memory by ending the DSP process.
770 
771   Arguments:    context: DSPProcessContext structure used in status management
772 
773   Returns:      None.
774  *---------------------------------------------------------------------------*/
DSP_QuitProcess(DSPProcessContext * context)775 void DSP_QuitProcess(DSPProcessContext *context)
776 {
777     OSIntrMode  bak_cpsr;
778 
779     // First, stop the TEAK DMA
780     DSP_StopDSPComponent();
781 
782     bak_cpsr = OS_DisableInterrupts();
783     // Stop the DSP
784     DSP_ResetOn();
785     DSP_PowerOff();
786     // Invalidate interrupts
787     (void)OS_DisableIrqMask(OS_IE_DSP);
788     OS_SetIrqFunction(OS_IE_DSP, NULL);
789     // Return all WRAM that were being used
790     (void)MI_FreeWram(MI_WRAM_B, MI_WRAM_DSP);
791     (void)MI_FreeWram(MI_WRAM_C, MI_WRAM_DSP);
792     {
793         // Release context from the processor list
794         DSPProcessContext  **pp = &DSPiCurrentComponent;
795         for (pp = &DSPiCurrentComponent; *pp; pp = &(*pp)->next)
796         {
797             if (*pp == context)
798             {
799                 *pp = (*pp)->next;
800                 break;
801             }
802         }
803         context->next = NULL;
804     }
805     (void)context;
806     (void)OS_RestoreInterrupts(bak_cpsr);
807 
808     // Delete the warning callback when sleeping
809     PM_DeletePreSleepCallback(DSPiPreSleepCallbackInfo);
810     DSPiPreSleepCallbackIsRegistered = FALSE;
811 }
812 
813 /*---------------------------------------------------------------------------*
814   Name:         DSP_FindProcess
815 
816   Description:  Search for the process being run.
817 
818   Arguments:    name: Process name
819                        Search for processes registered last if NULL is specified
820 
821   Returns:      DSPProcessContext structure that matches the specified name
822  *---------------------------------------------------------------------------*/
DSP_FindProcess(const char * name)823 DSPProcessContext* DSP_FindProcess(const char *name)
824 {
825     DSPProcessContext  *ptr = NULL;
826     OSIntrMode  bak_cpsr = OS_DisableInterrupts();
827     ptr = DSPiCurrentComponent;
828     if (name)
829     {
830         for (; ptr && (STD_CompareString(ptr->name, name) != 0); ptr = ptr->next)
831         {
832         }
833     }
834     (void)OS_RestoreInterrupts(bak_cpsr);
835     return ptr;
836 }
837 
838 /*---------------------------------------------------------------------------*
839   Name:         DSP_ReadProcessPipe
840 
841   Description:  Receives from pipe associated with predetermined port of the process.
842 
843   Arguments:    context: DSPProcessContext structure
844                 port: Reception source port
845                 buffer: Received data
846                 length: Received size (Byte Units)
847 
848   Returns:      None.
849  *---------------------------------------------------------------------------*/
DSP_ReadProcessPipe(DSPProcessContext * context,int port,void * buffer,u32 length)850 void DSP_ReadProcessPipe(DSPProcessContext *context, int port, void *buffer, u32 length)
851 {
852     DSPPipe input[1];
853     (void)DSP_LoadPipe(input, port, DSP_PIPE_INPUT);
854     DSP_ReadPipe(input, buffer, (DSPByte)length);
855     (void)context;
856 }
857 
858 /*---------------------------------------------------------------------------*
859   Name:         DSP_WriteProcessPipe
860 
861   Description:  Sends to the pipe associated with predetermined port of the process.
862 
863   Arguments:    context: DSPProcessContext structure
864                 port: Reception destination port
865                 buffer: Send data
866                 length: Send size (Byte Units)
867 
868   Returns:      None.
869  *---------------------------------------------------------------------------*/
DSP_WriteProcessPipe(DSPProcessContext * context,int port,const void * buffer,u32 length)870 void DSP_WriteProcessPipe(DSPProcessContext *context, int port, const void *buffer, u32 length)
871 {
872     DSPPipe output[1];
873     (void)DSP_LoadPipe(output, port, DSP_PIPE_OUTPUT);
874     DSP_WritePipe(output, buffer, (DSPByte)length);
875     (void)context;
876 }
877 
878 
879 #if defined(DSP_SUPPORT_OBSOLETE_LOADER)
880 /*---------------------------------------------------------------------------*
881  * The following shows candidate interfaces for termination because they are considered not to be currently in use
882  *---------------------------------------------------------------------------*/
883 
884 /*---------------------------------------------------------------------------*
885   Name:         DSPi_MapProcessSegmentCallback
886 
887   Description:  Callback to calculate segment maps occupied by the process.
888 
889   Arguments:    context: DSPProcessContext structure
890                 header: COFF file header information
891                 section: Enumerated section
892 
893   Returns:      TRUE, if enumeration is continued; FALSE, if acceptable to end
894  *---------------------------------------------------------------------------*/
DSPi_MapProcessSegmentCallback(DSPProcessContext * context,const COFFFileHeader * header,const COFFSectionTable * section)895 static BOOL DSPi_MapProcessSegmentCallback(DSPProcessContext *context,
896                                            const COFFFileHeader *header,
897                                            const COFFSectionTable *section)
898 {
899     (void)header;
900     // TODO: You might achieve higher speeds by avoiding division
901     if((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_CODE_MEMORY) != 0)
902     {
903         u32     addr = DSP_ADDR_TO_ARM(section->s_paddr);
904         int     lower = (int)(addr / DSP_WRAM_SLOT_SIZE);
905         int     upper = (int)((addr + section->s_size - 1) / DSP_WRAM_SLOT_SIZE);
906         int     segment;
907         for(segment = lower; segment <= upper; ++segment)
908         {
909             context->segmentCode |= (1 << segment);
910         }
911     }
912     else if((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_DATA_MEMORY) != 0)
913     {
914         u32     addr = DSP_ADDR_TO_ARM(section->s_vaddr);
915         int     lower = (int)(addr / DSP_WRAM_SLOT_SIZE);
916         int     upper = (int)((addr + section->s_size - 1) / DSP_WRAM_SLOT_SIZE);
917         int     segment;
918         for(segment = lower; segment <= upper; ++segment)
919         {
920             context->segmentData |= (1 << segment);
921         }
922     }
923     return TRUE;
924 }
925 
926 /*---------------------------------------------------------------------------*
927   Name:         DSPi_MapProcessSlotDefault
928 
929   Description:  Initializes the slot map using empty numbers from the lower order.
930 
931   Arguments:    context: DSPProcessContext structure
932                 slotB: WRAM-B allowed for use for code memory
933                 slotC: WRAM-C allowed for use for data memory
934 
935   Returns:      TRUE if the slot map that satisfies the conditions can be allocated.
936  *---------------------------------------------------------------------------*/
DSPi_MapProcessSlotDefault(DSPProcessContext * context,int slotB,int slotC)937 static BOOL DSPi_MapProcessSlotDefault(DSPProcessContext *context, int slotB, int slotC)
938 {
939     BOOL    retval = TRUE;
940     int     segment;
941     for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment)
942     {
943         if ((context->segmentCode & (1 << segment)) != 0)
944         {
945             int     slot = (int)MATH_CountTrailingZeros((u32)slotB);
946             if (slot >= MI_WRAM_B_MAX_NUM)
947             {
948                 retval = FALSE;
949                 break;
950             }
951             context->slotOfSegmentCode[segment] = slot;
952             slotB &= ~(1 << slot);
953         }
954     }
955     for (segment = 0; segment < MI_WRAM_C_MAX_NUM; ++segment)
956     {
957         if ((context->segmentData & (1 << segment)) != 0)
958         {
959             int     slot = (int)MATH_CountTrailingZeros((u32)slotC);
960             if (slot >= MI_WRAM_C_MAX_NUM)
961             {
962                 retval = FALSE;
963                 break;
964             }
965             context->slotOfSegmentData[segment] = slot;
966             slotC &= ~(1 << slot);
967         }
968     }
969     return retval;
970 }
971 
972 /*---------------------------------------------------------------------------*
973   Name:         DSP_IsProcessMemoryReady
974 
975   Description:  Checks whether the WRAM slot that should be assigned to the process is being used.
976 
977   Arguments:    context: DSPProcessContext structure
978 
979   Returns:      TRUE, if all WRAM slots are not being used but are in a prepared state
980  *---------------------------------------------------------------------------*/
DSP_IsProcessMemoryReady(DSPProcessContext * context)981 static BOOL DSP_IsProcessMemoryReady(DSPProcessContext *context)
982 {
983     BOOL    retval = TRUE;
984     int     segment;
985     for (segment = 0; segment < MI_WRAM_B_MAX_NUM; ++segment)
986     {
987         if ((context->segmentCode & (1 << segment)) != 0)
988         {
989             int     slot = context->slotOfSegmentCode[segment];
990             if (MI_IsWramSlotUsed(MI_WRAM_B, slot))
991             {
992                 OS_TWarning("slot:%d for DSP:%05X is now used by someone.\n", slot, segment * DSP_WRAM_SLOT_SIZE);
993                 retval = FALSE;
994                 break;
995             }
996         }
997     }
998     for (segment = 0; segment < MI_WRAM_C_MAX_NUM; ++segment)
999     {
1000         if ((context->segmentData & (1 << segment)) != 0)
1001         {
1002             int     slot = context->slotOfSegmentData[segment];
1003             if (MI_IsWramSlotUsed(MI_WRAM_C, slot))
1004             {
1005                 OS_TWarning("slot:%d for DSP:%05X is now used by someone.\n", slot, segment * DSP_WRAM_SLOT_SIZE);
1006                 retval = FALSE;
1007                 break;
1008             }
1009         }
1010     }
1011     return retval;
1012 }
1013 
1014 /*---------------------------------------------------------------------------*
1015   Name:         DSPi_LoadProcessImageCallback
1016 
1017   Description:  Loads the process image using the group method.
1018 
1019   Arguments:    context: DSPProcessContext structure
1020                 header: COFF file header information
1021                 section: Enumerated section
1022 
1023   Returns:      TRUE, if enumeration is continued; FALSE, if acceptable to end
1024  *---------------------------------------------------------------------------*/
DSPi_LoadProcessImageCallback(DSPProcessContext * context,const COFFFileHeader * header,const COFFSectionTable * section)1025 static BOOL DSPi_LoadProcessImageCallback(DSPProcessContext *context,
1026                                           const COFFFileHeader *header,
1027                                           const COFFSectionTable *section)
1028 {
1029     BOOL        retval = TRUE;
1030     MIWramPos   wram = MI_WRAM_A;
1031     int         dstofs = 0;
1032     BOOL        noload = FALSE;
1033     (void)header;
1034     // CODE section is WRAM-B
1035     if ((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_CODE_MEMORY) != 0)
1036     {
1037         wram = MI_WRAM_B;
1038         dstofs = (int)(section->s_paddr * 2);
1039         if ((section->s_flags & COFF_SECTION_ATTR_NOLOAD_FOR_CODE_MEMORY) != 0)
1040         {
1041             noload = TRUE;
1042         }
1043     }
1044     // DATA section is WRAM-C
1045     else if ((section->s_flags & COFF_SECTION_ATTR_MAPPED_IN_DATA_MEMORY) != 0)
1046     {
1047         wram = MI_WRAM_C;
1048         dstofs = (int)(section->s_vaddr * 2);
1049         if ((section->s_flags & COFF_SECTION_ATTR_NOLOAD_FOR_DATA_MEMORY) != 0)
1050         {
1051             noload = TRUE;
1052         }
1053     }
1054     // Select the appropriate WRAM slot for each segment
1055     // (The constant "no WRAM" does not exist, so substitute WRAM-A)
1056     if (wram != MI_WRAM_A)
1057     {
1058         // If noload is specified, ignore
1059         if (noload)
1060         {
1061             DSP_DPRINTF("$%04X (noload)\n", dstofs);
1062         }
1063         else
1064         {
1065             // Split load so as not to overlap the slot boundary
1066             int     length = (int)section->s_size;
1067             int     srcofs = (int)section->s_scnptr;
1068             while (length > 0)
1069             {
1070                 // Clip the size at the slot boundary
1071                 int     ceil = MATH_ROUNDUP(dstofs + 1, DSP_WRAM_SLOT_SIZE);
1072                 int     curlen = MATH_MIN(length, ceil - dstofs);
1073                 // Calculate the corresponding offset in this time's transfer range
1074                 u8     *dstbuf = (u8*)DSP_ConvertProcessAddressFromDSP(context, wram, dstofs/2);
1075                 if (!DSPi_ReadProcessImage(context, srcofs, dstbuf, length))
1076                 {
1077                     retval = FALSE;
1078                     break;
1079                 }
1080                 DSP_DPRINTF("$%04X -> mem:%08X\n", dstofs, dstbuf);
1081                 srcofs += curlen;
1082                 dstofs += curlen;
1083                 length -= curlen;
1084             }
1085         }
1086     }
1087     return retval;
1088 }
1089 
1090 /*---------------------------------------------------------------------------*
1091   Name:         DSP_MapProcessSegment
1092 
1093   Description:  Calculates segment maps occupied by the process.
1094 
1095   Arguments:    context: DSPProcessContext structure
1096 
1097   Returns:      None.
1098  *---------------------------------------------------------------------------*/
DSP_MapProcessSegment(DSPProcessContext * context)1099 void DSP_MapProcessSegment(DSPProcessContext *context)
1100 {
1101     (void)DSP_EnumSections(context, DSPi_MapProcessSegmentCallback);
1102 }
1103 
1104 /*---------------------------------------------------------------------------*
1105   Name:         DSP_LoadProcessImage
1106 
1107   Description:  Loads the specified process image.
1108                 The segment map must already be calculated.
1109 
1110   Arguments:    context: DSPProcessContext structure
1111 
1112   Returns:      TRUE if all images are correctly loaded
1113  *---------------------------------------------------------------------------*/
DSP_LoadProcessImage(DSPProcessContext * context)1114 BOOL DSP_LoadProcessImage(DSPProcessContext *context)
1115 {
1116     BOOL    retval = FALSE;
1117     // Check whether it is really possible to use the WRAM slot that should be assigned to DSP
1118     if (DSP_IsProcessMemoryReady(context))
1119     {
1120         const u32   dspMemSize = DSP_ADDR_TO_DSP(DSP_WRAM_SLOT_SIZE) * MI_WRAM_B_MAX_NUM;
1121         // Switch all assigned WRAM slots to ARM9
1122         if (DSP_SwitchProcessMemory(context, MI_WRAM_B, 0, dspMemSize, MI_WRAM_ARM9) &&
1123             DSP_SwitchProcessMemory(context, MI_WRAM_C, 0, dspMemSize, MI_WRAM_ARM9))
1124         {
1125             // Actually load the process image and flush to WRAM
1126             if (DSP_EnumSections(context, DSPi_LoadProcessImageCallback))
1127             {
1128                 DC_FlushRange((const void*)MI_GetWramMapStart_B(), MI_WRAM_B_SIZE);
1129                 DC_FlushRange((const void*)MI_GetWramMapStart_C(), MI_WRAM_C_SIZE);
1130                 // Switch all assigned WRAM slots to DSP
1131                 if (DSP_SwitchProcessMemory(context, MI_WRAM_B, 0, dspMemSize, MI_WRAM_DSP) &&
1132                     DSP_SwitchProcessMemory(context, MI_WRAM_C, 0, dspMemSize, MI_WRAM_DSP))
1133                 {
1134                     retval = TRUE;
1135                 }
1136             }
1137         }
1138     }
1139     return retval;
1140 }
1141 
1142 /*---------------------------------------------------------------------------*
1143   Name:         DSP_StartupProcess
1144 
1145   Description:  Calculates the process image segment map and loads to WRAM.
1146                 Version combining the DSP_MapProcessSegment and the DSP_LoadProcessImage functions.
1147 
1148   Arguments:    context: DSPProcessContext structure
1149                 image: File handle that specifies the process image
1150                              Referenced only in this function
1151                 slotB: WRAM-B allowed for use for code memory
1152                 slotC: WRAM-C allowed for use for data memory
1153                 slotMapper: Algorithm that assigns WRAM slots to segments
1154                              If NULL is specified, the optimum default process is selected
1155 
1156   Returns:      TRUE if all images are correctly loaded
1157  *---------------------------------------------------------------------------*/
DSP_StartupProcess(DSPProcessContext * context,FSFile * image,int slotB,int slotC,BOOL (* slotMapper)(DSPProcessContext *,int,int))1158 BOOL DSP_StartupProcess(DSPProcessContext *context, FSFile *image,
1159                         int slotB, int slotC, BOOL (*slotMapper)(DSPProcessContext *, int, int))
1160 {
1161     BOOL    retval = FALSE;
1162     if (!slotMapper)
1163     {
1164         slotMapper = DSPi_MapProcessSlotDefault;
1165     }
1166     if (!FS_IsAvailable())
1167     {
1168         OS_TWarning("FS is not initialized yet.\n");
1169         FS_Init(FS_DMA_NOT_USE);
1170     }
1171     context->image = image;
1172     DSP_MapProcessSegment(context);
1173     if (!(*slotMapper)(context, slotB, slotC) ||
1174         !DSP_LoadProcessImage(context))
1175     {
1176         OS_TWarning("you should check wram\n");
1177     }
1178     else
1179     {
1180         retval = TRUE;
1181     }
1182     context->image = NULL;
1183     return retval;
1184 }
1185 
1186 
1187 #endif
1188 
1189 /*---------------------------------------------------------------------------*
1190   End of file
1191  *---------------------------------------------------------------------------*/
1192