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