1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - OS
3   File:     os_china.c
4 
5   Copyright 2005-2009 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2009-05-28#$
14   $Rev: 10638 $
15   $Author: mizutani_nakaba $
16  *---------------------------------------------------------------------------*/
17 
18 #include <nitro/os.h>
19 #include <nitro/gx.h>
20 #include <nitro/spi.h>
21 #include <nitro/pad.h>
22 #include <nitro/os/ARM9/china.h>
23 
24 #include <nitro/version_begin.h>
25 static char checkString[] = OS_BURY_STRING_FORCHINA;
26 #include <nitro/version_end.h>
27 
28 #if defined(SDK_ARM9) && defined(SDK_TWLLTD)
29 #include <os_attention.h>
30 #endif
31 
32 
33 
34 /*****************************************************************************/
35 /* Constants */
36 
37 /* Display fade and stop frame count */
38 #define     OSi_FORCHINA_FADE_SPAN          32
39 #define     OSi_FORCHINA_DISP_SPAN          128
40 
41 /* Index to data images managed by using table */
42 typedef enum
43 {
44     IMAGE_NOTES_CHR,
45     IMAGE_NOTES_SCR,
46     IMAGE_NOTES_DIGIT,
47     IMAGE_LOGO_CHR,
48     IMAGE_LOGO_SCR,
49     IMAGE_LOGO_PAL,
50     IMAGE_MAX
51 }
52 ImageIndex;
53 
54 /* ONLY_FORCHINA setting */
55 #define     OSi_ONLY_FORCHINA_CHAR_WIDTH    15
56 #define     OSi_ONLY_FORCHINA_CHAR_HEIGHT   2
57 #define     OSi_ONLY_FORCHINA_DOT_WIDTH     117
58 #define     OSi_ONLY_FORCHINA_DOT_HEIGHT    16
59 #define     OSi_ONLY_FORCHINA_CHAR_SIZE     240
60 
61 /* Character data for startup limitation screen (0-fixed pack of 3 bits into 4 bits) */
62 static u8 only_forChina_charData[OSi_ONLY_FORCHINA_CHAR_SIZE] ATTRIBUTE_ALIGN(4) =
63 {
64 0x00, 0x7c, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x8c, 0x8c, 0x9c, 0x9c, 0xbc, 0xbc, 0xec,
65         0x00, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x00, 0xc3, 0xc3, 0x66, 0x66, 0x3c,
66         0x3c, 0x18, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0xcf, 0xef, 0x60,
67         0x60, 0x60, 0x67, 0x67, 0x00, 0xc7, 0xcf, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x0f,
68         0x1f, 0x18, 0x18, 0x18, 0x1f, 0x0f, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x60,
69         0x00, 0x7c, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x8c, 0x8c, 0x8c,
70         0x8c, 0x8c, 0x00, 0x00, 0x00, 0xf1, 0xf9, 0x19, 0x19, 0xf9, 0x00, 0xc0, 0xc0, 0xc1,
71         0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x87, 0xcf, 0xdc, 0xd8, 0xd8, 0xd8, 0x98, 0x00, 0x0f,
72         0x0f, 0x00, 0x00, 0x00, 0x07, 0x0f, 0xc6, 0xc6, 0xc6, 0xfe, 0x7c, 0x00, 0x00, 0x00,
73         0xec, 0xcc, 0xcc, 0x8c, 0x8c, 0x00, 0x00, 0x00, 0x19, 0x19, 0x19, 0xf9, 0xf9, 0x00,
74         0x00, 0x00, 0x18, 0x18, 0x18, 0x19, 0x19, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0,
75         0xc0, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0xe0, 0xc0, 0x00, 0x00, 0x00, 0xcc, 0xcc,
76         0xcc, 0xcf, 0xc7, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x00, 0x00, 0x00,
77         0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xfe, 0x7c, 0xe0,
78         0xc0, 0x00, 0x8c, 0x8c, 0x8c, 0xfc, 0xf8, 0x01, 0x01, 0x00, 0xf9, 0x19, 0x19, 0xf9,
79         0xf1, 0x00, 0x00, 0x00, 0xc3, 0xc0, 0xc0, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x18, 0x18,
80         0x1c, 0xcf, 0xc7, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0f, 0x07, 0x00, 0x00, 0x00};
81 
82 /*****************************************************************************/
83 /* Declaration */
84 extern void OSi_InitCommon(void);   // os_init.c
85 
86 static u8*  LoadImage(ImageIndex index, u32 *p_size);
87 static void WaitForNextFrame(void);
88 static void VBlankIntr(void);
89 static void SetISBNString(const char **isbn, BOOL disp_isbn);
90 
91 static void CheckLanguageCode(void);
92 static void CheckDetectFold(void);
93 static void DispExclusiveMessage(void);
94 static void DispLogoAndNotes(const char **isbn);
95 
96 
97 /*****************************************************************************/
98 /* Functions */
99 
100 /*---------------------------------------------------------------------------*
101   Name:         LoadImage
102 
103   Description:  Allocates memory of sufficient size and loads the specified file.
104 
105   Arguments:    index: Index to the file to load
106                 p_size: Pointer to u32 that stores the file size
107                             If not needed, you can specify NULL and it will be ignored.
108 
109   Returns:      Memory that is allocated inside the call and where the file is loaded.
110                 When the OS_ShowAttentionChina function ends, each heap is destroyed with the OS_ClearAlloc function so you don't need to explicitly deallocate the memory returned here.
111 
112 
113  *---------------------------------------------------------------------------*/
LoadImage(ImageIndex index,u32 * p_size)114 static u8 *LoadImage(ImageIndex index, u32 *p_size)
115 {
116     void   *ptr = NULL;
117 #if defined(SDK_CHINA_DATA_FROM_FILE)
118 
119     /* *INDENT-OFF* */
120     static const char *(path_table[IMAGE_MAX]) =
121     {
122         "notes_forChina_chrData.bin",
123         "notes_forChina_scrData.bin",
124         "digit_chrData.bin",
125         "logo_forChina_chrData.bin",
126         "logo_forChina_scrData.bin",
127         "logo_forChina_palData.bin",
128     };
129     /* *INDENT-ON* */
130     const char *path = path_table[index];
131 
132     FSFile  file[1];
133 
134     FS_InitFile(file);
135     if (!FS_OpenFileEx(file, path, FS_FILEMODE_R))
136     {
137         OS_TPanic("failed to open file \"%s\"", path);
138     }
139     else
140     {
141         u32     len = FS_GetFileLength(file);
142         ptr = OS_Alloc(len);
143         if (!ptr)
144         {
145             OS_TPanic("failed to allocate memory for image \"%s\"", path);
146         }
147         else
148         {
149             if (p_size)
150             {
151                 *p_size = len;
152             }
153             if (FS_ReadFile(file, ptr, (int)len) != len)
154             {
155                 OS_TPanic("failed to read file \"%s\"", path);
156             }
157         }
158         (void)FS_CloseFile(file);
159     }
160 
161 #else
162 
163     extern u8 _binary_notes_forChina_chrData_bin[];
164     extern u8 _binary_notes_forChina_chrData_bin_end[];
165     extern u8 _binary_notes_forChina_scrData_bin[];
166     extern u8 _binary_notes_forChina_scrData_bin_end[];
167     extern u8 _binary_digit_chrData_bin[];
168     extern u8 _binary_digit_chrData_bin_end[];
169     extern u8 _binary_logo_forChina_chrData_bin[];
170     extern u8 _binary_logo_forChina_chrData_bin_end[];
171     extern u8 _binary_logo_forChina_scrData_bin[];
172     extern u8 _binary_logo_forChina_scrData_bin_end[];
173     extern u8 _binary_logo_forChina_palData_bin[];
174     extern u8 _binary_logo_forChina_palData_bin_end[];
175     /* *INDENT-OFF* */
176     static u8 *(ptr_table[]) =
177     {
178         _binary_notes_forChina_chrData_bin,
179         _binary_notes_forChina_scrData_bin,
180         _binary_digit_chrData_bin,
181         _binary_logo_forChina_chrData_bin,
182         _binary_logo_forChina_scrData_bin,
183         _binary_logo_forChina_palData_bin,
184     };
185     static u8 *(ptr_end_table[]) =
186     {
187         _binary_notes_forChina_chrData_bin_end,
188         _binary_notes_forChina_scrData_bin_end,
189         _binary_digit_chrData_bin_end,
190         _binary_logo_forChina_chrData_bin_end,
191         _binary_logo_forChina_scrData_bin_end,
192         _binary_logo_forChina_palData_bin_end,
193     };
194     /* *INDENT-ON* */
195     ptr = ptr_table[index];
196     if (p_size)
197     {
198         *p_size = (u32)(ptr_end_table[index] - ptr_table[index]);
199     }
200 
201 #endif
202 
203     return (u8 *)ptr;
204 }
205 
206 /*---------------------------------------------------------------------------*
207   Name:         WaitForNextFrame
208 
209   Description:  Processing when system is opened and closed, V-Blank wait, and sound update processing.
210 
211   Arguments:    None.
212 
213   Returns:      None.
214  *---------------------------------------------------------------------------*/
WaitForNextFrame(void)215 static void WaitForNextFrame(void)
216 {
217     /* Confirm that cover is closed */
218     if (PAD_DetectFold())
219     {
220         PM_GoSleepMode(PM_TRIGGER_COVER_OPEN | PM_TRIGGER_CARD, 0, 0);
221     }
222     SVC_WaitVBlankIntr();
223 }
224 
225 /*---------------------------------------------------------------------------*
226   Name:         VBlankIntr
227 
228   Description:  V-Blank interrupt vector in exclusive startup control screen.
229 
230   Arguments:    None.
231 
232   Returns:      None.
233  *---------------------------------------------------------------------------*/
VBlankIntr(void)234 static void VBlankIntr(void)
235 {
236     OS_SetIrqCheckFlag(OS_IE_V_BLANK);
237 }
238 
239 /*---------------------------------------------------------------------------*
240   Name:         SetISBNString
241 
242   Description:  Applies the ISBN number (and other information) in a prescribed location in the screen data.
243 
244   Arguments:    isbn: String array relating to the ISBN number (and other information)
245                 disp_isbn: If this is TRUE, display the ISBN number; if it is FALSE, hide the ISBN number
246 
247   Returns:      None.
248 
249   Note:         A single character is made up of two 8x16 characters.
250                 The actual character sizes are as follows.
251                     '-'      : 5x10
252                     '0'-'9'  : 7x10
253  *---------------------------------------------------------------------------*/
SetISBNString(const char ** isbn,BOOL disp_isbn)254 static void SetISBNString(const char **isbn, BOOL disp_isbn)
255 {
256     s32     i, j;
257     const struct
258     {
259         u8      x, y;                  /* Leading display offset */
260         u8      length;                /* Display character count */
261     }
262     pos[] =
263     {
264         {
265         80, 142, 17}
266         ,                              /* ISBN */
267         {
268         125, 157, 12}
269         ,                              /* Joint registration number */
270         {
271         106, 173, 4}
272         ,                              /* New sound output tube (left) */
273         {
274         139, 173, 3}
275         ,                              /* New sound output tube (right) */
276     }
277     ;
278     const int   count = sizeof(pos) / sizeof(*pos); /* Total number of lines */
279     const int   digit_num = 12;                     /* Number of characters for the digit display ("0123456789- ") */
280     u16         pos_x;
281     u16         pos_y;
282     u16         index;
283     u16         width;
284 
285     GXOamAttr *dst = (GXOamAttr *)HW_DB_OAM;
286     for (i = disp_isbn ? 0 : 1; i < count; i++)
287     {
288         pos_x = pos[i].x;
289         pos_y = pos[i].y;
290 
291         for (j = 0; j < pos[i].length; ++j)
292         {
293             if (isbn[i][j] == '-')
294             {
295                 index = 10;
296                 width = 5;
297             }
298             else if ((isbn[i][j] >= '0') && (isbn[i][j] <= '9'))
299             {
300                 index = (u16)(isbn[i][j] - '0');
301                 width = 7;
302             }
303             else
304             {
305                 index = 11;
306                 width = 7;
307             }
308 
309             dst[0].attr01 = (u32)((pos_x << 16) | (pos_y << 0));
310             dst[0].attr2  = index;
311             dst[1].attr01 = (u32)((pos_x << 16) | (pos_y + 8 << 0));
312             dst[1].attr2  = (u16)(index + digit_num);
313 
314             pos_x   += width;
315             dst     += 2;
316         }
317     }
318 }
319 
320 /*---------------------------------------------------------------------------*
321   Name:         OS_InitChina
322 
323   Description:  Initializes SDK's OS library.
324                 Exclusively for applications for the China region.
325                 Use this function instead of OS_Init function.
326 
327   Arguments:    isbn: String array relating to the ISBN number (and other information)
328                             {
329                                 char    ISBN[ 17 ] ,
330                                 char    Joint registration code  [ 12 ] ,
331                                 char    New output sound pipe (left)[ 4 ] ,
332                                 char    New output sound pipe (right) [ 4 ] ,
333                             }
334 
335                 param: Behavior related to displaying ISBN numbers
336 
337   Returns:      None.
338  *---------------------------------------------------------------------------*/
OS_InitChina(const char ** isbn,OSChinaIsbn param)339 void OS_InitChina(const char **isbn, OSChinaIsbn param)
340 {
341     SDK_REFER_SYMBOL(checkString);
342 
343 #ifdef SDK_ARM9
344     //---- System shared area check
345     SDK_ASSERT((u32)&(OS_GetSystemWork()->command_area) == HW_CMD_AREA);
346 
347 #ifdef SDK_TWLLTD
348     if (OS_IsLimitedRomRunningOnTwl() != TRUE)
349     {
350         OS_ShowAttentionOfLimitedRomChina(); // Never return
351     }
352 #endif
353 
354 #endif  // SDK_ARM9
355 
356     //---- Initialize SDK OS common
357     OSi_InitCommon();
358 
359     //---- Check if possible to boot
360     CheckLanguageCode();
361 
362     //---- Display logo and notes about health care
363     OS_ShowAttentionChina(isbn, param);
364 }
365 
366 /*---------------------------------------------------------------------------*
367   Name:         CheckLanguageCode
368 
369   Description:  Confirms whether to perform exclusive startup control based on language code.
370 
371   Arguments:    None.
372 
373   Returns:      None.
374  *---------------------------------------------------------------------------*/
CheckLanguageCode(void)375 static void CheckLanguageCode(void)
376 {
377     NVRAMConfig *src;
378 
379     src = (NVRAMConfig *)(OS_GetSystemWork()->nvramUserInfo);
380     // Confirms whether it has been determined that language code should not be booted by ARM7
381     if ((src->ncd.version == 0xff) && (src->ncd.option.language == 7))
382     {
383         DispExclusiveMessage();
384         // Never return
385     }
386 }
387 
388 /*---------------------------------------------------------------------------*
389   Name:         DispExclusiveMessage
390 
391   Description:  Indicates that exclusive startup control was performed based on language code.
392 
393   Arguments:    None.
394 
395   Returns:      None.
396  *---------------------------------------------------------------------------*/
DispExclusiveMessage(void)397 static void DispExclusiveMessage(void)
398 {
399     // Stop display
400     reg_GX_DISPCNT = 0;
401     reg_GXS_DB_DISPCNT = 0;
402 
403     // Initialize power control
404     if (!(reg_GX_POWCNT & REG_GX_POWCNT_LCD_MASK))
405     {
406         // When changing LCD enable from OFF to ON, wait 100 ms
407         SVC_WaitByLoop(HW_CPU_CLOCK_ARM9 / 40);
408     }
409     reg_GX_POWCNT = (u16)(REG_GX_POWCNT_DSEL_MASK | REG_GX_POWCNT_E2DG_MASK |
410                           REG_GX_POWCNT_E2DGB_MASK | REG_GX_POWCNT_LCD_MASK);
411     // Initialization of master brightness
412     reg_GX_MASTER_BRIGHT = 0;
413     reg_GXS_DB_MASTER_BRIGHT = 0;
414 
415     // Set main LCD
416     {
417         reg_GX_VRAMCNT_A =
418             (u8)((1 << REG_GX_VRAMCNT_A_MST_SHIFT) | (1 << REG_GX_VRAMCNT_A_E_SHIFT));
419         reg_G2_BG0CNT =
420             (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2_BG0CNT_SCREENSIZE_SHIFT) |
421                   (GX_BG_COLORMODE_16 << REG_G2_BG0CNT_COLORMODE_SHIFT) | (GX_BG_SCRBASE_0xf000 <<
422                                                                            REG_G2_BG0CNT_SCREENBASE_SHIFT)
423                   | (GX_BG_CHARBASE_0x00000 << REG_G2_BG0CNT_CHARBASE_SHIFT) | (GX_BG_EXTPLTT_01 <<
424                                                                                 REG_G2_BG0CNT_BGPLTTSLOT_SHIFT)
425                   | (0 << REG_G2_BG0CNT_PRIORITY_SHIFT));
426         reg_G2_BG0HOFS = (u16)(-(256 - OSi_ONLY_FORCHINA_DOT_WIDTH) / 2);
427         reg_G2_BG0VOFS = (u16)(-(192 - OSi_ONLY_FORCHINA_DOT_HEIGHT) / 2 + 2);
428         reg_GX_DISPCNT |= ((GX_BGMODE_0 << REG_GX_DISPCNT_BGMODE_SHIFT) |
429                            (GX_PLANEMASK_BG0 << REG_GX_DISPCNT_DISPLAY_SHIFT));
430         // Load character data into VRAM
431         {
432             MIUnpackBitsParam param = { OSi_ONLY_FORCHINA_CHAR_SIZE, 1, 4, 0, 0 };
433 
434             SVC_UnpackBits(only_forChina_charData, (u32 *)(HW_BG_VRAM + 0x20), &param);
435         }
436         // Load screen data into VRAM
437         {
438             s32     i;
439             s32     j;
440             u16     code = 1;
441             u16    *dst = (u16 *)(HW_BG_VRAM + 0xf000);
442 
443             for (i = 0; i < OSi_ONLY_FORCHINA_CHAR_HEIGHT; i++)
444             {
445                 for (j = 0; j < OSi_ONLY_FORCHINA_CHAR_WIDTH; j++)
446                 {
447                     *dst++ = code++;
448                 }
449                 dst += (0x20 - OSi_ONLY_FORCHINA_CHAR_WIDTH);
450             }
451         }
452     }
453 
454     // Set sub LCD
455     {
456         reg_GX_VRAMCNT_C =
457             (u8)((4 << REG_GX_VRAMCNT_C_MST_SHIFT) | (1 << REG_GX_VRAMCNT_C_E_SHIFT));
458         reg_G2S_DB_BG0CNT =
459             (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2S_DB_BG0CNT_SCREENSIZE_SHIFT) |
460                   (GX_BG_COLORMODE_16 << REG_G2S_DB_BG0CNT_COLORMODE_SHIFT) | (GX_BG_SCRBASE_0xf000
461                                                                                <<
462                                                                                REG_G2S_DB_BG0CNT_SCREENBASE_SHIFT)
463                   | (GX_BG_CHARBASE_0x00000 << REG_G2S_DB_BG0CNT_CHARBASE_SHIFT) | (GX_BG_EXTPLTT_01
464                                                                                     <<
465                                                                                     REG_G2S_DB_BG0CNT_BGPLTTSLOT_SHIFT)
466                   | (0 << REG_G2S_DB_BG0CNT_PRIORITY_SHIFT));
467         reg_G2S_DB_BG0OFS = 0;
468         reg_GXS_DB_DISPCNT |= ((GX_BGMODE_0 << REG_GXS_DB_DISPCNT_BGMODE_SHIFT) |
469                                (GX_PLANEMASK_BG0 << REG_GXS_DB_DISPCNT_DISPLAY_SHIFT));
470     }
471 
472     // V-Blank interrupt settings
473     (void)OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
474     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
475     (void)OS_EnableIrq();
476     reg_GX_DISPSTAT |= REG_GX_DISPSTAT_VBI_MASK;
477 
478     // Start display
479     reg_GX_DISPCNT |= (u32)(GX_DISPMODE_GRAPHICS << REG_GX_DISPCNT_MODE_SHIFT);
480     reg_GXS_DB_DISPCNT |= (u32)(REG_GXS_DB_DISPCNT_MODE_MASK);
481 
482     {
483         u16     data = 0x001f;
484         u16     target = 0x0001 << 5;
485         s32     i;
486 
487         // Cycle palette colors between red and yellow and loop indefinitely
488         while (TRUE)
489         {
490             for (i = 0; i < 31; i++)
491             {
492                 // Confirm that cover is closed
493                 if (TRUE == PAD_DetectFold())
494                 {
495                     // Turn off power
496                     (void)PM_ForceToPowerOff();
497                 }
498                 OS_WaitVBlankIntr();
499                 *((u16 *)(HW_BG_PLTT + 2)) = data;
500                 data += target;
501             }
502             target = (u16)(~target + 1);
503         }
504     }
505 }
506 
507 /*---------------------------------------------------------------------------*
508   Name:         OS_ShowAttentionChina
509 
510   Description:  Displays logo for China and notice for prescribed time.
511 
512   Arguments:    isbn: String array relating to the ISBN number (and other information)
513                 param: Behavior related to displaying ISBN numbers
514 
515   Returns:      None.
516  *---------------------------------------------------------------------------*/
OS_ShowAttentionChina(const char ** isbn,OSChinaIsbn param)517 SDK_WEAK_SYMBOL void OS_ShowAttentionChina(const char **isbn, OSChinaIsbn param)
518 {
519     /* Structure for saving register */
520     struct
521     {
522         u32     gx_dispcnt;
523         u32     gxs_dispcnt;
524         u16     gx_powcnt;
525         u16     gx_dispstat;
526         u16     gx_bright;
527         u16     gxs_bright;
528         u16     gx_bg0cnt;
529         u16     gxs_bg0cnt;
530         u32     gx_bg0ofs;
531         u32     gxs_bg0ofs;
532         u8      gx_vramcnt_a;
533         u8      gx_vramcnt_c;
534         u8      gx_vramcnt_d;
535         u8      reserved[1];
536 
537         OSIrqFunction irqFunction;
538         OSIrqMask irqMask;
539         BOOL    irq;
540         OSIntrMode interrupts;
541 
542     }
543     shelter;
544 
545     /* Pre-processing */
546     {
547         /* Save each register */
548         shelter.gx_dispstat = reg_GX_DISPSTAT;
549         shelter.gx_dispcnt = reg_GX_DISPCNT;
550         shelter.gxs_dispcnt = reg_GXS_DB_DISPCNT;
551         shelter.gx_bright = reg_GX_MASTER_BRIGHT;
552         shelter.gxs_bright = reg_GXS_DB_MASTER_BRIGHT;
553         shelter.gx_powcnt = reg_GX_POWCNT;
554         shelter.gx_vramcnt_a = reg_GX_VRAMCNT_A;
555         shelter.gx_vramcnt_c = reg_GX_VRAMCNT_C;
556         shelter.gx_vramcnt_d = reg_GX_VRAMCNT_D;
557         shelter.gx_bg0cnt = reg_G2_BG0CNT;
558         shelter.gxs_bg0cnt = reg_G2S_DB_BG0CNT;
559         shelter.gx_bg0ofs = reg_G2_BG0OFS;
560         shelter.gxs_bg0ofs = reg_G2S_DB_BG0OFS;
561 
562         /* Stop display */
563         reg_GX_DISPCNT = 0;
564         reg_GXS_DB_DISPCNT = 0;
565 
566         /* Initialize power control */
567         if (!(shelter.gx_powcnt & REG_GX_POWCNT_LCD_MASK))
568         {
569             /* When changing LCD enable from OFF to ON, wait 100 ms */
570             SVC_WaitByLoop(HW_CPU_CLOCK_ARM9 / 40);
571         }
572         reg_GX_POWCNT = (u16)(REG_GX_POWCNT_DSEL_MASK | REG_GX_POWCNT_E2DG_MASK |
573                               REG_GX_POWCNT_E2DGB_MASK | REG_GX_POWCNT_LCD_MASK);
574 
575         /* Initialization of master brightness */
576         reg_GX_MASTER_BRIGHT = (u16)((1 << REG_GX_MASTER_BRIGHT_E_MOD_SHIFT) | 16);
577         reg_GXS_DB_MASTER_BRIGHT = reg_GX_MASTER_BRIGHT;
578 
579         /* Prepare display of logo screen */
580         {
581             reg_GX_VRAMCNT_A =
582                 (u8)((1 << REG_GX_VRAMCNT_A_MST_SHIFT) | (1 << REG_GX_VRAMCNT_A_E_SHIFT));
583             reg_G2_BG0CNT =
584                 (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2_BG0CNT_SCREENSIZE_SHIFT) |
585                       (GX_BG_COLORMODE_256 << REG_G2_BG0CNT_COLORMODE_SHIFT) | (GX_BG_SCRBASE_0xf000
586                                                                                 <<
587                                                                                 REG_G2_BG0CNT_SCREENBASE_SHIFT)
588                       | (GX_BG_CHARBASE_0x00000 << REG_G2_BG0CNT_CHARBASE_SHIFT) | (GX_BG_EXTPLTT_01
589                                                                                     <<
590                                                                                     REG_G2_BG0CNT_BGPLTTSLOT_SHIFT)
591                       | (0 << REG_G2_BG0CNT_PRIORITY_SHIFT));
592             reg_G2_BG0OFS = 0;
593             reg_GX_DISPCNT |= ((GX_BGMODE_0 << REG_GX_DISPCNT_BGMODE_SHIFT) |
594                                (GX_PLANEMASK_BG0 << REG_GX_DISPCNT_DISPLAY_SHIFT));
595 
596             /* Load character data into VRAM */
597             {
598                 void   *const src = LoadImage(IMAGE_LOGO_CHR, NULL);
599                 MI_UncompressLZ16(src, (u32 *)HW_BG_VRAM);
600             }
601             /* Load screen data into VRAM */
602             {
603                 u16     *src = (u16*)LoadImage(IMAGE_LOGO_SCR, NULL);
604                 u16     *dst;
605                 s32     i;
606                 s32     j;
607 
608                 // Place 12x12 (96x96-pixel) screen data at the center
609                 SVC_CpuClearFast(0, (u32 *)(HW_BG_VRAM + 0xf000), 32*24*2);
610                 dst = (u16*)(HW_BG_VRAM + 0xf000 + ((6*32+10)*2));
611 
612                 for (i = 0; i < 12; i++, dst += 32)
613                 {
614                     for (j = 0; j < 12; j++, src++)
615                     {
616                         dst[j] = *src;
617                     }
618                 }
619             }
620             /* Load palette data into VRAM */
621             {
622                 u32     size;
623                 void   *const src = LoadImage(IMAGE_LOGO_PAL, &size);
624                 SVC_CpuCopyFast(src, (u32 *)(HW_BG_PLTT), size);
625             }
626         }
627 
628         /* Prepare to display Notes screen */
629         {
630             reg_GX_VRAMCNT_C =
631                 (u8)((4 << REG_GX_VRAMCNT_C_MST_SHIFT) | (1 << REG_GX_VRAMCNT_C_E_SHIFT));
632             reg_GX_VRAMCNT_D =
633                 (u8)((4 << REG_GX_VRAMCNT_D_MST_SHIFT) | (1 << REG_GX_VRAMCNT_D_E_SHIFT));
634             reg_G2S_DB_BG0CNT =
635                 (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2S_DB_BG0CNT_SCREENSIZE_SHIFT) |
636                       (GX_BG_COLORMODE_16 << REG_G2S_DB_BG0CNT_COLORMODE_SHIFT) |
637                       (GX_BG_SCRBASE_0xf000 << REG_G2S_DB_BG0CNT_SCREENBASE_SHIFT) |
638                       (GX_BG_CHARBASE_0x00000 << REG_G2S_DB_BG0CNT_CHARBASE_SHIFT) |
639                       (GX_BG_EXTPLTT_01 << REG_G2S_DB_BG0CNT_BGPLTTSLOT_SHIFT) | (0 <<
640                                                                                   REG_G2S_DB_BG0CNT_PRIORITY_SHIFT));
641             reg_G2S_DB_BG0OFS = 0;
642             reg_GXS_DB_DISPCNT |= ((GX_BGMODE_0 << REG_GXS_DB_DISPCNT_BGMODE_SHIFT) |
643                                    ((GX_PLANEMASK_BG0 | GX_PLANEMASK_OBJ) <<
644                                     REG_GXS_DB_DISPCNT_DISPLAY_SHIFT));
645 
646             /* Load character data into VRAM */
647             {
648                 u32     size;
649                 void   *src;
650                 /* Background text */
651                 src = LoadImage(IMAGE_NOTES_CHR, &size);
652                 MI_UncompressLZ16(src, (u32 *)(HW_DB_BG_VRAM));
653                 /* OBJ text */
654                 src = LoadImage(IMAGE_NOTES_DIGIT, &size);
655                 MI_UncompressLZ16(src, (u32 *)(HW_DB_OBJ_VRAM));
656                 SVC_CpuClear(0xC0, (void *)HW_DB_OAM, HW_OAM_SIZE, 32);
657             }
658             /* Load screen data into VRAM */
659             {
660                 u32     size;
661                 void   *const src = LoadImage(IMAGE_NOTES_SCR, &size);
662                 SVC_CpuCopyFast(src, (u32 *)(HW_DB_BG_VRAM + 0xf000), size);
663             }
664 
665             // Display settings for the ISBN string
666             SetISBNString(isbn, (BOOL)(param != OS_CHINA_ISBN_NO_DISP));
667 
668             /* Edit palette */
669             *((u16 *)(HW_DB_BG_PLTT + 0)) = 0x7fff;
670             *((u16 *)(HW_DB_BG_PLTT + 2)) = 0x0000;
671             *((u16 *)(HW_DB_OBJ_PLTT + 0)) = 0x7fff;
672             *((u16 *)(HW_DB_OBJ_PLTT + 2)) = 0x0000;
673         }
674 
675         /* Start display */
676         reg_GX_DISPCNT |= (u32)(GX_DISPMODE_GRAPHICS << REG_GX_DISPCNT_MODE_SHIFT);
677         reg_GXS_DB_DISPCNT |= (u32)(REG_GXS_DB_DISPCNT_MODE_MASK);
678 
679         /* Interrupt settings */
680         reg_GX_DISPSTAT |= REG_GX_DISPSTAT_VBI_MASK;
681         shelter.irqFunction = OS_GetIrqFunction(OS_IE_V_BLANK);
682         OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
683         shelter.irqMask = OS_EnableIrqMask(OS_IE_V_BLANK);
684         shelter.irq = OS_EnableIrq();
685         shelter.interrupts = OS_EnableInterrupts();
686 
687     }
688 
689     /* Main process */
690     {
691         s32     i;
692         u16     pad_old;
693         u16     pad;
694 
695         /* Fade in */
696         for (i = 0; i < OSi_FORCHINA_FADE_SPAN; i++)
697         {
698             reg_GX_MASTER_BRIGHT = (u16)((1 << REG_GX_MASTER_BRIGHT_E_MOD_SHIFT) |
699                                          16 - ((i + 1) * 16 / OSi_FORCHINA_FADE_SPAN));
700             reg_GXS_DB_MASTER_BRIGHT = reg_GX_MASTER_BRIGHT;
701             WaitForNextFrame();
702         }
703 
704         /* Skip based on button input */
705         pad_old = PAD_Read();
706         for (i = 0; i < OSi_FORCHINA_DISP_SPAN; i++)
707         {
708             pad = PAD_Read();
709             if ((param == OS_CHINA_ISBN_CHECK_ROM) && (~pad_old & pad))
710             {
711                 /* If any button push trigger was detected, skip */
712                 break;
713             }
714             pad_old = pad;
715             WaitForNextFrame();
716 
717             // On ROMs used for censorship purposes, do not exit until a button is pressed
718             if(param == OS_CHINA_ISBN_CHECK_ROM) i = 0;
719         }
720 
721         /* Fade out */
722         for (i = 0; i < OSi_FORCHINA_FADE_SPAN; i++)
723         {
724             reg_GX_MASTER_BRIGHT = (u16)((1 << REG_GX_MASTER_BRIGHT_E_MOD_SHIFT) |
725                                          (i + 1) * 16 / OSi_FORCHINA_FADE_SPAN);
726             reg_GXS_DB_MASTER_BRIGHT = reg_GX_MASTER_BRIGHT;
727             WaitForNextFrame();
728         }
729     }
730 
731     /* Post-processing */
732     {
733         /* Stop display */
734         reg_GX_DISPCNT &= ~(GX_DISPMODE_GRAPHICS << REG_GX_DISPCNT_MODE_SHIFT);
735         reg_GXS_DB_DISPCNT &= ~REG_GXS_DB_DISPCNT_MODE_MASK;
736 
737         /* Restore interrupt-related items */
738         (void)OS_RestoreInterrupts(shelter.interrupts);
739         (void)OS_RestoreIrq(shelter.irq);
740         (void)OS_SetIrqMask(shelter.irqMask);
741         OS_SetIrqFunction(OS_IE_V_BLANK, shelter.irqFunction);
742         reg_GX_DISPSTAT &= ~REG_GX_DISPSTAT_VBI_MASK;
743 
744         /* Clear used VRAM (cannot restore initial values because the size is too big) */
745         SVC_CpuClearFast(0, (u32 *)HW_BG_VRAM, 0x10000);
746         SVC_CpuClearFast(0, (u32 *)HW_BG_PLTT, 0x200);
747         SVC_CpuClearFast(0, (u32 *)HW_DB_BG_VRAM, 0x10000);
748         SVC_CpuClearFast(0, (u32 *)HW_DB_BG_PLTT, 0x04);
749         SVC_CpuClearFast(0, (u32 *)HW_DB_OAM, HW_OAM_SIZE);
750 
751         /* Restore each register */
752         reg_G2S_DB_BG0OFS = shelter.gxs_bg0ofs;
753         reg_G2_BG0OFS = shelter.gx_bg0ofs;
754         reg_G2S_DB_BG0CNT = shelter.gxs_bg0cnt;
755         reg_G2_BG0CNT = shelter.gx_bg0cnt;
756         reg_GX_VRAMCNT_D = shelter.gx_vramcnt_d;
757         reg_GX_VRAMCNT_C = shelter.gx_vramcnt_c;
758         reg_GX_VRAMCNT_A = shelter.gx_vramcnt_a;
759         reg_GX_POWCNT = shelter.gx_powcnt;
760         if (!(shelter.gx_powcnt & REG_GX_POWCNT_LCD_MASK))
761         {
762             /* When changing LCD enable from ON to OFF, wait 100ms */
763             SVC_WaitByLoop(HW_CPU_CLOCK_ARM9 / 40);
764         }
765         reg_GXS_DB_MASTER_BRIGHT = shelter.gxs_bright;
766         reg_GX_MASTER_BRIGHT = shelter.gx_bright;
767         reg_GXS_DB_DISPCNT = shelter.gxs_dispcnt;
768         reg_GX_DISPCNT = shelter.gx_dispcnt;
769         reg_GX_DISPSTAT = shelter.gx_dispstat;
770     }
771 }
772 
773 /*---------------------------------------------------------------------------*
774     End of file
775  *---------------------------------------------------------------------------*/
776