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-11-24#$
14   $Rev: 11190 $
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);
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: u32 pointer to the file size
107                         If not needed, 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 ending OS_ShowAttentionChina(),
111                 each heap is deleted with OS_ClearAlloc() so
112                 you don't need to explicitly free the memory returned here.
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) to a prescribed location in the screen data.
243 
244   Arguments:    isbn: String array relating to the ISBN number (and other information)
245 
246   Returns:      None.
247 
248   Note:         A single character is equivalent to two 8x16 characters.
249                 The actual character sizes are as follows.
250                     '-'      : 5x10
251                     '0'-'9'  : 7x10
252  *---------------------------------------------------------------------------*/
SetISBNString(const char ** isbn)253 static void SetISBNString(const char **isbn)
254 {
255     s32     i, j;
256     const struct
257     {
258         u8      x, y;                  /* Leading display offset */
259         u8      length;                /* Display character count */
260     }
261     pos[] =
262     {
263         {
264         80, 136, 17}
265         ,                              /* ISBN */
266         {
267         125, 151, 12}
268         ,                              /* Joint registration number */
269         {
270         106, 167, 4}
271         ,                              /* (China-specific information displayed on left side) */
272         {
273         139, 167, 3}
274         ,                              /* (China-specific information displayed on right side) */
275     }
276     ;
277     const int   count = sizeof(pos) / sizeof(*pos); /* Total number of lines */
278     const int   digit_num = 12;                     /* Number of characters for the digit display ("0123456789- ") */
279     u16         pos_x;
280     u16         pos_y;
281     u16         index;
282     u16         width;
283 
284     GXOamAttr *dst = (GXOamAttr *)HW_DB_OAM;
285     for (i = 0; i < count; i++)
286     {
287         pos_x = pos[i].x;
288         pos_y = pos[i].y;
289 
290         for (j = 0; j < pos[i].length; ++j)
291         {
292             if (isbn[i][j] == '-')
293             {
294                 index = 10;
295                 width = 5;
296             }
297             else if ((isbn[i][j] >= '0') && (isbn[i][j] <= '9'))
298             {
299                 index = (u16)(isbn[i][j] - '0');
300                 width = 7;
301             }
302             else
303             {
304                 index = 11;
305                 width = 7;
306             }
307 
308             dst[0].attr01 = (u32)((pos_x << 16) | (pos_y << 0));
309             dst[0].attr2  = index;
310             dst[1].attr01 = (u32)((pos_x << 16) | (pos_y + 8 << 0));
311             dst[1].attr2  = (u16)(index + digit_num);
312 
313             pos_x   += width;
314             dst     += 2;
315         }
316     }
317 }
318 
319 /*---------------------------------------------------------------------------*
320   Name:         OS_InitChina
321 
322   Description:  Initializes SDK's OS library.
323                 Exclusively for applications for the China region.
324                 Use this function instead of OS_Init function.
325 
326   Arguments:    isbn: String array relating to the ISBN number (and other information)
327                             {
328                                 char    ISBN[ 17 ] ,
329                                 char    Joint registration code  [ 12 ] ,
330                                 char    (China-specific information displayed on left side) [ 4 ] ,
331                                 char    (China-specific information displayed on right side) [ 3 ]
332                             }
333 
334                 param: Behavior related to displaying ISBN numbers
335 
336   Returns:      None.
337  *---------------------------------------------------------------------------*/
OS_InitChina(const char ** isbn,OSChinaIsbn param)338 void OS_InitChina(const char **isbn, OSChinaIsbn param)
339 {
340     SDK_REFER_SYMBOL(checkString);
341 
342 #ifdef SDK_ARM9
343     //---- System shared area check
344     SDK_ASSERT((u32)&(OS_GetSystemWork()->command_area) == HW_CMD_AREA);
345 #endif  // SDK_ARM9
346 
347     //---- Initialize SDK OS common
348     OSi_InitCommon();
349 
350     //---- Check if possible to boot
351 //    CheckLanguageCode(); Eliminated starting restrictions
352 
353     //---- Display logo and notes about health care
354     OS_ShowAttentionChina(isbn, param);
355 }
356 
357 /*---------------------------------------------------------------------------*
358   Name:         CheckLanguageCode
359 
360   Description:  Confirms whether to perform exclusive startup control based on language code.
361 
362   Arguments:    None.
363 
364   Returns:      None.
365  *---------------------------------------------------------------------------*/
CheckLanguageCode(void)366 static void CheckLanguageCode(void)
367 {
368     NVRAMConfig *src;
369 
370     src = (NVRAMConfig *)(OS_GetSystemWork()->nvramUserInfo);
371     // Confirms whether it has been determined that language code should not be booted by ARM7
372     if ((src->ncd.version == 0xff) && (src->ncd.option.language == 7))
373     {
374         DispExclusiveMessage();
375         // Never return
376     }
377 }
378 
379 /*---------------------------------------------------------------------------*
380   Name:         DispExclusiveMessage
381 
382   Description:  Indicates that exclusive startup control was performed based on language code.
383 
384   Arguments:    None.
385 
386   Returns:      None.
387  *---------------------------------------------------------------------------*/
DispExclusiveMessage(void)388 static void DispExclusiveMessage(void)
389 {
390     // Stop display
391     reg_GX_DISPCNT = 0;
392     reg_GXS_DB_DISPCNT = 0;
393 
394     // Initialize power control
395     if (!(reg_GX_POWCNT & REG_GX_POWCNT_LCD_MASK))
396     {
397         // When changing LCD enable from OFF to ON, wait 100 ms
398         SVC_WaitByLoop(HW_CPU_CLOCK_ARM9 / 40);
399     }
400     reg_GX_POWCNT = (u16)(REG_GX_POWCNT_DSEL_MASK | REG_GX_POWCNT_E2DG_MASK |
401                           REG_GX_POWCNT_E2DGB_MASK | REG_GX_POWCNT_LCD_MASK);
402     // Initialization of master brightness
403     reg_GX_MASTER_BRIGHT = 0;
404     reg_GXS_DB_MASTER_BRIGHT = 0;
405 
406     // Set main LCD
407     {
408         reg_GX_VRAMCNT_A =
409             (u8)((1 << REG_GX_VRAMCNT_A_MST_SHIFT) | (1 << REG_GX_VRAMCNT_A_E_SHIFT));
410         reg_G2_BG0CNT =
411             (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2_BG0CNT_SCREENSIZE_SHIFT) |
412                   (GX_BG_COLORMODE_16 << REG_G2_BG0CNT_COLORMODE_SHIFT) | (GX_BG_SCRBASE_0xf000 <<
413                                                                            REG_G2_BG0CNT_SCREENBASE_SHIFT)
414                   | (GX_BG_CHARBASE_0x00000 << REG_G2_BG0CNT_CHARBASE_SHIFT) | (GX_BG_EXTPLTT_01 <<
415                                                                                 REG_G2_BG0CNT_BGPLTTSLOT_SHIFT)
416                   | (0 << REG_G2_BG0CNT_PRIORITY_SHIFT));
417         reg_G2_BG0HOFS = (u16)(-(256 - OSi_ONLY_FORCHINA_DOT_WIDTH) / 2);
418         reg_G2_BG0VOFS = (u16)(-(192 - OSi_ONLY_FORCHINA_DOT_HEIGHT) / 2 + 2);
419         reg_GX_DISPCNT |= ((GX_BGMODE_0 << REG_GX_DISPCNT_BGMODE_SHIFT) |
420                            (GX_PLANEMASK_BG0 << REG_GX_DISPCNT_DISPLAY_SHIFT));
421         // Load character data into VRAM
422         {
423             MIUnpackBitsParam param = { OSi_ONLY_FORCHINA_CHAR_SIZE, 1, 4, 0, 0 };
424 
425             SVC_UnpackBits(only_forChina_charData, (u32 *)(HW_BG_VRAM + 0x20), &param);
426         }
427         // Load screen data into VRAM
428         {
429             s32     i;
430             s32     j;
431             u16     code = 1;
432             u16    *dst = (u16 *)(HW_BG_VRAM + 0xf000);
433 
434             for (i = 0; i < OSi_ONLY_FORCHINA_CHAR_HEIGHT; i++)
435             {
436                 for (j = 0; j < OSi_ONLY_FORCHINA_CHAR_WIDTH; j++)
437                 {
438                     *dst++ = code++;
439                 }
440                 dst += (0x20 - OSi_ONLY_FORCHINA_CHAR_WIDTH);
441             }
442         }
443     }
444 
445     // Set sub LCD
446     {
447         reg_GX_VRAMCNT_C =
448             (u8)((4 << REG_GX_VRAMCNT_C_MST_SHIFT) | (1 << REG_GX_VRAMCNT_C_E_SHIFT));
449         reg_G2S_DB_BG0CNT =
450             (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2S_DB_BG0CNT_SCREENSIZE_SHIFT) |
451                   (GX_BG_COLORMODE_16 << REG_G2S_DB_BG0CNT_COLORMODE_SHIFT) | (GX_BG_SCRBASE_0xf000
452                                                                                <<
453                                                                                REG_G2S_DB_BG0CNT_SCREENBASE_SHIFT)
454                   | (GX_BG_CHARBASE_0x00000 << REG_G2S_DB_BG0CNT_CHARBASE_SHIFT) | (GX_BG_EXTPLTT_01
455                                                                                     <<
456                                                                                     REG_G2S_DB_BG0CNT_BGPLTTSLOT_SHIFT)
457                   | (0 << REG_G2S_DB_BG0CNT_PRIORITY_SHIFT));
458         reg_G2S_DB_BG0OFS = 0;
459         reg_GXS_DB_DISPCNT |= ((GX_BGMODE_0 << REG_GXS_DB_DISPCNT_BGMODE_SHIFT) |
460                                (GX_PLANEMASK_BG0 << REG_GXS_DB_DISPCNT_DISPLAY_SHIFT));
461     }
462 
463     // V-Blank interrupt settings
464     (void)OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
465     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
466     (void)OS_EnableIrq();
467     reg_GX_DISPSTAT |= REG_GX_DISPSTAT_VBI_MASK;
468 
469     // Start display
470     reg_GX_DISPCNT |= (u32)(GX_DISPMODE_GRAPHICS << REG_GX_DISPCNT_MODE_SHIFT);
471     reg_GXS_DB_DISPCNT |= (u32)(REG_GXS_DB_DISPCNT_MODE_MASK);
472 
473     {
474         u16     data = 0x001f;
475         u16     target = 0x0001 << 5;
476         s32     i;
477 
478         // Cycle palette colors between red and yellow and loop indefinitely
479         while (TRUE)
480         {
481             for (i = 0; i < 31; i++)
482             {
483                 // Confirm that cover is closed
484                 if (TRUE == PAD_DetectFold())
485                 {
486                     // Turn off power
487                     (void)PM_ForceToPowerOff();
488                 }
489                 OS_WaitVBlankIntr();
490                 *((u16 *)(HW_BG_PLTT + 2)) = data;
491                 data += target;
492             }
493             target = (u16)(~target + 1);
494         }
495     }
496 }
497 
498 /*---------------------------------------------------------------------------*
499   Name:         OS_ShowAttentionChina
500 
501   Description:  Displays logo for China and notice for prescribed time.
502 
503   Arguments:    isbn: String array relating to the ISBN number (and other information)
504                 param: Behavior related to displaying ISBN numbers
505 
506   Returns:      None.
507  *---------------------------------------------------------------------------*/
OS_ShowAttentionChina(const char ** isbn,OSChinaIsbn param)508 void OS_ShowAttentionChina(const char **isbn, OSChinaIsbn param)
509 {
510     /* Structure for saving register */
511     struct
512     {
513         u32     gx_dispcnt;
514         u32     gxs_dispcnt;
515         u16     gx_powcnt;
516         u16     gx_dispstat;
517         u16     gx_bright;
518         u16     gxs_bright;
519         u16     gx_bg0cnt;
520         u16     gxs_bg0cnt;
521         u32     gx_bg0ofs;
522         u32     gxs_bg0ofs;
523         u8      gx_vramcnt_a;
524         u8      gx_vramcnt_c;
525         u8      gx_vramcnt_d;
526         u8      reserved[1];
527 
528         OSIrqFunction irqFunction;
529         OSIrqMask irqMask;
530         BOOL    irq;
531         OSIntrMode interrupts;
532 
533     }
534     shelter;
535 
536     // Returns immediately if not displaying ISBN.
537     if ( param == OS_CHINA_ISBN_NO_DISP )
538     {
539         return;
540     }
541 
542     /* Pre-processing */
543     {
544         /* Save each register */
545         shelter.gx_dispstat = reg_GX_DISPSTAT;
546         shelter.gx_dispcnt = reg_GX_DISPCNT;
547         shelter.gxs_dispcnt = reg_GXS_DB_DISPCNT;
548         shelter.gx_bright = reg_GX_MASTER_BRIGHT;
549         shelter.gxs_bright = reg_GXS_DB_MASTER_BRIGHT;
550         shelter.gx_powcnt = reg_GX_POWCNT;
551         shelter.gx_vramcnt_a = reg_GX_VRAMCNT_A;
552         shelter.gx_vramcnt_c = reg_GX_VRAMCNT_C;
553         shelter.gx_vramcnt_d = reg_GX_VRAMCNT_D;
554         shelter.gx_bg0cnt = reg_G2_BG0CNT;
555         shelter.gxs_bg0cnt = reg_G2S_DB_BG0CNT;
556         shelter.gx_bg0ofs = reg_G2_BG0OFS;
557         shelter.gxs_bg0ofs = reg_G2S_DB_BG0OFS;
558 
559         /* Stop display */
560         reg_GX_DISPCNT = 0;
561         reg_GXS_DB_DISPCNT = 0;
562 
563         /* Initialize power control */
564         if (!(shelter.gx_powcnt & REG_GX_POWCNT_LCD_MASK))
565         {
566             /* When changing LCD enable from OFF to ON, wait 100 ms */
567             SVC_WaitByLoop(HW_CPU_CLOCK_ARM9 / 40);
568         }
569         reg_GX_POWCNT = (u16)(REG_GX_POWCNT_DSEL_MASK | REG_GX_POWCNT_E2DG_MASK |
570                               REG_GX_POWCNT_E2DGB_MASK | REG_GX_POWCNT_LCD_MASK);
571 
572         /* Initialization of master brightness */
573         reg_GX_MASTER_BRIGHT = (u16)((1 << REG_GX_MASTER_BRIGHT_E_MOD_SHIFT) | 16);
574         reg_GXS_DB_MASTER_BRIGHT = reg_GX_MASTER_BRIGHT;
575 
576         /* Prepare display of logo screen */
577         {
578             reg_GX_VRAMCNT_A =
579                 (u8)((1 << REG_GX_VRAMCNT_A_MST_SHIFT) | (1 << REG_GX_VRAMCNT_A_E_SHIFT));
580             reg_G2_BG0CNT =
581                 (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2_BG0CNT_SCREENSIZE_SHIFT) |
582                       (GX_BG_COLORMODE_256 << REG_G2_BG0CNT_COLORMODE_SHIFT) | (GX_BG_SCRBASE_0xf000
583                                                                                 <<
584                                                                                 REG_G2_BG0CNT_SCREENBASE_SHIFT)
585                       | (GX_BG_CHARBASE_0x00000 << REG_G2_BG0CNT_CHARBASE_SHIFT) | (GX_BG_EXTPLTT_01
586                                                                                     <<
587                                                                                     REG_G2_BG0CNT_BGPLTTSLOT_SHIFT)
588                       | (0 << REG_G2_BG0CNT_PRIORITY_SHIFT));
589             reg_G2_BG0OFS = 0;
590             reg_GX_DISPCNT |= ((GX_BGMODE_0 << REG_GX_DISPCNT_BGMODE_SHIFT) |
591                                (GX_PLANEMASK_BG0 << REG_GX_DISPCNT_DISPLAY_SHIFT));
592 
593             /* Load character data into VRAM */
594             {
595                 void   *const src = LoadImage(IMAGE_LOGO_CHR, NULL);
596                 MI_UncompressLZ16(src, (u32 *)HW_BG_VRAM);
597             }
598             /* Load screen data into VRAM */
599             {
600                 u16     *src = (u16*)LoadImage(IMAGE_LOGO_SCR, NULL);
601                 u16     *dst;
602                 s32     i;
603                 s32     j;
604 
605                 // Place 12x12 (96x96-pixel) screen data at the center
606                 SVC_CpuClearFast(0, (u32 *)(HW_BG_VRAM + 0xf000), 32*24*2);
607                 dst = (u16*)(HW_BG_VRAM + 0xf000 + ((6*32+10)*2));
608 
609                 for (i = 0; i < 12; i++, dst += 32)
610                 {
611                     for (j = 0; j < 12; j++, src++)
612                     {
613                         dst[j] = *src;
614                     }
615                 }
616             }
617             /* Load palette data into VRAM */
618             {
619                 u32     size;
620                 void   *const src = LoadImage(IMAGE_LOGO_PAL, &size);
621                 SVC_CpuCopyFast(src, (u32 *)(HW_BG_PLTT), size);
622             }
623         }
624 
625         /* Prepare to display Notes screen */
626         {
627             reg_GX_VRAMCNT_C =
628                 (u8)((4 << REG_GX_VRAMCNT_C_MST_SHIFT) | (1 << REG_GX_VRAMCNT_C_E_SHIFT));
629             reg_GX_VRAMCNT_D =
630                 (u8)((4 << REG_GX_VRAMCNT_D_MST_SHIFT) | (1 << REG_GX_VRAMCNT_D_E_SHIFT));
631             reg_G2S_DB_BG0CNT =
632                 (u16)((GX_BG_SCRSIZE_TEXT_256x256 << REG_G2S_DB_BG0CNT_SCREENSIZE_SHIFT) |
633                       (GX_BG_COLORMODE_16 << REG_G2S_DB_BG0CNT_COLORMODE_SHIFT) |
634                       (GX_BG_SCRBASE_0xf000 << REG_G2S_DB_BG0CNT_SCREENBASE_SHIFT) |
635                       (GX_BG_CHARBASE_0x00000 << REG_G2S_DB_BG0CNT_CHARBASE_SHIFT) |
636                       (GX_BG_EXTPLTT_01 << REG_G2S_DB_BG0CNT_BGPLTTSLOT_SHIFT) | (0 <<
637                                                                                   REG_G2S_DB_BG0CNT_PRIORITY_SHIFT));
638             reg_G2S_DB_BG0OFS = 0;
639             reg_GXS_DB_DISPCNT |= ((GX_BGMODE_0 << REG_GXS_DB_DISPCNT_BGMODE_SHIFT) |
640                                    ((GX_PLANEMASK_BG0 | GX_PLANEMASK_OBJ) <<
641                                     REG_GXS_DB_DISPCNT_DISPLAY_SHIFT));
642 
643             /* Load character data into VRAM */
644             {
645                 u32     size;
646                 void   *src;
647                 /* Background text */
648                 src = LoadImage(IMAGE_NOTES_CHR, &size);
649                 MI_UncompressLZ16(src, (u32 *)(HW_DB_BG_VRAM));
650                 /* OBJ text */
651                 src = LoadImage(IMAGE_NOTES_DIGIT, &size);
652                 MI_UncompressLZ16(src, (u32 *)(HW_DB_OBJ_VRAM));
653                 SVC_CpuClear(0xC0, (void *)HW_DB_OAM, HW_OAM_SIZE, 32);
654             }
655             /* Load screen data into VRAM */
656             {
657                 u32     size;
658                 void   *const src = LoadImage(IMAGE_NOTES_SCR, &size);
659                 SVC_CpuCopyFast(src, (u32 *)(HW_DB_BG_VRAM + 0xf000), size);
660             }
661 
662             // Display settings for the ISBN string
663             SetISBNString(isbn);
664 
665             /* Edit palette */
666             *((u16 *)(HW_DB_BG_PLTT + 0)) = 0x7fff;
667             *((u16 *)(HW_DB_BG_PLTT + 2)) = 0x0000;
668             *((u16 *)(HW_DB_OBJ_PLTT + 0)) = 0x7fff;
669             *((u16 *)(HW_DB_OBJ_PLTT + 2)) = 0x0000;
670         }
671 
672         /* Start display */
673         reg_GX_DISPCNT |= (u32)(GX_DISPMODE_GRAPHICS << REG_GX_DISPCNT_MODE_SHIFT);
674         reg_GXS_DB_DISPCNT |= (u32)(REG_GXS_DB_DISPCNT_MODE_MASK);
675 
676         /* Interrupt settings */
677         reg_GX_DISPSTAT |= REG_GX_DISPSTAT_VBI_MASK;
678         shelter.irqFunction = OS_GetIrqFunction(OS_IE_V_BLANK);
679         OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
680         shelter.irqMask = OS_EnableIrqMask(OS_IE_V_BLANK);
681         shelter.irq = OS_EnableIrq();
682         shelter.interrupts = OS_EnableInterrupts();
683 
684     }
685 
686     /* Main process */
687     {
688         s32     i;
689         u16     pad_old;
690         u16     pad;
691 
692         /* Fade in */
693         for (i = 0; i < OSi_FORCHINA_FADE_SPAN; i++)
694         {
695             reg_GX_MASTER_BRIGHT = (u16)((1 << REG_GX_MASTER_BRIGHT_E_MOD_SHIFT) |
696                                          16 - ((i + 1) * 16 / OSi_FORCHINA_FADE_SPAN));
697             reg_GXS_DB_MASTER_BRIGHT = reg_GX_MASTER_BRIGHT;
698             WaitForNextFrame();
699         }
700 
701         /* Skip based on button input */
702         pad_old = PAD_Read();
703         for (i = 0; i < OSi_FORCHINA_DISP_SPAN; i++)
704         {
705             pad = PAD_Read();
706             if ((param == OS_CHINA_ISBN_CHECK_ROM) && (~pad_old & pad))
707             {
708                 /* If any button presses were detected, skip */
709                 break;
710             }
711             pad_old = pad;
712             WaitForNextFrame();
713 
714             // On ROMs used for censorship purposes, do not exit until a button is pressed
715             if(param == OS_CHINA_ISBN_CHECK_ROM) i = 0;
716         }
717 
718         /* Fade out */
719         for (i = 0; i < OSi_FORCHINA_FADE_SPAN; i++)
720         {
721             reg_GX_MASTER_BRIGHT = (u16)((1 << REG_GX_MASTER_BRIGHT_E_MOD_SHIFT) |
722                                          (i + 1) * 16 / OSi_FORCHINA_FADE_SPAN);
723             reg_GXS_DB_MASTER_BRIGHT = reg_GX_MASTER_BRIGHT;
724             WaitForNextFrame();
725         }
726     }
727 
728     /* Post-processing */
729     {
730         /* Stop display */
731         reg_GX_DISPCNT &= ~(GX_DISPMODE_GRAPHICS << REG_GX_DISPCNT_MODE_SHIFT);
732         reg_GXS_DB_DISPCNT &= ~REG_GXS_DB_DISPCNT_MODE_MASK;
733 
734         /* Restore interrupt-related items */
735         (void)OS_RestoreInterrupts(shelter.interrupts);
736         (void)OS_RestoreIrq(shelter.irq);
737         (void)OS_SetIrqMask(shelter.irqMask);
738         OS_SetIrqFunction(OS_IE_V_BLANK, shelter.irqFunction);
739         reg_GX_DISPSTAT &= ~REG_GX_DISPSTAT_VBI_MASK;
740 
741         /* Clear used VRAM (cannot restore initial values because the size is too big) */
742         SVC_CpuClearFast(0, (u32 *)HW_BG_VRAM, 0x10000);
743         SVC_CpuClearFast(0, (u32 *)HW_BG_PLTT, 0x200);
744         SVC_CpuClearFast(0, (u32 *)HW_DB_BG_VRAM, 0x10000);
745         SVC_CpuClearFast(0, (u32 *)HW_DB_BG_PLTT, 0x04);
746         SVC_CpuClearFast(0, (u32 *)HW_DB_OAM, HW_OAM_SIZE);
747 
748         /* Restore each register */
749         reg_G2S_DB_BG0OFS = shelter.gxs_bg0ofs;
750         reg_G2_BG0OFS = shelter.gx_bg0ofs;
751         reg_G2S_DB_BG0CNT = shelter.gxs_bg0cnt;
752         reg_G2_BG0CNT = shelter.gx_bg0cnt;
753         reg_GX_VRAMCNT_D = shelter.gx_vramcnt_d;
754         reg_GX_VRAMCNT_C = shelter.gx_vramcnt_c;
755         reg_GX_VRAMCNT_A = shelter.gx_vramcnt_a;
756         reg_GX_POWCNT = shelter.gx_powcnt;
757         if (!(shelter.gx_powcnt & REG_GX_POWCNT_LCD_MASK))
758         {
759             /* When changing LCD enable from ON to OFF, wait 100 ms */
760             SVC_WaitByLoop(HW_CPU_CLOCK_ARM9 / 40);
761         }
762         reg_GXS_DB_MASTER_BRIGHT = shelter.gxs_bright;
763         reg_GX_MASTER_BRIGHT = shelter.gx_bright;
764         reg_GXS_DB_DISPCNT = shelter.gxs_dispcnt;
765         reg_GX_DISPCNT = shelter.gx_dispcnt;
766         reg_GX_DISPSTAT = shelter.gx_dispstat;
767     }
768 }
769 
770 /*---------------------------------------------------------------------------*
771     End of file
772  *---------------------------------------------------------------------------*/
773