summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2023-04-15 19:27:50 +0200
committerBad Diode <bd@badd10de.dev>2023-04-15 19:27:50 +0200
commit87e5c991bf866db9253915f403bdb8e30f9d6449 (patch)
tree82dc49354723d133f242ac7a1c5406ba1d1a5a47
parentb22d1bb0081a93f43856077917b8b029a8e97ec9 (diff)
downloadgba-link-cable-tester-87e5c991bf866db9253915f403bdb8e30f9d6449.tar.gz
gba-link-cable-tester-87e5c991bf866db9253915f403bdb8e30f9d6449.zip
Add initial double buffering mode0 renderer
-rw-r--r--src/main.c24
-rw-r--r--src/renderer_m0.c687
2 files changed, 703 insertions, 8 deletions
diff --git a/src/main.c b/src/main.c
index 23f4fe3..a7bad92 100644
--- a/src/main.c
+++ b/src/main.c
@@ -11,7 +11,7 @@ WITH REGARD TO THIS SOFTWARE.
11 11
12#include "gba/gba.h" 12#include "gba/gba.h"
13 13
14#include "renderer.c" 14#include "renderer_m0.c"
15 15
16// 16//
17// Config parameters. 17// Config parameters.
@@ -121,6 +121,14 @@ int main(void) {
121 irs_set(IRQ_VBLANK, irs_stub); 121 irs_set(IRQ_VBLANK, irs_stub);
122 122
123 // Initialize sequencer. 123 // Initialize sequencer.
124 // flip_buffer();
125 // screen_fill(1);
126 // screen_fill(2);
127 // backbuf[0] = 0x11111111;
128 // screen_fill(1);
129 // flip_buffer();
130 // screen_fill(2);
131 // flip_buffer();
124 132
125 // Main loop. 133 // Main loop.
126 PROF_INIT(); 134 PROF_INIT();
@@ -128,13 +136,13 @@ int main(void) {
128 bios_vblank_wait(); 136 bios_vblank_wait();
129 137
130 PROF(test_clear(), test_clear_cycles); 138 PROF(test_clear(), test_clear_cycles);
131 PROF(test_lines(), test_lines_cycles); 139 // PROF(test_lines(), test_lines_cycles);
132 PROF(test_rect(), test_rect_cycles); 140 // PROF(test_rect(), test_rect_cycles);
133 PROF(test_fill_rect(), test_fill_rect_cycles); 141 // PROF(test_fill_rect(), test_fill_rect_cycles);
134 PROF(test_chr(), test_chr_cycles); 142 // PROF(test_chr(), test_chr_cycles);
135 PROF(test_icn(), test_icn_cycles); 143 // PROF(test_icn(), test_icn_cycles);
136 draw_filled_rect(0, 0, 140, 60, 0); 144 // draw_filled_rect(0, 0, 140, 60, 0);
137 PROF_SHOW(); 145 // PROF_SHOW();
138 PROF(flip_buffer(), flip_cycles); 146 PROF(flip_buffer(), flip_cycles);
139 } 147 }
140 148
diff --git a/src/renderer_m0.c b/src/renderer_m0.c
new file mode 100644
index 0000000..3034d40
--- /dev/null
+++ b/src/renderer_m0.c
@@ -0,0 +1,687 @@
1//
2// This Mode 0 renderer provides a way of drawing directly to a framebuffer
3// (similar to Mode 3 and 4) while retaining the flexibility of using other
4// backgrounds if needed. It also performs double buffering to avoid tearing
5// artifacts and tries to only draw tiles that changed on each frame.
6//
7// In addition to the frontbuffer (displayed on background 0), a tiled text
8// layer is displayed on background 1, which can be used for application
9// development or for debug information.
10//
11// These two layers occupy the first and second background charblocks, leaving
12// the remaining two available for other background layers. There are 14KB of
13// sprite memory available, since the backbuffer is located at the end of the
14// VRAM, but if more space is needed it can be moved to the end of the BG
15// charblocks instead.
16//
17
18#include "renderer.h"
19#include "text.h"
20
21// Front/back buffers for double buffering.
22#define BUF_0 ((u32*)(MEM_VRAM))
23#define BUF_1 ((u32*)(MEM_VRAM + KB(20)))
24// Position of the tilemap.
25#define TILE_MAP ((u32*)(MEM_VRAM + KB(40)))
26
27// Charblock and screenblock for both render buffers.
28#define CB_0 0
29#define CB_1 1
30#define SB_0 20
31#define SB_1 21
32
33// Available storage for other memory.
34// #define FG_PIXELS ((u32*)(MEM_VRAM + KB(44)))
35// #define BG_PIXELS ((u32*)(MEM_VRAM + KB(64)))
36
37// // The frontbuffs,eer is located at the beginning of the VRAM, and requires 20KB of
38// // video memory for 32 * 20 tiles at 4bpp.
39// #define FRONTBUF ((u32*)(MEM_VRAM))
40
41// // Adjust both of these if the location of the map changes. Each screnblock
42// // requires less than 2KB.
43// #define FRONTBUF_TILEMAP ((u16*)(MEM_VRAM + KB(20)))
44// #define FRONTBUF_SB 10
45
46// The backbuffer is located at the end of the VRAM. This can allow us to use
47// more backgrounds but eats into the available memory for sprites. This should
48// be fine for non sprite intensive applications. If more sprite memory is
49// needed, the backbuffer can be located at the end of the background memory
50// instead (64KB - 20KB).
51// #define BACKBUF ((u32*)(MEM_VRAM + KB(96) - KB(20)))
52
53// Keep track of which tiles need to be copied to the frontbuffer.
54static u32 dirty_tiles[21] = {0};
55static u32 *backbuf = BUF_1;
56
57// Boundchecks can be disable at compile time but this will not always improve
58// the performance and can in fact make it worse. It is possible that this is
59// due to some aliasing optimiztions but not sure at this moment.
60#ifdef DISABLE_BOUNDCHECK_SCREEN
61#define BOUNDCHECK_SCREEN(X,Y)
62#else
63#define BOUNDCHECK_SCREEN(X,Y) if ((X) >= SCREEN_WIDTH || (Y) >= SCREEN_HEIGHT) return;
64#endif
65
66// Swap A and B values without a tmp variable.
67#define SWAP(A, B) (((A) ^= (B)), ((B) ^= (A)), ((A) ^= (B)))
68
69// Swap A and B values to make sure A <= B.
70#define MAYBE_SWAP(A,B) if ((A) > (B)) { SWAP(A,B); }
71
72IWRAM_CODE
73void screen_fill(u8 clr) {
74#if 0
75// u32 *dst = backbuf;
76// for(int i = 0; i < KB(75) / 8; i++) {
77// *dst++ = 0x01010101 * clr;
78// }
79#else
80 dma_fill(backbuf, 0x11111111 * clr, KB(20), 3);
81#endif
82}
83
84IWRAM_CODE
85void
86draw_pixel(size_t x, size_t y, u8 clr) {
87 BOUNDCHECK_SCREEN(x, y);
88
89 // Find row position for the given x/y coordinates.
90 size_t tile_x = x / 8;
91 size_t tile_y = y / 8;
92 size_t start_col = x % 8;
93 size_t start_row = y % 8;
94 size_t pos = start_row + (tile_x + tile_y * 32) * 8;
95
96 // Update backbuffer.
97 size_t shift = start_col * sizeof(u32);
98 backbuf[pos] = (backbuf[pos] & ~(0xF << shift)) | clr << shift;
99
100 // Mark tile as dirty.
101 dirty_tiles[tile_y] |= 1 << tile_x;
102}
103
104IWRAM_CODE
105static inline
106void
107draw_hline(size_t x0, size_t x1, size_t y0, u8 clr) {
108 // TODO
109}
110
111IWRAM_CODE
112UNROLL_LOOPS
113static inline
114void
115draw_vline(size_t x0, size_t y0, size_t y1, u8 clr) {
116 // TODO
117}
118
119IWRAM_CODE
120void
121draw_line(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) {
122 // BOUNDCHECK_SCREEN(x0, y0);
123 // BOUNDCHECK_SCREEN(x1, y1);
124 if (y0 == y1) {
125 MAYBE_SWAP(x0, x1);
126 draw_hline(x0, x1, y0, clr);
127 } else if (x0 == x1) {
128 MAYBE_SWAP(y0, y1);
129 draw_vline(x0, y0, y1, clr);
130 } else {
131 // // Diagonal line.
132 // int dx = x0 > x1 ? x0 - x1 : x1 - x0;
133 // int dy = y0 > y1 ? y0 - y1 : y1 - y0;
134 // int x_step = x0 > x1 ? -1 : 1;
135 // int y_step = y0 > y1 ? -SCREEN_WIDTH : SCREEN_WIDTH;
136
137 // u16 *dst = NULL;
138 // uintptr_t addr = ((uintptr_t)backbuf + y0 * SCREEN_WIDTH + x0);
139 // u32 mask = x0 & 1 ? ~0xFF : 0xFF;
140 // u32 color = (clr & 0xFF) | ((clr & 0xFF) << 8);
141 // if (dx >= dy) {
142 // int diff = 2 * dy - dx;
143 // for (int i = 0; i < dx + 1; i++) {
144 // dst = (u16 *)(addr - (mask >> 31));
145 // *dst = (*dst & ~mask) | (color & mask);
146 // if (diff >= 0) {
147 // diff -= 2 * dx;
148 // addr += y_step;
149 // }
150 // diff += 2 * dy;
151 // addr += x_step;
152 // mask = ~mask;
153 // }
154 // } else {
155 // int diff = 2 * dx - dy;
156 // for (int i = 0; i < dy + 1; i++) {
157 // dst = (u16 *)(addr - (mask >> 31));
158 // *dst = (*dst & ~mask) | (color & mask);
159 // if (diff >= 0) {
160 // diff -= 2 * dy;
161 // addr += x_step;
162 // mask = ~mask;
163 // }
164 // diff += 2 * dx;
165 // addr += y_step;
166 // }
167 // }
168 }
169
170 // // Find row positions for the given x/y coordinates.
171 // size_t tile_x0 = x0 / 8;
172 // size_t tile_y0 = y0 / 8;
173 // size_t tile_x1 = x1 / 8;
174 // size_t tile_y1 = y1 / 8;
175 // size_t start_col0 = x0 % 8;
176 // size_t start_col1 = x1 % 8;
177 // size_t start_row0 = y0 % 8;
178 // size_t start_row1 = y1 % 8;
179
180 // // Get a pointer to the backbuffer and the tile row.
181 // u32 *backbuffer = &BACKBUF[start_row0 + (tile_x0 + tile_y0 * 32) * 8];
182
183 // if (y0 == y1) {
184 // // Horizontal line. There are 3 cases:
185 // // 1. Lines fit on a single tile.
186 // // 2. Lines go through 2 tiles, both require partial row updates.
187 // // 3. Lines go through 3 or more tiles, first and last tiles use
188 // // partial row updates, rows in the middle can write the.
189 // size_t dx = tile_x1 - tile_x0;
190 // if (dx < 1) {
191 // u32 row_mask = 0xFFFFFFFF;
192 // row_mask >>= (7 - start_col1 - dx) * 4;
193 // row_mask &= 0xFFFFFFFF << start_col0 * 4;
194 // u32 row = (0x11111111 * clr) & row_mask;
195 // backbuffer[0] = (backbuffer[0] & ~row_mask) | row;
196 // dirty_tiles[tile_y0] |= 1 << tile_x0;
197 // } else {
198 // size_t shift_left = start_col0 * 4;
199 // size_t shift_right = (7 - start_col1) * 4;
200 // u32 row_mask = 0xFFFFFFFF;
201 // u32 row = 0x11111111 * clr;
202 // backbuffer[0] = backbuffer[0] & ~(row_mask << shift_left);
203 // backbuffer[0] |= row << shift_left;
204 // dirty_tiles[tile_y0] |= 1 << tile_x0;
205 // for (size_t i = 1; i < dx; i++) {
206 // backbuffer[i * 8] = row;
207 // dirty_tiles[tile_y0] |= 1 << (tile_x0 + i);
208 // }
209 // backbuffer[dx * 8] = backbuffer[dx * 8] & ~(row_mask >> shift_right);
210 // backbuffer[dx * 8] |= row >> shift_right;
211 // dirty_tiles[tile_y0] |= 1 << (tile_x0 + dx);
212 // }
213 // } else if (x0 == x1) {
214 // // Vertical line. The cases are analogous to the horizontal ones.
215 // size_t dy = tile_y1 - tile_y0;
216 // u32 row_mask = 0xF << start_col0 * 4;
217 // u32 row_left = (0x11111111 * clr) & row_mask;
218 // if (dy < 1) {
219 // for (size_t i = 0; i <= y1 - y0; i++, backbuffer++) {
220 // backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left;
221 // }
222 // } else {
223 // for (size_t i = 0; i < (8 - start_row0); i++, backbuffer++) {
224 // backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left;
225 // }
226 // dirty_tiles[tile_y0] |= 1 << tile_x0;
227 // backbuffer += 8 * 31;
228 // for (size_t j = 1; j < dy; j++) {
229 // for (size_t i = 0; i < 8; i++, backbuffer++) {
230 // backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left;
231 // }
232 // backbuffer += 8 * 31;
233 // dirty_tiles[tile_y0 + j] |= 1 << tile_x0;
234 // }
235 // for (size_t i = 0; i <= start_row1; i++, backbuffer++) {
236 // backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left;
237 // }
238 // dirty_tiles[tile_y1] |= 1 << tile_x0;
239 // }
240 // } else {
241 // // Diagonal line.
242 // int dx = x0 > x1 ? x0 - x1 : x1 - x0;
243 // int dy = y0 > y1 ? y1 - y0 : y0 - y1;
244 // int x_step = x0 < x1 ? 1 : -1;
245 // int y_step = y0 < y1 ? 1 : -1;
246 // int err = dx + dy;
247 // while (!(x0 == x1 && y0 == y1)) {
248 // draw_pixel(x0, y0, clr);
249 // int diff = 2 * err;
250 // if (diff >= dy) {
251 // err += dy;
252 // x0 += x_step;
253 // }
254 // if (diff <= dx) {
255 // err += dx;
256 // y0 += y_step;
257 // }
258 // }
259 // }
260}
261
262IWRAM_CODE
263void
264draw_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) {
265 BOUNDCHECK_SCREEN(x0, y0);
266 BOUNDCHECK_SCREEN(x1, y1);
267 MAYBE_SWAP(x0, x1);
268 MAYBE_SWAP(y0, y1);
269
270 draw_hline(x0, x1, y0, clr);
271 draw_hline(x0, x1, y1, clr);
272 draw_vline(x0, y0, y1, clr);
273 draw_vline(x1, y0, y1, clr);
274
275 // TODO: check if this is better.
276// BOUNDCHECK_SCREEN(x0, y0);
277// BOUNDCHECK_SCREEN(x1, y1);
278
279// // Find row positions for the given x/y coordinates.
280// size_t tile_x0 = x0 / 8;
281// size_t tile_y0 = y0 / 8;
282// size_t tile_x1 = x1 / 8;
283// size_t tile_y1 = y1 / 8;
284// size_t start_col0 = x0 % 8;
285// size_t start_col1 = x1 % 8;
286// size_t start_row0 = y0 % 8;
287// size_t start_row1 = y1 % 8;
288
289// // Get a pointer to the backbuffer and the tile row.
290// u32 *buf_top = &BACKBUF[start_row0 + (tile_x0 + tile_y0 * 32) * 8];
291// u32 *buf_bot = &BACKBUF[start_row1 + (tile_x0 + tile_y1 * 32) * 8];
292
293// size_t dx = tile_x1 - tile_x0;
294// size_t dy = tile_y1 - tile_y0;
295
296// memset(buf_top, 3, x1 - x0);
297// memset(buf_bot, 3, x1 - x0);
298
299// if (dx < 1) {
300// dirty_tiles[tile_y0] |= 1 << tile_x0;
301// dirty_tiles[tile_y1] |= 1 << tile_x0;
302// } else {
303// dirty_tiles[tile_y0] |= 1 << tile_x0;
304// dirty_tiles[tile_y1] |= 1 << tile_x0;
305// for (size_t i = 1; i < dx; i++) {
306// dirty_tiles[tile_y0] |= 1 << (tile_x0 + i);
307// dirty_tiles[tile_y1] |= 1 << (tile_x0 + i);
308// }
309// dirty_tiles[tile_y0] |= 1 << (tile_x0 + dx);
310// dirty_tiles[tile_y1] |= 1 << (tile_x0 + dx);
311// }
312// if (dy < 1) {
313// } else {
314// for (size_t j = 1; j < dy; j++) {
315// dirty_tiles[tile_y0 + j] |= 1 << tile_x0;
316// dirty_tiles[tile_y0 + j] |= 1 << (tile_x0 + dx);
317// }
318// }
319}
320
321IWRAM_CODE
322void
323draw_filled_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) {
324 BOUNDCHECK_SCREEN(x0, y0);
325 BOUNDCHECK_SCREEN(x1, y1);
326 MAYBE_SWAP(x0, x1);
327 MAYBE_SWAP(y0, y1);
328
329 // Special condition. If the screen is to be completely filled, use the DMA
330 // instead.
331 if (x0 == 0 && x1 >= (SCREEN_WIDTH - 1) && y0 == 0 && y1 >= (SCREEN_HEIGHT - 1)) {
332 screen_fill(clr);
333 return;
334 }
335
336 // Drawline implementation.
337 for (size_t y = y0; y <= y1; y++) {
338 // NOTE: Unclear why here draw_hline is faster than draw_line.
339 draw_hline(x0, x1, y, clr);
340 }
341
342 // TODO: check if this is better.
343 // BOUNDCHECK_SCREEN(x0, y0);
344 // BOUNDCHECK_SCREEN(x1, y1);
345
346 // size_t dx = x1 - x0;
347 // size_t dy = y1 - y0;
348 // u8 *buf = &BACKBUF[0];
349 // memset(buf, 0x11 * clr, 16);
350 //for (size_t j = 0; j < 1; j++) {
351 // // for (size_t i = 0; i < dx; i++) {
352 // // buf[i + j * 16] = clr;
353 // // }
354 // //
355 // // BACKBUF[j + 0] = 0x11111111 * clr;
356 // // BACKBUF[j + 1] = 0x11111111 * clr;
357 // // BACKBUF[j + 2] = 0x11111111 * clr;
358 // // BACKBUF[j + 3] = 0x11111111 * clr;
359 // // BACKBUF[j + 4] = 0x11111111 * clr;
360 // // BACKBUF[j + 5] = 0x11111111 * clr;
361 // // BACKBUF[j + 6] = 0x11111111 * clr;
362 // // BACKBUF[j + 7] = 0x11111111 * clr;
363
364 // buf[j + 0] = 0x1 * clr;
365 // buf[j + 1] = 0x1 * clr;
366 // buf[j + 2] = 0x1 * clr;
367 // buf[j + 3] = 0x1 * clr;
368 // // buf[j + 4] = 0x1111 * clr;
369 // // buf[j + 5] = 0x1111 * clr;
370 // // buf[j + 6] = 0x1111 * clr;
371 // // buf[j + 7] = 0x1111 * clr;
372 //}
373 // u8 *buf = &BACKBUF[0];
374 // buf[8 * 16 + 0] = clr;
375 // buf[8 * 16 + 1] = clr;
376 // buf[8 * 16 + 2] = clr;
377 // buf[8 * 16 + 3] = clr;
378 // buf[8 * 16 + 4] = clr;
379 // buf[8 * 16 + 5] = clr;
380 // buf[8 * 16 + 6] = clr;
381 // buf[8 * 16 + 7] = clr;
382 // for (size_t j = 0; j < dy; j++) {
383 // for (size_t i = 0; i < dx; i++) {
384 // buf[i + j * 16] = clr;
385 // }
386 // }
387 // size_t n_rect = MIN(dx, dy);
388 // n_rect = n_rect / 2 + 1;
389 // for (size_t i = 0; i < n_rect; i++) {
390 // draw_rect(x0 + i, y0 + i, x1 - i, y1 - i, clr);
391 // }
392}
393
394// IWRAM_CODE
395// void
396// draw_tile(size_t x, size_t y, Tile *tile, u8 clr) {
397// BOUNDCHECK_SCREEN(x, y);
398
399// // Find row position for the given x/y coordinates.
400// size_t tile_x = x / 8;
401// size_t tile_y = y / 8;
402// size_t start_col = x % 8;
403// size_t start_row = y % 8;
404
405// // Get a pointer to the backbuffer and the tile row.
406// size_t pos = start_row + (tile_x + tile_y * 32) * 8;
407// u32 *backbuffer = &BACKBUF[pos];
408// u32 *row = tile;
409
410// // This will blend all colors weirdly if using tiles that contain colors
411// // higher than 1.
412// size_t shift_left = start_col * 4;
413// size_t shift_right = (8 - start_col) * 4;
414// // u32 row_mask_left = merge ? 0 : 0xFFFFFFFF << shift_left;
415// // u32 row_mask_right = merge ? 0 : 0xFFFFFFFF >> shift_right;
416
417// // Draw the tiles. There are 4 possible cases:
418// // 1. The tile is exactly at the tile boundary.
419// // 2. The tile spans 2 tiles horizontally.
420// // 3. The tile spans 2 tiles vertically.
421// // 4. The tile spans 4 tiles.
422// if (start_col == 0 && start_row == 0) {
423// for (size_t i = 0; i < (8 - start_row); i++, backbuffer++) {
424// BOUNDCHECK_SCREEN(x, y + i);
425// backbuffer[0] = (backbuffer[0] & ~row_mask_left) | row[i] * clr;
426// }
427// dirty_tiles[tile_y] |= 1 << tile_x;
428// } else if (start_row == 0) {
429// for (size_t i = 0; i < 8; i++, backbuffer++) {
430// BOUNDCHECK_SCREEN(x, y + i);
431// backbuffer[0] = (backbuffer[0] & ~row_mask_left) | (row[i] * clr << shift_left);
432// backbuffer[8] = (backbuffer[8] & ~row_mask_right) | (row[i] * clr >> shift_right);
433// }
434// dirty_tiles[tile_y] |= 1 << tile_x;
435// dirty_tiles[tile_y] |= 1 << (tile_x + 1);
436// } else if (start_col == 0) {
437// for (size_t i = 0; i < (8 - start_row); i++, backbuffer++) {
438// BOUNDCHECK_SCREEN(x, y + i);
439// backbuffer[0] = (backbuffer[0] & ~row_mask_left) | row[i] * clr;
440// }
441// backbuffer += 8 * 31;
442// for (size_t i = (8 - start_row); i < 8; i++, backbuffer++) {
443// BOUNDCHECK_SCREEN(x, y + i);
444// backbuffer[0] = (backbuffer[0] & ~row_mask_left) | row[i] * clr;
445// }
446// dirty_tiles[tile_y] |= 1 << tile_x;
447// dirty_tiles[tile_y + 1] |= 1 << tile_x;
448// } else {
449// for (size_t i = 0; i < (8 - start_row); i++, backbuffer++) {
450// BOUNDCHECK_SCREEN(x, y + i);
451// backbuffer[0] = (backbuffer[0] & ~row_mask_left) | (row[i] * clr << shift_left);
452// backbuffer[8] = (backbuffer[8] & ~row_mask_right) | (row[i] * clr >> shift_right);
453// }
454// backbuffer += 8 * 31;
455// for (size_t i = (8 - start_row); i < 8; i++, backbuffer++) {
456// BOUNDCHECK_SCREEN(x, y + i);
457// backbuffer[0] = (backbuffer[0] & ~row_mask_left) | (row[i] * clr << shift_left);
458// backbuffer[8] = (backbuffer[8] & ~row_mask_right) | (row[i] * clr >> shift_right);
459// }
460// dirty_tiles[tile_y] |= 1 << tile_x;
461// dirty_tiles[tile_y] |= 1 << (tile_x + 1);
462// dirty_tiles[tile_y + 1] |= 1 << tile_x;
463// dirty_tiles[tile_y + 1] |= 1 << (tile_x + 1);
464// }
465// }
466
467// void
468// clear_screen(void) {
469// dma_fill(FRONTBUF, 0, KB(20), 3);
470// }
471
472IWRAM_CODE
473void
474flip_buffer(void) {
475 if (backbuf == BUF_0) {
476 backbuf = BUF_1;
477 DISP_CTRL = DISP_MODE_0 | DISP_BG_1 | DISP_OBJ;
478 BG_CTRL(0) = BG_CHARBLOCK(CB_0) | BG_SCREENBLOCK(SB_0) | BG_PRIORITY(2);
479 BG_CTRL(1) = BG_CHARBLOCK(CB_1) | BG_SCREENBLOCK(SB_1) | BG_PRIORITY(1);
480 } else {
481 backbuf = BUF_0;
482 DISP_CTRL = DISP_MODE_0 | DISP_BG_0 | DISP_OBJ;
483 BG_CTRL(0) = BG_CHARBLOCK(CB_0) | BG_SCREENBLOCK(SB_0) | BG_PRIORITY(1);
484 BG_CTRL(1) = BG_CHARBLOCK(CB_1) | BG_SCREENBLOCK(SB_1) | BG_PRIORITY(2);
485 }
486 // TODO: Copying all tiles for now. Study if it's better to use dirty_tiles
487 // or dirty_lines.
488 // Copy dirty tiles from the backbuffer to the frontbuffer.
489 // Tile *dst = FRONTBUF;
490 // Tile *src = BACKBUF;
491 // for (size_t j = 0; j < 20; ++j) {
492 // // if (dirty_tiles[j] == 0) {
493 // // continue;
494 // // }
495 // for (size_t i = 0, k = 1; i < 30; ++i, k <<= 1) {
496 // // if (dirty_tiles[j] & k) {
497 // dst[i + j * 32] = src[i + j * 32];
498 // // }
499 // }
500 // // dirty_tiles[j] = 0;
501 // }
502}
503
504static u32 dec_nibble[] = {
505 0x00000000, 0x01000000, 0x00010000, 0x01010000,
506 0x00000100, 0x01000100, 0x00010100, 0x01010100,
507 0x00000001, 0x01000001, 0x00010001, 0x01010001,
508 0x00000101, 0x01000101, 0x00010101, 0x01010101,
509};
510
511static u32 dec_nibble_flip_x[] = {
512 0x00000000, 0x00000001, 0x00000100, 0x00000101,
513 0x00010000, 0x00010001, 0x00010100, 0x00010101,
514 0x01000000, 0x01000001, 0x01000100, 0x01000101,
515 0x01010000, 0x01010001, 0x01010100, 0x01010101,
516};
517
518IWRAM_CODE
519static inline
520u64
521decode_1bpp(u8 row, u8 flip_x) {
522 if (flip_x) {
523 u32 *lut = dec_nibble_flip_x;
524 return (u64)lut[(row >> 4) & 0xF] << 32 | (u64)lut[(row >> 0) & 0xF];
525 }
526 u32 *lut = dec_nibble;
527 return (u64)lut[(row >> 0) & 0xF] << 32 | (u64)lut[(row >> 4) & 0xF];
528}
529
530IWRAM_CODE
531static inline
532void
533draw_2bpp_row(size_t x, size_t y, u8 a, u8 b, u8 flip_x) {
534 // BOUNDCHECK_SCREEN(x, y);
535
536 // size_t tile_x = x / 8;
537 // size_t start_col = x % 8;
538 // size_t shift_left = start_col * 8;
539 // size_t shift_right = (8 - start_col) * 8;
540
541 // u64 *dst = &backbuf[(y * 30 + tile_x) * 8 / 2];
542 // if (start_col == 0) {
543 // u64 clr_a = decode_1bpp(a, flip_x);
544 // u64 clr_b = decode_1bpp(b, flip_x);
545 // u64 mask_a = (clr_a * 0xF);
546 // u64 mask_b = (clr_b * 0xF);
547 // u64 mask = (mask_a | mask_b);
548 // u64 color = clr_a + (clr_b << 1);
549 // dst[0] = (dst[0] & ~mask) | color;
550 // } else {
551 // u64 clr_a = decode_1bpp(a, flip_x);
552 // u64 clr_b = decode_1bpp(b, flip_x);
553 // u64 mask_a = (clr_a * 0xF);
554 // u64 mask_b = (clr_b * 0xF);
555 // u64 mask = (mask_a | mask_b);
556 // u64 color = clr_a + (clr_b << 1);
557 // dst[0] = (dst[0] & ~(mask << shift_left)) | (color << shift_left);
558 // if ((x + 7) > (SCREEN_WIDTH)) {
559 // return;
560 // }
561 // dst[1] = (dst[1] & ~(mask >> shift_right)) | (color >> shift_right);
562 // }
563
564 // TODO: different blend modes?
565}
566
567IWRAM_CODE
568static inline
569void
570draw_1bpp_row(size_t x, size_t y, u8 a, u8 clr, u8 flip_x) {
571 // BOUNDCHECK_SCREEN(x, y);
572
573 // size_t tile_x = x / 8;
574 // size_t start_col = x % 8;
575 // size_t shift_left = start_col * 8;
576 // size_t shift_right = (8 - start_col) * 8;
577
578 // u64 *dst = &backbuf[(y * 30 + tile_x) * 8 / 2];
579 // if (start_col == 0) {
580 // u64 color = decode_1bpp(a, flip_x);
581 // u64 mask = (color * 0xF);
582 // color *= clr;
583 // dst[0] = (dst[0] & ~mask) | color;
584 // } else {
585 // u64 color = decode_1bpp(a, flip_x);
586 // u64 mask = (color * 0xF);
587 // color *= clr;
588 // dst[0] = (dst[0] & ~(mask << shift_left)) | (color << shift_left);
589 // if ((x + 7) > (SCREEN_WIDTH)) {
590 // return;
591 // }
592 // dst[1] = (dst[1] & ~(mask >> shift_right)) | (color >> shift_right);
593 // }
594
595 // TODO: different blend modes?
596}
597
598IWRAM_CODE
599void
600draw_chr(size_t x, size_t y, u8 *sprite, u8 flip_x, u8 flip_y) {
601 // BOUNDCHECK_SCREEN(x, y);
602 // if (!flip_y) {
603 // for(size_t v = 0; v < 8; v++) {
604 // if ((y + v) >= SCREEN_HEIGHT) break;
605 // u8 ch1 = sprite[v + 0];
606 // u8 ch2 = sprite[v + 8];
607 // draw_2bpp_row(x, y + v, ch1, ch2, flip_x);
608 // }
609 // } else {
610 // for(size_t v = 0; v < 8; v++) {
611 // if ((y + v) >= SCREEN_HEIGHT) break;
612 // u8 ch1 = sprite[(7 - v) + 0];
613 // u8 ch2 = sprite[(7 - v) + 8];
614 // draw_2bpp_row(x, y + v, ch1, ch2, flip_x);
615 // }
616 // }
617}
618
619IWRAM_CODE
620void
621draw_icn(size_t x, size_t y, u8 *sprite, u8 clr, u8 flip_x, u8 flip_y) {
622 // BOUNDCHECK_SCREEN(x, y);
623 // if (!flip_y) {
624 // for(size_t v = 0; v < 8; v++) {
625 // if ((y + v) >= SCREEN_HEIGHT) break;
626 // u8 ch1 = sprite[v];
627 // draw_1bpp_row(x, y + v, ch1, clr, flip_x);
628 // }
629 // } else {
630 // for(size_t v = 0; v < 8; v++) {
631 // if ((y + v) >= SCREEN_HEIGHT) break;
632 // u8 ch1 = sprite[(7 - v)];
633 // draw_1bpp_row(x, y + v, ch1, clr, flip_x);
634 // }
635 // }
636}
637
638#include "font.h"
639
640// Font rendering function for the text engine.
641void
642txt_drawc(char c, size_t x, size_t y, u8 clr) {
643 u8 *tile = font_icn;
644 draw_icn(x, y, tile + 8 * c, clr, 1, 0);
645}
646
647void
648renderer_init(void) {
649 // Initialize display mode and bg palette.
650 DISP_CTRL = DISP_MODE_0 | DISP_BG_0 | DISP_BG_1 | DISP_OBJ;
651
652 // Clear VRAM.
653 dma_fill(MEM_VRAM, 0, KB(96), 3);
654
655
656 // Initialize backgrounds.
657 BG_CTRL(0) = BG_CHARBLOCK(CB_0) | BG_SCREENBLOCK(SB_0) | BG_PRIORITY(1);
658 BG_CTRL(1) = BG_CHARBLOCK(CB_1) | BG_SCREENBLOCK(SB_1) | BG_PRIORITY(2);
659
660 DISP_CTRL = DISP_MODE_0 | DISP_OBJ;
661
662 // Initialize background memory map for frontbuffer.
663 // for (size_t i = 0; i < 32 * 20; ++i) {
664 // TILE_MAP[i] = i;
665 // }
666 u16 *mem_map_fg = SCREENBLOCK_MEM[SB_0];
667 u16 *mem_map_bg = SCREENBLOCK_MEM[SB_1];
668 size_t k = 0;
669 for (size_t i = 0; i < 32 * 20; ++i, ++k) {
670 mem_map_fg[i] = k;
671 mem_map_bg[i] = k + 32 * 4;
672 }
673
674 // Initialize default palette.
675 PAL_BUFFER_BG[0] = COLOR_BLACK;
676 PAL_BUFFER_BG[1] = COLOR_WHITE;
677 PAL_BUFFER_BG[2] = COLOR_RED;
678 PAL_BUFFER_BG[3] = COLOR_BLUE;
679 PAL_BUFFER_BG[4] = COLOR_CYAN;
680 PAL_BUFFER_BG[5] = COLOR_PURPLE;
681 PAL_BUFFER_BG[6] = COLOR_YELLOW;
682 PAL_BUFFER_BG[7] = COLOR_GREEN;
683 PAL_BUFFER_BG[8] = COLOR_GREY;
684
685 // Initialize text engine.
686 txt_init(txt_drawc);
687}