aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2023-08-06 11:13:22 +0200
committerBad Diode <bd@badd10de.dev>2023-08-06 11:13:22 +0200
commit8aa5155ecfbaaa2e67f82561352a5857bc86d39e (patch)
tree511b4efe38f661287a939a6758f91d2b64cf71c1
parente66f7459129285ff0c69d17089c144189b0dad80 (diff)
downloadstepper-8aa5155ecfbaaa2e67f82561352a5857bc86d39e.tar.gz
stepper-8aa5155ecfbaaa2e67f82561352a5857bc86d39e.zip
Add missing .c files and wishlist TODOs
-rw-r--r--.gitignore4
-rw-r--r--src/dsound.c61
-rw-r--r--src/main.c9
-rw-r--r--src/renderer_m4.c601
4 files changed, 675 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 378eac2..de3f8f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
1build 1build
2assets
3design
4releases
5scripts
diff --git a/src/dsound.c b/src/dsound.c
new file mode 100644
index 0000000..cba8c86
--- /dev/null
+++ b/src/dsound.c
@@ -0,0 +1,61 @@
1static bool audio_sync_click = false;
2#define AUDIO_FREQ 40137
3#define AUDIO_BUF_LEN 672
4#define AUDIO_TIMER 65118
5
6s8 audio_buffer[AUDIO_BUF_LEN * 2];
7static int audio_buf_active = 1;
8
9void
10click_timer(void) {
11 DMA_TRANSFER[1].ctrl = 0;
12 TIMER_CTRL_0 = 0;
13 TIMER_CTRL_1 = 0;
14}
15
16void
17play_click(void) {
18 DMA_TRANSFER[1].ctrl = 0;
19 audio_buf_active = 1;
20 TIMER_CTRL_0 = 0;
21 TIMER_CTRL_1 = 0;
22 TIMER_DATA_1 = 0xFFFF - 1500;
23 TIMER_CTRL_1 = TIMER_CTRL_ENABLE | TIMER_CTRL_CASCADE;
24 irs_set(IRQ_TIMER_1, click_timer);
25 TIMER_DATA_0 = AUDIO_TIMER;
26 TIMER_CTRL_0 = TIMER_CTRL_ENABLE;
27 audio_sync_click = false;
28}
29
30void
31init_dsound(void) {
32 // Initialize audio buffers/channels.
33 int high = 1;
34 for (size_t i = 0; i < AUDIO_BUF_LEN * 2; i++) {
35 audio_buffer[i] = 127 * high;
36 if (i % AUDIO_BUF_LEN == 0) {
37 high *= -1;
38 }
39 }
40
41 // Enable the sound chip.
42 SOUND_STATUS = SOUND_ENABLE;
43}
44
45
46void
47sound_vsync() {
48 if(audio_buf_active == 1) {
49 dma_transfer_copy(SOUND_FIFO_A, audio_buffer, 1, 1,
50 DMA_DST_FIXED
51 | DMA_CHUNK_32
52 | DMA_REFRESH
53 | DMA_REPEAT
54 | DMA_ENABLE);
55 // Start playing and set the backbuffer.
56 audio_buf_active = 0;
57 } else {
58 // Flip front/backbuffer.
59 audio_buf_active = 1;
60 }
61}
diff --git a/src/main.c b/src/main.c
index 69feb20..eb16687 100644
--- a/src/main.c
+++ b/src/main.c
@@ -17,6 +17,8 @@ WITH REGARD TO THIS SOFTWARE.
17// - Improve "grey" cursor with dithering instead. 17// - Improve "grey" cursor with dithering instead.
18// - Remove thin cursor option and make the fat one default, it's just better. 18// - Remove thin cursor option and make the fat one default, it's just better.
19// - Settings page overhaul. 19// - Settings page overhaul.
20// - Make sure there is an ALL notification when modifying channel params so
21// that it's clear it's affecting all triggers.
20// 22//
21// Quality of life improvements. 23// Quality of life improvements.
22// - When not on play mode, adjusting a note or a parameter triggers the sound. 24// - When not on play mode, adjusting a note or a parameter triggers the sound.
@@ -41,6 +43,13 @@ WITH REGARD TO THIS SOFTWARE.
41// - Add settings for "performance mode" in which banks are not saved by 43// - Add settings for "performance mode" in which banks are not saved by
42// default while changing patterns. 44// default while changing patterns.
43// - Make sure sync works with the same cable for in/out. 45// - Make sure sync works with the same cable for in/out.
46// - Per-channel N steps to create polymeters?
47// - Higher resolution clock to allow for microtiming and more accurate tempo.
48// - Study how to better embed data into the cart that doesn't involve the
49// build system to generate .c files. Just use the linker to put binary data
50// into the ROM.
51// - Improve SRAM saving to make room for longer patterns and/or more banks.
52// - Add CLEAR ALL to the settings menu.
44// 53//
45 54
46#include "gba/gba.h" 55#include "gba/gba.h"
diff --git a/src/renderer_m4.c b/src/renderer_m4.c
new file mode 100644
index 0000000..d50bab3
--- /dev/null
+++ b/src/renderer_m4.c
@@ -0,0 +1,601 @@
1#include "renderer.h"
2#include "text.h"
3
4//
5// Parameters.
6//
7
8#define SUBPIXEL_LINES 0
9#define DEC_BIG_LUT 1
10
11// Front/back buffers for double buffering.
12#define BUF_0 ((u32*)(MEM_VRAM))
13#define BUF_1 ((u32*)(MEM_VRAM ^ 0x0A000))
14
15// Pointer to the backbuffer.
16static u16 *backbuf = BUF_1;
17
18// Boundchecks can be disable at compile time but this will not always improve
19// the performance and can in fact make it worse. It is possible that this is
20// due to some aliasing optimiztions but not sure at this moment.
21#ifdef DISABLE_BOUNDCHECK_SCREEN
22#define BOUNDCHECK_SCREEN(X,Y)
23#else
24#define BOUNDCHECK_SCREEN(X,Y) if ((X) >= SCREEN_WIDTH || (Y) >= SCREEN_HEIGHT) return;
25#endif
26
27// Swap A and B values without a tmp variable.
28#define SWAP(A, B) (((A) ^= (B)), ((B) ^= (A)), ((A) ^= (B)))
29
30// Swap A and B values to make sure A <= B.
31#define MAYBE_SWAP(A,B) if ((A) > (B)) { SWAP(A,B); }
32
33//
34// Basic primitives.
35//
36
37IWRAM_CODE
38void screen_fill(u8 clr) {
39 dma_fill(backbuf, 0x01010101 * clr, KB(75) / 2, 3);
40}
41
42IWRAM_CODE
43void
44draw_pixel(size_t x, size_t y, u8 clr) {
45 BOUNDCHECK_SCREEN(x, y);
46 u16 *dst = &backbuf[(x + y * SCREEN_WIDTH) / 2];
47 if(x & 1) {
48 *dst = (*dst & 0xF) | (clr << 8);
49 } else {
50 *dst = (*dst & ~0xF) | clr;
51 }
52}
53
54IWRAM_CODE
55static inline
56void
57draw_hline(size_t x0, size_t x1, size_t y0, u8 clr) {
58 BOUNDCHECK_SCREEN(x0, y0);
59 BOUNDCHECK_SCREEN(x1, y0);
60 // Find row positions for the given x/y coordinates.
61 size_t tile_x0 = x0 / 8;
62 size_t tile_x1 = x1 / 8;
63 size_t start_col = x0 % 8;
64 size_t end_col = x1 % 8;
65 // Horizontal line. There are 3 cases:
66 // 1. Lines fit on a single tile.
67 // 2. Lines go through 2 tiles, both require partial row updates.
68 // 3. Lines go through 3 or more tiles, first and last tiles use
69 // partial row updates, rows in the middle can write the entire
70 // row.
71 size_t dx = tile_x1 - tile_x0;
72 u64 *dst = &backbuf[(tile_x0 * 8 + y0 * SCREEN_WIDTH) / 2];
73 if (dx < 1) {
74 u64 row_mask = 0xFFFFFFFFFFFFFFFF;
75 row_mask >>= (7 - end_col - dx) * 8;
76 row_mask &= 0xFFFFFFFFFFFFFFFF << start_col * 8;
77 u64 row = (0x0101010101010101 * clr) & row_mask;
78 *dst = (*dst & ~row_mask) | row;
79 } else {
80 size_t shift_left = start_col * 8;
81 size_t shift_right = (7 - end_col) * 8;
82 u64 row_mask = 0xFFFFFFFFFFFFFFFF;
83 u64 row = 0x0101010101010101 * clr;
84 dst[0] = (dst[0] & ~(row_mask << shift_left)) | row << shift_left;
85 if (dx != 1) {
86 dma_fill(&dst[1], 0x01010101 * clr, (dx - 1) * 8, 3);
87 }
88 dst[dx] = dst[dx] & ~(row_mask >> shift_right);
89 dst[dx] |= row >> shift_right;
90 }
91}
92
93IWRAM_CODE
94UNROLL_LOOPS
95static inline
96void
97draw_vline(size_t x0, size_t y0, size_t y1, u8 clr) {
98 BOUNDCHECK_SCREEN(x0, y0);
99 BOUNDCHECK_SCREEN(x0, y1);
100 size_t tile_x0 = x0 / 8;
101 size_t start_col = x0 % 8;
102 u16 *dst = &backbuf[(start_col + tile_x0 * 8 + y0 * SCREEN_WIDTH) / 2];
103 if(x0 & 1) {
104 for (size_t i = 0; i <= y1 - y0; i++, dst += SCREEN_WIDTH / 2) {
105 *dst = (*dst & 0xF) | (clr << 8);
106 }
107 } else {
108 for (size_t i = 0; i <= y1 - y0; i++, dst += SCREEN_WIDTH / 2) {
109 *dst = (*dst & ~0xF) | clr;
110 }
111 }
112}
113
114IWRAM_CODE
115UNROLL_LOOPS
116void
117draw_line(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) {
118 BOUNDCHECK_SCREEN(x0, y0);
119 BOUNDCHECK_SCREEN(x1, y1);
120
121 if (y0 == y1) {
122 MAYBE_SWAP(x0, x1);
123 draw_hline(x0, x1, y0, clr);
124 } else if (x0 == x1) {
125 MAYBE_SWAP(y0, y1);
126 draw_vline(x0, y0, y1, clr);
127 } else {
128#if SUBPIXEL_LINES == 1
129 // Fixed Precision constants.
130 const int fp_bit = 6;
131 const int fp_one = FP_NUM(1, fp_bit);
132 const int fp_half = fp_one >> 1;
133
134 int dx = x0 > x1 ? x0 - x1 : x1 - x0;
135 int dy = y0 > y1 ? y0 - y1 : y1 - y0;
136 int dxf = (dx << fp_bit);
137 int dyf = (dy << fp_bit);
138
139 if ((dx >= dy && x0 > x1) || (dx < dy && y0 > y1)) {
140 SWAP(x0, x1);
141 SWAP(y0, y1);
142 }
143
144 int frac_x = x0 > x1 ? FP_NUM(x0 - x1, fp_bit) : FP_NUM(x1 - x0, fp_bit);
145 int frac_y = y0 > y1 ? FP_NUM(y0 - y1, fp_bit) : FP_NUM(y1 - y0, fp_bit);
146 int x_step = x0 > x1 ? -1 : 1;
147 int y_step = y0 > y1 ? -SCREEN_WIDTH : SCREEN_WIDTH;
148
149 u16 *dst = NULL;
150 uintptr_t addr = ((uintptr_t)backbuf + y0 * SCREEN_WIDTH + x0);
151 u32 mask = x0 & 1 ? ~0xFF : 0xFF;
152 u32 color = (clr & 0xFF) | ((clr & 0xFF) << 8);
153 if (dx >= dy) {
154 int distance = (frac_y - fp_one) * dx - (frac_x - fp_half) * dy;
155 int remaining = dx;
156 while (distance <= 0 && remaining > 0) {
157 dst = (u16*)(addr - (mask >> 31));
158 *dst = (*dst & ~mask) | (color & mask);
159 distance += 2 * dyf;
160 addr += x_step;
161 remaining--;
162 mask = ~mask;
163 }
164 distance -= 2 * dxf;
165 addr += y_step;
166
167 while (remaining >= 0) {
168 dst = (u16*)(addr - (mask >> 31));
169 *dst = (*dst & ~mask) | (color & mask);
170 if (distance >= 0) {
171 distance -= 2 * dxf;
172 addr += y_step;
173 }
174 distance += 2 * dyf;
175 addr += x_step;
176 mask = ~mask;
177 remaining--;
178 }
179 } else {
180 int distance = (frac_x - fp_one) * dy - (frac_y - fp_half) * dx;
181 int remaining = dy;
182 while (distance <= 0 && remaining > 0) {
183 dst = (u16*)(addr - (mask >> 31));
184 *dst = (*dst & ~mask) | (color & mask);
185 distance += 2 * dxf;
186 addr += y_step;
187 remaining--;
188 }
189 distance -= 2 * dyf;
190 addr += x_step;
191 mask = ~mask;
192
193 while (remaining >= 0) {
194 dst = (u16*)(addr - (mask >> 31));
195 *dst = (*dst & ~mask) | (color & mask);
196 if (distance >= 0) {
197 distance -= 2 * dyf;
198 addr += x_step;
199 mask = ~mask;
200 }
201 distance += 2 * dxf;
202 addr += y_step;
203 remaining--;
204 }
205 }
206#else
207 // Diagonal line.
208 int dx = x0 > x1 ? x0 - x1 : x1 - x0;
209 int dy = y0 > y1 ? y0 - y1 : y1 - y0;
210 int x_step = x0 > x1 ? -1 : 1;
211 int y_step = y0 > y1 ? -SCREEN_WIDTH : SCREEN_WIDTH;
212
213 u16 *dst = NULL;
214 uintptr_t addr = ((uintptr_t)backbuf + y0 * SCREEN_WIDTH + x0);
215 u32 mask = x0 & 1 ? ~0xFF : 0xFF;
216 u32 color = (clr & 0xFF) | ((clr & 0xFF) << 8);
217 if (dx >= dy) {
218 int diff = 2 * dy - dx;
219 for (int i = 0; i < dx + 1; i++) {
220 dst = (u16*)(addr - (mask >> 31));
221 *dst = (*dst & ~mask) | (color & mask);
222 if (diff >= 0) {
223 diff -= 2 * dx;
224 addr += y_step;
225 }
226 diff += 2 * dy;
227 addr += x_step;
228 mask = ~mask;
229 }
230 } else {
231 int diff = 2 * dx - dy;
232 for (int i = 0; i < dy + 1; i++) {
233 dst = (u16*)(addr - (mask >> 31));
234 *dst = (*dst & ~mask) | (color & mask);
235 if (diff >= 0) {
236 diff -= 2 * dy;
237 addr += x_step;
238 mask = ~mask;
239 }
240 diff += 2 * dx;
241 addr += y_step;
242 }
243 }
244#endif
245 }
246}
247
248IWRAM_CODE
249void
250draw_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) {
251 BOUNDCHECK_SCREEN(x0, y0);
252 BOUNDCHECK_SCREEN(x1, y1);
253 MAYBE_SWAP(x0, x1);
254 MAYBE_SWAP(y0, y1);
255
256 draw_hline(x0, x1, y0, clr);
257 draw_hline(x0, x1, y1, clr);
258 draw_vline(x0, y0, y1, clr);
259 draw_vline(x1, y0, y1, clr);
260}
261
262IWRAM_CODE
263void
264draw_filled_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 // Special condition. If the screen is to be completely filled, use the DMA
271 // instead.
272 if (x0 == 0 && x1 >= (SCREEN_WIDTH - 1) && y0 == 0 && y1 >= (SCREEN_HEIGHT - 1)) {
273 screen_fill(clr);
274 return;
275 }
276
277 // Drawline implementation.
278 for (size_t y = y0; y <= y1; y++) {
279 draw_hline(x0, x1, y, clr);
280 }
281}
282
283//
284// Sprites (chr/icn).
285//
286
287#if DEC_BIG_LUT == 1
288
289static u64 dec_byte_flip_x[256] = {
290 0x0000000000000000, 0x0000000000000001, 0x0000000000000100, 0x0000000000000101, 0x0000000000010000,
291 0x0000000000010001, 0x0000000000010100, 0x0000000000010101, 0x0000000001000000, 0x0000000001000001,
292 0x0000000001000100, 0x0000000001000101, 0x0000000001010000, 0x0000000001010001, 0x0000000001010100,
293 0x0000000001010101, 0x0000000100000000, 0x0000000100000001, 0x0000000100000100, 0x0000000100000101,
294 0x0000000100010000, 0x0000000100010001, 0x0000000100010100, 0x0000000100010101, 0x0000000101000000,
295 0x0000000101000001, 0x0000000101000100, 0x0000000101000101, 0x0000000101010000, 0x0000000101010001,
296 0x0000000101010100, 0x0000000101010101, 0x0000010000000000, 0x0000010000000001, 0x0000010000000100,
297 0x0000010000000101, 0x0000010000010000, 0x0000010000010001, 0x0000010000010100, 0x0000010000010101,
298 0x0000010001000000, 0x0000010001000001, 0x0000010001000100, 0x0000010001000101, 0x0000010001010000,
299 0x0000010001010001, 0x0000010001010100, 0x0000010001010101, 0x0000010100000000, 0x0000010100000001,
300 0x0000010100000100, 0x0000010100000101, 0x0000010100010000, 0x0000010100010001, 0x0000010100010100,
301 0x0000010100010101, 0x0000010101000000, 0x0000010101000001, 0x0000010101000100, 0x0000010101000101,
302 0x0000010101010000, 0x0000010101010001, 0x0000010101010100, 0x0000010101010101, 0x0001000000000000,
303 0x0001000000000001, 0x0001000000000100, 0x0001000000000101, 0x0001000000010000, 0x0001000000010001,
304 0x0001000000010100, 0x0001000000010101, 0x0001000001000000, 0x0001000001000001, 0x0001000001000100,
305 0x0001000001000101, 0x0001000001010000, 0x0001000001010001, 0x0001000001010100, 0x0001000001010101,
306 0x0001000100000000, 0x0001000100000001, 0x0001000100000100, 0x0001000100000101, 0x0001000100010000,
307 0x0001000100010001, 0x0001000100010100, 0x0001000100010101, 0x0001000101000000, 0x0001000101000001,
308 0x0001000101000100, 0x0001000101000101, 0x0001000101010000, 0x0001000101010001, 0x0001000101010100,
309 0x0001000101010101, 0x0001010000000000, 0x0001010000000001, 0x0001010000000100, 0x0001010000000101,
310 0x0001010000010000, 0x0001010000010001, 0x0001010000010100, 0x0001010000010101, 0x0001010001000000,
311 0x0001010001000001, 0x0001010001000100, 0x0001010001000101, 0x0001010001010000, 0x0001010001010001,
312 0x0001010001010100, 0x0001010001010101, 0x0001010100000000, 0x0001010100000001, 0x0001010100000100,
313 0x0001010100000101, 0x0001010100010000, 0x0001010100010001, 0x0001010100010100, 0x0001010100010101,
314 0x0001010101000000, 0x0001010101000001, 0x0001010101000100, 0x0001010101000101, 0x0001010101010000,
315 0x0001010101010001, 0x0001010101010100, 0x0001010101010101, 0x0100000000000000, 0x0100000000000001,
316 0x0100000000000100, 0x0100000000000101, 0x0100000000010000, 0x0100000000010001, 0x0100000000010100,
317 0x0100000000010101, 0x0100000001000000, 0x0100000001000001, 0x0100000001000100, 0x0100000001000101,
318 0x0100000001010000, 0x0100000001010001, 0x0100000001010100, 0x0100000001010101, 0x0100000100000000,
319 0x0100000100000001, 0x0100000100000100, 0x0100000100000101, 0x0100000100010000, 0x0100000100010001,
320 0x0100000100010100, 0x0100000100010101, 0x0100000101000000, 0x0100000101000001, 0x0100000101000100,
321 0x0100000101000101, 0x0100000101010000, 0x0100000101010001, 0x0100000101010100, 0x0100000101010101,
322 0x0100010000000000, 0x0100010000000001, 0x0100010000000100, 0x0100010000000101, 0x0100010000010000,
323 0x0100010000010001, 0x0100010000010100, 0x0100010000010101, 0x0100010001000000, 0x0100010001000001,
324 0x0100010001000100, 0x0100010001000101, 0x0100010001010000, 0x0100010001010001, 0x0100010001010100,
325 0x0100010001010101, 0x0100010100000000, 0x0100010100000001, 0x0100010100000100, 0x0100010100000101,
326 0x0100010100010000, 0x0100010100010001, 0x0100010100010100, 0x0100010100010101, 0x0100010101000000,
327 0x0100010101000001, 0x0100010101000100, 0x0100010101000101, 0x0100010101010000, 0x0100010101010001,
328 0x0100010101010100, 0x0100010101010101, 0x0101000000000000, 0x0101000000000001, 0x0101000000000100,
329 0x0101000000000101, 0x0101000000010000, 0x0101000000010001, 0x0101000000010100, 0x0101000000010101,
330 0x0101000001000000, 0x0101000001000001, 0x0101000001000100, 0x0101000001000101, 0x0101000001010000,
331 0x0101000001010001, 0x0101000001010100, 0x0101000001010101, 0x0101000100000000, 0x0101000100000001,
332 0x0101000100000100, 0x0101000100000101, 0x0101000100010000, 0x0101000100010001, 0x0101000100010100,
333 0x0101000100010101, 0x0101000101000000, 0x0101000101000001, 0x0101000101000100, 0x0101000101000101,
334 0x0101000101010000, 0x0101000101010001, 0x0101000101010100, 0x0101000101010101, 0x0101010000000000,
335 0x0101010000000001, 0x0101010000000100, 0x0101010000000101, 0x0101010000010000, 0x0101010000010001,
336 0x0101010000010100, 0x0101010000010101, 0x0101010001000000, 0x0101010001000001, 0x0101010001000100,
337 0x0101010001000101, 0x0101010001010000, 0x0101010001010001, 0x0101010001010100, 0x0101010001010101,
338 0x0101010100000000, 0x0101010100000001, 0x0101010100000100, 0x0101010100000101, 0x0101010100010000,
339 0x0101010100010001, 0x0101010100010100, 0x0101010100010101, 0x0101010101000000, 0x0101010101000001,
340 0x0101010101000100, 0x0101010101000101, 0x0101010101010000, 0x0101010101010001, 0x0101010101010100,
341 0x0101010101010101
342};
343
344static u64 dec_byte[256] = {
345 0x0000000000000000, 0x0100000000000000, 0x0001000000000000, 0x0101000000000000, 0x0000010000000000,
346 0x0100010000000000, 0x0001010000000000, 0x0101010000000000, 0x0000000100000000, 0x0100000100000000,
347 0x0001000100000000, 0x0101000100000000, 0x0000010100000000, 0x0100010100000000, 0x0001010100000000,
348 0x0101010100000000, 0x0000000001000000, 0x0100000001000000, 0x0001000001000000, 0x0101000001000000,
349 0x0000010001000000, 0x0100010001000000, 0x0001010001000000, 0x0101010001000000, 0x0000000101000000,
350 0x0100000101000000, 0x0001000101000000, 0x0101000101000000, 0x0000010101000000, 0x0100010101000000,
351 0x0001010101000000, 0x0101010101000000, 0x0000000000010000, 0x0100000000010000, 0x0001000000010000,
352 0x0101000000010000, 0x0000010000010000, 0x0100010000010000, 0x0001010000010000, 0x0101010000010000,
353 0x0000000100010000, 0x0100000100010000, 0x0001000100010000, 0x0101000100010000, 0x0000010100010000,
354 0x0100010100010000, 0x0001010100010000, 0x0101010100010000, 0x0000000001010000, 0x0100000001010000,
355 0x0001000001010000, 0x0101000001010000, 0x0000010001010000, 0x0100010001010000, 0x0001010001010000,
356 0x0101010001010000, 0x0000000101010000, 0x0100000101010000, 0x0001000101010000, 0x0101000101010000,
357 0x0000010101010000, 0x0100010101010000, 0x0001010101010000, 0x0101010101010000, 0x0000000000000100,
358 0x0100000000000100, 0x0001000000000100, 0x0101000000000100, 0x0000010000000100, 0x0100010000000100,
359 0x0001010000000100, 0x0101010000000100, 0x0000000100000100, 0x0100000100000100, 0x0001000100000100,
360 0x0101000100000100, 0x0000010100000100, 0x0100010100000100, 0x0001010100000100, 0x0101010100000100,
361 0x0000000001000100, 0x0100000001000100, 0x0001000001000100, 0x0101000001000100, 0x0000010001000100,
362 0x0100010001000100, 0x0001010001000100, 0x0101010001000100, 0x0000000101000100, 0x0100000101000100,
363 0x0001000101000100, 0x0101000101000100, 0x0000010101000100, 0x0100010101000100, 0x0001010101000100,
364 0x0101010101000100, 0x0000000000010100, 0x0100000000010100, 0x0001000000010100, 0x0101000000010100,
365 0x0000010000010100, 0x0100010000010100, 0x0001010000010100, 0x0101010000010100, 0x0000000100010100,
366 0x0100000100010100, 0x0001000100010100, 0x0101000100010100, 0x0000010100010100, 0x0100010100010100,
367 0x0001010100010100, 0x0101010100010100, 0x0000000001010100, 0x0100000001010100, 0x0001000001010100,
368 0x0101000001010100, 0x0000010001010100, 0x0100010001010100, 0x0001010001010100, 0x0101010001010100,
369 0x0000000101010100, 0x0100000101010100, 0x0001000101010100, 0x0101000101010100, 0x0000010101010100,
370 0x0100010101010100, 0x0001010101010100, 0x0101010101010100, 0x0000000000000001, 0x0100000000000001,
371 0x0001000000000001, 0x0101000000000001, 0x0000010000000001, 0x0100010000000001, 0x0001010000000001,
372 0x0101010000000001, 0x0000000100000001, 0x0100000100000001, 0x0001000100000001, 0x0101000100000001,
373 0x0000010100000001, 0x0100010100000001, 0x0001010100000001, 0x0101010100000001, 0x0000000001000001,
374 0x0100000001000001, 0x0001000001000001, 0x0101000001000001, 0x0000010001000001, 0x0100010001000001,
375 0x0001010001000001, 0x0101010001000001, 0x0000000101000001, 0x0100000101000001, 0x0001000101000001,
376 0x0101000101000001, 0x0000010101000001, 0x0100010101000001, 0x0001010101000001, 0x0101010101000001,
377 0x0000000000010001, 0x0100000000010001, 0x0001000000010001, 0x0101000000010001, 0x0000010000010001,
378 0x0100010000010001, 0x0001010000010001, 0x0101010000010001, 0x0000000100010001, 0x0100000100010001,
379 0x0001000100010001, 0x0101000100010001, 0x0000010100010001, 0x0100010100010001, 0x0001010100010001,
380 0x0101010100010001, 0x0000000001010001, 0x0100000001010001, 0x0001000001010001, 0x0101000001010001,
381 0x0000010001010001, 0x0100010001010001, 0x0001010001010001, 0x0101010001010001, 0x0000000101010001,
382 0x0100000101010001, 0x0001000101010001, 0x0101000101010001, 0x0000010101010001, 0x0100010101010001,
383 0x0001010101010001, 0x0101010101010001, 0x0000000000000101, 0x0100000000000101, 0x0001000000000101,
384 0x0101000000000101, 0x0000010000000101, 0x0100010000000101, 0x0001010000000101, 0x0101010000000101,
385 0x0000000100000101, 0x0100000100000101, 0x0001000100000101, 0x0101000100000101, 0x0000010100000101,
386 0x0100010100000101, 0x0001010100000101, 0x0101010100000101, 0x0000000001000101, 0x0100000001000101,
387 0x0001000001000101, 0x0101000001000101, 0x0000010001000101, 0x0100010001000101, 0x0001010001000101,
388 0x0101010001000101, 0x0000000101000101, 0x0100000101000101, 0x0001000101000101, 0x0101000101000101,
389 0x0000010101000101, 0x0100010101000101, 0x0001010101000101, 0x0101010101000101, 0x0000000000010101,
390 0x0100000000010101, 0x0001000000010101, 0x0101000000010101, 0x0000010000010101, 0x0100010000010101,
391 0x0001010000010101, 0x0101010000010101, 0x0000000100010101, 0x0100000100010101, 0x0001000100010101,
392 0x0101000100010101, 0x0000010100010101, 0x0100010100010101, 0x0001010100010101, 0x0101010100010101,
393 0x0000000001010101, 0x0100000001010101, 0x0001000001010101, 0x0101000001010101, 0x0000010001010101,
394 0x0100010001010101, 0x0001010001010101, 0x0101010001010101, 0x0000000101010101, 0x0100000101010101,
395 0x0001000101010101, 0x0101000101010101, 0x0000010101010101, 0x0100010101010101, 0x0001010101010101,
396 0x0101010101010101
397};
398
399IWRAM_CODE
400static inline
401u64
402decode_1bpp(u8 row, u8 flip_x) {
403 if (flip_x) {
404 return dec_byte_flip_x[row];
405 }
406 return dec_byte[row];
407}
408
409#else
410
411static u32 dec_nibble[] = {
412 0x00000000, 0x01000000, 0x00010000, 0x01010000,
413 0x00000100, 0x01000100, 0x00010100, 0x01010100,
414 0x00000001, 0x01000001, 0x00010001, 0x01010001,
415 0x00000101, 0x01000101, 0x00010101, 0x01010101,
416};
417
418static u32 dec_nibble_flip_x[] = {
419 0x00000000, 0x00000001, 0x00000100, 0x00000101,
420 0x00010000, 0x00010001, 0x00010100, 0x00010101,
421 0x01000000, 0x01000001, 0x01000100, 0x01000101,
422 0x01010000, 0x01010001, 0x01010100, 0x01010101,
423};
424
425IWRAM_CODE
426static inline
427u64
428decode_1bpp(u8 row, u8 flip_x) {
429 if (flip_x) {
430 u32 *lut = dec_nibble_flip_x;
431 return (u64)lut[(row >> 4) & 0xF] << 32 | (u64)lut[(row >> 0) & 0xF];
432 }
433 u32 *lut = dec_nibble;
434 return (u64)lut[(row >> 0) & 0xF] << 32 | (u64)lut[(row >> 4) & 0xF];
435}
436
437#endif
438
439IWRAM_CODE
440static inline
441void
442draw_2bpp_row(size_t x, size_t y, u8 a, u8 b, u8 clr, u8 flip_x) {
443 BOUNDCHECK_SCREEN(x, y);
444
445 size_t tile_x = x / 8;
446 size_t start_col = x % 8;
447 size_t shift_left = start_col * 8;
448 size_t shift_right = (8 - start_col) * 8;
449
450 u64 *dst = &backbuf[(y * 30 + tile_x) * 8 / 2];
451#if DEC_BIG_LUT
452 u64 *lut = flip_x ? dec_byte_flip_x : dec_byte;
453 u64 clr_a = lut[a];
454 u64 clr_b = lut[b];
455#else
456 u64 clr_a = decode_1bpp(a, flip_x);
457 u64 clr_b = decode_1bpp(b, flip_x);
458#endif
459 u64 mask_a = (clr_a * 0xF);
460 u64 mask_b = (clr_b * 0xF);
461 u64 mask = (mask_a | mask_b);
462 u64 color;
463 if (clr == 0) {
464 color = clr_a + (clr_b << 1);
465 } else if (clr == 15) {
466 color = 0;
467 } else {
468 color = (clr_a | clr_b) * clr;
469 }
470 dst[0] = (dst[0] & ~(mask << shift_left)) | (color << shift_left);
471 dst[1] = (dst[1] & ~(mask >> shift_right)) | (color >> shift_right);
472}
473
474IWRAM_CODE
475static inline
476void
477draw_1bpp_row(size_t x, size_t y, u8 a, u8 clr, u8 flip_x) {
478 BOUNDCHECK_SCREEN(x, y);
479
480 size_t tile_x = x / 8;
481 size_t start_col = x % 8;
482 size_t shift_left = start_col * 8;
483 size_t shift_right = (8 - start_col) * 8;
484
485 u64 *dst = &backbuf[(y * 30 + tile_x) * 8 / 2];
486 u64 color = decode_1bpp(a, flip_x);
487 u64 mask = (color * 0xF);
488 color *= clr;
489 dst[0] = (dst[0] & ~(mask << shift_left)) | (color << shift_left);
490 dst[1] = (dst[1] & ~(mask >> shift_right)) | (color >> shift_right);
491}
492
493IWRAM_CODE
494void
495draw_chr(size_t x, size_t y, u8 *sprite, u8 clr, u8 flip_x, u8 flip_y) {
496 BOUNDCHECK_SCREEN(x, y);
497 if (!flip_y) {
498 for(size_t v = 0; v < 8; v++) {
499 if ((y + v) >= SCREEN_HEIGHT) break;
500 u8 ch1 = sprite[v + 0];
501 u8 ch2 = sprite[v + 8];
502 draw_2bpp_row(x, y + v, ch1, ch2, clr, flip_x);
503 }
504 } else {
505 for(size_t v = 0; v < 8; v++) {
506 if ((y + v) >= SCREEN_HEIGHT) break;
507 u8 ch1 = sprite[(7 - v) + 0];
508 u8 ch2 = sprite[(7 - v) + 8];
509 draw_2bpp_row(x, y + v, ch1, ch2, clr, flip_x);
510 }
511 }
512}
513
514IWRAM_CODE
515void
516draw_icn(size_t x, size_t y, u8 *sprite, u8 clr, u8 flip_x, u8 flip_y) {
517 BOUNDCHECK_SCREEN(x, y);
518 if (!flip_y) {
519 for(size_t v = 0; v < 8; v++) {
520 if ((y + v) >= SCREEN_HEIGHT) break;
521 u8 ch1 = sprite[v];
522 draw_1bpp_row(x, y + v, ch1, clr, flip_x);
523 }
524 } else {
525 for(size_t v = 0; v < 8; v++) {
526 if ((y + v) >= SCREEN_HEIGHT) break;
527 u8 ch1 = sprite[(7 - v)];
528 draw_1bpp_row(x, y + v, ch1, clr, flip_x);
529 }
530 }
531}
532
533//
534// Flipping buffers/copying memory.
535//
536
537IWRAM_CODE
538void
539flip_buffer(void) {
540 backbuf = (u16*)((u32)backbuf ^ 0x0A000);
541 DISP_CTRL ^= DISP_PAGE;
542}
543
544//
545// Text rendering.
546//
547
548#include "font.h"
549
550// Font rendering function for the text engine.
551void
552txt_drawc(char c, size_t x, size_t y, u8 clr) {
553 u8 *tile = font_icn;
554 draw_icn(x, y, tile + 8 * c, clr, 1, 0);
555}
556
557void
558txt_drawc_small(char c, size_t x, size_t y, u8 clr) {
559 u8 *tile = font_icn;
560 c = c < 'a' ? c + 16 * 6 : c + 16 * 4;
561 draw_icn(x, y, tile + 8 * c, clr, 1, 0);
562}
563
564#define txt_drawf_small(msg, x, y, clr, ...) \
565 { \
566 char buf[256] = {0}; \
567 posprintf(buf, msg, ##__VA_ARGS__); \
568 u8 tmp = text_engine.spacing;\
569 txt_spacing(4);\
570 text_engine.drawc = txt_drawc_small;\
571 txt_draws(buf, x, y, clr); \
572 txt_spacing(tmp);\
573 text_engine.drawc = txt_drawc;\
574 }
575
576//
577// Initialization.
578//
579
580void
581renderer_init(void) {
582 // Initialize display mode and bg palette.
583 DISP_CTRL = DISP_MODE_4 | DISP_BG_2;
584
585 // Clear VRAM.
586 dma_fill((u16*)MEM_VRAM, 0, KB(96), 3);
587
588 // Initialize default palette.
589 PAL_BUFFER_BG[0] = COLOR_BLACK;
590 PAL_BUFFER_BG[1] = COLOR_WHITE;
591 PAL_BUFFER_BG[2] = COLOR_RED;
592 PAL_BUFFER_BG[3] = COLOR_BLUE;
593 PAL_BUFFER_BG[4] = COLOR_CYAN;
594 PAL_BUFFER_BG[5] = COLOR_GREY;
595 PAL_BUFFER_BG[6] = COLOR_WHITE;
596 PAL_BUFFER_BG[7] = COLOR_GREEN;
597 PAL_BUFFER_BG[8] = COLOR_PURPLE;
598
599 // Initialize text engine.
600 txt_init(txt_drawc);
601}