summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2023-04-15 18:26:03 +0200
committerBad Diode <bd@badd10de.dev>2023-04-15 18:29:26 +0200
commitb22d1bb0081a93f43856077917b8b029a8e97ec9 (patch)
tree26f630892dfe534b324d4fca9c585223f85b8953
parent984f3c399b55bddcc177a2eb2a5e002fdc09a69d (diff)
downloadgba-link-cable-tester-b22d1bb0081a93f43856077917b8b029a8e97ec9.tar.gz
gba-link-cable-tester-b22d1bb0081a93f43856077917b8b029a8e97ec9.zip
Prepare for work on M0 renderers
-rw-r--r--src/main.c50
-rw-r--r--src/renderer.c726
-rw-r--r--src/renderer_m4.c137
-rw-r--r--src/text/text.h94
4 files changed, 522 insertions, 485 deletions
diff --git a/src/main.c b/src/main.c
index 78ee1b2..23f4fe3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -11,7 +11,7 @@ WITH REGARD TO THIS SOFTWARE.
11 11
12#include "gba/gba.h" 12#include "gba/gba.h"
13 13
14#include "renderer_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
102void 102void
103test_lines(void) { 103test_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
112int main(void) { 112int 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.
48static u32 dirty_tiles[21] = {0}; 38static 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
55IWRAM_CODE
56void 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
59IWRAM_CODE 68IWRAM_CODE
60void 69void
61draw_pixel(size_t x, size_t y, u8 clr) { 70draw_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
79IWRAM_CODE 88IWRAM_CODE
89static inline
80void 90void
81draw_line(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { 91draw_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. 95IWRAM_CODE
96 u32 *backbuffer = &BACKBUF[start_row0 + (tile_x0 + tile_y0 * 32) * 8]; 96UNROLL_LOOPS
97static inline
98void
99draw_vline(size_t x0, size_t y0, size_t y1, u8 clr) {
100 // TODO
101}
97 102
103IWRAM_CODE
104void
105draw_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
177IWRAM_CODE 246IWRAM_CODE
@@ -179,47 +248,58 @@ void
179draw_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { 248draw_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
225IWRAM_CODE 305IWRAM_CODE
@@ -227,11 +307,30 @@ void
227draw_filled_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) { 307draw_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
279IWRAM_CODE 378// IWRAM_CODE
280void 379// void
281draw_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
352void 451// void
353clear_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
357IWRAM_CODE 456IWRAM_CODE
358void 457void
359flip_buffer(void) { 458flip_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
376void m3_fill(u16 clr) { 477static 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
484static 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
491IWRAM_CODE
492static inline
493u64
494decode_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
503IWRAM_CODE
504static inline
505void
506draw_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
540IWRAM_CODE
541static inline
542void
543draw_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
571IWRAM_CODE
572void
573draw_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
592IWRAM_CODE
593void
594draw_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.
614void
615txt_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
384void 620void
@@ -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
21static u16 *backbuf = (u16 *)(MEM_VRAM ^ 0x0A000); 4static 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
6typedef void (*TxtDrawc)(char c, size_t x, size_t y, u8 clr);
9typedef struct TextEngine { 7typedef 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
26static TextEngine text_engine = {0}; 20static TextEngine text_engine = {0};
@@ -28,20 +22,11 @@ static TextEngine text_engine = {0};
28// Initializes the text engine. 22// Initializes the text engine.
29static inline 23static inline
30void 24void
31txt_init(u32 *font_data, u16 *font_tilemap, u16 font_offset) { 25txt_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.
88static inline 73static inline
89void 74void
90txt_clear_screen(void) { 75txt_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.
93void
94txt_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.
110static inline 109static inline
111void 110void
112txt_draws(char *msg, size_t x, size_t y, size_t spacing, u8 clr) { 111txt_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
122static inline
123void
124txt_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