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