render.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. /* MegaZeux
  2. *
  3. * Copyright (C) 2007-2009 Kevin Vance <kvance@kvance.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. */
  19. /* NOMENCLATURE NOTICE:
  20. * What we call the "subscreen" is called the "main screen" by libnds.
  21. * But in terms of functionality, it only contains auxiliary screen modes.
  22. */
  23. #include <string.h>
  24. #include "render.h"
  25. #include "../../src/renderers.h"
  26. #include "../../src/graphics.h"
  27. #include "../../src/render.h"
  28. #include <nds/arm9/keyboard.h>
  29. // These variables control the panning along the 1:1 "main" screen.
  30. static int cell_pan_x = 0;
  31. static int cell_pan_y = 0;
  32. // When set to >= 0, the upper screen will attempt to focus to this position
  33. // the next time it is drawn and then reset them to -1.
  34. static int focus_x = -1;
  35. static int focus_y = -1;
  36. // This table stores scroll register values for each scanline.
  37. static u16 scroll_table[192];
  38. // This table maps palette color combinations to their index in BG_PALETTE.
  39. static int palette_idx_table[16][16];
  40. // If we're looking around with the mouse, ignore the next call to focus.
  41. static boolean mouselook;
  42. // The current transition state.
  43. static struct {
  44. enum { TRANSITION_NONE = 0, TRANSITION_IN, TRANSITION_OUT } state;
  45. int time;
  46. } transition;
  47. // The subscreen can display different information.
  48. enum Subscreen_Mode last_subscreen_mode;
  49. enum Subscreen_Mode subscreen_mode;
  50. // Forward declarations
  51. static void nds_keyboard_scroll_in(void);
  52. static void nds_keyboard_scroll_out(void);
  53. // Do this every vblank irq:
  54. static void nds_on_vblank(void)
  55. {
  56. /* Handle sleep mode. */
  57. nds_sleep_check();
  58. /* Do all special video handling. */
  59. nds_video_rasterhack();
  60. nds_video_jitter();
  61. nds_video_do_transition();
  62. }
  63. boolean is_scaled_mode(enum Subscreen_Mode mode)
  64. {
  65. return (mode == SUBSCREEN_SCALED);
  66. }
  67. static void palette_idx_table_init(void)
  68. {
  69. // Start the palette at 16, reserving colors 0-16 for the overlay.
  70. const int offset = 16;
  71. int idx;
  72. // Store the normal palette sequentially.
  73. for(idx = 0; idx < 16; idx++)
  74. palette_idx_table[idx][idx] = idx + offset;
  75. // Store the blends after that.
  76. idx += offset;
  77. for(int a = 0; a < 16; a++)
  78. {
  79. for(int b = a+1; b < 16; b++)
  80. {
  81. palette_idx_table[a][b] = idx;
  82. palette_idx_table[b][a] = idx;
  83. idx++;
  84. }
  85. }
  86. }
  87. static void nds_subscreen_scaled_init(void)
  88. {
  89. int xscale = (int)(320.0/256.0 * 256.0);
  90. int yscale = (int)(350.0/192.0 * 256.0);
  91. /* Use banks A and B for the ZZT screen. */
  92. videoSetMode(MODE_5_2D | DISPLAY_BG2_ACTIVE | DISPLAY_BG3_ACTIVE);
  93. vramSetBankA(VRAM_A_MAIN_BG);
  94. vramSetBankB(VRAM_B_MAIN_BG);
  95. /* Use flickered alpha lerp to scale 320x350 to 256x192. */
  96. /* (Bump the BG up to base 3 (+0xc0000) to make room for the keyboard.) */
  97. REG_BG2CNT = BG_BMP8_512x512 | BG_BMP_BASE(3) | BG_PRIORITY(0);
  98. REG_BG2PA = xscale;
  99. REG_BG2PB = 0;
  100. REG_BG2PC = 0;
  101. REG_BG2PD = yscale;
  102. REG_BG2X = 0;
  103. REG_BG2Y = 0;
  104. REG_BG3CNT = BG_BMP8_512x512 | BG_BMP_BASE(3) | BG_PRIORITY(0);
  105. REG_BG3PA = xscale;
  106. REG_BG3PB = 0;
  107. REG_BG3PC = 0;
  108. REG_BG3PD = yscale;
  109. REG_BG3X = 0;
  110. REG_BG3Y = 0;
  111. /* Enable BG2/BG3 blending. */
  112. REG_BLDCNT = BLEND_ALPHA | BLEND_SRC_BG2 | BLEND_DST_BG3;
  113. REG_BLDALPHA = (8 << 8) | 8; /* 50% / 50% */
  114. update_palette();
  115. update_screen();
  116. }
  117. static void nds_subscreen_keyboard_init(void)
  118. {
  119. Keyboard *kb = keyboardGetDefault();
  120. // BG0: Keyboard 256x512 text, map following tiles (45056 bytes total)
  121. // Clear the keyboard area before drawing it.
  122. dmaFillWords(0, BG_MAP_RAM(20), 4096);
  123. keyboardInit(kb, 0, BgType_Text4bpp, BgSize_T_256x512, 20, 0, true, true);
  124. bgHide(0);
  125. kb->scrollSpeed = 7;
  126. transition.time = 0;
  127. transition.state = TRANSITION_IN;
  128. }
  129. static void nds_subscreen_keyboard_exit(void)
  130. {
  131. transition.time = 0;
  132. transition.state = TRANSITION_OUT;
  133. }
  134. static void nds_mainscreen_init(struct graphics_data *graphics)
  135. {
  136. u16 *vram;
  137. int i;
  138. graphics->fullscreen = true;
  139. graphics->resolution_width = 640;
  140. graphics->resolution_height = 350;
  141. /* Use bank C for the text screens. */
  142. videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE);
  143. vramSetBankC(VRAM_C_SUB_BG);
  144. /* BG0: foreground characters. */
  145. REG_BG0CNT_SUB = BG_64x32 | BG_COLOR_16 | BG_MAP_BASE(0) |
  146. BG_TILE_BASE(1);
  147. REG_BG0HOFS_SUB = 0;
  148. REG_BG0VOFS_SUB = 0;
  149. /* BG1: background characters. */
  150. REG_BG1CNT_SUB = BG_64x32 | BG_COLOR_16 | BG_MAP_BASE(2) |
  151. BG_TILE_BASE(1);
  152. REG_BG1HOFS_SUB = 0;
  153. REG_BG1VOFS_SUB = 0;
  154. // By default, pan to the center of the screen.
  155. focus_x = 640/2;
  156. focus_y = 350/2;
  157. // Add a solid tile for background colors.
  158. vram = (u16*)BG_TILE_RAM_SUB(1) + 32*256;
  159. for(i = 0; i < 16; i++)
  160. *(vram++) = 1 << 12 | 1 << 8 | 1 << 4 | 1;
  161. }
  162. void nds_video_jitter(void)
  163. {
  164. const u16 jitterF4[] = {
  165. 0x060, 0x000, /* 0.375, 0.000 */
  166. 0x020, 0x100, /* 0.125, 1.000 */
  167. 0x0e0, 0x000, /* 0.875, 0.000 */
  168. 0x0a0, 0x100, /* 0.625, 1.000 */
  169. };
  170. static size_t jidx = 0; /* jitter table index */
  171. /* Jitter the backgrounds for scaled mode. */
  172. if(jidx >= sizeof(jitterF4)/sizeof(*jitterF4))
  173. jidx = 0;
  174. REG_BG2X = jitterF4[jidx];
  175. REG_BG2Y = jitterF4[jidx+1];
  176. REG_BG3X = jitterF4[jidx+2];
  177. REG_BG3Y = jitterF4[jidx+3];
  178. jidx += 4;
  179. }
  180. /* Use HBlank DMA to load row scroll values in for each line. */
  181. void nds_video_rasterhack(void)
  182. {
  183. /* Prepare DMA transfer. */
  184. DMA1_CR = 0;
  185. REG_BG0VOFS_SUB = scroll_table[0];
  186. DMA1_SRC = (u32)(scroll_table + 1);
  187. DMA1_DEST = (u32)&REG_BG0VOFS_SUB;
  188. DMA1_CR = DMA_DST_FIX | DMA_SRC_INC | DMA_REPEAT | DMA_16_BIT |
  189. DMA_START_HBL | DMA_ENABLE | 1;
  190. DMA2_CR = 0;
  191. REG_BG1VOFS_SUB = scroll_table[0];
  192. DMA2_SRC = (u32)(scroll_table + 1);
  193. DMA2_DEST = (u32)&REG_BG1VOFS_SUB;
  194. DMA2_CR = DMA_DST_FIX | DMA_SRC_INC | DMA_REPEAT | DMA_16_BIT |
  195. DMA_START_HBL | DMA_ENABLE | 1;
  196. }
  197. // Handle the transition animation.
  198. void nds_video_do_transition(void)
  199. {
  200. // On transitioning in, use the current screen. Out, use the last screen.
  201. enum Subscreen_Mode mode;
  202. if(transition.state == TRANSITION_NONE)
  203. return;
  204. if(transition.state == TRANSITION_IN)
  205. mode = subscreen_mode;
  206. else // TRANSITION_OUT
  207. mode = last_subscreen_mode;
  208. if(mode == SUBSCREEN_KEYBOARD)
  209. {
  210. if(transition.state == TRANSITION_IN)
  211. nds_keyboard_scroll_in();
  212. else
  213. nds_keyboard_scroll_out();
  214. }
  215. }
  216. static void nds_keyboard_scroll_in(void)
  217. {
  218. Keyboard *kb = keyboardGetDefault();
  219. if(transition.time == 0)
  220. {
  221. // Show the background.
  222. kb->visible = 1;
  223. bgSetScroll(kb->background, 0, -192);
  224. bgUpdate();
  225. bgShow(kb->background);
  226. }
  227. else
  228. {
  229. // Scroll 80 pixels.
  230. if(transition.time < 80)
  231. {
  232. bgSetScroll(kb->background, 0, -192 + transition.time);
  233. }
  234. else
  235. {
  236. bgSetScroll(kb->background, 0, kb->offset_y);
  237. transition.state = TRANSITION_NONE;
  238. }
  239. bgUpdate();
  240. }
  241. transition.time += kb->scrollSpeed;
  242. }
  243. static void nds_keyboard_scroll_out(void)
  244. {
  245. Keyboard *kb = keyboardGetDefault();
  246. if(transition.time == 0)
  247. kb->visible = 0;
  248. if(transition.time >= 80)
  249. {
  250. // Hide the background.
  251. bgHide(kb->background);
  252. transition.state = TRANSITION_NONE;
  253. }
  254. else
  255. {
  256. // Scroll 80 pixels.
  257. bgSetScroll(kb->background, 0, kb->offset_y - transition.time);
  258. bgUpdate();
  259. }
  260. transition.time += kb->scrollSpeed;
  261. }
  262. void nds_sleep_check(void)
  263. {
  264. static boolean asleep = false;
  265. // Check if we were just woken up.
  266. if(asleep)
  267. {
  268. asleep = false;
  269. // Wait a bit until returning power.
  270. while(REG_VCOUNT != 0);
  271. while(REG_VCOUNT == 0);
  272. while(REG_VCOUNT != 0);
  273. powerOn(POWER_ALL);
  274. }
  275. // Check if it's time to go to sleep.
  276. if(keysDown() & KEY_LID)
  277. {
  278. // Cancel DMA from raster effects.
  279. DMA1_CR = 0;
  280. DMA2_CR = 0;
  281. // Power everything off.
  282. powerOff(POWER_ALL);
  283. asleep = true;
  284. }
  285. }
  286. static boolean nds_init_video(struct graphics_data *graphics,
  287. struct config_info *config)
  288. {
  289. lcdMainOnBottom();
  290. // Build the palette blend mapping table.
  291. palette_idx_table_init();
  292. // Start with scaled mode.
  293. subscreen_mode = SUBSCREEN_SCALED;
  294. last_subscreen_mode = SUBSCREEN_MODE_INVALID;
  295. nds_subscreen_scaled_init();
  296. // Initialize the 1:1 scaled "main" screen.
  297. nds_mainscreen_init(graphics);
  298. mouselook = false;
  299. transition.state = TRANSITION_NONE;
  300. transition.time = 0;
  301. // Now that we're initialized, install the vblank handler.
  302. irqSet(IRQ_VBLANK, nds_on_vblank);
  303. return true;
  304. }
  305. static boolean nds_check_video_mode(struct graphics_data *graphics,
  306. int width, int height, int depth, boolean fullscreen, boolean resize)
  307. {
  308. return true; // stub
  309. }
  310. static boolean nds_set_video_mode(struct graphics_data *graphics,
  311. int width, int height, int depth, boolean fullscreen, boolean resize)
  312. {
  313. return true; // stub
  314. }
  315. // Focus on a given screen position (in pixels up to 640x350).
  316. static void nds_mainscreen_focus(int x, int y)
  317. {
  318. static int old_x = -1;
  319. static int old_y = -1;
  320. int scroll_x, scroll_y, i, ypos, ycounter;
  321. u16 *sptr;
  322. if(mouselook)
  323. {
  324. // We're mouselooking, don't move the focus.
  325. return;
  326. }
  327. if(x == old_x && y == old_y)
  328. {
  329. // Already focused there, quick abort.
  330. return;
  331. }
  332. old_x = x;
  333. old_y = y;
  334. // Clamp the coordinates so we stay within the screen.
  335. if(x < 132) x = 132;
  336. if(x > 508) x = 508;
  337. if(y < 96) y = 96;
  338. if(y > 253) y = 253;
  339. // Move to the top-left corner.
  340. x -= 264/2;
  341. y -= 192/2;
  342. // Find the relevant cell coordinates and scroll offsets into those cells.
  343. cell_pan_x = x / 8;
  344. scroll_x = x % 8;
  345. cell_pan_y = y / 14;
  346. scroll_y = y % 14;
  347. // Adjust the X scroll registers now.
  348. REG_BG0HOFS_SUB = scroll_x;
  349. REG_BG1HOFS_SUB = scroll_x;
  350. // Recalculate the scroll table.
  351. sptr = scroll_table;
  352. ypos = scroll_y;
  353. ycounter = scroll_y;
  354. for(i = 0; i < 192; i++)
  355. {
  356. (*sptr++) = ypos;
  357. ycounter++;
  358. if(ycounter == 14)
  359. {
  360. ycounter = 0;
  361. ypos += 2;
  362. }
  363. }
  364. }
  365. // Render the scaled screen.
  366. static void nds_render_graph_scaled(struct graphics_data *graphics)
  367. {
  368. int const WIDTH_IN_PIXELS = 80 * 4; // Screen width in pixels
  369. int const WIDTH_IN_CELLS = 80; // Screen width in chars
  370. int const CHARACTER_HEIGHT = 14; // Character height in pixels
  371. int const VRAM_WIDTH = 512; // HW surface width in pixels
  372. struct char_element *text_cell = graphics->text_video;
  373. u32 *vram_ptr = (u32*)BG_BMP_RAM(3);
  374. int chars, lines;
  375. /* Iterate over every character in text memory. */
  376. int columns = 0;
  377. for(chars = 0; chars < 80*25; chars++)
  378. {
  379. /* Extract the character, and fore/back colors. */
  380. int chr = (*text_cell).char_value & 0xFF;
  381. int bg = (*text_cell).bg_color & 0x0F;
  382. int fg = (*text_cell).fg_color & 0x0F;
  383. // Construct a table mapping charset two-bit pairs to palette entries.
  384. u32 bg_idx = bg + 16; // Solid colors are offset by 16.
  385. u32 fg_idx = fg + 16;
  386. u32 mix_idx = palette_idx_table[bg][fg];
  387. u32 pal_tbl[4] = {
  388. bg_idx, /* 00: bg color */
  389. mix_idx, /* 01: fg+bg blend */
  390. mix_idx, /* 10: " " */
  391. fg_idx /* 11: fg color */
  392. };
  393. /* Iterate over every line in the character. */
  394. u8 *line = graphics->charset + CHARACTER_HEIGHT * chr;
  395. u32* vram_next = vram_ptr + 1;
  396. for(lines = 0; lines < CHARACTER_HEIGHT; lines++)
  397. {
  398. /* Plot four pixels using the two-bit pair table. */
  399. u32 fourpx = pal_tbl[((*line) & 0xC0) >> 6] ;
  400. fourpx |= pal_tbl[((*line) & 0x30) >> 4] << 8;
  401. fourpx |= pal_tbl[((*line) & 0x0C) >> 2] << 16;
  402. fourpx |= pal_tbl[((*line) & 0x03) ] << 24;
  403. *((u32*)vram_ptr) = fourpx;
  404. /* Advance to the next line. */
  405. line++;
  406. vram_ptr += VRAM_WIDTH/4;
  407. }
  408. /* Advance to the next character. */
  409. text_cell++;
  410. vram_ptr = vram_next;
  411. /* Insert padding at the end of a line. */
  412. columns++;
  413. if(columns == WIDTH_IN_CELLS)
  414. {
  415. columns = 0;
  416. vram_ptr += (VRAM_WIDTH - WIDTH_IN_PIXELS)/4;
  417. vram_ptr += VRAM_WIDTH * 13 / 4;
  418. }
  419. }
  420. }
  421. static void nds_render_graph_1to1(struct graphics_data *graphics)
  422. {
  423. struct char_element *text_start = graphics->text_video;
  424. struct char_element *text_cell;
  425. u16 *vram;
  426. int x, y;
  427. // Before drawing the screen, check if the focus needs to be updated!
  428. if(focus_x > -1 && focus_y > -1)
  429. {
  430. nds_mainscreen_focus(focus_x, focus_y);
  431. focus_x = -1;
  432. focus_y = -1;
  433. }
  434. text_start += cell_pan_x + 80*cell_pan_y;
  435. text_cell = text_start;
  436. /* Plot the first 32x30 foreground tiles. */
  437. vram = (u16*)BG_MAP_RAM_SUB(0);
  438. for(y = 0; y < 15; y++)
  439. {
  440. // Draw the top halves of this line for tile_offset=0, then the bottom
  441. // halves for tile_offset=1.
  442. int tile_offset = 0;
  443. while(tile_offset != 2)
  444. {
  445. int chr;
  446. int fg;
  447. for(x = 0; x < 32; x++)
  448. {
  449. // Extract the character and the foreground color.
  450. chr = (*text_cell).char_value & 0xFF;
  451. fg = (*text_cell).fg_color & 0x0F;
  452. *vram = (2*chr + tile_offset) | (fg << 12);
  453. text_cell++;
  454. vram++;
  455. }
  456. // Plot the 33rd column (in the next plane)
  457. chr = (*text_cell).char_value & 0xFF;
  458. fg = (*text_cell).fg_color & 0x0F;
  459. *(vram+992) = (2*chr + tile_offset) | (fg << 12);
  460. // Move back.
  461. text_cell -= 32;
  462. tile_offset++;
  463. }
  464. // Move to the next row.
  465. text_cell += 80;
  466. }
  467. // Plot the first 32x30 background tiles.
  468. text_cell = text_start;
  469. vram = (u16*)BG_MAP_RAM_SUB(2);
  470. for(y = 0; y < 15; y++)
  471. {
  472. // Draw the top halves of this line for tile_offset=0, then the bottom
  473. // halves for tile_offset=1.
  474. int tile_offset = 0;
  475. while(tile_offset != 2)
  476. {
  477. int bg;
  478. for(x = 0; x < 32; x++)
  479. {
  480. // Extract the background color only.
  481. bg = (*text_cell).bg_color & 0x0F;
  482. *vram = 512 | (bg << 12);
  483. text_cell++;
  484. vram++;
  485. }
  486. // Plot the 33rd column (in the next plane)
  487. bg = (*text_cell).bg_color & 0x0F;
  488. *(vram+992) = 512 | (bg << 12);
  489. // Move back.
  490. text_cell -= 32;
  491. tile_offset++;
  492. }
  493. // Move to the next row.
  494. text_cell += 80;
  495. }
  496. }
  497. static void nds_render_graph(struct graphics_data *graphics)
  498. {
  499. // Always render the scaled screen.
  500. nds_render_graph_scaled(graphics);
  501. // Always render the 1:1 "main" screen.
  502. nds_render_graph_1to1(graphics);
  503. }
  504. static void nds_update_palette_entry(struct rgb_color *palette, Uint32 idx)
  505. {
  506. struct rgb_color color1 = palette[idx];
  507. int idx2;
  508. if(idx < 16)
  509. {
  510. // Update the mainscreen palette.
  511. BG_PALETTE_SUB[16*idx + 1] = RGB15(color1.r/8, color1.g/8, color1.b/8);
  512. // Update the subscreen palette.
  513. /* Iterate over an entire column of the palette, blending this color
  514. * with all 16 other colors (including itself). */
  515. for(idx2 = 0; idx2 < 16; idx2++)
  516. {
  517. /* Average the colors while reducing their accuracy to 5 bits. */
  518. struct rgb_color color2 = palette[idx2];
  519. u16 r = (color1.r + color2.r) / 16;
  520. u16 g = (color1.g + color2.g) / 16;
  521. u16 b = (color1.b + color2.b) / 16;
  522. BG_PALETTE[ palette_idx_table[idx][idx2] ] = RGB15(r, g, b);
  523. }
  524. }
  525. }
  526. static void nds_update_colors(struct graphics_data *graphics,
  527. struct rgb_color *palette, Uint32 count)
  528. {
  529. Uint32 i;
  530. for(i = 0; i < count; i++)
  531. nds_update_palette_entry(palette, i);
  532. }
  533. static void nds_resize_screen(struct graphics_data *graphics, int w, int h)
  534. {
  535. // stub
  536. }
  537. static void nds_render_cursor(struct graphics_data *graphics,
  538. Uint32 x, Uint32 y, Uint16 color, Uint8 lines, Uint8 offset)
  539. {
  540. // stub
  541. }
  542. static void nds_render_mouse(struct graphics_data *graphics,
  543. Uint32 x, Uint32 y, Uint8 w, Uint8 h)
  544. {
  545. // stub
  546. }
  547. static void nds_sync_screen(struct graphics_data *graphics)
  548. {
  549. // stub
  550. }
  551. static void nds_remap_char(struct graphics_data *graphics, Uint16 chr)
  552. {
  553. if(chr < 256)
  554. {
  555. /* Each character is 64 bytes. Advance the vram pointer. */
  556. u16* vram = (u16*)BG_TILE_RAM_SUB(1) + 32*chr;
  557. u8* charset = graphics->charset + 14*chr;
  558. /* Iterate over every line. */
  559. u8 *end = charset + 14;
  560. for(; charset < end; charset++)
  561. {
  562. u16 line = *charset;
  563. /* Plot 8 pixels, 4 pixels at a time. */
  564. *(vram++) = ((line >> 7) & 1) |
  565. ((line >> 6) & 1) << 4 |
  566. ((line >> 5) & 1) << 8 |
  567. ((line >> 4) & 1) << 12;
  568. *(vram++) = ((line >> 3) & 1) |
  569. ((line >> 2) & 1) << 4 |
  570. ((line >> 1) & 1) << 8 |
  571. ((line ) & 1) << 12;
  572. }
  573. }
  574. }
  575. static void nds_remap_charbyte(struct graphics_data *graphics, Uint16 chr,
  576. Uint8 byte)
  577. {
  578. if(chr < 256)
  579. {
  580. /* Each character is 64 bytes. Advance the vram pointer. */
  581. u16* vram = (u16*)BG_TILE_RAM_SUB(1) + 32*chr + 2*byte;
  582. u8* charset = graphics->charset + 14*chr + byte;
  583. u16 line = *charset;
  584. /* Plot 8 pixels, 4 pixels at a time. */
  585. *(vram++) = ((line >> 7) & 1) |
  586. ((line >> 6) & 1) << 4 |
  587. ((line >> 5) & 1) << 8 |
  588. ((line >> 4) & 1) << 12;
  589. *(vram++) = ((line >> 3) & 1) |
  590. ((line >> 2) & 1) << 4 |
  591. ((line >> 1) & 1) << 8 |
  592. ((line ) & 1) << 12;
  593. }
  594. }
  595. static void nds_remap_char_range(struct graphics_data *graphics, Uint16 first,
  596. Uint16 count)
  597. {
  598. int stop = first + count;
  599. int chr;
  600. if(stop > 256)
  601. stop = 256;
  602. for(chr = first; chr < stop; chr++)
  603. nds_remap_char(graphics, chr);
  604. }
  605. static void nds_focus_pixel(struct graphics_data *graphics, Uint32 x, Uint32 y)
  606. {
  607. // We want these values to be handled later while render_graph is run.
  608. focus_x = (int)x;
  609. focus_y = (int)y;
  610. }
  611. void render_nds_register(struct renderer *renderer)
  612. {
  613. memset(renderer, 0, sizeof(struct renderer));
  614. renderer->init_video = nds_init_video;
  615. renderer->check_video_mode = nds_check_video_mode;
  616. renderer->set_video_mode = nds_set_video_mode;
  617. renderer->update_colors = nds_update_colors;
  618. renderer->resize_screen = nds_resize_screen;
  619. renderer->remap_char_range = nds_remap_char_range;
  620. renderer->remap_char = nds_remap_char;
  621. renderer->remap_charbyte = nds_remap_charbyte;
  622. renderer->get_screen_coords = get_screen_coords_centered;
  623. renderer->set_screen_coords = set_screen_coords_centered;
  624. renderer->render_graph = nds_render_graph;
  625. renderer->render_cursor = nds_render_cursor;
  626. renderer->render_mouse = nds_render_mouse;
  627. renderer->sync_screen = nds_sync_screen;
  628. renderer->focus_pixel = nds_focus_pixel;
  629. }
  630. void nds_subscreen_switch(void)
  631. {
  632. // Don't switch during a transition.
  633. if(transition.state != TRANSITION_NONE)
  634. return;
  635. // Call appropriate exit function.
  636. if(subscreen_mode == SUBSCREEN_KEYBOARD)
  637. nds_subscreen_keyboard_exit();
  638. // Cycle between modes.
  639. last_subscreen_mode = subscreen_mode;
  640. if(++subscreen_mode == SUBSCREEN_MODE_COUNT)
  641. subscreen_mode = 0;
  642. // Call the appropriate init function.
  643. if(subscreen_mode == SUBSCREEN_KEYBOARD)
  644. nds_subscreen_keyboard_init();
  645. }
  646. void nds_mouselook(boolean enabled)
  647. {
  648. mouselook = enabled;
  649. }
  650. // Display a warning screen.
  651. void warning_screen(u8 *pcx_data)
  652. {
  653. sImage image;
  654. consoleDemoInit();
  655. if(loadPCX(pcx_data, &image))
  656. {
  657. u16 *img = image.image.data16;
  658. int ox, oy;
  659. u16 *gfx;
  660. videoSetMode(MODE_5_2D | DISPLAY_BG2_ACTIVE);
  661. vramSetBankA(VRAM_A_MAIN_BG);
  662. REG_BG2CNT = BG_BMP8_256x256;
  663. REG_BG2PA = 1 << 8;
  664. REG_BG2PB = 0;
  665. REG_BG2PC = 0;
  666. REG_BG2PD = 1 << 8;
  667. REG_BG2X = 0;
  668. REG_BG2Y = 0;
  669. memcpy(BG_PALETTE, image.palette, 2*256);
  670. ox = 128 - image.width/2;
  671. oy = 96 - image.height/2;
  672. gfx = (u16*)(0x06000000 + 256*oy + ox);
  673. for(int row = 0; row < image.height; row++)
  674. {
  675. for(int col = 0; col < image.width/2; col++)
  676. *(gfx++) = *(img++);
  677. gfx += ox;
  678. }
  679. while(!keysDown())
  680. scanKeys();
  681. imageDestroy(&image);
  682. }
  683. }