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-08-20#$
14   $Rev: 10997 $
15   $Author: yada $
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 #endif  // SDK_ARM9
347 
348     //---- Initialize SDK OS common
349     OSi_InitCommon();
350 
351     //---- Check if possible to boot
352     CheckLanguageCode();
353 
354     //---- Display logo and notes about health care
355     OS_ShowAttentionChina(isbn, param);
356 }
357 
358 /*---------------------------------------------------------------------------*
359   Name:         CheckLanguageCode
360 
361   Description:  Confirms whether to perform exclusive startup control based on language code.
362 
363   Arguments:    None.
364 
365   Returns:      None.
366  *---------------------------------------------------------------------------*/
CheckLanguageCode(void)367 static void CheckLanguageCode(void)
368 {
369     NVRAMConfig *src;
370 
371     src = (NVRAMConfig *)(OS_GetSystemWork()->nvramUserInfo);
372     // Confirms whether it has been determined that language code should not be booted by ARM7
373     if ((src->ncd.version == 0xff) && (src->ncd.option.language == 7))
374     {
375         DispExclusiveMessage();
376         // Never return
377     }
378 }
379 
380 /*---------------------------------------------------------------------------*
381   Name:         DispExclusiveMessage
382 
383   Description:  Indicates that exclusive startup control was performed based on language code.
384 
385   Arguments:    None.
386 
387   Returns:      None.
388  *---------------------------------------------------------------------------*/
DispExclusiveMessage(void)389 static void DispExclusiveMessage(void)
390 {
391     // Stop display
392     reg_GX_DISPCNT = 0;
393     reg_GXS_DB_DISPCNT = 0;
394 
395     // Initialize power control
396     if (!(reg_GX_POWCNT & REG_GX_POWCNT_LCD_MASK))
397     {
398         // When changing LCD enable from OFF to ON, wait 100 ms
399         SVC_WaitByLoop(HW_CPU_CLOCK_ARM9 / 40);
400     }
401     reg_GX_POWCNT = (u16)(REG_GX_POWCNT_DSEL_MASK | REG_GX_POWCNT_E2DG_MASK |
402                           REG_GX_POWCNT_E2DGB_MASK | REG_GX_POWCNT_LCD_MASK);
403     // Initialization of master brightness
404     reg_GX_MASTER_BRIGHT = 0;
405     reg_GXS_DB_MASTER_BRIGHT = 0;
406 
407     // Set main LCD
408     {
409         reg_GX_VRAMCNT_A =
410             (u8)((1 << REG_GX_VRAMCNT_A_MST_SHIFT) | (1 << REG_GX_VRAMCNT_A_E_SHIFT));
411         reg_G2_BG0CNT =
412             (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2_BG0CNT_SCREENSIZE_SHIFT) |
413                   (GX_BG_COLORMODE_16 << REG_G2_BG0CNT_COLORMODE_SHIFT) | (GX_BG_SCRBASE_0xf000 <<
414                                                                            REG_G2_BG0CNT_SCREENBASE_SHIFT)
415                   | (GX_BG_CHARBASE_0x00000 << REG_G2_BG0CNT_CHARBASE_SHIFT) | (GX_BG_EXTPLTT_01 <<
416                                                                                 REG_G2_BG0CNT_BGPLTTSLOT_SHIFT)
417                   | (0 << REG_G2_BG0CNT_PRIORITY_SHIFT));
418         reg_G2_BG0HOFS = (u16)(-(256 - OSi_ONLY_FORCHINA_DOT_WIDTH) / 2);
419         reg_G2_BG0VOFS = (u16)(-(192 - OSi_ONLY_FORCHINA_DOT_HEIGHT) / 2 + 2);
420         reg_GX_DISPCNT |= ((GX_BGMODE_0 << REG_GX_DISPCNT_BGMODE_SHIFT) |
421                            (GX_PLANEMASK_BG0 << REG_GX_DISPCNT_DISPLAY_SHIFT));
422         // Load character data into VRAM
423         {
424             MIUnpackBitsParam param = { OSi_ONLY_FORCHINA_CHAR_SIZE, 1, 4, 0, 0 };
425 
426             SVC_UnpackBits(only_forChina_charData, (u32 *)(HW_BG_VRAM + 0x20), &param);
427         }
428         // Load screen data into VRAM
429         {
430             s32     i;
431             s32     j;
432             u16     code = 1;
433             u16    *dst = (u16 *)(HW_BG_VRAM + 0xf000);
434 
435             for (i = 0; i < OSi_ONLY_FORCHINA_CHAR_HEIGHT; i++)
436             {
437                 for (j = 0; j < OSi_ONLY_FORCHINA_CHAR_WIDTH; j++)
438                 {
439                     *dst++ = code++;
440                 }
441                 dst += (0x20 - OSi_ONLY_FORCHINA_CHAR_WIDTH);
442             }
443         }
444     }
445 
446     // Set sub LCD
447     {
448         reg_GX_VRAMCNT_C =
449             (u8)((4 << REG_GX_VRAMCNT_C_MST_SHIFT) | (1 << REG_GX_VRAMCNT_C_E_SHIFT));
450         reg_G2S_DB_BG0CNT =
451             (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2S_DB_BG0CNT_SCREENSIZE_SHIFT) |
452                   (GX_BG_COLORMODE_16 << REG_G2S_DB_BG0CNT_COLORMODE_SHIFT) | (GX_BG_SCRBASE_0xf000
453                                                                                <<
454                                                                                REG_G2S_DB_BG0CNT_SCREENBASE_SHIFT)
455                   | (GX_BG_CHARBASE_0x00000 << REG_G2S_DB_BG0CNT_CHARBASE_SHIFT) | (GX_BG_EXTPLTT_01
456                                                                                     <<
457                                                                                     REG_G2S_DB_BG0CNT_BGPLTTSLOT_SHIFT)
458                   | (0 << REG_G2S_DB_BG0CNT_PRIORITY_SHIFT));
459         reg_G2S_DB_BG0OFS = 0;
460         reg_GXS_DB_DISPCNT |= ((GX_BGMODE_0 << REG_GXS_DB_DISPCNT_BGMODE_SHIFT) |
461                                (GX_PLANEMASK_BG0 << REG_GXS_DB_DISPCNT_DISPLAY_SHIFT));
462     }
463 
464     // V-Blank interrupt settings
465     (void)OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
466     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
467     (void)OS_EnableIrq();
468     reg_GX_DISPSTAT |= REG_GX_DISPSTAT_VBI_MASK;
469 
470     // Start display
471     reg_GX_DISPCNT |= (u32)(GX_DISPMODE_GRAPHICS << REG_GX_DISPCNT_MODE_SHIFT);
472     reg_GXS_DB_DISPCNT |= (u32)(REG_GXS_DB_DISPCNT_MODE_MASK);
473 
474     {
475         u16     data = 0x001f;
476         u16     target = 0x0001 << 5;
477         s32     i;
478 
479         // Cycle palette colors between red and yellow and loop indefinitely
480         while (TRUE)
481         {
482             for (i = 0; i < 31; i++)
483             {
484                 // Confirm that cover is closed
485                 if (TRUE == PAD_DetectFold())
486                 {
487                     // Turn off power
488                     (void)PM_ForceToPowerOff();
489                 }
490                 OS_WaitVBlankIntr();
491                 *((u16 *)(HW_BG_PLTT + 2)) = data;
492                 data += target;
493             }
494             target = (u16)(~target + 1);
495         }
496     }
497 }
498 
499 /*---------------------------------------------------------------------------*
500   Name:         OS_ShowAttentionChina
501 
502   Description:  Displays logo for China and notice for prescribed time.
503 
504   Arguments:    isbn: String array relating to the ISBN number (and other information)
505                 param: Behavior related to displaying ISBN numbers
506 
507   Returns:      None.
508  *---------------------------------------------------------------------------*/
OS_ShowAttentionChina(const char ** isbn,OSChinaIsbn param)509 SDK_WEAK_SYMBOL void OS_ShowAttentionChina(const char **isbn, OSChinaIsbn param)
510 {
511     /* Structure for saving register */
512     struct
513     {
514         u32     gx_dispcnt;
515         u32     gxs_dispcnt;
516         u16     gx_powcnt;
517         u16     gx_dispstat;
518         u16     gx_bright;
519         u16     gxs_bright;
520         u16     gx_bg0cnt;
521         u16     gxs_bg0cnt;
522         u32     gx_bg0ofs;
523         u32     gxs_bg0ofs;
524         u8      gx_vramcnt_a;
525         u8      gx_vramcnt_c;
526         u8      gx_vramcnt_d;
527         u8      reserved[1];
528 
529         OSIrqFunction irqFunction;
530         OSIrqMask irqMask;
531         BOOL    irq;
532         OSIntrMode interrupts;
533 
534     }
535     shelter;
536 
537     /* Pre-processing */
538     {
539         /* Save each register */
540         shelter.gx_dispstat = reg_GX_DISPSTAT;
541         shelter.gx_dispcnt = reg_GX_DISPCNT;
542         shelter.gxs_dispcnt = reg_GXS_DB_DISPCNT;
543         shelter.gx_bright = reg_GX_MASTER_BRIGHT;
544         shelter.gxs_bright = reg_GXS_DB_MASTER_BRIGHT;
545         shelter.gx_powcnt = reg_GX_POWCNT;
546         shelter.gx_vramcnt_a = reg_GX_VRAMCNT_A;
547         shelter.gx_vramcnt_c = reg_GX_VRAMCNT_C;
548         shelter.gx_vramcnt_d = reg_GX_VRAMCNT_D;
549         shelter.gx_bg0cnt = reg_G2_BG0CNT;
550         shelter.gxs_bg0cnt = reg_G2S_DB_BG0CNT;
551         shelter.gx_bg0ofs = reg_G2_BG0OFS;
552         shelter.gxs_bg0ofs = reg_G2S_DB_BG0OFS;
553 
554         /* Stop display */
555         reg_GX_DISPCNT = 0;
556         reg_GXS_DB_DISPCNT = 0;
557 
558         /* Initialize power control */
559         if (!(shelter.gx_powcnt & REG_GX_POWCNT_LCD_MASK))
560         {
561             /* When changing LCD enable from OFF to ON, wait 100 ms */
562             SVC_WaitByLoop(HW_CPU_CLOCK_ARM9 / 40);
563         }
564         reg_GX_POWCNT = (u16)(REG_GX_POWCNT_DSEL_MASK | REG_GX_POWCNT_E2DG_MASK |
565                               REG_GX_POWCNT_E2DGB_MASK | REG_GX_POWCNT_LCD_MASK);
566 
567         /* Initialization of master brightness */
568         reg_GX_MASTER_BRIGHT = (u16)((1 << REG_GX_MASTER_BRIGHT_E_MOD_SHIFT) | 16);
569         reg_GXS_DB_MASTER_BRIGHT = reg_GX_MASTER_BRIGHT;
570 
571         /* Prepare display of logo screen */
572         {
573             reg_GX_VRAMCNT_A =
574                 (u8)((1 << REG_GX_VRAMCNT_A_MST_SHIFT) | (1 << REG_GX_VRAMCNT_A_E_SHIFT));
575             reg_G2_BG0CNT =
576                 (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2_BG0CNT_SCREENSIZE_SHIFT) |
577                       (GX_BG_COLORMODE_256 << REG_G2_BG0CNT_COLORMODE_SHIFT) | (GX_BG_SCRBASE_0xf000
578                                                                                 <<
579                                                                                 REG_G2_BG0CNT_SCREENBASE_SHIFT)
580                       | (GX_BG_CHARBASE_0x00000 << REG_G2_BG0CNT_CHARBASE_SHIFT) | (GX_BG_EXTPLTT_01
581                                                                                     <<
582                                                                                     REG_G2_BG0CNT_BGPLTTSLOT_SHIFT)
583                       | (0 << REG_G2_BG0CNT_PRIORITY_SHIFT));
584             reg_G2_BG0OFS = 0;
585             reg_GX_DISPCNT |= ((GX_BGMODE_0 << REG_GX_DISPCNT_BGMODE_SHIFT) |
586                                (GX_PLANEMASK_BG0 << REG_GX_DISPCNT_DISPLAY_SHIFT));
587 
588             /* Load character data into VRAM */
589             {
590                 void   *const src = LoadImage(IMAGE_LOGO_CHR, NULL);
591                 MI_UncompressLZ16(src, (u32 *)HW_BG_VRAM);
592             }
593             /* Load screen data into VRAM */
594             {
595                 u16     *src = (u16*)LoadImage(IMAGE_LOGO_SCR, NULL);
596                 u16     *dst;
597                 s32     i;
598                 s32     j;
599 
600                 // Place 12x12 (96x96-pixel) screen data at the center
601                 SVC_CpuClearFast(0, (u32 *)(HW_BG_VRAM + 0xf000), 32*24*2);
602                 dst = (u16*)(HW_BG_VRAM + 0xf000 + ((6*32+10)*2));
603 
604                 for (i = 0; i < 12; i++, dst += 32)
605                 {
606                     for (j = 0; j < 12; j++, src++)
607                     {
608                         dst[j] = *src;
609                     }
610                 }
611             }
612             /* Load palette data into VRAM */
613             {
614                 u32     size;
615                 void   *const src = LoadImage(IMAGE_LOGO_PAL, &size);
616                 SVC_CpuCopyFast(src, (u32 *)(HW_BG_PLTT), size);
617             }
618         }
619 
620         /* Prepare to display Notes screen */
621         {
622             reg_GX_VRAMCNT_C =
623                 (u8)((4 << REG_GX_VRAMCNT_C_MST_SHIFT) | (1 << REG_GX_VRAMCNT_C_E_SHIFT));
624             reg_GX_VRAMCNT_D =
625                 (u8)((4 << REG_GX_VRAMCNT_D_MST_SHIFT) | (1 << REG_GX_VRAMCNT_D_E_SHIFT));
626             reg_G2S_DB_BG0CNT =
627                 (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2S_DB_BG0CNT_SCREENSIZE_SHIFT) |
628                       (GX_BG_COLORMODE_16 << REG_G2S_DB_BG0CNT_COLORMODE_SHIFT) |
629                       (GX_BG_SCRBASE_0xf000 << REG_G2S_DB_BG0CNT_SCREENBASE_SHIFT) |
630                       (GX_BG_CHARBASE_0x00000 << REG_G2S_DB_BG0CNT_CHARBASE_SHIFT) |
631                       (GX_BG_EXTPLTT_01 << REG_G2S_DB_BG0CNT_BGPLTTSLOT_SHIFT) | (0 <<
632                                                                                   REG_G2S_DB_BG0CNT_PRIORITY_SHIFT));
633             reg_G2S_DB_BG0OFS = 0;
634             reg_GXS_DB_DISPCNT |= ((GX_BGMODE_0 << REG_GXS_DB_DISPCNT_BGMODE_SHIFT) |
635                                    ((GX_PLANEMASK_BG0 | GX_PLANEMASK_OBJ) <<
636                                     REG_GXS_DB_DISPCNT_DISPLAY_SHIFT));
637 
638             /* Load character data into VRAM */
639             {
640                 u32     size;
641                 void   *src;
642                 /* Background text */
643                 src = LoadImage(IMAGE_NOTES_CHR, &size);
644                 MI_UncompressLZ16(src, (u32 *)(HW_DB_BG_VRAM));
645                 /* OBJ text */
646                 src = LoadImage(IMAGE_NOTES_DIGIT, &size);
647                 MI_UncompressLZ16(src, (u32 *)(HW_DB_OBJ_VRAM));
648                 SVC_CpuClear(0xC0, (void *)HW_DB_OAM, HW_OAM_SIZE, 32);
649             }
650             /* Load screen data into VRAM */
651             {
652                 u32     size;
653                 void   *const src = LoadImage(IMAGE_NOTES_SCR, &size);
654                 SVC_CpuCopyFast(src, (u32 *)(HW_DB_BG_VRAM + 0xf000), size);
655             }
656 
657             // Display settings for the ISBN string
658             SetISBNString(isbn, (BOOL)(param != OS_CHINA_ISBN_NO_DISP));
659 
660             /* Edit palette */
661             *((u16 *)(HW_DB_BG_PLTT + 0)) = 0x7fff;
662             *((u16 *)(HW_DB_BG_PLTT + 2)) = 0x0000;
663             *((u16 *)(HW_DB_OBJ_PLTT + 0)) = 0x7fff;
664             *((u16 *)(HW_DB_OBJ_PLTT + 2)) = 0x0000;
665         }
666 
667         /* Start display */
668         reg_GX_DISPCNT |= (u32)(GX_DISPMODE_GRAPHICS << REG_GX_DISPCNT_MODE_SHIFT);
669         reg_GXS_DB_DISPCNT |= (u32)(REG_GXS_DB_DISPCNT_MODE_MASK);
670 
671         /* Interrupt settings */
672         reg_GX_DISPSTAT |= REG_GX_DISPSTAT_VBI_MASK;
673         shelter.irqFunction = OS_GetIrqFunction(OS_IE_V_BLANK);
674         OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
675         shelter.irqMask = OS_EnableIrqMask(OS_IE_V_BLANK);
676         shelter.irq = OS_EnableIrq();
677         shelter.interrupts = OS_EnableInterrupts();
678 
679     }
680 
681     /* Main process */
682     {
683         s32     i;
684         u16     pad_old;
685         u16     pad;
686 
687         /* Fade in */
688         for (i = 0; i < OSi_FORCHINA_FADE_SPAN; i++)
689         {
690             reg_GX_MASTER_BRIGHT = (u16)((1 << REG_GX_MASTER_BRIGHT_E_MOD_SHIFT) |
691                                          16 - ((i + 1) * 16 / OSi_FORCHINA_FADE_SPAN));
692             reg_GXS_DB_MASTER_BRIGHT = reg_GX_MASTER_BRIGHT;
693             WaitForNextFrame();
694         }
695 
696         /* Skip based on button input */
697         pad_old = PAD_Read();
698         for (i = 0; i < OSi_FORCHINA_DISP_SPAN; i++)
699         {
700             pad = PAD_Read();
701             if ((param == OS_CHINA_ISBN_CHECK_ROM) && (~pad_old & pad))
702             {
703                 /* If any button push trigger was detected, skip */
704                 break;
705             }
706             pad_old = pad;
707             WaitForNextFrame();
708 
709             // On ROMs used for censorship purposes, do not exit until a button is pressed
710             if(param == OS_CHINA_ISBN_CHECK_ROM) i = 0;
711         }
712 
713         /* Fade out */
714         for (i = 0; i < OSi_FORCHINA_FADE_SPAN; i++)
715         {
716             reg_GX_MASTER_BRIGHT = (u16)((1 << REG_GX_MASTER_BRIGHT_E_MOD_SHIFT) |
717                                          (i + 1) * 16 / OSi_FORCHINA_FADE_SPAN);
718             reg_GXS_DB_MASTER_BRIGHT = reg_GX_MASTER_BRIGHT;
719             WaitForNextFrame();
720         }
721     }
722 
723     /* Post-processing */
724     {
725         /* Stop display */
726         reg_GX_DISPCNT &= ~(GX_DISPMODE_GRAPHICS << REG_GX_DISPCNT_MODE_SHIFT);
727         reg_GXS_DB_DISPCNT &= ~REG_GXS_DB_DISPCNT_MODE_MASK;
728 
729         /* Restore interrupt-related items */
730         (void)OS_RestoreInterrupts(shelter.interrupts);
731         (void)OS_RestoreIrq(shelter.irq);
732         (void)OS_SetIrqMask(shelter.irqMask);
733         OS_SetIrqFunction(OS_IE_V_BLANK, shelter.irqFunction);
734         reg_GX_DISPSTAT &= ~REG_GX_DISPSTAT_VBI_MASK;
735 
736         /* Clear used VRAM (cannot restore initial values because the size is too big) */
737         SVC_CpuClearFast(0, (u32 *)HW_BG_VRAM, 0x10000);
738         SVC_CpuClearFast(0, (u32 *)HW_BG_PLTT, 0x200);
739         SVC_CpuClearFast(0, (u32 *)HW_DB_BG_VRAM, 0x10000);
740         SVC_CpuClearFast(0, (u32 *)HW_DB_BG_PLTT, 0x04);
741         SVC_CpuClearFast(0, (u32 *)HW_DB_OAM, HW_OAM_SIZE);
742 
743         /* Restore each register */
744         reg_G2S_DB_BG0OFS = shelter.gxs_bg0ofs;
745         reg_G2_BG0OFS = shelter.gx_bg0ofs;
746         reg_G2S_DB_BG0CNT = shelter.gxs_bg0cnt;
747         reg_G2_BG0CNT = shelter.gx_bg0cnt;
748         reg_GX_VRAMCNT_D = shelter.gx_vramcnt_d;
749         reg_GX_VRAMCNT_C = shelter.gx_vramcnt_c;
750         reg_GX_VRAMCNT_A = shelter.gx_vramcnt_a;
751         reg_GX_POWCNT = shelter.gx_powcnt;
752         if (!(shelter.gx_powcnt & REG_GX_POWCNT_LCD_MASK))
753         {
754             /* When changing LCD enable from ON to OFF, wait 100ms */
755             SVC_WaitByLoop(HW_CPU_CLOCK_ARM9 / 40);
756         }
757         reg_GXS_DB_MASTER_BRIGHT = shelter.gxs_bright;
758         reg_GX_MASTER_BRIGHT = shelter.gx_bright;
759         reg_GXS_DB_DISPCNT = shelter.gxs_dispcnt;
760         reg_GX_DISPCNT = shelter.gx_dispcnt;
761         reg_GX_DISPSTAT = shelter.gx_dispstat;
762     }
763 }
764 
765 /*---------------------------------------------------------------------------*
766     End of file
767  *---------------------------------------------------------------------------*/
768