diff options
author | Bad Diode <bd@badd10de.dev> | 2023-04-15 18:26:03 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2023-04-15 18:29:26 +0200 |
commit | b22d1bb0081a93f43856077917b8b029a8e97ec9 (patch) | |
tree | 26f630892dfe534b324d4fca9c585223f85b8953 | |
parent | 984f3c399b55bddcc177a2eb2a5e002fdc09a69d (diff) | |
download | gba-link-cable-tester-b22d1bb0081a93f43856077917b8b029a8e97ec9.tar.gz gba-link-cable-tester-b22d1bb0081a93f43856077917b8b029a8e97ec9.zip |
Prepare for work on M0 renderers
-rw-r--r-- | src/main.c | 50 | ||||
-rw-r--r-- | src/renderer.c | 726 | ||||
-rw-r--r-- | src/renderer_m4.c | 137 | ||||
-rw-r--r-- | src/text/text.h | 94 |
4 files changed, 522 insertions, 485 deletions
@@ -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_m4.c" | 14 | #include "renderer.c" |
15 | 15 | ||
16 | // | 16 | // |
17 | // Config parameters. | 17 | // Config parameters. |
@@ -101,12 +101,12 @@ test_icn(void) { | |||
101 | 101 | ||
102 | void | 102 | void |
103 | test_lines(void) { | 103 | test_lines(void) { |
104 | for (size_t i = 0; i < 10; i++) { | 104 | for (size_t i = 0; i < 20; i++) { |
105 | draw_line(0, i * 8, (30 * 8 - 1), ((20 - i) * 8 - 1), 5); | 105 | draw_line(0, i * 8, (30 * 8 - 1), ((20 - i) * 8 - 1), 5); |
106 | } | 106 | } |
107 | // for (size_t i = 0; i < 30; i++) { | 107 | for (size_t i = 0; i < 30; i++) { |
108 | // draw_line(i * 8, (20 * 8 - 1), ((30 - i) * 8 - 1), 0, 5); | 108 | draw_line(i * 8, (20 * 8 - 1), ((30 - i) * 8 - 1), 0, 5); |
109 | // } | 109 | } |
110 | } | 110 | } |
111 | 111 | ||
112 | int main(void) { | 112 | int main(void) { |
@@ -128,42 +128,12 @@ int main(void) { | |||
128 | bios_vblank_wait(); | 128 | bios_vblank_wait(); |
129 | 129 | ||
130 | PROF(test_clear(), test_clear_cycles); | 130 | PROF(test_clear(), test_clear_cycles); |
131 | // draw_line(0, 0, 100, 50, 1); | ||
132 | // // draw_line(0, 0, 100, 100, 2); | ||
133 | // // draw_line(0, 0, 50, 100, 3); | ||
134 | // // draw_line(5, 0, 0, 5, 2); | ||
135 | // draw_line(100, 0, 0, 50, 1); | ||
136 | // // draw_line(100, 0, 0, 100, 2); | ||
137 | // // draw_line(50, 0, 0, 100, 3); | ||
138 | // draw_line(0, 0, 50, 159, 1); | ||
139 | |||
140 | // draw_line(0, 0, 50, 159, 1); | ||
141 | // draw_line(0, 0, 60, 159, 1); | ||
142 | // draw_line(8, 0, 50, 159, 2); | ||
143 | // draw_line(8, 0, 60, 159, 2); | ||
144 | // draw_line(50, 159,0, 0, 1); | ||
145 | // draw_line(60, 159,0, 0, 1); | ||
146 | // draw_line(50, 159,8, 0, 2); | ||
147 | // draw_line(60, 159,8, 0, 2); | ||
148 | // draw_line(0, 0, 100, 50, 1); | ||
149 | // draw_line(10, 0, 100, 50, 1); | ||
150 | // draw_line(100, 50,0, 0, 2); | ||
151 | // draw_line(100, 50,10, 0, 2); | ||
152 | |||
153 | // draw_line(5, 5, 50, 159, 2); | ||
154 | // draw_line(50, 159, 0, 0, 1); | ||
155 | // txt_render(); | ||
156 | // txt_clear(); | ||
157 | // txt_printf("dx: %d\n", dx); | ||
158 | // txt_printf("dy: %d\n", dy); | ||
159 | // txt_render(); | ||
160 | // txt_clear(); | ||
161 | PROF(test_lines(), test_lines_cycles); | 131 | PROF(test_lines(), test_lines_cycles); |
162 | draw_filled_rect(0, 0, 150, 60, 0); | 132 | PROF(test_rect(), test_rect_cycles); |
163 | // PROF(test_rect(), test_rect_cycles); | 133 | PROF(test_fill_rect(), test_fill_rect_cycles); |
164 | // PROF(test_fill_rect(), test_fill_rect_cycles); | 134 | PROF(test_chr(), test_chr_cycles); |
165 | // PROF(test_chr(), test_chr_cycles); | 135 | PROF(test_icn(), test_icn_cycles); |
166 | // PROF(test_icn(), test_icn_cycles); | 136 | draw_filled_rect(0, 0, 140, 60, 0); |
167 | PROF_SHOW(); | 137 | PROF_SHOW(); |
168 | PROF(flip_buffer(), flip_cycles); | 138 | PROF(flip_buffer(), flip_cycles); |
169 | } | 139 | } |
diff --git a/src/renderer.c b/src/renderer.c index 495585e..5ef9cab 100644 --- a/src/renderer.c +++ b/src/renderer.c | |||
@@ -34,16 +34,6 @@ | |||
34 | // instead (64KB - 20KB). | 34 | // instead (64KB - 20KB). |
35 | #define BACKBUF ((u32*)(MEM_VRAM + KB(96) - KB(20))) | 35 | #define BACKBUF ((u32*)(MEM_VRAM + KB(96) - KB(20))) |
36 | 36 | ||
37 | // The font data is located at the end of the frontbuffer memory, after the tile | ||
38 | // map and requires 8KB for 256 8x8 characters at 4bpp. This, along with the | ||
39 | // tilemap information allow us to store the frontbuffer and font for a text | ||
40 | // background in the first 2 charblocks (32KB). | ||
41 | #define FONT_DATA ((u32*)(MEM_VRAM + KB(96) - KB(8))) | ||
42 | #define FONT_TILEMAP ((u16*)(MEM_VRAM + KB(96) - KB(8) - KB(1))) | ||
43 | #define FONT_SB 15 | ||
44 | #define FONT_OFFSET 192 | ||
45 | |||
46 | |||
47 | // Keep track of which tiles need to be copied to the frontbuffer. | 37 | // Keep track of which tiles need to be copied to the frontbuffer. |
48 | static u32 dirty_tiles[21] = {0}; | 38 | static u32 dirty_tiles[21] = {0}; |
49 | 39 | ||
@@ -56,6 +46,25 @@ static u32 dirty_tiles[21] = {0}; | |||
56 | #define BOUNDCHECK_SCREEN(X,Y) if ((X) >= SCREEN_WIDTH || (Y) >= SCREEN_HEIGHT) return; | 46 | #define BOUNDCHECK_SCREEN(X,Y) if ((X) >= SCREEN_WIDTH || (Y) >= SCREEN_HEIGHT) return; |
57 | #endif | 47 | #endif |
58 | 48 | ||
49 | // Swap A and B values without a tmp variable. | ||
50 | #define SWAP(A, B) (((A) ^= (B)), ((B) ^= (A)), ((A) ^= (B))) | ||
51 | |||
52 | // Swap A and B values to make sure A <= B. | ||
53 | #define MAYBE_SWAP(A,B) if ((A) > (B)) { SWAP(A,B); } | ||
54 | |||
55 | IWRAM_CODE | ||
56 | void screen_fill(u8 clr) { | ||
57 | // #if 0 | ||
58 | // u32 *dst = backbuf; | ||
59 | // for(int i = 0; i < KB(75) / 8; i++) { | ||
60 | // *dst++ = 0x01010101 * clr; | ||
61 | // } | ||
62 | // #else | ||
63 | // dma_fill(backbuf, 0x01010101 * clr, KB(75) / 2, 3); | ||
64 | // screen_updated = true; | ||
65 | // #endif | ||
66 | } | ||
67 | |||
59 | IWRAM_CODE | 68 | IWRAM_CODE |
60 | void | 69 | void |
61 | draw_pixel(size_t x, size_t y, u8 clr) { | 70 | draw_pixel(size_t x, size_t y, u8 clr) { |
@@ -77,101 +86,161 @@ draw_pixel(size_t x, size_t y, u8 clr) { | |||
77 | } | 86 | } |
78 | 87 | ||
79 | IWRAM_CODE | 88 | IWRAM_CODE |
89 | static inline | ||
80 | void | 90 | void |
81 | draw_line(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { | 91 | draw_hline(size_t x0, size_t x1, size_t y0, u8 clr) { |
82 | BOUNDCHECK_SCREEN(x0, y0); | 92 | // TODO |
83 | BOUNDCHECK_SCREEN(x1, y1); | 93 | } |
84 | |||
85 | // Find row positions for the given x/y coordinates. | ||
86 | size_t tile_x0 = x0 / 8; | ||
87 | size_t tile_y0 = y0 / 8; | ||
88 | size_t tile_x1 = x1 / 8; | ||
89 | size_t tile_y1 = y1 / 8; | ||
90 | size_t start_col0 = x0 % 8; | ||
91 | size_t start_col1 = x1 % 8; | ||
92 | size_t start_row0 = y0 % 8; | ||
93 | size_t start_row1 = y1 % 8; | ||
94 | 94 | ||
95 | // Get a pointer to the backbuffer and the tile row. | 95 | IWRAM_CODE |
96 | u32 *backbuffer = &BACKBUF[start_row0 + (tile_x0 + tile_y0 * 32) * 8]; | 96 | UNROLL_LOOPS |
97 | static inline | ||
98 | void | ||
99 | draw_vline(size_t x0, size_t y0, size_t y1, u8 clr) { | ||
100 | // TODO | ||
101 | } | ||
97 | 102 | ||
103 | IWRAM_CODE | ||
104 | void | ||
105 | draw_line(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { | ||
106 | // BOUNDCHECK_SCREEN(x0, y0); | ||
107 | // BOUNDCHECK_SCREEN(x1, y1); | ||
98 | if (y0 == y1) { | 108 | if (y0 == y1) { |
99 | // Horizontal line. There are 3 cases: | 109 | MAYBE_SWAP(x0, x1); |
100 | // 1. Lines fit on a single tile. | 110 | draw_hline(x0, x1, y0, clr); |
101 | // 2. Lines go through 2 tiles, both require partial row updates. | ||
102 | // 3. Lines go through 3 or more tiles, first and last tiles use | ||
103 | // partial row updates, rows in the middle can write the. | ||
104 | size_t dx = tile_x1 - tile_x0; | ||
105 | if (dx < 1) { | ||
106 | u32 row_mask = 0xFFFFFFFF; | ||
107 | row_mask >>= (7 - start_col1 - dx) * 4; | ||
108 | row_mask &= 0xFFFFFFFF << start_col0 * 4; | ||
109 | u32 row = (0x11111111 * clr) & row_mask; | ||
110 | backbuffer[0] = (backbuffer[0] & ~row_mask) | row; | ||
111 | dirty_tiles[tile_y0] |= 1 << tile_x0; | ||
112 | } else { | ||
113 | size_t shift_left = start_col0 * 4; | ||
114 | size_t shift_right = (7 - start_col1) * 4; | ||
115 | u32 row_mask = 0xFFFFFFFF; | ||
116 | u32 row = 0x11111111 * clr; | ||
117 | backbuffer[0] = backbuffer[0] & ~(row_mask << shift_left); | ||
118 | backbuffer[0] |= row << shift_left; | ||
119 | dirty_tiles[tile_y0] |= 1 << tile_x0; | ||
120 | for (size_t i = 1; i < dx; i++) { | ||
121 | backbuffer[i * 8] = row; | ||
122 | dirty_tiles[tile_y0] |= 1 << (tile_x0 + i); | ||
123 | } | ||
124 | backbuffer[dx * 8] = backbuffer[dx * 8] & ~(row_mask >> shift_right); | ||
125 | backbuffer[dx * 8] |= row >> shift_right; | ||
126 | dirty_tiles[tile_y0] |= 1 << (tile_x0 + dx); | ||
127 | } | ||
128 | } else if (x0 == x1) { | 111 | } else if (x0 == x1) { |
129 | // Vertical line. The cases are analogous to the horizontal ones. | 112 | MAYBE_SWAP(y0, y1); |
130 | size_t dy = tile_y1 - tile_y0; | 113 | draw_vline(x0, y0, y1, clr); |
131 | u32 row_mask = 0xF << start_col0 * 4; | ||
132 | u32 row_left = (0x11111111 * clr) & row_mask; | ||
133 | if (dy < 1) { | ||
134 | for (size_t i = 0; i <= y1 - y0; i++, backbuffer++) { | ||
135 | backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left; | ||
136 | } | ||
137 | } else { | ||
138 | for (size_t i = 0; i < (8 - start_row0); i++, backbuffer++) { | ||
139 | backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left; | ||
140 | } | ||
141 | dirty_tiles[tile_y0] |= 1 << tile_x0; | ||
142 | backbuffer += 8 * 31; | ||
143 | for (size_t j = 1; j < dy; j++) { | ||
144 | for (size_t i = 0; i < 8; i++, backbuffer++) { | ||
145 | backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left; | ||
146 | } | ||
147 | backbuffer += 8 * 31; | ||
148 | dirty_tiles[tile_y0 + j] |= 1 << tile_x0; | ||
149 | } | ||
150 | for (size_t i = 0; i <= start_row1; i++, backbuffer++) { | ||
151 | backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left; | ||
152 | } | ||
153 | dirty_tiles[tile_y1] |= 1 << tile_x0; | ||
154 | } | ||
155 | } else { | 114 | } else { |
156 | // Diagonal line. | 115 | // // Diagonal line. |
157 | int dx = x0 > x1 ? x0 - x1 : x1 - x0; | 116 | // int dx = x0 > x1 ? x0 - x1 : x1 - x0; |
158 | int dy = y0 > y1 ? y1 - y0 : y0 - y1; | 117 | // int dy = y0 > y1 ? y0 - y1 : y1 - y0; |
159 | int x_step = x0 < x1 ? 1 : -1; | 118 | // int x_step = x0 > x1 ? -1 : 1; |
160 | int y_step = y0 < y1 ? 1 : -1; | 119 | // int y_step = y0 > y1 ? -SCREEN_WIDTH : SCREEN_WIDTH; |
161 | int err = dx + dy; | 120 | |
162 | while (!(x0 == x1 && y0 == y1)) { | 121 | // u16 *dst = NULL; |
163 | draw_pixel(x0, y0, clr); | 122 | // uintptr_t addr = ((uintptr_t)backbuf + y0 * SCREEN_WIDTH + x0); |
164 | int diff = 2 * err; | 123 | // u32 mask = x0 & 1 ? ~0xFF : 0xFF; |
165 | if (diff >= dy) { | 124 | // u32 color = (clr & 0xFF) | ((clr & 0xFF) << 8); |
166 | err += dy; | 125 | // if (dx >= dy) { |
167 | x0 += x_step; | 126 | // int diff = 2 * dy - dx; |
168 | } | 127 | // for (int i = 0; i < dx + 1; i++) { |
169 | if (diff <= dx) { | 128 | // dst = (u16 *)(addr - (mask >> 31)); |
170 | err += dx; | 129 | // *dst = (*dst & ~mask) | (color & mask); |
171 | y0 += y_step; | 130 | // if (diff >= 0) { |
172 | } | 131 | // diff -= 2 * dx; |
173 | } | 132 | // addr += y_step; |
133 | // } | ||
134 | // diff += 2 * dy; | ||
135 | // addr += x_step; | ||
136 | // mask = ~mask; | ||
137 | // } | ||
138 | // } else { | ||
139 | // int diff = 2 * dx - dy; | ||
140 | // for (int i = 0; i < dy + 1; i++) { | ||
141 | // dst = (u16 *)(addr - (mask >> 31)); | ||
142 | // *dst = (*dst & ~mask) | (color & mask); | ||
143 | // if (diff >= 0) { | ||
144 | // diff -= 2 * dy; | ||
145 | // addr += x_step; | ||
146 | // mask = ~mask; | ||
147 | // } | ||
148 | // diff += 2 * dx; | ||
149 | // addr += y_step; | ||
150 | // } | ||
151 | // } | ||
174 | } | 152 | } |
153 | |||
154 | // // Find row positions for the given x/y coordinates. | ||
155 | // size_t tile_x0 = x0 / 8; | ||
156 | // size_t tile_y0 = y0 / 8; | ||
157 | // size_t tile_x1 = x1 / 8; | ||
158 | // size_t tile_y1 = y1 / 8; | ||
159 | // size_t start_col0 = x0 % 8; | ||
160 | // size_t start_col1 = x1 % 8; | ||
161 | // size_t start_row0 = y0 % 8; | ||
162 | // size_t start_row1 = y1 % 8; | ||
163 | |||
164 | // // Get a pointer to the backbuffer and the tile row. | ||
165 | // u32 *backbuffer = &BACKBUF[start_row0 + (tile_x0 + tile_y0 * 32) * 8]; | ||
166 | |||
167 | // if (y0 == y1) { | ||
168 | // // Horizontal line. There are 3 cases: | ||
169 | // // 1. Lines fit on a single tile. | ||
170 | // // 2. Lines go through 2 tiles, both require partial row updates. | ||
171 | // // 3. Lines go through 3 or more tiles, first and last tiles use | ||
172 | // // partial row updates, rows in the middle can write the. | ||
173 | // size_t dx = tile_x1 - tile_x0; | ||
174 | // if (dx < 1) { | ||
175 | // u32 row_mask = 0xFFFFFFFF; | ||
176 | // row_mask >>= (7 - start_col1 - dx) * 4; | ||
177 | // row_mask &= 0xFFFFFFFF << start_col0 * 4; | ||
178 | // u32 row = (0x11111111 * clr) & row_mask; | ||
179 | // backbuffer[0] = (backbuffer[0] & ~row_mask) | row; | ||
180 | // dirty_tiles[tile_y0] |= 1 << tile_x0; | ||
181 | // } else { | ||
182 | // size_t shift_left = start_col0 * 4; | ||
183 | // size_t shift_right = (7 - start_col1) * 4; | ||
184 | // u32 row_mask = 0xFFFFFFFF; | ||
185 | // u32 row = 0x11111111 * clr; | ||
186 | // backbuffer[0] = backbuffer[0] & ~(row_mask << shift_left); | ||
187 | // backbuffer[0] |= row << shift_left; | ||
188 | // dirty_tiles[tile_y0] |= 1 << tile_x0; | ||
189 | // for (size_t i = 1; i < dx; i++) { | ||
190 | // backbuffer[i * 8] = row; | ||
191 | // dirty_tiles[tile_y0] |= 1 << (tile_x0 + i); | ||
192 | // } | ||
193 | // backbuffer[dx * 8] = backbuffer[dx * 8] & ~(row_mask >> shift_right); | ||
194 | // backbuffer[dx * 8] |= row >> shift_right; | ||
195 | // dirty_tiles[tile_y0] |= 1 << (tile_x0 + dx); | ||
196 | // } | ||
197 | // } else if (x0 == x1) { | ||
198 | // // Vertical line. The cases are analogous to the horizontal ones. | ||
199 | // size_t dy = tile_y1 - tile_y0; | ||
200 | // u32 row_mask = 0xF << start_col0 * 4; | ||
201 | // u32 row_left = (0x11111111 * clr) & row_mask; | ||
202 | // if (dy < 1) { | ||
203 | // for (size_t i = 0; i <= y1 - y0; i++, backbuffer++) { | ||
204 | // backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left; | ||
205 | // } | ||
206 | // } else { | ||
207 | // for (size_t i = 0; i < (8 - start_row0); i++, backbuffer++) { | ||
208 | // backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left; | ||
209 | // } | ||
210 | // dirty_tiles[tile_y0] |= 1 << tile_x0; | ||
211 | // backbuffer += 8 * 31; | ||
212 | // for (size_t j = 1; j < dy; j++) { | ||
213 | // for (size_t i = 0; i < 8; i++, backbuffer++) { | ||
214 | // backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left; | ||
215 | // } | ||
216 | // backbuffer += 8 * 31; | ||
217 | // dirty_tiles[tile_y0 + j] |= 1 << tile_x0; | ||
218 | // } | ||
219 | // for (size_t i = 0; i <= start_row1; i++, backbuffer++) { | ||
220 | // backbuffer[0] = (backbuffer[0] & ~row_mask) | row_left; | ||
221 | // } | ||
222 | // dirty_tiles[tile_y1] |= 1 << tile_x0; | ||
223 | // } | ||
224 | // } else { | ||
225 | // // Diagonal line. | ||
226 | // int dx = x0 > x1 ? x0 - x1 : x1 - x0; | ||
227 | // int dy = y0 > y1 ? y1 - y0 : y0 - y1; | ||
228 | // int x_step = x0 < x1 ? 1 : -1; | ||
229 | // int y_step = y0 < y1 ? 1 : -1; | ||
230 | // int err = dx + dy; | ||
231 | // while (!(x0 == x1 && y0 == y1)) { | ||
232 | // draw_pixel(x0, y0, clr); | ||
233 | // int diff = 2 * err; | ||
234 | // if (diff >= dy) { | ||
235 | // err += dy; | ||
236 | // x0 += x_step; | ||
237 | // } | ||
238 | // if (diff <= dx) { | ||
239 | // err += dx; | ||
240 | // y0 += y_step; | ||
241 | // } | ||
242 | // } | ||
243 | // } | ||
175 | } | 244 | } |
176 | 245 | ||
177 | IWRAM_CODE | 246 | IWRAM_CODE |
@@ -179,47 +248,58 @@ void | |||
179 | draw_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { | 248 | draw_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { |
180 | BOUNDCHECK_SCREEN(x0, y0); | 249 | BOUNDCHECK_SCREEN(x0, y0); |
181 | BOUNDCHECK_SCREEN(x1, y1); | 250 | BOUNDCHECK_SCREEN(x1, y1); |
182 | 251 | MAYBE_SWAP(x0, x1); | |
183 | // Find row positions for the given x/y coordinates. | 252 | MAYBE_SWAP(y0, y1); |
184 | size_t tile_x0 = x0 / 8; | 253 | |
185 | size_t tile_y0 = y0 / 8; | 254 | draw_hline(x0, x1, y0, clr); |
186 | size_t tile_x1 = x1 / 8; | 255 | draw_hline(x0, x1, y1, clr); |
187 | size_t tile_y1 = y1 / 8; | 256 | draw_vline(x0, y0, y1, clr); |
188 | size_t start_col0 = x0 % 8; | 257 | draw_vline(x1, y0, y1, clr); |
189 | size_t start_col1 = x1 % 8; | 258 | |
190 | size_t start_row0 = y0 % 8; | 259 | // TODO: check if this is better. |
191 | size_t start_row1 = y1 % 8; | 260 | // BOUNDCHECK_SCREEN(x0, y0); |
192 | 261 | // BOUNDCHECK_SCREEN(x1, y1); | |
193 | // Get a pointer to the backbuffer and the tile row. | 262 | |
194 | u32 *buf_top = &BACKBUF[start_row0 + (tile_x0 + tile_y0 * 32) * 8]; | 263 | // // Find row positions for the given x/y coordinates. |
195 | u32 *buf_bot = &BACKBUF[start_row1 + (tile_x0 + tile_y1 * 32) * 8]; | 264 | // size_t tile_x0 = x0 / 8; |
196 | 265 | // size_t tile_y0 = y0 / 8; | |
197 | size_t dx = tile_x1 - tile_x0; | 266 | // size_t tile_x1 = x1 / 8; |
198 | size_t dy = tile_y1 - tile_y0; | 267 | // size_t tile_y1 = y1 / 8; |
199 | 268 | // size_t start_col0 = x0 % 8; | |
200 | memset(buf_top, 3, x1 - x0); | 269 | // size_t start_col1 = x1 % 8; |
201 | memset(buf_bot, 3, x1 - x0); | 270 | // size_t start_row0 = y0 % 8; |
202 | 271 | // size_t start_row1 = y1 % 8; | |
203 | if (dx < 1) { | 272 | |
204 | dirty_tiles[tile_y0] |= 1 << tile_x0; | 273 | // // Get a pointer to the backbuffer and the tile row. |
205 | dirty_tiles[tile_y1] |= 1 << tile_x0; | 274 | // u32 *buf_top = &BACKBUF[start_row0 + (tile_x0 + tile_y0 * 32) * 8]; |
206 | } else { | 275 | // u32 *buf_bot = &BACKBUF[start_row1 + (tile_x0 + tile_y1 * 32) * 8]; |
207 | dirty_tiles[tile_y0] |= 1 << tile_x0; | 276 | |
208 | dirty_tiles[tile_y1] |= 1 << tile_x0; | 277 | // size_t dx = tile_x1 - tile_x0; |
209 | for (size_t i = 1; i < dx; i++) { | 278 | // size_t dy = tile_y1 - tile_y0; |
210 | dirty_tiles[tile_y0] |= 1 << (tile_x0 + i); | 279 | |
211 | dirty_tiles[tile_y1] |= 1 << (tile_x0 + i); | 280 | // memset(buf_top, 3, x1 - x0); |
212 | } | 281 | // memset(buf_bot, 3, x1 - x0); |
213 | dirty_tiles[tile_y0] |= 1 << (tile_x0 + dx); | 282 | |
214 | dirty_tiles[tile_y1] |= 1 << (tile_x0 + dx); | 283 | // if (dx < 1) { |
215 | } | 284 | // dirty_tiles[tile_y0] |= 1 << tile_x0; |
216 | if (dy < 1) { | 285 | // dirty_tiles[tile_y1] |= 1 << tile_x0; |
217 | } else { | 286 | // } else { |
218 | for (size_t j = 1; j < dy; j++) { | 287 | // dirty_tiles[tile_y0] |= 1 << tile_x0; |
219 | dirty_tiles[tile_y0 + j] |= 1 << tile_x0; | 288 | // dirty_tiles[tile_y1] |= 1 << tile_x0; |
220 | dirty_tiles[tile_y0 + j] |= 1 << (tile_x0 + dx); | 289 | // for (size_t i = 1; i < dx; i++) { |
221 | } | 290 | // dirty_tiles[tile_y0] |= 1 << (tile_x0 + i); |
222 | } | 291 | // dirty_tiles[tile_y1] |= 1 << (tile_x0 + i); |
292 | // } | ||
293 | // dirty_tiles[tile_y0] |= 1 << (tile_x0 + dx); | ||
294 | // dirty_tiles[tile_y1] |= 1 << (tile_x0 + dx); | ||
295 | // } | ||
296 | // if (dy < 1) { | ||
297 | // } else { | ||
298 | // for (size_t j = 1; j < dy; j++) { | ||
299 | // dirty_tiles[tile_y0 + j] |= 1 << tile_x0; | ||
300 | // dirty_tiles[tile_y0 + j] |= 1 << (tile_x0 + dx); | ||
301 | // } | ||
302 | // } | ||
223 | } | 303 | } |
224 | 304 | ||
225 | IWRAM_CODE | 305 | IWRAM_CODE |
@@ -227,11 +307,30 @@ void | |||
227 | draw_filled_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { | 307 | draw_filled_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { |
228 | BOUNDCHECK_SCREEN(x0, y0); | 308 | BOUNDCHECK_SCREEN(x0, y0); |
229 | BOUNDCHECK_SCREEN(x1, y1); | 309 | BOUNDCHECK_SCREEN(x1, y1); |
310 | MAYBE_SWAP(x0, x1); | ||
311 | MAYBE_SWAP(y0, y1); | ||
312 | |||
313 | // Special condition. If the screen is to be completely filled, use the DMA | ||
314 | // instead. | ||
315 | if (x0 == 0 && x1 >= (SCREEN_WIDTH - 1) && y0 == 0 && y1 >= (SCREEN_HEIGHT - 1)) { | ||
316 | screen_fill(clr); | ||
317 | return; | ||
318 | } | ||
319 | |||
320 | // Drawline implementation. | ||
321 | for (size_t y = y0; y <= y1; y++) { | ||
322 | // NOTE: Unclear why here draw_hline is faster than draw_line. | ||
323 | draw_hline(x0, x1, y, clr); | ||
324 | } | ||
325 | |||
326 | // TODO: check if this is better. | ||
327 | // BOUNDCHECK_SCREEN(x0, y0); | ||
328 | // BOUNDCHECK_SCREEN(x1, y1); | ||
230 | 329 | ||
231 | size_t dx = x1 - x0; | 330 | // size_t dx = x1 - x0; |
232 | size_t dy = y1 - y0; | 331 | // size_t dy = y1 - y0; |
233 | u8 *buf = &BACKBUF[0]; | 332 | // u8 *buf = &BACKBUF[0]; |
234 | memset(buf, 0x11 * clr, 16); | 333 | // memset(buf, 0x11 * clr, 16); |
235 | //for (size_t j = 0; j < 1; j++) { | 334 | //for (size_t j = 0; j < 1; j++) { |
236 | // // for (size_t i = 0; i < dx; i++) { | 335 | // // for (size_t i = 0; i < dx; i++) { |
237 | // // buf[i + j * 16] = clr; | 336 | // // buf[i + j * 16] = clr; |
@@ -276,87 +375,89 @@ draw_filled_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { | |||
276 | // } | 375 | // } |
277 | } | 376 | } |
278 | 377 | ||
279 | IWRAM_CODE | 378 | // IWRAM_CODE |
280 | void | 379 | // void |
281 | draw_tile(size_t x, size_t y, Tile *tile, u8 clr) { | 380 | // draw_tile(size_t x, size_t y, Tile *tile, u8 clr) { |
282 | BOUNDCHECK_SCREEN(x, y); | 381 | // BOUNDCHECK_SCREEN(x, y); |
283 | 382 | ||
284 | // Find row position for the given x/y coordinates. | 383 | // // Find row position for the given x/y coordinates. |
285 | size_t tile_x = x / 8; | 384 | // size_t tile_x = x / 8; |
286 | size_t tile_y = y / 8; | 385 | // size_t tile_y = y / 8; |
287 | size_t start_col = x % 8; | 386 | // size_t start_col = x % 8; |
288 | size_t start_row = y % 8; | 387 | // size_t start_row = y % 8; |
289 | 388 | ||
290 | // Get a pointer to the backbuffer and the tile row. | 389 | // // Get a pointer to the backbuffer and the tile row. |
291 | size_t pos = start_row + (tile_x + tile_y * 32) * 8; | 390 | // size_t pos = start_row + (tile_x + tile_y * 32) * 8; |
292 | u32 *backbuffer = &BACKBUF[pos]; | 391 | // u32 *backbuffer = &BACKBUF[pos]; |
293 | u32 *row = tile; | 392 | // u32 *row = tile; |
294 | 393 | ||
295 | // This will blend all colors weirdly if using tiles that contain colors | 394 | // // This will blend all colors weirdly if using tiles that contain colors |
296 | // higher than 1. | 395 | // // higher than 1. |
297 | size_t shift_left = start_col * 4; | 396 | // size_t shift_left = start_col * 4; |
298 | size_t shift_right = (8 - start_col) * 4; | 397 | // size_t shift_right = (8 - start_col) * 4; |
299 | // u32 row_mask_left = merge ? 0 : 0xFFFFFFFF << shift_left; | 398 | // // u32 row_mask_left = merge ? 0 : 0xFFFFFFFF << shift_left; |
300 | // u32 row_mask_right = merge ? 0 : 0xFFFFFFFF >> shift_right; | 399 | // // u32 row_mask_right = merge ? 0 : 0xFFFFFFFF >> shift_right; |
301 | 400 | ||
302 | // Draw the tiles. There are 4 possible cases: | 401 | // // Draw the tiles. There are 4 possible cases: |
303 | // 1. The tile is exactly at the tile boundary. | 402 | // // 1. The tile is exactly at the tile boundary. |
304 | // 2. The tile spans 2 tiles horizontally. | 403 | // // 2. The tile spans 2 tiles horizontally. |
305 | // 3. The tile spans 2 tiles vertically. | 404 | // // 3. The tile spans 2 tiles vertically. |
306 | // 4. The tile spans 4 tiles. | 405 | // // 4. The tile spans 4 tiles. |
307 | if (start_col == 0 && start_row == 0) { | 406 | // if (start_col == 0 && start_row == 0) { |
308 | for (size_t i = 0; i < (8 - start_row); i++, backbuffer++) { | 407 | // for (size_t i = 0; i < (8 - start_row); i++, backbuffer++) { |
309 | BOUNDCHECK_SCREEN(x, y + i); | 408 | // BOUNDCHECK_SCREEN(x, y + i); |
310 | backbuffer[0] = (backbuffer[0] & ~row_mask_left) | row[i] * clr; | 409 | // backbuffer[0] = (backbuffer[0] & ~row_mask_left) | row[i] * clr; |
311 | } | 410 | // } |
312 | dirty_tiles[tile_y] |= 1 << tile_x; | 411 | // dirty_tiles[tile_y] |= 1 << tile_x; |
313 | } else if (start_row == 0) { | 412 | // } else if (start_row == 0) { |
314 | for (size_t i = 0; i < 8; i++, backbuffer++) { | 413 | // for (size_t i = 0; i < 8; i++, backbuffer++) { |
315 | BOUNDCHECK_SCREEN(x, y + i); | 414 | // BOUNDCHECK_SCREEN(x, y + i); |
316 | backbuffer[0] = (backbuffer[0] & ~row_mask_left) | (row[i] * clr << shift_left); | 415 | // backbuffer[0] = (backbuffer[0] & ~row_mask_left) | (row[i] * clr << shift_left); |
317 | backbuffer[8] = (backbuffer[8] & ~row_mask_right) | (row[i] * clr >> shift_right); | 416 | // backbuffer[8] = (backbuffer[8] & ~row_mask_right) | (row[i] * clr >> shift_right); |
318 | } | 417 | // } |
319 | dirty_tiles[tile_y] |= 1 << tile_x; | 418 | // dirty_tiles[tile_y] |= 1 << tile_x; |
320 | dirty_tiles[tile_y] |= 1 << (tile_x + 1); | 419 | // dirty_tiles[tile_y] |= 1 << (tile_x + 1); |
321 | } else if (start_col == 0) { | 420 | // } else if (start_col == 0) { |
322 | for (size_t i = 0; i < (8 - start_row); i++, backbuffer++) { | 421 | // for (size_t i = 0; i < (8 - start_row); i++, backbuffer++) { |
323 | BOUNDCHECK_SCREEN(x, y + i); | 422 | // BOUNDCHECK_SCREEN(x, y + i); |
324 | backbuffer[0] = (backbuffer[0] & ~row_mask_left) | row[i] * clr; | 423 | // backbuffer[0] = (backbuffer[0] & ~row_mask_left) | row[i] * clr; |
325 | } | 424 | // } |
326 | backbuffer += 8 * 31; | 425 | // backbuffer += 8 * 31; |
327 | for (size_t i = (8 - start_row); i < 8; i++, backbuffer++) { | 426 | // for (size_t i = (8 - start_row); i < 8; i++, backbuffer++) { |
328 | BOUNDCHECK_SCREEN(x, y + i); | 427 | // BOUNDCHECK_SCREEN(x, y + i); |
329 | backbuffer[0] = (backbuffer[0] & ~row_mask_left) | row[i] * clr; | 428 | // backbuffer[0] = (backbuffer[0] & ~row_mask_left) | row[i] * clr; |
330 | } | 429 | // } |
331 | dirty_tiles[tile_y] |= 1 << tile_x; | 430 | // dirty_tiles[tile_y] |= 1 << tile_x; |
332 | dirty_tiles[tile_y + 1] |= 1 << tile_x; | 431 | // dirty_tiles[tile_y + 1] |= 1 << tile_x; |
333 | } else { | 432 | // } else { |
334 | for (size_t i = 0; i < (8 - start_row); i++, backbuffer++) { | 433 | // for (size_t i = 0; i < (8 - start_row); i++, backbuffer++) { |
335 | BOUNDCHECK_SCREEN(x, y + i); | 434 | // BOUNDCHECK_SCREEN(x, y + i); |
336 | backbuffer[0] = (backbuffer[0] & ~row_mask_left) | (row[i] * clr << shift_left); | 435 | // backbuffer[0] = (backbuffer[0] & ~row_mask_left) | (row[i] * clr << shift_left); |
337 | backbuffer[8] = (backbuffer[8] & ~row_mask_right) | (row[i] * clr >> shift_right); | 436 | // backbuffer[8] = (backbuffer[8] & ~row_mask_right) | (row[i] * clr >> shift_right); |
338 | } | 437 | // } |
339 | backbuffer += 8 * 31; | 438 | // backbuffer += 8 * 31; |
340 | for (size_t i = (8 - start_row); i < 8; i++, backbuffer++) { | 439 | // for (size_t i = (8 - start_row); i < 8; i++, backbuffer++) { |
341 | BOUNDCHECK_SCREEN(x, y + i); | 440 | // BOUNDCHECK_SCREEN(x, y + i); |
342 | backbuffer[0] = (backbuffer[0] & ~row_mask_left) | (row[i] * clr << shift_left); | 441 | // backbuffer[0] = (backbuffer[0] & ~row_mask_left) | (row[i] * clr << shift_left); |
343 | backbuffer[8] = (backbuffer[8] & ~row_mask_right) | (row[i] * clr >> shift_right); | 442 | // backbuffer[8] = (backbuffer[8] & ~row_mask_right) | (row[i] * clr >> shift_right); |
344 | } | 443 | // } |
345 | dirty_tiles[tile_y] |= 1 << tile_x; | 444 | // dirty_tiles[tile_y] |= 1 << tile_x; |
346 | dirty_tiles[tile_y] |= 1 << (tile_x + 1); | 445 | // dirty_tiles[tile_y] |= 1 << (tile_x + 1); |
347 | dirty_tiles[tile_y + 1] |= 1 << tile_x; | 446 | // dirty_tiles[tile_y + 1] |= 1 << tile_x; |
348 | dirty_tiles[tile_y + 1] |= 1 << (tile_x + 1); | 447 | // dirty_tiles[tile_y + 1] |= 1 << (tile_x + 1); |
349 | } | 448 | // } |
350 | } | 449 | // } |
351 | 450 | ||
352 | void | 451 | // void |
353 | clear_screen(void) { | 452 | // clear_screen(void) { |
354 | dma_fill(FRONTBUF, 0, KB(20), 3); | 453 | // dma_fill(FRONTBUF, 0, KB(20), 3); |
355 | } | 454 | // } |
356 | 455 | ||
357 | IWRAM_CODE | 456 | IWRAM_CODE |
358 | void | 457 | void |
359 | flip_buffer(void) { | 458 | flip_buffer(void) { |
459 | // TODO: Copying all tiles for now. Study if it's better to use dirty_tiles | ||
460 | // or dirty_lines. | ||
360 | // Copy dirty tiles from the backbuffer to the frontbuffer. | 461 | // Copy dirty tiles from the backbuffer to the frontbuffer. |
361 | Tile *dst = FRONTBUF; | 462 | Tile *dst = FRONTBUF; |
362 | Tile *src = BACKBUF; | 463 | Tile *src = BACKBUF; |
@@ -369,16 +470,151 @@ flip_buffer(void) { | |||
369 | dst[i + j * 32] = src[i + j * 32]; | 470 | dst[i + j * 32] = src[i + j * 32]; |
370 | // } | 471 | // } |
371 | } | 472 | } |
372 | dirty_tiles[j] = 0; | 473 | // dirty_tiles[j] = 0; |
373 | } | 474 | } |
374 | } | 475 | } |
375 | 476 | ||
376 | void m3_fill(u16 clr) { | 477 | static u32 dec_nibble[] = { |
377 | int ii; | 478 | 0x00000000, 0x01000000, 0x00010000, 0x01010000, |
378 | u32 *dst= (u32*)MEM_VRAM; | 479 | 0x00000100, 0x01000100, 0x00010100, 0x01010100, |
379 | u32 wd= (clr<<16) | clr; | 480 | 0x00000001, 0x01000001, 0x00010001, 0x01010001, |
380 | for(ii=0; ii<KB(96)/4; ii++) | 481 | 0x00000101, 0x01000101, 0x00010101, 0x01010101, |
381 | *dst++= wd; | 482 | }; |
483 | |||
484 | static u32 dec_nibble_flip_x[] = { | ||
485 | 0x00000000, 0x00000001, 0x00000100, 0x00000101, | ||
486 | 0x00010000, 0x00010001, 0x00010100, 0x00010101, | ||
487 | 0x01000000, 0x01000001, 0x01000100, 0x01000101, | ||
488 | 0x01010000, 0x01010001, 0x01010100, 0x01010101, | ||
489 | }; | ||
490 | |||
491 | IWRAM_CODE | ||
492 | static inline | ||
493 | u64 | ||
494 | decode_1bpp(u8 row, u8 flip_x) { | ||
495 | if (flip_x) { | ||
496 | u32 *lut = dec_nibble_flip_x; | ||
497 | return (u64)lut[(row >> 4) & 0xF] << 32 | (u64)lut[(row >> 0) & 0xF]; | ||
498 | } | ||
499 | u32 *lut = dec_nibble; | ||
500 | return (u64)lut[(row >> 0) & 0xF] << 32 | (u64)lut[(row >> 4) & 0xF]; | ||
501 | } | ||
502 | |||
503 | IWRAM_CODE | ||
504 | static inline | ||
505 | void | ||
506 | draw_2bpp_row(size_t x, size_t y, u8 a, u8 b, u8 flip_x) { | ||
507 | // BOUNDCHECK_SCREEN(x, y); | ||
508 | |||
509 | // size_t tile_x = x / 8; | ||
510 | // size_t start_col = x % 8; | ||
511 | // size_t shift_left = start_col * 8; | ||
512 | // size_t shift_right = (8 - start_col) * 8; | ||
513 | |||
514 | // u64 *dst = &backbuf[(y * 30 + tile_x) * 8 / 2]; | ||
515 | // if (start_col == 0) { | ||
516 | // u64 clr_a = decode_1bpp(a, flip_x); | ||
517 | // u64 clr_b = decode_1bpp(b, flip_x); | ||
518 | // u64 mask_a = (clr_a * 0xF); | ||
519 | // u64 mask_b = (clr_b * 0xF); | ||
520 | // u64 mask = (mask_a | mask_b); | ||
521 | // u64 color = clr_a + (clr_b << 1); | ||
522 | // dst[0] = (dst[0] & ~mask) | color; | ||
523 | // } else { | ||
524 | // u64 clr_a = decode_1bpp(a, flip_x); | ||
525 | // u64 clr_b = decode_1bpp(b, flip_x); | ||
526 | // u64 mask_a = (clr_a * 0xF); | ||
527 | // u64 mask_b = (clr_b * 0xF); | ||
528 | // u64 mask = (mask_a | mask_b); | ||
529 | // u64 color = clr_a + (clr_b << 1); | ||
530 | // dst[0] = (dst[0] & ~(mask << shift_left)) | (color << shift_left); | ||
531 | // if ((x + 7) > (SCREEN_WIDTH)) { | ||
532 | // return; | ||
533 | // } | ||
534 | // dst[1] = (dst[1] & ~(mask >> shift_right)) | (color >> shift_right); | ||
535 | // } | ||
536 | |||
537 | // TODO: different blend modes? | ||
538 | } | ||
539 | |||
540 | IWRAM_CODE | ||
541 | static inline | ||
542 | void | ||
543 | draw_1bpp_row(size_t x, size_t y, u8 a, u8 clr, u8 flip_x) { | ||
544 | // BOUNDCHECK_SCREEN(x, y); | ||
545 | |||
546 | // size_t tile_x = x / 8; | ||
547 | // size_t start_col = x % 8; | ||
548 | // size_t shift_left = start_col * 8; | ||
549 | // size_t shift_right = (8 - start_col) * 8; | ||
550 | |||
551 | // u64 *dst = &backbuf[(y * 30 + tile_x) * 8 / 2]; | ||
552 | // if (start_col == 0) { | ||
553 | // u64 color = decode_1bpp(a, flip_x); | ||
554 | // u64 mask = (color * 0xF); | ||
555 | // color *= clr; | ||
556 | // dst[0] = (dst[0] & ~mask) | color; | ||
557 | // } else { | ||
558 | // u64 color = decode_1bpp(a, flip_x); | ||
559 | // u64 mask = (color * 0xF); | ||
560 | // color *= clr; | ||
561 | // dst[0] = (dst[0] & ~(mask << shift_left)) | (color << shift_left); | ||
562 | // if ((x + 7) > (SCREEN_WIDTH)) { | ||
563 | // return; | ||
564 | // } | ||
565 | // dst[1] = (dst[1] & ~(mask >> shift_right)) | (color >> shift_right); | ||
566 | // } | ||
567 | |||
568 | // TODO: different blend modes? | ||
569 | } | ||
570 | |||
571 | IWRAM_CODE | ||
572 | void | ||
573 | draw_chr(size_t x, size_t y, u8 *sprite, u8 flip_x, u8 flip_y) { | ||
574 | // BOUNDCHECK_SCREEN(x, y); | ||
575 | // if (!flip_y) { | ||
576 | // for(size_t v = 0; v < 8; v++) { | ||
577 | // if ((y + v) >= SCREEN_HEIGHT) break; | ||
578 | // u8 ch1 = sprite[v + 0]; | ||
579 | // u8 ch2 = sprite[v + 8]; | ||
580 | // draw_2bpp_row(x, y + v, ch1, ch2, flip_x); | ||
581 | // } | ||
582 | // } else { | ||
583 | // for(size_t v = 0; v < 8; v++) { | ||
584 | // if ((y + v) >= SCREEN_HEIGHT) break; | ||
585 | // u8 ch1 = sprite[(7 - v) + 0]; | ||
586 | // u8 ch2 = sprite[(7 - v) + 8]; | ||
587 | // draw_2bpp_row(x, y + v, ch1, ch2, flip_x); | ||
588 | // } | ||
589 | // } | ||
590 | } | ||
591 | |||
592 | IWRAM_CODE | ||
593 | void | ||
594 | draw_icn(size_t x, size_t y, u8 *sprite, u8 clr, u8 flip_x, u8 flip_y) { | ||
595 | // BOUNDCHECK_SCREEN(x, y); | ||
596 | // if (!flip_y) { | ||
597 | // for(size_t v = 0; v < 8; v++) { | ||
598 | // if ((y + v) >= SCREEN_HEIGHT) break; | ||
599 | // u8 ch1 = sprite[v]; | ||
600 | // draw_1bpp_row(x, y + v, ch1, clr, flip_x); | ||
601 | // } | ||
602 | // } else { | ||
603 | // for(size_t v = 0; v < 8; v++) { | ||
604 | // if ((y + v) >= SCREEN_HEIGHT) break; | ||
605 | // u8 ch1 = sprite[(7 - v)]; | ||
606 | // draw_1bpp_row(x, y + v, ch1, clr, flip_x); | ||
607 | // } | ||
608 | // } | ||
609 | } | ||
610 | |||
611 | #include "font.h" | ||
612 | |||
613 | // Font rendering function for the text engine. | ||
614 | void | ||
615 | txt_drawc(char c, size_t x, size_t y, u8 clr) { | ||
616 | u8 *tile = font_icn; | ||
617 | draw_icn(x, y, tile + 8 * c, clr, 1, 0); | ||
382 | } | 618 | } |
383 | 619 | ||
384 | void | 620 | void |
@@ -389,31 +625,29 @@ renderer_init(void) { | |||
389 | // Clear VRAM. | 625 | // Clear VRAM. |
390 | dma_fill(MEM_VRAM, 0, KB(96), 3); | 626 | dma_fill(MEM_VRAM, 0, KB(96), 3); |
391 | 627 | ||
392 | m3_fill(COLOR_BLUE); | 628 | // Initialize display mode and bg palette. |
629 | DISP_CTRL = DISP_MODE_0 | DISP_BG_0 | DISP_BG_1 | DISP_OBJ; | ||
393 | 630 | ||
394 | // Initialize backgrounds. | 631 | // Initialize backgrounds. |
395 | // BG_CTRL(0) = BG_CHARBLOCK(0) | BG_SCREENBLOCK(FRONTBUF_SB) | BG_PRIORITY(1); | 632 | BG_CTRL(0) = BG_CHARBLOCK(0) | BG_SCREENBLOCK(FRONTBUF_SB) | BG_PRIORITY(1); |
396 | // BG_CTRL(1) = BG_CHARBLOCK(1) | BG_SCREENBLOCK(FONT_SB) | BG_PRIORITY(0); | 633 | // BG_CTRL(1) = BG_CHARBLOCK(1) | BG_SCREENBLOCK(FONT_SB) | BG_PRIORITY(0); |
397 | 634 | ||
398 | // Use DMA to clear front and back buffers as well as the font memory map. | 635 | // Initialize background memory map for frontbuffer. |
399 | // dma_fill(FRONTBUF_TILEMAP, 0, KB(2), 3); | 636 | for (size_t i = 0; i < 32 * 20; ++i) { |
400 | // dma_fill(BACKBUF, 0, KB(20), 3); | 637 | FRONTBUF_TILEMAP[i] = i; |
401 | // dma_fill(FONT_DATA, 0, KB(8), 3); | 638 | } |
402 | // dma_fill(FONT_TILEMAP, (FONT_OFFSET << 16) | FONT_OFFSET, KB(2), 3); | ||
403 | 639 | ||
404 | // Initialize default palette. | 640 | // Initialize default palette. |
405 | // PAL_BUFFER_BG[0] = COLOR_BLACK; | 641 | PAL_BUFFER_BG[0] = COLOR_BLACK; |
406 | // PAL_BUFFER_BG[1] = COLOR_WHITE; | 642 | PAL_BUFFER_BG[1] = COLOR_WHITE; |
407 | // PAL_BUFFER_BG[2] = COLOR_RED; | 643 | PAL_BUFFER_BG[2] = COLOR_RED; |
408 | // PAL_BUFFER_BG[3] = COLOR_BLUE; | 644 | PAL_BUFFER_BG[3] = COLOR_BLUE; |
409 | // PAL_BUFFER_BG[4] = COLOR_CYAN; | 645 | PAL_BUFFER_BG[4] = COLOR_CYAN; |
410 | // PAL_BUFFER_BG[5] = COLOR_GREY; | 646 | PAL_BUFFER_BG[5] = COLOR_PURPLE; |
411 | 647 | PAL_BUFFER_BG[6] = COLOR_YELLOW; | |
412 | // Initialize background memory map for frontbuffer and font backgorund. | 648 | PAL_BUFFER_BG[7] = COLOR_GREEN; |
413 | // for (size_t i = 0; i < 32 * 20; ++i) { | 649 | PAL_BUFFER_BG[8] = COLOR_GREY; |
414 | // FRONTBUF_TILEMAP[i] = i; | ||
415 | // } | ||
416 | 650 | ||
417 | // Initialize text engine. | 651 | // Initialize text engine. |
418 | // txt_init(FONT_DATA, FONT_TILEMAP, FONT_OFFSET); | 652 | txt_init(txt_drawc); |
419 | } | 653 | } |
diff --git a/src/renderer_m4.c b/src/renderer_m4.c index e564c1e..4cb526b 100644 --- a/src/renderer_m4.c +++ b/src/renderer_m4.c | |||
@@ -1,22 +1,5 @@ | |||
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" | 1 | #include "renderer.h" |
19 | #include "text_m4.h" | 2 | #include "text.h" |
20 | 3 | ||
21 | static u16 *backbuf = (u16 *)(MEM_VRAM ^ 0x0A000); | 4 | static u16 *backbuf = (u16 *)(MEM_VRAM ^ 0x0A000); |
22 | 5 | ||
@@ -148,123 +131,6 @@ draw_line(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { | |||
148 | MAYBE_SWAP(y0, y1); | 131 | MAYBE_SWAP(y0, y1); |
149 | draw_vline(x0, y0, y1, clr); | 132 | draw_vline(x0, y0, y1, clr); |
150 | } else { | 133 | } else { |
151 | // int dx = x0 > x1 ? x0 - x1 : x1 - x0; | ||
152 | // int dy = y0 > y1 ? y0 - y1 : y1 - y0; | ||
153 | // | ||
154 | // NOTE: Simplified bresenham. | ||
155 | // int dx=x1-x0; | ||
156 | // int dy=y1-y0; | ||
157 | // int d=2*dy-dx; | ||
158 | // int e=2*dy; | ||
159 | // int ne=2*(dy-dx); | ||
160 | // int x=x0; | ||
161 | // int y=y0; | ||
162 | // for (x=x0;x<=x1;x++) { | ||
163 | // draw_pixel(x, y, clr); | ||
164 | // if (d<=0) { | ||
165 | // d+=e; | ||
166 | // } else { | ||
167 | // d+=ne; | ||
168 | // y++; // sign? | ||
169 | // } | ||
170 | // } | ||
171 | // NOTE: Simplified fp-line. | ||
172 | // s32 f; | ||
173 | // int x; | ||
174 | // s32 m = ((s32)(y1-y0)<<16)/(x1-x0); | ||
175 | |||
176 | // f=y0<<16; | ||
177 | // for (x=x0;x<=x1;x++,f += m) { | ||
178 | // s32 g = f; | ||
179 | // g += 32767; | ||
180 | // draw_pixel(x, g >> 16, clr); | ||
181 | // } | ||
182 | |||
183 | // u16 *dst = NULL; | ||
184 | // uintptr_t addr = ((uintptr_t)backbuf + y0 * SCREEN_WIDTH + x0); | ||
185 | // u32 mask = x0 & 1 ? ~0xFF : 0xFF; | ||
186 | // u32 color = (clr & 0xFF) | ((clr & 0xFF) << 8); | ||
187 | #if 0 | ||
188 | int dx = x0 > x1 ? x0 - x1 : x1 - x0; | ||
189 | int dy = y0 > y1 ? y0 - y1 : y1 - y0; | ||
190 | |||
191 | u16 *dst = NULL; | ||
192 | uintptr_t addr = ((uintptr_t)backbuf + y0 * SCREEN_WIDTH + x0); | ||
193 | u32 mask = x0 & 1 ? ~0xFF : 0xFF; | ||
194 | u32 color = (clr & 0xFF) | ((clr & 0xFF) << 8); | ||
195 | |||
196 | if (dx >= dy) { | ||
197 | int fp_val = y1 - y0; | ||
198 | int fp_div = x1 - x0; | ||
199 | int fp_inc = (fp_val << 8) / fp_div; | ||
200 | int fp_step = fp_div > 0 ? fp_inc : -fp_inc; | ||
201 | int i_step = fp_div > 0 ? 1 : -1; | ||
202 | int y_step = 0; | ||
203 | for (int i = 0; i <= dx; i++) { | ||
204 | dst = (u16 *)(addr - (mask >> 31)); | ||
205 | *dst = (*dst & ~mask) | (color & mask); | ||
206 | addr += i_step; | ||
207 | y_step += fp_step; | ||
208 | if (y_step >> 8) { | ||
209 | addr += SCREEN_WIDTH * (y_step >> 8); | ||
210 | y_step -= (y_step >> 8) << 8; | ||
211 | } | ||
212 | // TODO: +/- inc? | ||
213 | // addr += SCREEN_WIDTH * (y_step >> 8); | ||
214 | // y_step -= (y_step >> 8) << 8; | ||
215 | mask = ~mask; | ||
216 | } | ||
217 | } else { | ||
218 | // int fp_val = x1 - x0; | ||
219 | // int fp_div = y1 - y0; | ||
220 | // int fp_inc = (fp_val << 8) / fp_div; | ||
221 | // int fp_step = fp_div > 0 ? fp_inc : -fp_inc; | ||
222 | // int x_step = 0; | ||
223 | // u32 masks[] = { ~0xFF, 0xFF, }; | ||
224 | // u32 colors[] = { color & ~0xFF, color & 0xFF, }; | ||
225 | // for (int i = 0; i <= dy; i++) { | ||
226 | // // TODO: +/- inc? | ||
227 | // // METHOD 1: Conditional | ||
228 | // // dst = (u16 *)(addr - (mask >> 31)); | ||
229 | // // *dst = (*dst & ~mask) | (color & mask); | ||
230 | // // addr -= SCREEN_WIDTH; | ||
231 | // // x_step += fp_step; | ||
232 | // // if (x_step >> 8) { | ||
233 | // // addr += (x_step >> 8); | ||
234 | // // x_step -= (x_step >> 8) << 8; | ||
235 | // // mask = ~mask; | ||
236 | // // } | ||
237 | |||
238 | // // METHOD 1-2 | ||
239 | // dst = (u16 *)(addr - (masks[(x_step >> 8)] >> 31)); | ||
240 | // *dst = (*dst & ~masks[(x_step >> 8)]) | colors[(x_step>>8)]; | ||
241 | // addr -= SCREEN_WIDTH; | ||
242 | // x_step += fp_step; | ||
243 | // if (x_step >> 8) { | ||
244 | // addr += (x_step >> 8); | ||
245 | // x_step -= (x_step >> 8) << 8; | ||
246 | // } | ||
247 | |||
248 | // // METHOD 2: branchless lut | ||
249 | // // dst = (u16 *)(addr - (masks[(x_step >> 8)] >> 31)); | ||
250 | // // *dst = (*dst & ~masks[(x_step >> 8)]) | colors[(x_step>>8)]; | ||
251 | // // x_step += fp_step; | ||
252 | // // addr += (x_step >> 8); | ||
253 | // // addr -= SCREEN_WIDTH; | ||
254 | // // x_step -= (x_step >> 8) << 8; | ||
255 | |||
256 | // // METHOD 3: branchless bit hacking | ||
257 | // // dst = (u16 *)(addr - (mask >> 31)); | ||
258 | // // *dst = (*dst & ~mask) | (color & mask); | ||
259 | // // x_step += fp_step; | ||
260 | // // mask ^= ((x_step >> 8) | -(x_step >> 8)); | ||
261 | // // addr += (x_step >> 8); | ||
262 | // // addr -= SCREEN_WIDTH; | ||
263 | // // x_step -= (x_step >> 8) << 8; | ||
264 | // } | ||
265 | } | ||
266 | |||
267 | #elif 1 | ||
268 | // Diagonal line. | 134 | // Diagonal line. |
269 | int dx = x0 > x1 ? x0 - x1 : x1 - x0; | 135 | int dx = x0 > x1 ? x0 - x1 : x1 - x0; |
270 | int dy = y0 > y1 ? y0 - y1 : y1 - y0; | 136 | int dy = y0 > y1 ? y0 - y1 : y1 - y0; |
@@ -302,7 +168,6 @@ draw_line(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { | |||
302 | addr += y_step; | 168 | addr += y_step; |
303 | } | 169 | } |
304 | } | 170 | } |
305 | #endif | ||
306 | } | 171 | } |
307 | screen_updated = true; | 172 | screen_updated = true; |
308 | } | 173 | } |
diff --git a/src/text/text.h b/src/text/text.h index d0d4c17..4f66058 100644 --- a/src/text/text.h +++ b/src/text/text.h | |||
@@ -2,10 +2,8 @@ | |||
2 | #define TEXT_H | 2 | #define TEXT_H |
3 | 3 | ||
4 | #include "posprintf.h" | 4 | #include "posprintf.h" |
5 | #include "renderer.h" | ||
6 | |||
7 | #include "font.h" | ||
8 | 5 | ||
6 | typedef void (*TxtDrawc)(char c, size_t x, size_t y, u8 clr); | ||
9 | typedef struct TextEngine { | 7 | typedef struct TextEngine { |
10 | // Cursor for tiled text mode The X and Y positions correspond to the tile | 8 | // Cursor for tiled text mode The X and Y positions correspond to the tile |
11 | // X and Y starting from the top left of the screen. For a 240x160 screen, | 9 | // X and Y starting from the top left of the screen. For a 240x160 screen, |
@@ -13,14 +11,10 @@ typedef struct TextEngine { | |||
13 | size_t cursor_x; | 11 | size_t cursor_x; |
14 | size_t cursor_y; | 12 | size_t cursor_y; |
15 | 13 | ||
16 | // Memory location of font tile data and tilemap. Likely located on the | 14 | u8 buffer[30 * 20]; |
17 | // VRAM. | 15 | u8 spacing; |
18 | u32 *font_data; | 16 | u8 color; |
19 | u16 *font_tilemap; | 17 | TxtDrawc drawc; |
20 | |||
21 | // The font map for tiled text. Writing the character stored in this | ||
22 | // position on the tilemap will show a character on the screen. | ||
23 | u16 font_map[256]; | ||
24 | } TextEngine; | 18 | } TextEngine; |
25 | 19 | ||
26 | static TextEngine text_engine = {0}; | 20 | static TextEngine text_engine = {0}; |
@@ -28,20 +22,11 @@ static TextEngine text_engine = {0}; | |||
28 | // Initializes the text engine. | 22 | // Initializes the text engine. |
29 | static inline | 23 | static inline |
30 | void | 24 | void |
31 | txt_init(u32 *font_data, u16 *font_tilemap, u16 font_offset) { | 25 | txt_init(TxtDrawc drawc) { |
32 | // Load font data into VRAM. | ||
33 | unpack_tiles(&font, font_data, 256); | ||
34 | |||
35 | // Initialize the font map translation table. That way we can write | ||
36 | // a character on the text background layer with: | ||
37 | // FONT_TILEMAP[tile_x + 32 * tile_y] = font_map['A']; | ||
38 | for (size_t i = 0; i < 256; ++i) { | ||
39 | text_engine.font_map[i] = font_offset + i; | ||
40 | } | ||
41 | |||
42 | // Initialize remaining variables. | 26 | // Initialize remaining variables. |
43 | text_engine.font_data = font_data; | 27 | text_engine.spacing = 8; |
44 | text_engine.font_tilemap = font_tilemap; | 28 | text_engine.color = 1; |
29 | text_engine.drawc = drawc; | ||
45 | } | 30 | } |
46 | 31 | ||
47 | // Writes a message to the tile text background. | 32 | // Writes a message to the tile text background. |
@@ -59,7 +44,7 @@ txt_puts(char *msg) { | |||
59 | } else { | 44 | } else { |
60 | int x = text_engine.cursor_x; | 45 | int x = text_engine.cursor_x; |
61 | int y = text_engine.cursor_y; | 46 | int y = text_engine.cursor_y; |
62 | text_engine.font_tilemap[x + 32 * y] = text_engine.font_map[(u16)c]; | 47 | text_engine.buffer[x + 30 * y] = c; |
63 | text_engine.cursor_x += 1; | 48 | text_engine.cursor_x += 1; |
64 | if (text_engine.cursor_x >= 30) { | 49 | if (text_engine.cursor_x >= 30) { |
65 | text_engine.cursor_x = 0; | 50 | text_engine.cursor_x = 0; |
@@ -79,7 +64,7 @@ txt_clear_line(void) { | |||
79 | for (size_t i = 0; i < 30; ++i) { | 64 | for (size_t i = 0; i < 30; ++i) { |
80 | int x = text_engine.cursor_x; | 65 | int x = text_engine.cursor_x; |
81 | int y = text_engine.cursor_y; | 66 | int y = text_engine.cursor_y; |
82 | text_engine.font_tilemap[x + 32 * y] = text_engine.font_map[0]; | 67 | text_engine.buffer[x + 30 * y] = 0; |
83 | } | 68 | } |
84 | text_engine.cursor_x = 0; | 69 | text_engine.cursor_x = 0; |
85 | } | 70 | } |
@@ -87,7 +72,7 @@ txt_clear_line(void) { | |||
87 | // Clears the screen on the tile text mode. | 72 | // Clears the screen on the tile text mode. |
88 | static inline | 73 | static inline |
89 | void | 74 | void |
90 | txt_clear_screen(void) { | 75 | txt_clear(void) { |
91 | for (size_t j = 0; j < 20; ++j) { | 76 | for (size_t j = 0; j < 20; ++j) { |
92 | text_engine.cursor_y = j; | 77 | text_engine.cursor_y = j; |
93 | txt_clear_line(); | 78 | txt_clear_line(); |
@@ -104,30 +89,33 @@ txt_position(size_t tile_x, size_t tile_y) { | |||
104 | text_engine.cursor_y = tile_y; | 89 | text_engine.cursor_y = tile_y; |
105 | } | 90 | } |
106 | 91 | ||
92 | // Renders the contents of the scrollback buffer to the screen. | ||
93 | void | ||
94 | txt_render(void) { | ||
95 | for (size_t y = 0; y < 20; y++) { | ||
96 | for (size_t x = 0; x < 30; x++) { | ||
97 | text_engine.drawc( | ||
98 | text_engine.buffer[x + y * 30], | ||
99 | x * text_engine.spacing, | ||
100 | y * text_engine.spacing, | ||
101 | text_engine.color); | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
107 | // Draws a message where the first character's top-left corner begins at the | 106 | // Draws a message where the first character's top-left corner begins at the |
108 | // given x and y position. The character spacing can be adjusted, but beware of | 107 | // given x and y position. The character spacing can be adjusted, but beware of |
109 | // color merging issues. | 108 | // color merging issues. |
110 | static inline | 109 | static inline |
111 | void | 110 | void |
112 | txt_draws(char *msg, size_t x, size_t y, size_t spacing, u8 clr) { | 111 | txt_draws(char *msg, size_t x, size_t y, u8 clr) { |
112 | size_t i = 0; | ||
113 | while (*msg) { | 113 | while (*msg) { |
114 | char c = *msg++; | 114 | char c = *msg++; |
115 | Tile *tile = FONT_DATA; | 115 | text_engine.drawc(c, x + i++ * text_engine.spacing, y, clr); |
116 | tile += c; | ||
117 | draw_tile(x, y, tile, clr); | ||
118 | x += spacing; | ||
119 | } | 116 | } |
120 | } | 117 | } |
121 | 118 | ||
122 | static inline | ||
123 | void | ||
124 | txt_drawc(char c, size_t x, size_t y, size_t spacing, u8 clr) { | ||
125 | Tile *tile = FONT_DATA; | ||
126 | tile += c; | ||
127 | draw_tile(x, y, tile, clr); | ||
128 | x += spacing; | ||
129 | } | ||
130 | |||
131 | // Print text to the screen with formatting. | 119 | // Print text to the screen with formatting. |
132 | #define txt_printf(msg, ...) \ | 120 | #define txt_printf(msg, ...) \ |
133 | { \ | 121 | { \ |
@@ -138,31 +126,11 @@ txt_drawc(char c, size_t x, size_t y, size_t spacing, u8 clr) { | |||
138 | 126 | ||
139 | // Draws text to the screen with formatting starting on the x and y position and | 127 | // Draws text to the screen with formatting starting on the x and y position and |
140 | // with custom character spacing. | 128 | // with custom character spacing. |
141 | #define txt_drawf(msg, x, y, s, c, ...) \ | 129 | #define txt_drawf(msg, x, y, c, ...) \ |
142 | { \ | ||
143 | char buf[256] = {0}; \ | ||
144 | posprintf(buf, msg, ##__VA_ARGS__); \ | ||
145 | txt_draws(buf, x, y, s, c); \ | ||
146 | } | ||
147 | |||
148 | // Small font is located after the initial ASCII characters, and only supports | ||
149 | // lowercase characters. | ||
150 | // NOTE: Slow, we should do this with a LUT. | ||
151 | #define txt_drawf_small(msg, x, y, s, c, ...) \ | ||
152 | { \ | 130 | { \ |
153 | char buf[256] = {0}; \ | 131 | char buf[256] = {0}; \ |
154 | posprintf(buf, msg, ##__VA_ARGS__); \ | 132 | posprintf(buf, msg, ##__VA_ARGS__); \ |
155 | for (size_t i = 0; i < 256; i++) { \ | 133 | txt_draws(buf, x, y, c); \ |
156 | if (buf[i] == 0) { \ | ||
157 | break; \ | ||
158 | } \ | ||
159 | if (buf[i] < 'a') { \ | ||
160 | buf[i] += 16 * 6; \ | ||
161 | } else { \ | ||
162 | buf[i] += 16 * 4; \ | ||
163 | }\ | ||
164 | } \ | ||
165 | txt_draws(buf, x, y, s, c); \ | ||
166 | } | 134 | } |
167 | 135 | ||
168 | #endif // TEXT_H | 136 | #endif // TEXT_H |