diff options
author | Bad Diode <bd@badd10de.dev> | 2023-08-06 11:13:22 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2023-08-06 11:13:22 +0200 |
commit | 8aa5155ecfbaaa2e67f82561352a5857bc86d39e (patch) | |
tree | 511b4efe38f661287a939a6758f91d2b64cf71c1 | |
parent | e66f7459129285ff0c69d17089c144189b0dad80 (diff) | |
download | stepper-8aa5155ecfbaaa2e67f82561352a5857bc86d39e.tar.gz stepper-8aa5155ecfbaaa2e67f82561352a5857bc86d39e.zip |
Add missing .c files and wishlist TODOs
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | src/dsound.c | 61 | ||||
-rw-r--r-- | src/main.c | 9 | ||||
-rw-r--r-- | src/renderer_m4.c | 601 |
4 files changed, 675 insertions, 0 deletions
@@ -1 +1,5 @@ | |||
1 | build | 1 | build |
2 | assets | ||
3 | design | ||
4 | releases | ||
5 | scripts | ||
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 @@ | |||
1 | static bool audio_sync_click = false; | ||
2 | #define AUDIO_FREQ 40137 | ||
3 | #define AUDIO_BUF_LEN 672 | ||
4 | #define AUDIO_TIMER 65118 | ||
5 | |||
6 | s8 audio_buffer[AUDIO_BUF_LEN * 2]; | ||
7 | static int audio_buf_active = 1; | ||
8 | |||
9 | void | ||
10 | click_timer(void) { | ||
11 | DMA_TRANSFER[1].ctrl = 0; | ||
12 | TIMER_CTRL_0 = 0; | ||
13 | TIMER_CTRL_1 = 0; | ||
14 | } | ||
15 | |||
16 | void | ||
17 | play_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 | |||
30 | void | ||
31 | init_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 | |||
46 | void | ||
47 | sound_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 | } | ||
@@ -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. | ||
16 | static 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 | |||
37 | IWRAM_CODE | ||
38 | void screen_fill(u8 clr) { | ||
39 | dma_fill(backbuf, 0x01010101 * clr, KB(75) / 2, 3); | ||
40 | } | ||
41 | |||
42 | IWRAM_CODE | ||
43 | void | ||
44 | draw_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 | |||
54 | IWRAM_CODE | ||
55 | static inline | ||
56 | void | ||
57 | draw_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 | |||
93 | IWRAM_CODE | ||
94 | UNROLL_LOOPS | ||
95 | static inline | ||
96 | void | ||
97 | draw_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 | |||
114 | IWRAM_CODE | ||
115 | UNROLL_LOOPS | ||
116 | void | ||
117 | draw_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 | |||
248 | IWRAM_CODE | ||
249 | void | ||
250 | draw_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 | |||
262 | IWRAM_CODE | ||
263 | void | ||
264 | draw_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 | |||
289 | static 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 | |||
344 | static 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 | |||
399 | IWRAM_CODE | ||
400 | static inline | ||
401 | u64 | ||
402 | decode_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 | |||
411 | static 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 | |||
418 | static 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 | |||
425 | IWRAM_CODE | ||
426 | static inline | ||
427 | u64 | ||
428 | decode_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 | |||
439 | IWRAM_CODE | ||
440 | static inline | ||
441 | void | ||
442 | draw_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 | |||
474 | IWRAM_CODE | ||
475 | static inline | ||
476 | void | ||
477 | draw_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 | |||
493 | IWRAM_CODE | ||
494 | void | ||
495 | draw_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 | |||
514 | IWRAM_CODE | ||
515 | void | ||
516 | draw_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 | |||
537 | IWRAM_CODE | ||
538 | void | ||
539 | flip_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. | ||
551 | void | ||
552 | txt_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 | |||
557 | void | ||
558 | txt_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 | |||
580 | void | ||
581 | renderer_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 | } | ||