aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2023-04-22 21:12:14 +0200
committerBad Diode <bd@badd10de.dev>2023-04-22 21:12:14 +0200
commitdeb9c48fbd3dc5854de4ae3a04dc999029c10ae0 (patch)
tree53d34672d676c5f9b9b56b2b185c511470c068a0
parentd4fe4d95f105d8b9b47d26264c4876cbf4095a5d (diff)
downloadstepper-deb9c48fbd3dc5854de4ae3a04dc999029c10ae0.tar.gz
stepper-deb9c48fbd3dc5854de4ae3a04dc999029c10ae0.zip
Add new renderer and prepare for render overhaul
-rw-r--r--src/main.c34
-rw-r--r--src/profiling.c106
-rw-r--r--src/renderer.h45
-rw-r--r--src/renderer_m0.c811
-rw-r--r--src/sequencer.c94
-rw-r--r--src/text/font.h131
-rw-r--r--src/text/text.h115
7 files changed, 1122 insertions, 214 deletions
diff --git a/src/main.c b/src/main.c
index e694057..f89e743 100644
--- a/src/main.c
+++ b/src/main.c
@@ -12,17 +12,33 @@ WITH REGARD TO THIS SOFTWARE.
12#include "gba/gba.h" 12#include "gba/gba.h"
13 13
14#include "filesystem.c" 14#include "filesystem.c"
15#include "renderer.c" 15#include "renderer_m0.c"
16#include "sequencer.c" 16#include "sequencer.c"
17 17
18#define PROF_ENABLE 0 18#define PROF_ENABLE 1
19#include "profiling.c" 19#include "profiling.c"
20 20
21//
22// Config parameters.
23//
24 21
25int main(void) { 22void
23render(void) {
24 PROF(screen_fill(0), clear_cycles);
25 PROF(draw_triggers(), draw_trigs_cycles);
26 PROF(draw_channels(), draw_btn_cycles);
27 PROF(draw_pattern_buttons(), draw_btn_cycles);
28 PROF(draw_bank_buttons(), draw_btn_cycles);
29 PROF(draw_bpm(), draw_btn_cycles);
30 PROF(draw_play(), draw_btn_cycles);
31 PROF(draw_stop(), draw_btn_cycles);
32 PROF(draw_piano(), draw_piano_cycles);
33 PROF(draw_parameters(), draw_param_cycles);
34 PROF(draw_trig_cursor(trig_selection_loc, COL_CURSOR), draw_cursor_cycles);
35 PROF(draw_channel_cursor(channel_selection_loc, COL_GREY), draw_cursor_cycles);
36 PROF(draw_pattern_cursor(pattern_selection_loc, COL_GREY), draw_cursor_cycles);
37 PROF(draw_current_step(COL_RED), draw_cursor_cycles);
38}
39
40int
41main(void) {
26 // Adjust system wait times. 42 // Adjust system wait times.
27 SYSTEM_WAIT = SYSTEM_WAIT_CARTRIDGE; 43 SYSTEM_WAIT = SYSTEM_WAIT_CARTRIDGE;
28 44
@@ -38,15 +54,17 @@ int main(void) {
38 54
39 // Initialize sequencer. 55 // Initialize sequencer.
40 sequencer_init(); 56 sequencer_init();
57 txt_spacing(6);
41 58
42 // Main loop. 59 // Main loop.
43 while (true) { 60 while (true) {
44 poll_keys(); 61 poll_keys();
45 bios_vblank_wait(); 62 bios_vblank_wait();
46 FRAME_START();
47 PROF(handle_sequencer_input(), input_cycles);
48 PROF_SHOW(); 63 PROF_SHOW();
64 FRAME_START();
49 PROF(flip_buffer(), flip_cycles); 65 PROF(flip_buffer(), flip_cycles);
66 PROF(handle_sequencer_input(), input_cycles);
67 PROF(render(), render_cycles);
50 FRAME_END(); 68 FRAME_END();
51 } 69 }
52 70
diff --git a/src/profiling.c b/src/profiling.c
index de969d2..7a4f4ad 100644
--- a/src/profiling.c
+++ b/src/profiling.c
@@ -52,22 +52,23 @@ static bool profile_bg_show = true;
52 profile_bg_show ^= 1;\ 52 profile_bg_show ^= 1;\
53 }\ 53 }\
54 if (profile_show) {\ 54 if (profile_show) {\
55 txt_color(1);\
55 txt_position((PROF_SHOW_X), (PROF_SHOW_Y));\ 56 txt_position((PROF_SHOW_X), (PROF_SHOW_Y));\
56 draw_filled_rect((PROF_SHOW_X), (PROF_SHOW_X), 8 * 18, 8 * 16, 0);\ 57 if (profile_bg_show) {\
58 draw_filled_rect((PROF_SHOW_X), (PROF_SHOW_X), 8 * 18, 8 * 14, 0);\
59 }\
57 txt_printf("VIDEO\n");\ 60 txt_printf("VIDEO\n");\
58 txt_printf(">CLEAR %.8lu\n", avg_clear_cycles);\ 61 txt_printf(">CLEAR %.8lu\n", avg_clear_cycles);\
59 txt_printf(">LINES %.8lu\n", avg_line_cycles);\
60 txt_printf(">RECT %.8lu\n", avg_rect_cycles);\
61 txt_printf(">FRECT %.8lu\n", avg_fill_rect_cycles);\
62 txt_printf(">1BPP %.8lu\n", avg_icn_cycles);\
63 txt_printf(">2BPP %.8lu\n", avg_chr_cycles);\
64 txt_printf(">FLIP %.8lu\n", avg_flip_cycles);\ 62 txt_printf(">FLIP %.8lu\n", avg_flip_cycles);\
65 txt_printf("TEXT\n");\ 63 txt_printf("SEQUENCER RENDER\n");\
66 txt_printf(">DRAWF %.8lu\n", avg_txt_drawf_cycles);\ 64 txt_printf(">TRIGS %.8lu\n", avg_draw_trigs_cycles);\
67 txt_printf(">PRINTF %.8lu\n", avg_txt_printf_cycles);\ 65 txt_printf(">BTNS %.8lu\n", avg_draw_btns_cycles);\
68 txt_printf(">RENDER %.8lu\n", avg_txt_render_cycles);\ 66 txt_printf(">PARAM %.8lu\n", avg_draw_param_cycles);\
69 txt_printf(">CLEAR %.8lu\n", avg_txt_clear_cycles);\ 67 txt_printf(">PIANO %.8lu\n", avg_draw_piano_cycles);\
68 txt_printf(">CURSOR %.8lu\n", avg_draw_cursor_cycles);\
69 txt_printf(">RENDER %.8lu\n", avg_render_cycles);\
70 txt_printf("TOTAL %.8lu\n", avg_frame_cycles);\ 70 txt_printf("TOTAL %.8lu\n", avg_frame_cycles);\
71 txt_render();\
71 }\ 72 }\
72 if (profile_bg_show) {\ 73 if (profile_bg_show) {\
73 u32 frame_time =\ 74 u32 frame_time =\
@@ -80,40 +81,35 @@ static bool profile_bg_show = true;
80 FP_NUM(280896 * 60, 2),\ 81 FP_NUM(280896 * 60, 2),\
81 FP_NUM(avg_frame_cycles + 1, 2),\ 82 FP_NUM(avg_frame_cycles + 1, 2),\
82 2);\ 83 2);\
83 txt_printf("TIME %.8lu\n", frame_time >> 2);\ 84 draw_filled_rect(8 * 18, 0, 239, 16, 0);\
84 txt_printf("FPS %.8lu\n", (fps >> 2) + 1);\ 85 txt_drawf("TIME: %.6lu", 8 * 18, 0, 1, frame_time >> 2);\
86 txt_drawf("MAX FPS:%.4lu", 8 * 18, 8, 1, (fps >> 2) + 1);\
85 }\ 87 }\
86 } while (0) 88 } while (0)
87 89
88static u32 prof_frame_counter = 0; 90static u32 prof_frame_counter = 0;
89 91
90static u32 frame_cycles = 0; 92static u32 frame_cycles = 0;
91static u32 flip_cycles = 0; 93static u32 flip_cycles = 0;
92static u32 clear_cycles = 0; 94static u32 clear_cycles = 0;
93static u32 line_cycles = 0; 95static u32 input_cycles = 0;
94static u32 rect_cycles = 0; 96static u32 draw_trigs_cycles = 0;
95static u32 fill_rect_cycles = 0; 97static u32 draw_btn_cycles = 0;
96static u32 chr_cycles = 0; 98static u32 draw_piano_cycles = 0;
97static u32 icn_cycles = 0; 99static u32 draw_param_cycles = 0;
98static u32 txt_drawf_cycles = 0; 100static u32 draw_cursor_cycles = 0;
99static u32 txt_printf_cycles = 0; 101static u32 render_cycles = 0;
100static u32 txt_render_cycles = 0;
101static u32 txt_clear_cycles = 0;
102static u32 input_cycles = 0;
103 102
104static u32 avg_frame_cycles = 0; 103static u32 avg_frame_cycles = 0;
105static u32 avg_flip_cycles = 0; 104static u32 avg_flip_cycles = 0;
106static u32 avg_clear_cycles = 0; 105static u32 avg_clear_cycles = 0;
107static u32 avg_line_cycles = 0; 106static u32 avg_input_cycles = 0;
108static u32 avg_rect_cycles = 0; 107static u32 avg_draw_trigs_cycles = 0;
109static u32 avg_fill_rect_cycles = 0; 108static u32 avg_draw_btns_cycles = 0;
110static u32 avg_chr_cycles = 0; 109static u32 avg_draw_piano_cycles = 0;
111static u32 avg_icn_cycles = 0; 110static u32 avg_draw_param_cycles = 0;
112static u32 avg_txt_drawf_cycles = 0; 111static u32 avg_draw_cursor_cycles = 0;
113static u32 avg_txt_printf_cycles = 0; 112static u32 avg_render_cycles = 0;
114static u32 avg_txt_render_cycles = 0;
115static u32 avg_txt_clear_cycles = 0;
116static u32 avg_input_cycles = 0;
117 113
118#if PROF_ENABLE == 1 114#if PROF_ENABLE == 1
119#define FRAME_START()\ 115#define FRAME_START()\
@@ -122,29 +118,23 @@ static u32 avg_input_cycles = 0;
122 avg_frame_cycles = frame_cycles / prof_frame_counter;\ 118 avg_frame_cycles = frame_cycles / prof_frame_counter;\
123 avg_flip_cycles = flip_cycles / prof_frame_counter;\ 119 avg_flip_cycles = flip_cycles / prof_frame_counter;\
124 avg_clear_cycles = clear_cycles / prof_frame_counter;\ 120 avg_clear_cycles = clear_cycles / prof_frame_counter;\
125 avg_line_cycles = line_cycles / prof_frame_counter;\ 121 avg_draw_trigs_cycles = draw_trigs_cycles / prof_frame_counter;\
126 avg_rect_cycles = rect_cycles / prof_frame_counter;\ 122 avg_draw_btns_cycles = draw_btn_cycles / prof_frame_counter;\
127 avg_fill_rect_cycles = fill_rect_cycles / prof_frame_counter;\ 123 avg_draw_piano_cycles = draw_piano_cycles / prof_frame_counter;\
128 avg_chr_cycles = chr_cycles / prof_frame_counter;\ 124 avg_draw_param_cycles = draw_param_cycles / prof_frame_counter;\
129 avg_icn_cycles = icn_cycles / prof_frame_counter;\ 125 avg_draw_cursor_cycles = draw_cursor_cycles / prof_frame_counter;\
130 avg_txt_drawf_cycles = txt_drawf_cycles / prof_frame_counter;\ 126 avg_input_cycles = input_cycles / prof_frame_counter;\
131 avg_txt_printf_cycles = txt_printf_cycles / prof_frame_counter;\ 127 avg_render_cycles = render_cycles / prof_frame_counter;\
132 avg_txt_render_cycles = txt_render_cycles / prof_frame_counter;\
133 avg_txt_clear_cycles = txt_clear_cycles / prof_frame_counter;\
134 avg_input_cycles = input_cycles / prof_frame_counter;\
135 frame_cycles = 0;\ 128 frame_cycles = 0;\
136 flip_cycles = 0;\ 129 flip_cycles = 0;\
137 clear_cycles = 0;\ 130 clear_cycles = 0;\
138 line_cycles = 0;\
139 rect_cycles = 0;\
140 fill_rect_cycles = 0;\
141 chr_cycles = 0;\
142 icn_cycles = 0;\
143 txt_drawf_cycles = 0;\
144 txt_printf_cycles = 0;\
145 txt_render_cycles = 0;\
146 txt_clear_cycles = 0;\
147 input_cycles = 0;\ 131 input_cycles = 0;\
132 render_cycles = 0;\
133 draw_trigs_cycles = 0;\
134 draw_param_cycles = 0;\
135 draw_cursor_cycles = 0;\
136 draw_btn_cycles = 0;\
137 draw_piano_cycles = 0;\
148 prof_frame_counter = 0;\ 138 prof_frame_counter = 0;\
149 }\ 139 }\
150 profile_start();\ 140 profile_start();\
diff --git a/src/renderer.h b/src/renderer.h
index 4620c27..e6637ef 100644
--- a/src/renderer.h
+++ b/src/renderer.h
@@ -3,31 +3,6 @@
3 3
4#include "gba/gba.h" 4#include "gba/gba.h"
5 5
6// The frontbuffer is located at the beginning of the VRAM, and requires 20KB of
7// video memory for 32 * 20 tiles at 4bpp.
8#define FRONTBUF ((u32*)(MEM_VRAM))
9
10// Adjust both of these if the location of the map changes. Each screnblock
11// requires less than 2KB.
12#define FRONTBUF_TILEMAP ((u16*)(MEM_VRAM + KB(20)))
13#define FRONTBUF_SB 10
14
15// The backbuffer is located at the end of the VRAM. This can allow us to use
16// more backgrounds but eats into the available memory for sprites. This should
17// be fine for non sprite intensive applications. If more sprite memory is
18// needed, the backbuffer can be located at the end of the background memory
19// instead (64KB - 20KB).
20#define BACKBUF ((u32*)(MEM_VRAM + KB(96) - KB(20)))
21
22// The font data is located at the end of the frontbuffer memory, after the tile
23// map and requires 8KB for 256 8x8 characters at 4bpp. This, along with the
24// tilemap information allow us to store the frontbuffer and font for a text
25// background in the first 2 charblocks (32KB).
26#define FONT_DATA ((u32*)(MEM_VRAM + KB(22)))
27#define FONT_TILEMAP ((u16*)(MEM_VRAM + KB(30)))
28#define FONT_SB 15
29#define FONT_OFFSET 192
30
31// Draws a pixel to the given (x, y) position on the framebuffer. All drawing 6// Draws a pixel to the given (x, y) position on the framebuffer. All drawing
32// functions use paletted colors (clr: 0-15). 7// functions use paletted colors (clr: 0-15).
33void draw_pixel(size_t x, size_t y, u8 clr); 8void draw_pixel(size_t x, size_t y, u8 clr);
@@ -41,17 +16,19 @@ void draw_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr);
41// Draw a filled rectangle between (x0, y0) and (x1, y1) (x0 <= x1 and y0 <= y1). 16// Draw a filled rectangle between (x0, y0) and (x1, y1) (x0 <= x1 and y0 <= y1).
42void draw_filled_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr); 17void draw_filled_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr);
43 18
44// Draw a 8x8 tile starting at the (x, y) position. If the merge parameter is 19// Fills the framebuffer with the given color.
45// set, colors will be added together instead of replaced. This could lead to 20void screen_fill(u8 clr);
46// some merging issues if we are not careful with the chosen colors. The tile 21
47// color will be multiplied by the given clr parameter, which is useful to 22// Draws a chr sprite (16 * u8). The first 8 bytes correspond to ch0 and the
48// change the color of flat tiles. 23// last 8 to ch1. If clr is 0 the regular 4bit color will be used, from clr 1-14
49void draw_tile(size_t x, size_t y, Tile *tile, u8 clr, bool merge); 24// the given color will overwrite the existing one. Color 15 will "clear" the
25// sprite instead.
26void draw_chr(size_t x, size_t y, u8 *sprite, u8 clr, u8 flip_x, u8 flip_y);
50 27
51// Fills the framebuffer with color 0. 28// Draws a 1bpp icn sprite in the given color.
52void clear_screen(void); 29void draw_icn(size_t x, size_t y, u8 *sprite, u8 clr, u8 flip_x, u8 flip_y);
53 30
54// Copies the content of dirty tiles from the backbuffer into the frontbuffer. 31// Copies data and performs page flipping if needed.
55// To be called exactly once at the beginning of the VBlank. 32// To be called exactly once at the beginning of the VBlank.
56void flip_buffer(void); 33void flip_buffer(void);
57 34
diff --git a/src/renderer_m0.c b/src/renderer_m0.c
new file mode 100644
index 0000000..8bd4263
--- /dev/null
+++ b/src/renderer_m0.c
@@ -0,0 +1,811 @@
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
8#include "renderer.h"
9#include "text.h"
10
11//
12// Parameters.
13//
14
15#define SUBPIXEL_LINES 1
16#define DEC_BIG_LUT 1
17#define FLIP_TYPE 3
18
19// Front/back buffers for double buffering.
20#define BUF_0 ((u32*)(MEM_VRAM))
21#define BUF_1 ((u32*)(MEM_VRAM + KB(20)))
22
23// Pointer to the backbuffer.
24static u32 *backbuf = BUF_1;
25
26// Tracking which tiles are "dirty" and need refreshing.
27static u32 dirty_tiles[21] = {0};
28
29// Position of the tilemap.
30#define TILE_MAP ((u32*)(MEM_VRAM + KB(40)))
31
32// Charblock and screenblock for both render buffers.
33#define CB_0 0
34#define CB_1 1
35#define SB_0 20
36#define SB_1 22
37
38// Boundchecks can be disable at compile time but this will not always improve
39// the performance and can in fact make it worse. It is possible that this is
40// due to some aliasing optimizations but not sure at this moment.
41#ifdef DISABLE_BOUNDCHECK_SCREEN
42#define BOUNDCHECK_SCREEN(X,Y)
43#else
44#define BOUNDCHECK_SCREEN(X,Y) if ((X) >= SCREEN_WIDTH || (Y) >= SCREEN_HEIGHT) return;
45#endif
46
47// Swap A and B values without a tmp variable.
48#define SWAP(A, B) (((A) ^= (B)), ((B) ^= (A)), ((A) ^= (B)))
49
50// Swap A and B values to make sure A <= B.
51#define MAYBE_SWAP(A,B) if ((A) > (B)) { SWAP(A,B); }
52
53//
54// Basic primitives.
55//
56
57static inline
58void
59redraw(void) {
60 for (size_t i = 0; i < 21; i++) {
61 dirty_tiles[i] = 0xFFFFFFFF;
62 }
63}
64
65IWRAM_CODE
66void screen_fill(u8 clr) {
67 // We have to make sure we leave the last tile blank to use as alpha channel
68 // when moving the BG during double buffering.
69 dma_fill(backbuf, 0x11111111 * clr, KB(20) - 32, 3);
70 redraw();
71}
72
73IWRAM_CODE
74void
75draw_pixel(size_t x, size_t y, u8 clr) {
76 BOUNDCHECK_SCREEN(x, y);
77
78 // Find row position for the given x/y coordinates.
79 size_t tile_x = x / 8;
80 size_t tile_y = y / 8;
81 size_t start_col = x % 8;
82 size_t start_row = y % 8;
83 u32 *dst = &backbuf[start_row + (tile_x + tile_y * 32) * 8];
84
85 // Update backbuffer.
86 size_t shift = start_col * sizeof(u32);
87 u32 mask = 0xF << shift;
88 u32 row = clr << shift;
89 *dst = (*dst & ~mask) | row;
90 dirty_tiles[tile_y] |= 1 << tile_x;
91}
92
93IWRAM_CODE
94static inline
95void
96draw_hline(size_t x0, size_t x1, size_t y0, u8 clr) {
97 BOUNDCHECK_SCREEN(x0, y0);
98 BOUNDCHECK_SCREEN(x1, y0);
99 // Find row positions for the given x/y coordinates.
100 size_t tile_x0 = x0 / 8;
101 size_t tile_x1 = x1 / 8;
102 size_t tile_y = y0 / 8;
103 size_t start_col = x0 % 8;
104 size_t end_col = x1 % 8;
105 size_t start_row = y0 % 8;
106 u32 dirty = (1 << tile_x0) | (1 << tile_x1);
107
108 // Horizontal line. There are 3 cases:
109 // 1. Lines fit on a single tile.
110 // 2. Lines go through 2 tiles, both require partial row updates.
111 // 3. Lines go through 3 or more tiles, first and last tiles use
112 // partial row updates, rows in the middle can write the entire
113 // row.
114 size_t dtx = tile_x1 - tile_x0;
115 u32 *dst = &backbuf[start_row + (tile_x0 + tile_y * 32) * 8];
116 if (dtx < 1) {
117 size_t shift_left = start_col * 4;
118 size_t shift_right = (7 - end_col) * 4;
119 u32 mask = (0xFFFFFFFF >> shift_right) & (0xFFFFFFFF << shift_left);
120 u32 row = (0x11111111 * clr) & mask;
121 *dst = (*dst & ~mask) | row;
122 } else {
123 size_t shift_left = start_col * 4;
124 size_t shift_right = (7 - end_col) * 4;
125 u32 mask = 0xFFFFFFFF;
126 u32 row = 0x11111111 * clr;
127 *dst = (*dst & ~(mask << shift_left)) | (row << shift_left);
128 dst += 8;
129 for (size_t i = 1; i < dtx; i++) {
130 dirty |= (1 << (tile_x0 + i));
131 *dst = row;
132 dst += 8;
133 }
134 *dst = (*dst & ~(mask >> shift_right)) | (row >> shift_right);
135 }
136 dirty_tiles[tile_y] |= dirty;
137}
138
139IWRAM_CODE
140UNROLL_LOOPS
141static inline
142void
143draw_vline(size_t x0, size_t y0, size_t y1, u8 clr) {
144 BOUNDCHECK_SCREEN(x0, y0);
145 BOUNDCHECK_SCREEN(x0, y1);
146 size_t tile_x = x0 / 8;
147 size_t tile_y = y0 / 8;
148 size_t tile_y0 = y0 / 8;
149 size_t tile_y1 = y1 / 8;
150 size_t start_col = x0 % 8;
151 size_t start_row0 = y0 % 8;
152 size_t start_row1 = y1 % 8;
153
154 size_t shift_left = start_col * 4;
155 u32 dirty = (1 << tile_x);
156
157 u32 *dst = &backbuf[start_row0 + (tile_x + tile_y * 32) * 8];
158 u32 mask = 0x0000000F << shift_left;
159 u32 row = (0x11111111 * clr) & mask;
160 u32 dty = tile_y1 - tile_y0;
161 if (dty < 1) {
162 for (size_t i = 0; i <= (y1 - y0); i++, dst++) {
163 dst[0] = (dst[0] & ~mask) | row;
164 }
165 } else {
166 for (size_t i = 0; i < (8 - start_row0); i++, dst++) {
167 dst[0] = (dst[0] & ~mask) | row;
168 }
169 dst += 8 * 31;
170 for (size_t j = 1; j < dty; j++) {
171 dirty_tiles[tile_y0 + j] |= dirty;
172 for (size_t i = 0; i < 8; i++, dst++) {
173 dst[0] = (dst[0] & ~mask) | row;
174 }
175 dst += 8 * 31;
176 }
177 for (size_t i = 0; i <= start_row1; i++, dst++) {
178 dst[0] = (dst[0] & ~mask) | row;
179 }
180 }
181 dirty_tiles[tile_y0] |= dirty;
182 dirty_tiles[tile_y1] |= dirty;
183}
184
185IWRAM_CODE
186void
187draw_line(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) {
188 BOUNDCHECK_SCREEN(x0, y0);
189 BOUNDCHECK_SCREEN(x1, y1);
190 if (y0 == y1) {
191 MAYBE_SWAP(x0, x1);
192 draw_hline(x0, x1, y0, clr);
193 } else if (x0 == x1) {
194 MAYBE_SWAP(y0, y1);
195 draw_vline(x0, y0, y1, clr);
196 } else {
197 // Fixed Precision constants.
198 const int fp_bit = 6;
199 const int fp_one = FP_NUM(1, fp_bit);
200 const int fp_half = fp_one >> 1;
201
202 int dx = x0 > x1 ? x0 - x1 : x1 - x0;
203 int dy = y0 > y1 ? y0 - y1 : y1 - y0;
204
205 if ((dx >= dy && x0 > x1) || (dx < dy && y0 > y1)) {
206 SWAP(x0, x1);
207 SWAP(y0, y1);
208 }
209
210#if SUBPIXEL_LINES == 1
211 int dxf = (dx << fp_bit);
212 int dyf = (dy << fp_bit);
213 int frac_x = x0 > x1 ? FP_NUM(x0 - x1, fp_bit) : FP_NUM(x1 - x0, fp_bit);
214 int frac_y = y0 > y1 ? FP_NUM(y0 - y1, fp_bit) : FP_NUM(y1 - y0, fp_bit);
215 int x_step = x0 > x1 ? -1 : 1;
216 int y_step = y0 > y1 ? -1 : 1;
217 int distance = (frac_y - fp_one) * dx - (frac_x - fp_half) * dy;
218 if (dx >= dy) {
219 int step = dxf / dyf;
220 int remaining = dx;
221 while (remaining > (step - 1)) {
222 distance += step * 2 * dyf;
223 if (distance >= 0) {
224 draw_hline(x0, x0 + step - 1, y0, clr);
225 x0 += x_step * step;
226 remaining -= step;
227 } else {
228 if (remaining < step) {
229 break;
230 }
231 draw_hline(x0, x0 + step, y0, clr);
232 distance += 2 * dyf;
233 x0 += x_step * (step + 1);
234 remaining -= step + 1;
235 }
236 distance -= 2 * dxf;
237 y0 += y_step;
238 }
239 if (remaining >= 0) {
240 draw_hline(x0, x0 + remaining, y0, clr);
241 }
242 } else {
243 int step = dyf / dxf;
244 int remaining = dy;
245 while (remaining > (step - 1)) {
246 distance += step * 2 * dxf;
247 if (distance >= 0) {
248 draw_vline(x0, y0, y0 + step - 1, clr);
249 y0 += y_step * step;
250 remaining -= step;
251 } else {
252 draw_vline(x0, y0, y0 + step, clr);
253 distance += 2 * dxf;
254 y0 += y_step * (step + 1);
255 remaining -= step + 1;
256 }
257 distance -= 2 * dyf;
258 x0 += x_step;
259 }
260 if (remaining >= 0) {
261 draw_vline(x0, y0, y0 + remaining, clr);
262 }
263 }
264#else
265 int x_step = x0 > x1 ? -1 : 1;
266 int y_step = y0 > y1 ? -1 : 1;
267 if (dx >= dy) {
268 int diff = 2 * dy - dx;
269 for (int i = 0; i < dx + 1; i++) {
270 draw_pixel(x0, y0, clr);
271 if (diff >= 0) {
272 diff -= 2 * dx;
273 y0 += y_step;
274 }
275 diff += 2 * dy;
276 x0 += x_step;
277 }
278 } else {
279 int diff = 2 * dx - dy;
280 for (int i = 0; i < dy + 1; i++) {
281 draw_pixel(x0, y0, clr);
282 if (diff >= 0) {
283 diff -= 2 * dy;
284 x0 += x_step;
285 }
286 diff += 2 * dx;
287 y0 += y_step;
288 }
289 }
290#endif
291 }
292}
293
294IWRAM_CODE
295void
296draw_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) {
297 BOUNDCHECK_SCREEN(x0, y0);
298 BOUNDCHECK_SCREEN(x1, y1);
299 MAYBE_SWAP(x0, x1);
300 MAYBE_SWAP(y0, y1);
301
302 draw_hline(x0, x1, y0, clr);
303 draw_hline(x0, x1, y1, clr);
304 draw_vline(x0, y0, y1, clr);
305 draw_vline(x1, y0, y1, clr);
306}
307
308IWRAM_CODE
309void
310draw_filled_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) {
311 BOUNDCHECK_SCREEN(x0, y0);
312 BOUNDCHECK_SCREEN(x1, y1);
313 MAYBE_SWAP(x0, x1);
314 MAYBE_SWAP(y0, y1);
315
316 // Special condition. If the screen is to be completely filled, use the DMA
317 // instead.
318 if (x0 == 0 && x1 >= (SCREEN_WIDTH - 1) && y0 == 0 && y1 >= (SCREEN_HEIGHT - 1)) {
319 screen_fill(clr);
320 return;
321 }
322
323 for (size_t y = y0; y <= y1; y++) {
324 draw_hline(x0, x1, y, clr);
325 }
326}
327
328//
329// Sprites (chr/icn).
330//
331
332#if DEC_BIG_LUT == 1
333static u32 dec_byte_flip_x[256] = {
334 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100,
335 0x00000101, 0x00000110, 0x00000111, 0x00001000, 0x00001001,
336 0x00001010, 0x00001011, 0x00001100, 0x00001101, 0x00001110,
337 0x00001111, 0x00010000, 0x00010001, 0x00010010, 0x00010011,
338 0x00010100, 0x00010101, 0x00010110, 0x00010111, 0x00011000,
339 0x00011001, 0x00011010, 0x00011011, 0x00011100, 0x00011101,
340 0x00011110, 0x00011111, 0x00100000, 0x00100001, 0x00100010,
341 0x00100011, 0x00100100, 0x00100101, 0x00100110, 0x00100111,
342 0x00101000, 0x00101001, 0x00101010, 0x00101011, 0x00101100,
343 0x00101101, 0x00101110, 0x00101111, 0x00110000, 0x00110001,
344 0x00110010, 0x00110011, 0x00110100, 0x00110101, 0x00110110,
345 0x00110111, 0x00111000, 0x00111001, 0x00111010, 0x00111011,
346 0x00111100, 0x00111101, 0x00111110, 0x00111111, 0x01000000,
347 0x01000001, 0x01000010, 0x01000011, 0x01000100, 0x01000101,
348 0x01000110, 0x01000111, 0x01001000, 0x01001001, 0x01001010,
349 0x01001011, 0x01001100, 0x01001101, 0x01001110, 0x01001111,
350 0x01010000, 0x01010001, 0x01010010, 0x01010011, 0x01010100,
351 0x01010101, 0x01010110, 0x01010111, 0x01011000, 0x01011001,
352 0x01011010, 0x01011011, 0x01011100, 0x01011101, 0x01011110,
353 0x01011111, 0x01100000, 0x01100001, 0x01100010, 0x01100011,
354 0x01100100, 0x01100101, 0x01100110, 0x01100111, 0x01101000,
355 0x01101001, 0x01101010, 0x01101011, 0x01101100, 0x01101101,
356 0x01101110, 0x01101111, 0x01110000, 0x01110001, 0x01110010,
357 0x01110011, 0x01110100, 0x01110101, 0x01110110, 0x01110111,
358 0x01111000, 0x01111001, 0x01111010, 0x01111011, 0x01111100,
359 0x01111101, 0x01111110, 0x01111111, 0x10000000, 0x10000001,
360 0x10000010, 0x10000011, 0x10000100, 0x10000101, 0x10000110,
361 0x10000111, 0x10001000, 0x10001001, 0x10001010, 0x10001011,
362 0x10001100, 0x10001101, 0x10001110, 0x10001111, 0x10010000,
363 0x10010001, 0x10010010, 0x10010011, 0x10010100, 0x10010101,
364 0x10010110, 0x10010111, 0x10011000, 0x10011001, 0x10011010,
365 0x10011011, 0x10011100, 0x10011101, 0x10011110, 0x10011111,
366 0x10100000, 0x10100001, 0x10100010, 0x10100011, 0x10100100,
367 0x10100101, 0x10100110, 0x10100111, 0x10101000, 0x10101001,
368 0x10101010, 0x10101011, 0x10101100, 0x10101101, 0x10101110,
369 0x10101111, 0x10110000, 0x10110001, 0x10110010, 0x10110011,
370 0x10110100, 0x10110101, 0x10110110, 0x10110111, 0x10111000,
371 0x10111001, 0x10111010, 0x10111011, 0x10111100, 0x10111101,
372 0x10111110, 0x10111111, 0x11000000, 0x11000001, 0x11000010,
373 0x11000011, 0x11000100, 0x11000101, 0x11000110, 0x11000111,
374 0x11001000, 0x11001001, 0x11001010, 0x11001011, 0x11001100,
375 0x11001101, 0x11001110, 0x11001111, 0x11010000, 0x11010001,
376 0x11010010, 0x11010011, 0x11010100, 0x11010101, 0x11010110,
377 0x11010111, 0x11011000, 0x11011001, 0x11011010, 0x11011011,
378 0x11011100, 0x11011101, 0x11011110, 0x11011111, 0x11100000,
379 0x11100001, 0x11100010, 0x11100011, 0x11100100, 0x11100101,
380 0x11100110, 0x11100111, 0x11101000, 0x11101001, 0x11101010,
381 0x11101011, 0x11101100, 0x11101101, 0x11101110, 0x11101111,
382 0x11110000, 0x11110001, 0x11110010, 0x11110011, 0x11110100,
383 0x11110101, 0x11110110, 0x11110111, 0x11111000, 0x11111001,
384 0x11111010, 0x11111011, 0x11111100, 0x11111101, 0x11111110,
385 0x11111111
386};
387
388static u32 dec_byte[256] = {
389 0x00000000, 0x10000000, 0x01000000, 0x11000000, 0x00100000,
390 0x10100000, 0x01100000, 0x11100000, 0x00010000, 0x10010000,
391 0x01010000, 0x11010000, 0x00110000, 0x10110000, 0x01110000,
392 0x11110000, 0x00001000, 0x10001000, 0x01001000, 0x11001000,
393 0x00101000, 0x10101000, 0x01101000, 0x11101000, 0x00011000,
394 0x10011000, 0x01011000, 0x11011000, 0x00111000, 0x10111000,
395 0x01111000, 0x11111000, 0x00000100, 0x10000100, 0x01000100,
396 0x11000100, 0x00100100, 0x10100100, 0x01100100, 0x11100100,
397 0x00010100, 0x10010100, 0x01010100, 0x11010100, 0x00110100,
398 0x10110100, 0x01110100, 0x11110100, 0x00001100, 0x10001100,
399 0x01001100, 0x11001100, 0x00101100, 0x10101100, 0x01101100,
400 0x11101100, 0x00011100, 0x10011100, 0x01011100, 0x11011100,
401 0x00111100, 0x10111100, 0x01111100, 0x11111100, 0x00000010,
402 0x10000010, 0x01000010, 0x11000010, 0x00100010, 0x10100010,
403 0x01100010, 0x11100010, 0x00010010, 0x10010010, 0x01010010,
404 0x11010010, 0x00110010, 0x10110010, 0x01110010, 0x11110010,
405 0x00001010, 0x10001010, 0x01001010, 0x11001010, 0x00101010,
406 0x10101010, 0x01101010, 0x11101010, 0x00011010, 0x10011010,
407 0x01011010, 0x11011010, 0x00111010, 0x10111010, 0x01111010,
408 0x11111010, 0x00000110, 0x10000110, 0x01000110, 0x11000110,
409 0x00100110, 0x10100110, 0x01100110, 0x11100110, 0x00010110,
410 0x10010110, 0x01010110, 0x11010110, 0x00110110, 0x10110110,
411 0x01110110, 0x11110110, 0x00001110, 0x10001110, 0x01001110,
412 0x11001110, 0x00101110, 0x10101110, 0x01101110, 0x11101110,
413 0x00011110, 0x10011110, 0x01011110, 0x11011110, 0x00111110,
414 0x10111110, 0x01111110, 0x11111110, 0x00000001, 0x10000001,
415 0x01000001, 0x11000001, 0x00100001, 0x10100001, 0x01100001,
416 0x11100001, 0x00010001, 0x10010001, 0x01010001, 0x11010001,
417 0x00110001, 0x10110001, 0x01110001, 0x11110001, 0x00001001,
418 0x10001001, 0x01001001, 0x11001001, 0x00101001, 0x10101001,
419 0x01101001, 0x11101001, 0x00011001, 0x10011001, 0x01011001,
420 0x11011001, 0x00111001, 0x10111001, 0x01111001, 0x11111001,
421 0x00000101, 0x10000101, 0x01000101, 0x11000101, 0x00100101,
422 0x10100101, 0x01100101, 0x11100101, 0x00010101, 0x10010101,
423 0x01010101, 0x11010101, 0x00110101, 0x10110101, 0x01110101,
424 0x11110101, 0x00001101, 0x10001101, 0x01001101, 0x11001101,
425 0x00101101, 0x10101101, 0x01101101, 0x11101101, 0x00011101,
426 0x10011101, 0x01011101, 0x11011101, 0x00111101, 0x10111101,
427 0x01111101, 0x11111101, 0x00000011, 0x10000011, 0x01000011,
428 0x11000011, 0x00100011, 0x10100011, 0x01100011, 0x11100011,
429 0x00010011, 0x10010011, 0x01010011, 0x11010011, 0x00110011,
430 0x10110011, 0x01110011, 0x11110011, 0x00001011, 0x10001011,
431 0x01001011, 0x11001011, 0x00101011, 0x10101011, 0x01101011,
432 0x11101011, 0x00011011, 0x10011011, 0x01011011, 0x11011011,
433 0x00111011, 0x10111011, 0x01111011, 0x11111011, 0x00000111,
434 0x10000111, 0x01000111, 0x11000111, 0x00100111, 0x10100111,
435 0x01100111, 0x11100111, 0x00010111, 0x10010111, 0x01010111,
436 0x11010111, 0x00110111, 0x10110111, 0x01110111, 0x11110111,
437 0x00001111, 0x10001111, 0x01001111, 0x11001111, 0x00101111,
438 0x10101111, 0x01101111, 0x11101111, 0x00011111, 0x10011111,
439 0x01011111, 0x11011111, 0x00111111, 0x10111111, 0x01111111,
440 0x11111111
441};
442
443IWRAM_CODE
444static inline
445u32
446decode_1bpp(u8 row, u8 flip_x) {
447 if (flip_x) {
448 return dec_byte_flip_x[row];
449 }
450 return dec_byte[row];
451}
452#else
453static u16 dec_nibble[] = {
454 0x0000, 0x1000, 0x0100, 0x1100,
455 0x0010, 0x1010, 0x0110, 0x1110,
456 0x0001, 0x1001, 0x0101, 0x1101,
457 0x0011, 0x1011, 0x0111, 0x1111,
458};
459
460static u16 dec_nibble_flip_x[] = {
461 0x0000, 0x0001, 0x0010, 0x0011,
462 0x0100, 0x0101, 0x0110, 0x0111,
463 0x1000, 0x1001, 0x1010, 0x1011,
464 0x1100, 0x1101, 0x1110, 0x1111,
465};
466
467IWRAM_CODE
468static inline
469u32
470decode_1bpp(u8 row, u8 flip_x) {
471 if (flip_x) {
472 u16 *lut = dec_nibble_flip_x;
473 return (u32)lut[(row >> 4) & 0xF] << 16 | (u32)lut[(row >> 0) & 0xF];
474 }
475 u16 *lut = dec_nibble;
476 return (u32)lut[(row >> 0) & 0xF] << 16 | (u32)lut[(row >> 4) & 0xF];
477}
478#endif
479
480IWRAM_CODE
481UNROLL_LOOPS
482void
483draw_chr(size_t x, size_t y, u8 *sprite, u8 clr, u8 flip_x, u8 flip_y) {
484 BOUNDCHECK_SCREEN(x, y);
485 size_t tile_x0 = x / 8;
486 size_t tile_x1 = (x + 7) / 8;
487 size_t tile_y = y / 8;
488 size_t start_col = x % 8;
489 size_t start_row = y % 8;
490 size_t shift_left = start_col * 4;
491 size_t shift_right = (8 - start_col) * 4;
492 u32 dirty = (1 << tile_x0) | (1 << tile_x1);
493 u32 *dst = &backbuf[start_row + (tile_x0 + tile_y * 32) * 8];
494#if DEC_BIG_LUT
495 u32 *lut = flip_x ? dec_byte_flip_x : dec_byte;
496#endif
497 if (!flip_y) {
498 for(size_t v = 0; v < 8; v++, dst++) {
499 if ((y + v) >= SCREEN_HEIGHT) break;
500 u8 ch1 = sprite[v + 0];
501 u8 ch2 = sprite[v + 8];
502#if DEC_BIG_LUT
503 u32 clr_a = lut[ch1];
504 u32 clr_b = lut[ch2];
505#else
506 u32 clr_a = decode_1bpp(ch1, flip_x);
507 u32 clr_b = decode_1bpp(ch2, flip_x);
508#endif
509 u32 mask_a = (clr_a * 0xF);
510 u32 mask_b = (clr_b * 0xF);
511 u32 mask = (mask_a | mask_b);
512 u32 color;
513 if (clr == 0) {
514 color = clr_a + (clr_b << 1);
515 } else if (clr == 15) {
516 color = 0;
517 } else {
518 color = (clr_a | clr_b) * clr;
519 }
520 dst[0] = (dst[0] & ~(mask << shift_left)) | (color << shift_left);
521 dst[8] = (dst[8] & ~(mask >> shift_right)) | (color >> shift_right);
522 if ((start_row + v) == 7) {
523 dirty_tiles[tile_y + 1] |= dirty;
524 dst += (32 - 1) * 8;
525 }
526 }
527 } else {
528 for(size_t v = 0; v < 8; v++, dst++) {
529 if ((y + v) >= SCREEN_HEIGHT) break;
530 u8 ch1 = sprite[(7 - v) + 0];
531 u8 ch2 = sprite[(7 - v) + 8];
532#if DEC_BIG_LUT
533 u32 clr_a = lut[ch1];
534 u32 clr_b = lut[ch2];
535#else
536 u32 clr_a = decode_1bpp(ch1, flip_x);
537 u32 clr_b = decode_1bpp(ch2, flip_x);
538#endif
539 u32 mask_a = (clr_a * 0xF);
540 u32 mask_b = (clr_b * 0xF);
541 u32 mask = (mask_a | mask_b);
542 u32 color;
543 if (clr == 0) {
544 color = clr_a + (clr_b << 1);
545 } else if (clr == 15) {
546 color = 0;
547 } else {
548 color = (clr_a | clr_b) * clr;
549 }
550 dst[0] = (dst[0] & ~(mask << shift_left)) | (color << shift_left);
551 dst[8] = (dst[8] & ~(mask >> shift_right)) | (color >> shift_right);
552 if ((start_row + v) == 7) {
553 dirty_tiles[tile_y + 1] |= dirty;
554 dst += (32 - 1) * 8;
555 }
556 }
557 }
558 dirty_tiles[tile_y] |= dirty;
559}
560
561IWRAM_CODE
562UNROLL_LOOPS
563void
564draw_icn(size_t x, size_t y, u8 *sprite, u8 clr, u8 flip_x, u8 flip_y) {
565 BOUNDCHECK_SCREEN(x, y);
566 size_t tile_x0 = x / 8;
567 size_t tile_x1 = (x + 7) / 8;
568 size_t tile_y = y / 8;
569 size_t start_col = x % 8;
570 size_t start_row = y % 8;
571 size_t shift_left = start_col * 4;
572 size_t shift_right = (8 - start_col) * 4;
573 u32 dirty = (1 << tile_x0) | (1 << tile_x1);
574 u32 *dst = &backbuf[start_row + (tile_x0 + tile_y * 32) * 8];
575#if DEC_BIG_LUT
576 u32 *lut = flip_x ? dec_byte_flip_x : dec_byte;
577#endif
578 if (!flip_y) {
579 for(size_t v = 0; v < 8; v++, dst++) {
580 if ((y + v) >= SCREEN_HEIGHT) break;
581 u8 ch1 = sprite[v + 0];
582#if DEC_BIG_LUT
583 u32 color = lut[ch1];
584#else
585 u32 color = decode_1bpp(ch1, flip_x);
586#endif
587 u32 mask = (color * 0xF);
588 color *= clr;
589 dst[0] = (dst[0] & ~(mask << shift_left)) | (color << shift_left);
590 dst[8] = (dst[8] & ~(mask >> shift_right)) | (color >> shift_right);
591 if ((start_row + v) == 7) {
592 dirty_tiles[tile_y + 1] |= dirty;
593 dst += (32 - 1) * 8;
594 }
595 }
596 } else {
597 for(size_t v = 0; v < 8; v++, dst++) {
598 if ((y + v) >= SCREEN_HEIGHT) break;
599 u8 ch1 = sprite[(7 - v) + 0];
600#if DEC_BIG_LUT
601 u32 color = lut[ch1];
602#else
603 u32 color = decode_1bpp(ch1, flip_x);
604#endif
605 u32 mask = (color * 0xF);
606 color *= clr;
607 dst[0] = (dst[0] & ~(mask << shift_left)) | (color << shift_left);
608 dst[8] = (dst[8] & ~(mask >> shift_right)) | (color >> shift_right);
609 if ((start_row + v) == 7) {
610 dirty_tiles[tile_y + 1] |= dirty;
611 dst += (32 - 1) * 8;
612 }
613 }
614 }
615 dirty_tiles[tile_y] |= dirty;
616}
617
618//
619// Flipping buffers/copying memory.
620//
621
622IWRAM_CODE
623void
624flip_buffer(void) {
625// Mode 0: double buffering without dirty tiles.
626#if FLIP_TYPE == 0
627 if (backbuf == BUF_0) {
628 backbuf = BUF_1;
629 BG_H_SCROLL_0 = 0;
630 BG_H_SCROLL_1 = -240;
631 } else {
632 backbuf = BUF_0;
633 BG_H_SCROLL_0 = -240;
634 BG_H_SCROLL_1 = 0;
635 }
636
637// Mode 1: single buffer, copy the dirty lines from backbuffer (BUF_1) to
638// frontbuffer (BUF_0) using the DMA.
639#elif FLIP_TYPE == 1
640 u32 *front = BUF_0;
641 u32 *back = BUF_1;
642 BG_H_SCROLL_0 = 0;
643 BG_H_SCROLL_1 = -240;
644 for (size_t j = 0; j < 20; ++j) {
645 if (dirty_tiles[j] == 0) {
646 continue;
647 }
648 u32 offset = j * 32 * 8;
649 dma_copy(front + offset, back + offset, (30 * 8 * 4), 3);
650 dirty_tiles[j] = 0;
651 }
652
653// Mode 2: single buffer, copy the dirty tiles from backbuffer (BUF_1) to
654// frontbuffer (BUF_0).
655#elif FLIP_TYPE == 2
656 u32 *front = BUF_0;
657 u32 *back = BUF_1;
658 BG_H_SCROLL_0 = 0;
659 BG_H_SCROLL_1 = -240;
660 for (size_t j = 0; j < 20; ++j) {
661 if (dirty_tiles[j] == 0) {
662 continue;
663 }
664 size_t k = 1;
665 for (size_t i = 0; i < 30; ++i, k <<= 1) {
666 if (dirty_tiles[j] & k) {
667 Tile *mem_front = front;
668 Tile *mem_back = back;
669 mem_front[i + j * 32] = mem_back[i + j * 32];
670 }
671 }
672 dirty_tiles[j] = 0;
673 }
674
675// Mode 3: Double buffering with dirty line, copying the dirty lines if needed
676// after flipping buffers with the DMA.
677#elif FLIP_TYPE == 3
678 bool should_flip = false;
679 for (size_t j = 0; j < 20; ++j) {
680 if (dirty_tiles[j] == 0) {
681 continue;
682 }
683 should_flip = true;
684 break;
685 }
686 if (!should_flip) {
687 return;
688 }
689 u32 *frontbuf = backbuf;
690 if (backbuf == BUF_0) {
691 backbuf = BUF_1;
692 BG_H_SCROLL_0 = 0;
693 BG_H_SCROLL_1 = -240;
694 } else {
695 backbuf = BUF_0;
696 BG_H_SCROLL_0 = -240;
697 BG_H_SCROLL_1 = 0;
698 }
699 for (size_t j = 0; j < 20; ++j) {
700 if (dirty_tiles[j] == 0) {
701 continue;
702 }
703 u32 offset = j * 32 * 8;
704 dma_copy(backbuf + offset, frontbuf + offset, (30 * 8 * 4), 3);
705 dirty_tiles[j] = 0;
706 }
707
708// Mode 4: Double buffering with dirty tiles, copying the dirty tiles if needed
709// after flipping buffers.
710#elif FLIP_TYPE == 4
711 bool should_flip = false;
712 for (size_t j = 0; j < 20; ++j) {
713 if (dirty_tiles[j] == 0) {
714 continue;
715 }
716 should_flip = true;
717 break;
718 }
719 if (!should_flip) {
720 return;
721 }
722 u32 *frontbuf = backbuf;
723 if (backbuf == BUF_0) {
724 backbuf = BUF_1;
725 BG_H_SCROLL_0 = 0;
726 BG_H_SCROLL_1 = -240;
727 } else {
728 backbuf = BUF_0;
729 BG_H_SCROLL_0 = -240;
730 BG_H_SCROLL_1 = 0;
731 }
732 for (size_t j = 0; j < 20; ++j) {
733 if (dirty_tiles[j] == 0) {
734 continue;
735 }
736 size_t k = 1;
737 for (size_t i = 0; i < 30; ++i, k <<= 1) {
738 if (dirty_tiles[j] & k) {
739 Tile *mem_front = frontbuf;
740 Tile *mem_back = backbuf;
741 mem_back[i + j * 32] = mem_front[i + j * 32];
742 }
743 }
744 dirty_tiles[j] = 0;
745 }
746#endif
747}
748
749//
750// Text rendering.
751//
752
753#include "font.h"
754
755// Font rendering function for the text engine.
756void
757txt_drawc(char c, size_t x, size_t y, u8 clr) {
758 u8 *tile = font_icn;
759 draw_icn(x, y, tile + 8 * c, clr, 1, 0);
760}
761
762//
763// Initialization.
764//
765
766void
767renderer_init(void) {
768 // Initialize display mode and bg palette.
769 DISP_CTRL = DISP_MODE_0 | DISP_BG_0 | DISP_BG_1;
770
771 // Clear VRAM.
772 dma_fill((u32*)MEM_VRAM, 0, KB(96), 3);
773
774 // Initialize backgrounds.
775 BG_CTRL(0) = BG_CHARBLOCK(CB_0) | BG_SCREENBLOCK(SB_0) | BG_PRIORITY(0) | BG_SIZE(1);
776 BG_CTRL(1) = BG_CHARBLOCK(CB_1) | BG_SCREENBLOCK(SB_1) | BG_PRIORITY(1) | BG_SIZE(1);
777
778 // Initialize background memory map for the render buffers. The backgrounds
779 // are 64x32 each, with the second screenblock pointing to a zeroed tile.
780 // This makes it so while scrolling the backgrounds to the second screen we
781 // effectively disabling them. Thanks to this we can perform double
782 // buffering with mode 0 rendering.
783 u16 *mem_map_fg = SCREENBLOCK_MEM[SB_0];
784 u16 *mem_map_fg_blank = SCREENBLOCK_MEM[SB_0 + 1];
785 u16 *mem_map_bg = SCREENBLOCK_MEM[SB_1];
786 u16 *mem_map_bg_blank = SCREENBLOCK_MEM[SB_1 + 1];
787 for (size_t i = 0; i < 32 * 20; ++i) {
788 mem_map_fg[i] = i;
789 mem_map_fg_blank[i] = 32 * 20 - 1;
790 mem_map_bg[i] = i + 32 * 4;
791 mem_map_bg_blank[i] = (32 * 20 - 1) + 32 * 4;
792 }
793
794 // Setup initial background state.
795 BG_H_SCROLL_0 = -240;
796 BG_H_SCROLL_1 = -240;
797
798 // Initialize default palette.
799 PAL_BUFFER_BG[0] = COLOR_BLACK;
800 PAL_BUFFER_BG[1] = COLOR_WHITE;
801 PAL_BUFFER_BG[2] = COLOR_RED;
802 PAL_BUFFER_BG[3] = COLOR_BLUE;
803 PAL_BUFFER_BG[4] = COLOR_CYAN;
804 PAL_BUFFER_BG[5] = COLOR_GREY;
805 PAL_BUFFER_BG[6] = COLOR_WHITE;
806 PAL_BUFFER_BG[7] = COLOR_GREEN;
807 PAL_BUFFER_BG[8] = COLOR_PURPLE;
808
809 // Initialize text engine.
810 txt_init(txt_drawc);
811}
diff --git a/src/sequencer.c b/src/sequencer.c
index b582cf4..ff3904d 100644
--- a/src/sequencer.c
+++ b/src/sequencer.c
@@ -527,9 +527,9 @@ draw_channels(void) {
527 } 527 }
528 u8 clr = active ? COL_FG : COL_GREY; 528 u8 clr = active ? COL_FG : COL_GREY;
529 size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; 529 size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y;
530 draw_tile(CHAN_START_X, y, channel_tiles + k++, clr, false); 530 // draw_tile(CHAN_START_X, y, channel_tiles + k++, clr, false);
531 draw_tile(CHAN_START_X + 8, y, channel_tiles + k++, clr, false); 531 // draw_tile(CHAN_START_X + 8, y, channel_tiles + k++, clr, false);
532 draw_tile(CHAN_START_X + 16, y, channel_tiles + k++, clr, false); 532 // draw_tile(CHAN_START_X + 16, y, channel_tiles + k++, clr, false);
533 } 533 }
534} 534}
535 535
@@ -582,8 +582,8 @@ draw_trigger(size_t chan, size_t i) {
582 size_t y = TRIG_START_Y + offset_y; 582 size_t y = TRIG_START_Y + offset_y;
583 Tile *tiles = ASSETS_NOTE_NAMES; 583 Tile *tiles = ASSETS_NOTE_NAMES;
584 tiles += 2 * trig.note; 584 tiles += 2 * trig.note;
585 draw_tile(x, y, tiles, COL_FG, true); 585 // draw_tile(x, y, tiles, COL_FG, true);
586 draw_tile(x + 8, y, tiles + 1, COL_FG, true); 586 // draw_tile(x + 8, y, tiles + 1, COL_FG, true);
587 } else { 587 } else {
588 clear_trigger(i); 588 clear_trigger(i);
589 } 589 }
@@ -658,7 +658,7 @@ void
658draw_bank_buttons() { 658draw_bank_buttons() {
659 size_t x = BANK_START_X; 659 size_t x = BANK_START_X;
660 size_t y = BANK_START_Y; 660 size_t y = BANK_START_Y;
661 txt_drawf_small("BANK", x - 2, y - 10, 4, COL_FG); 661 // txt_drawf_small("BANK", x - 2, y - 10, 4, COL_FG);
662 char bank_names[] = { 662 char bank_names[] = {
663 'A', 'B', 'C', 'D', 663 'A', 'B', 'C', 'D',
664 }; 664 };
@@ -669,7 +669,7 @@ draw_bank_buttons() {
669 } 669 }
670 draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG); 670 draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG);
671 draw_rect(x, y, x + PAT_W, y + PAT_H, color); 671 draw_rect(x, y, x + PAT_W, y + PAT_H, color);
672 txt_drawc(bank_names[i], x + 4, y + 2, 6, color); 672 // txt_drawc(bank_names[i], x + 4, y + 2, 6, color);
673 y += PAT_OFFSET_Y; 673 y += PAT_OFFSET_Y;
674 } 674 }
675} 675}
@@ -678,7 +678,7 @@ void
678draw_pattern_buttons() { 678draw_pattern_buttons() {
679 size_t x = PAT_START_X; 679 size_t x = PAT_START_X;
680 size_t y = PAT_START_Y; 680 size_t y = PAT_START_Y;
681 txt_drawf_small("PAT", x, y - 10, 4, COL_FG); 681 // txt_drawf_small("PAT", x, y - 10, 4, COL_FG);
682 char pat_names[] = { 682 char pat_names[] = {
683 'A', 'B', 'C', 'D', 683 'A', 'B', 'C', 'D',
684 'E', 'F', 'G', 'H', 684 'E', 'F', 'G', 'H',
@@ -693,7 +693,7 @@ draw_pattern_buttons() {
693 } 693 }
694 draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG); 694 draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG);
695 draw_rect(x, y, x + PAT_W, y + PAT_H, color); 695 draw_rect(x, y, x + PAT_W, y + PAT_H, color);
696 txt_drawc(pat_names[i], x + 4, y + 2, 6, color); 696 // txt_drawc(pat_names[i], x + 4, y + 2, 6, color);
697 y += PAT_OFFSET_Y; 697 y += PAT_OFFSET_Y;
698 } 698 }
699} 699}
@@ -745,7 +745,7 @@ draw_bpm() {
745 draw_filled_rect(x, y, x + R_COL_W, y + BPM_H, COL_BG); 745 draw_filled_rect(x, y, x + R_COL_W, y + BPM_H, COL_BG);
746 draw_rect(x, y, x + R_COL_W, y + BPM_H, COL_FG); 746 draw_rect(x, y, x + R_COL_W, y + BPM_H, COL_FG);
747 draw_line(x + 5, y, x + 19, y, COL_BG); 747 draw_line(x + 5, y, x + 19, y, COL_BG);
748 txt_drawf_small("BPM", x + 5, y - 4, 4, COL_FG); 748 // txt_drawf_small("BPM", x + 5, y - 4, 4, COL_FG);
749 749
750 // Make sure its horizontally centered if only 2 digits 750 // Make sure its horizontally centered if only 2 digits
751 int bpm = patterns[pattern_selection_loc].bpm; 751 int bpm = patterns[pattern_selection_loc].bpm;
@@ -1069,24 +1069,24 @@ draw_parameters_wave(void) {
1069 1069
1070 // Wave text. 1070 // Wave text.
1071 x -= 2; 1071 x -= 2;
1072 txt_drawf_small("%02x%02x%02x%02x", x, y + 20, 4, COL_FG, 1072 // txt_drawf_small("%02x%02x%02x%02x", x, y + 20, 4, COL_FG,
1073 wave_a[0], wave_a[1], wave_a[2], wave_a[3]); 1073 // wave_a[0], wave_a[1], wave_a[2], wave_a[3]);
1074 txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 20, 4, COL_FG, 1074 // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 20, 4, COL_FG,
1075 wave_a[4], wave_a[5], wave_a[6], wave_a[7]); 1075 // wave_a[4], wave_a[5], wave_a[6], wave_a[7]);
1076 txt_drawf_small("%02x%02x%02x%02x", x, y + 28, 4, COL_FG, 1076 // txt_drawf_small("%02x%02x%02x%02x", x, y + 28, 4, COL_FG,
1077 wave_a[8], wave_a[9], wave_a[10], wave_a[11]); 1077 // wave_a[8], wave_a[9], wave_a[10], wave_a[11]);
1078 txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 28, 4, COL_FG, 1078 // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 28, 4, COL_FG,
1079 wave_a[12], wave_a[13], wave_a[14], wave_a[15]); 1079 // wave_a[12], wave_a[13], wave_a[14], wave_a[15]);
1080 1080
1081 x += 70; 1081 x += 70;
1082 txt_drawf_small("%02x%02x%02x%02x", x, y + 20, 4, COL_FG, 1082 // txt_drawf_small("%02x%02x%02x%02x", x, y + 20, 4, COL_FG,
1083 wave_b[0], wave_b[1], wave_b[2], wave_b[3]); 1083 // wave_b[0], wave_b[1], wave_b[2], wave_b[3]);
1084 txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 20, 4, COL_FG, 1084 // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 20, 4, COL_FG,
1085 wave_b[4], wave_b[5], wave_b[6], wave_b[7]); 1085 // wave_b[4], wave_b[5], wave_b[6], wave_b[7]);
1086 txt_drawf_small("%02x%02x%02x%02x", x, y + 28, 4, COL_FG, 1086 // txt_drawf_small("%02x%02x%02x%02x", x, y + 28, 4, COL_FG,
1087 wave_b[8], wave_b[9], wave_b[10], wave_b[11]); 1087 // wave_b[8], wave_b[9], wave_b[10], wave_b[11]);
1088 txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 28, 4, COL_FG, 1088 // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 28, 4, COL_FG,
1089 wave_b[12], wave_b[13], wave_b[14], wave_b[15]); 1089 // wave_b[12], wave_b[13], wave_b[14], wave_b[15]);
1090 } 1090 }
1091 1091
1092 // Draw default wave buttons. 1092 // Draw default wave buttons.
@@ -1095,12 +1095,12 @@ draw_parameters_wave(void) {
1095 size_t x = PARAMS_START_X; 1095 size_t x = PARAMS_START_X;
1096 size_t y = PARAMS_START_Y + PARAMS_H - 12; 1096 size_t y = PARAMS_START_Y + PARAMS_H - 12;
1097 for (size_t i = 0, k = 0; i < 4 * 2; i += 2, k++) { 1097 for (size_t i = 0, k = 0; i < 4 * 2; i += 2, k++) {
1098 draw_tile(x + 17 * k, y, wave_tiles + i, COL_FG, true); 1098 // draw_tile(x + 17 * k, y, wave_tiles + i, COL_FG, true);
1099 draw_tile(x + 17 * k + 8, y, wave_tiles + i + 1, COL_FG, true); 1099 // draw_tile(x + 17 * k + 8, y, wave_tiles + i + 1, COL_FG, true);
1100 } 1100 }
1101 for (size_t i = 0, k = 0; i < 4 * 2; i += 2, k++) { 1101 for (size_t i = 0, k = 0; i < 4 * 2; i += 2, k++) {
1102 draw_tile(x + 17 * k + 70, y, wave_tiles + i, COL_FG, true); 1102 // draw_tile(x + 17 * k + 70, y, wave_tiles + i, COL_FG, true);
1103 draw_tile(x + 17 * k + 8 + 70, y, wave_tiles + i + 1, COL_FG, true); 1103 // draw_tile(x + 17 * k + 8 + 70, y, wave_tiles + i + 1, COL_FG, true);
1104 } 1104 }
1105 } 1105 }
1106 1106
@@ -1113,7 +1113,7 @@ draw_parameters_wave(void) {
1113 draw_line(x, y + 5, x, y + 16, COL_FG); 1113 draw_line(x, y + 5, x, y + 16, COL_FG);
1114 draw_line(x + 30, y + 5, x + 30, y + 17, COL_FG); 1114 draw_line(x + 30, y + 5, x + 30, y + 17, COL_FG);
1115 draw_line(x, y + 17, x + 30, y + 17, COL_FG); 1115 draw_line(x, y + 17, x + 30, y + 17, COL_FG);
1116 txt_drawf_small("mode", x + 6, y, 4, COL_FG); 1116 // txt_drawf_small("mode", x + 6, y, 4, COL_FG);
1117 1117
1118 switch (pat->ch3.params[trig_selection_loc].wave_mode) { 1118 switch (pat->ch3.params[trig_selection_loc].wave_mode) {
1119 case 0: { 1119 case 0: {
@@ -1137,7 +1137,7 @@ draw_parameters_wave(void) {
1137 draw_line(x, y + 8, x, y + 19, COL_FG); 1137 draw_line(x, y + 8, x, y + 19, COL_FG);
1138 draw_line(x + 30, y + 8, x + 30, y + 19, COL_FG); 1138 draw_line(x + 30, y + 8, x + 30, y + 19, COL_FG);
1139 draw_line(x, y + 20, x + 30, y + 20, COL_FG); 1139 draw_line(x, y + 20, x + 30, y + 20, COL_FG);
1140 txt_drawf_small("vol", x + 8, y + 3, 4, COL_FG); 1140 // txt_drawf_small("vol", x + 8, y + 3, 4, COL_FG);
1141 1141
1142 switch (pat->ch3.params[trig_selection_loc].wave_volume) { 1142 switch (pat->ch3.params[trig_selection_loc].wave_volume) {
1143 case 0: { 1143 case 0: {
@@ -1232,7 +1232,7 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) {
1232 draw_line(x, y + 8, x, y + 19, COL_FG); 1232 draw_line(x, y + 8, x, y + 19, COL_FG);
1233 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1233 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1234 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1234 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1235 txt_drawf_small("duty", x + 3, y + 3, 4, COL_FG); 1235 // txt_drawf_small("duty", x + 3, y + 3, 4, COL_FG);
1236 1236
1237 switch (params->duty_cycle) { 1237 switch (params->duty_cycle) {
1238 case 0: { 1238 case 0: {
@@ -1291,7 +1291,7 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) {
1291 draw_line(x, y + 8, x, y + 19, COL_FG); 1291 draw_line(x, y + 8, x, y + 19, COL_FG);
1292 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1292 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1293 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1293 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1294 txt_drawf_small("vol", x + 5, y + 3, 4, COL_FG); 1294 // txt_drawf_small("vol", x + 5, y + 3, 4, COL_FG);
1295 1295
1296 switch (params->env_volume) { 1296 switch (params->env_volume) {
1297 case 0: { 1297 case 0: {
@@ -1354,7 +1354,7 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) {
1354 draw_line(x, y + 8, x, y + 19, COL_FG); 1354 draw_line(x, y + 8, x, y + 19, COL_FG);
1355 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1355 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1356 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1356 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1357 txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG); 1357 // txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG);
1358 1358
1359 char arr_up[2] = { 0x19, 0 }; 1359 char arr_up[2] = { 0x19, 0 };
1360 char arr_down[2] = { 0x18, 0 }; 1360 char arr_down[2] = { 0x18, 0 };
@@ -1377,7 +1377,7 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) {
1377 draw_line(x, y + 8, x, y + 19, COL_FG); 1377 draw_line(x, y + 8, x, y + 19, COL_FG);
1378 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1378 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1379 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1379 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1380 txt_drawf_small("time", x + 3, y + 3, 4, COL_FG); 1380 // txt_drawf_small("time", x + 3, y + 3, 4, COL_FG);
1381 1381
1382 switch (params->env_time) { 1382 switch (params->env_time) {
1383 case 0: { 1383 case 0: {
@@ -1417,7 +1417,7 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) {
1417 draw_line(x, y + 8, x, y + 19, COL_FG); 1417 draw_line(x, y + 8, x, y + 19, COL_FG);
1418 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1418 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1419 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1419 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1420 txt_drawf_small("num", x + 5, y + 3, 4, COL_FG); 1420 // txt_drawf_small("num", x + 5, y + 3, 4, COL_FG);
1421 1421
1422 switch (params->sweep_number) { 1422 switch (params->sweep_number) {
1423 case 0: { 1423 case 0: {
@@ -1456,7 +1456,7 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) {
1456 draw_line(x, y + 8, x, y + 19, COL_FG); 1456 draw_line(x, y + 8, x, y + 19, COL_FG);
1457 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1457 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1458 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1458 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1459 txt_drawf_small("time", x + 3, y + 3, 4, COL_FG); 1459 // txt_drawf_small("time", x + 3, y + 3, 4, COL_FG);
1460 1460
1461 switch (params->sweep_time) { 1461 switch (params->sweep_time) {
1462 case 0: { 1462 case 0: {
@@ -1495,7 +1495,7 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) {
1495 draw_line(x, y + 8, x, y + 19, COL_FG); 1495 draw_line(x, y + 8, x, y + 19, COL_FG);
1496 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1496 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1497 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1497 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1498 txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG); 1498 // txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG);
1499 1499
1500 char arr_up[2] = { 0x19, 0 }; 1500 char arr_up[2] = { 0x19, 0 };
1501 char arr_down[2] = { 0x18, 0 }; 1501 char arr_down[2] = { 0x18, 0 };
@@ -1513,10 +1513,10 @@ draw_parameters_square(ChannelSquareParams *params, bool sweep) {
1513 { 1513 {
1514 size_t x = PARAMS_START_X + x_offset; 1514 size_t x = PARAMS_START_X + x_offset;
1515 size_t y = PARAMS_START_Y + PARAMS_H - 45; 1515 size_t y = PARAMS_START_Y + PARAMS_H - 45;
1516 txt_drawf_small("shape", x + 1, y - 12, 4, COL_FG); 1516 // txt_drawf_small("shape", x + 1, y - 12, 4, COL_FG);
1517 txt_drawf_small("envelope", x + 54, y - 12, 4, COL_FG); 1517 // txt_drawf_small("envelope", x + 54, y - 12, 4, COL_FG);
1518 if (sweep) { 1518 if (sweep) {
1519 txt_drawf_small("sweep", x + 133, y - 12, 4, COL_FG); 1519 // txt_drawf_small("sweep", x + 133, y - 12, 4, COL_FG);
1520 } 1520 }
1521 } 1521 }
1522} 1522}
@@ -1538,7 +1538,7 @@ draw_parameters_noise(void) {
1538 draw_line(x, y + 8, x, y + 19, COL_FG); 1538 draw_line(x, y + 8, x, y + 19, COL_FG);
1539 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1539 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1540 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1540 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1541 txt_drawf_small("mode", x + 3, y + 3, 4, COL_FG); 1541 // txt_drawf_small("mode", x + 3, y + 3, 4, COL_FG);
1542 1542
1543 switch (params->bit_mode) { 1543 switch (params->bit_mode) {
1544 case 0: { 1544 case 0: {
@@ -1591,7 +1591,7 @@ draw_parameters_noise(void) {
1591 draw_line(x, y + 8, x, y + 19, COL_FG); 1591 draw_line(x, y + 8, x, y + 19, COL_FG);
1592 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1592 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1593 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1593 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1594 txt_drawf_small("vol", x + 5, y + 3, 4, COL_FG); 1594 // txt_drawf_small("vol", x + 5, y + 3, 4, COL_FG);
1595 1595
1596 switch (params->env_volume) { 1596 switch (params->env_volume) {
1597 case 0: { 1597 case 0: {
@@ -1654,7 +1654,7 @@ draw_parameters_noise(void) {
1654 draw_line(x, y + 8, x, y + 19, COL_FG); 1654 draw_line(x, y + 8, x, y + 19, COL_FG);
1655 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1655 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1656 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1656 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1657 txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG); 1657 // txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG);
1658 1658
1659 char arr_up[2] = { 0x19, 0 }; 1659 char arr_up[2] = { 0x19, 0 };
1660 char arr_down[2] = { 0x18, 0 }; 1660 char arr_down[2] = { 0x18, 0 };
@@ -1677,7 +1677,7 @@ draw_parameters_noise(void) {
1677 draw_line(x, y + 8, x, y + 19, COL_FG); 1677 draw_line(x, y + 8, x, y + 19, COL_FG);
1678 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); 1678 draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG);
1679 draw_line(x, y + 20, x + 24, y + 20, COL_FG); 1679 draw_line(x, y + 20, x + 24, y + 20, COL_FG);
1680 txt_drawf_small("time", x + 3, y + 3, 4, COL_FG); 1680 // txt_drawf_small("time", x + 3, y + 3, 4, COL_FG);
1681 1681
1682 switch (params->env_time) { 1682 switch (params->env_time) {
1683 case 0: { 1683 case 0: {
@@ -1712,7 +1712,7 @@ draw_parameters_noise(void) {
1712 { 1712 {
1713 size_t x = PARAMS_START_X + x_offset; 1713 size_t x = PARAMS_START_X + x_offset;
1714 size_t y = PARAMS_START_Y + PARAMS_H - 45; 1714 size_t y = PARAMS_START_Y + PARAMS_H - 45;
1715 txt_drawf_small("envelope", x + 54, y - 12, 4, COL_FG); 1715 // txt_drawf_small("envelope", x + 54, y - 12, 4, COL_FG);
1716 } 1716 }
1717} 1717}
1718 1718
diff --git a/src/text/font.h b/src/text/font.h
index daf0c24..e4b089c 100644
--- a/src/text/font.h
+++ b/src/text/font.h
@@ -128,3 +128,134 @@ static const u32 font[] = {
128 0x00000000, 0x00000000, 0x00000000, 0x00000000, 128 0x00000000, 0x00000000, 0x00000000, 0x00000000,
129 0x00000000, 0x00000000, 0x00000000, 0x00000000, 129 0x00000000, 0x00000000, 0x00000000, 0x00000000,
130}; 130};
131
132u32 font_icn[] = {
133 0x00000000, 0x00000000, 0x00240000, 0x00003c42,
134 0x00240000, 0x0000423c, 0x3e360000, 0x00081c3e,
135 0x3e1c0800, 0x00081c3e, 0x3e1c1c00, 0x1c083e36,
136 0x3e1c0800, 0x1c083e3e, 0x18000000, 0x00000018,
137 0x667e7e7e, 0x7e7e7e66, 0x24180000, 0x00001824,
138 0x5a667e7e, 0x7e7e665a, 0x3e1c0800, 0x1c221c08,
139 0x1c221c00, 0x083e0808, 0x28381800, 0x0c0c0808,
140 0x24243c00, 0x06363424, 0x08220800, 0x0822081c,
141 0x3c1c0c04, 0x00040c1c, 0x3c383020, 0x00203038,
142 0x083e1c08, 0x081c3e08, 0x14141400, 0x00140014,
143 0x2a2a3c00, 0x2828282c, 0x3c043800, 0x1e203c42,
144 0x00000000, 0x0000007e, 0x083e1c08, 0x3e081c3e,
145 0x3e1c0800, 0x00080808, 0x08080800, 0x00081c3e,
146 0x30100000, 0x0010307e, 0x0c080000, 0x00080c7e,
147 0x00000000, 0x3e020200, 0x36140000, 0x0014367f,
148 0x1c080800, 0x003e3e1c, 0x1c3e3e00, 0x0008081c,
149 0x00000000, 0x00000000, 0x08080800, 0x00080008,
150 0x14141400, 0x00000000, 0x3e140000, 0x00143e14,
151 0x021c0800, 0x081e201c, 0x16260000, 0x00323408,
152 0x08140800, 0x002c1234, 0x08080800, 0x00000000,
153 0x04080000, 0x00080404, 0x10080000, 0x00081010,
154 0x08140000, 0x0014083e, 0x08080000, 0x0008083e,
155 0x00000000, 0x04080c00, 0x00000000, 0x0000003e,
156 0x00000000, 0x000c0c00, 0x18302000, 0x0002060c,
157 0x221c0000, 0x001c222a, 0x0a0c0000, 0x003e0808,
158 0x201c0000, 0x003e021c, 0x221c0000, 0x001c2218,
159 0x12120000, 0x00103e12, 0x023e0000, 0x001e201e,
160 0x021c0000, 0x001c221e, 0x203e0000, 0x00040810,
161 0x221c0000, 0x001c221c, 0x221c0000, 0x001c203c,
162 0x0c0c0000, 0x000c0c00, 0x0c0c0000, 0x04080c00,
163 0x08100000, 0x00100804, 0x3e000000, 0x00003e00,
164 0x08040000, 0x00040810, 0x10221c00, 0x00080008,
165 0x2a1c0000, 0x001c023a, 0x221c0000, 0x00223e22,
166 0x221e0000, 0x001e221e, 0x221c0000, 0x001c2202,
167 0x221e0000, 0x001e2222, 0x023e0000, 0x003e021e,
168 0x023e0000, 0x0002021e, 0x021c0000, 0x001c2232,
169 0x22220000, 0x0022223e, 0x083e0000, 0x003e0808,
170 0x20200000, 0x001c2220, 0x12220000, 0x0022120e,
171 0x02020000, 0x003e0202, 0x36220000, 0x0022222a,
172 0x2a260000, 0x00222232, 0x221c0000, 0x001c2222,
173 0x221e0000, 0x00021e22, 0x221c0000, 0x002c1222,
174 0x221e0000, 0x00221e22, 0x021c0000, 0x001e201c,
175 0x083e0000, 0x00080808, 0x22220000, 0x001c2222,
176 0x22220000, 0x00081422, 0x2a220000, 0x00142a2a,
177 0x14220000, 0x00221408, 0x22220000, 0x00080814,
178 0x103e0000, 0x003e0408, 0x041c0000, 0x001c0404,
179 0x0c060200, 0x00203018, 0x101c0000, 0x001c1010,
180 0x22140800, 0x00000000, 0x00000000, 0x003e0000,
181 0x10080400, 0x00000000, 0x201c0000, 0x003c223c,
182 0x1e020200, 0x001e2222, 0x021c0000, 0x001c0202,
183 0x3c202000, 0x003c2222, 0x221c0000, 0x001c021e,
184 0x023c0000, 0x0002021e, 0x223c0000, 0x1c203c22,
185 0x1e020200, 0x00222222, 0x0c000800, 0x003e0808,
186 0x20002000, 0x1c222020, 0x0a120200, 0x00320a06,
187 0x08080c00, 0x003e0808, 0x2a160000, 0x002a2a2a,
188 0x320e0000, 0x00222222, 0x221c0000, 0x001c2222,
189 0x221e0000, 0x02021e22, 0x223c0000, 0x70203c22,
190 0x261a0000, 0x00020202, 0x021c0000, 0x001e201c,
191 0x043e0400, 0x00180404, 0x22220000, 0x001c2222,
192 0x22220000, 0x00081422, 0x22220000, 0x00142a2a,
193 0x14220000, 0x00221408, 0x22220000, 0x1c203c22,
194 0x103e0000, 0x003e0408, 0x041c0000, 0x001c0406,
195 0x08080000, 0x00080808, 0x101c0000, 0x001c1030,
196 0x1a2c0000, 0x00000000, 0x14080000, 0x003e2222,
197 0x00000000, 0x00000000, 0x08080000, 0x00080008,
198 0x14140000, 0x00000000, 0x3e140000, 0x00143e14,
199 0x041c0800, 0x081c101c, 0x10040000, 0x00100408,
200 0x14080000, 0x00381438, 0x08080000, 0x00000000,
201 0x04080000, 0x00080404, 0x10080000, 0x00081010,
202 0x08140000, 0x0014081c, 0x08000000, 0x0000081c,
203 0x00000000, 0x04080000, 0x00000000, 0x0000001c,
204 0x00000000, 0x00080000, 0x18100000, 0x00040c08,
205 0x141c0000, 0x001c1414, 0x0c080000, 0x001c0808,
206 0x101c0000, 0x001c041c, 0x101c0000, 0x001c1018,
207 0x14140000, 0x0010101c, 0x041c0000, 0x001c101c,
208 0x041c0000, 0x001c141c, 0x101c0000, 0x00040408,
209 0x141c0000, 0x001c141c, 0x141c0000, 0x0010101c,
210 0x08000000, 0x00080000, 0x08000000, 0x04080000,
211 0x08100000, 0x00100804, 0x1c000000, 0x00001c00,
212 0x08040000, 0x00040810, 0x101c0000, 0x00080008,
213 0x141c0000, 0x001c0414, 0x141c0000, 0x00141c14,
214 0x141c0000, 0x001c140c, 0x041c0000, 0x001c0404,
215 0x140c0000, 0x000c1414, 0x041c0000, 0x001c040c,
216 0x041c0000, 0x0004040c, 0x041c0000, 0x001c1414,
217 0x14140000, 0x0014141c, 0x081c0000, 0x001c0808,
218 0x10100000, 0x001c1410, 0x14140000, 0x0014140c,
219 0x04040000, 0x001c0404, 0x1c140000, 0x00141414,
220 0x140c0000, 0x00141414, 0x140c0000, 0x001c1414,
221 0x141c0000, 0x0004041c, 0x141c0000, 0x00101c14,
222 0x141c0000, 0x0014140c, 0x04180000, 0x000c101c,
223 0x081c0000, 0x00080808, 0x14140000, 0x001c1414,
224 0x14140000, 0x00181414, 0x14140000, 0x00141c14,
225 0x14140000, 0x00141408, 0x14140000, 0x0008081c,
226 0x101c0000, 0x001c0408, 0x041c0000, 0x001c0404,
227 0x0c040000, 0x00101808, 0x101c0000, 0x001c1010,
228 0x14080000, 0x00000000, 0x00000000, 0x001c0000,
229 0x00000000, 0x00000000, 0x00000000, 0x00000000,
230 0x00000000, 0x00000000, 0x00000000, 0x00000000,
231 0x00000000, 0x00000000, 0x00000000, 0x00000000,
232 0x00000000, 0x00000000, 0x00000000, 0x00000000,
233 0x00000000, 0x00000000, 0x00000000, 0x00000000,
234 0x00000000, 0x00000000, 0x00000000, 0x00000000,
235 0x00000000, 0x00000000, 0x00000000, 0x00000000,
236 0x00000000, 0x00000000, 0x00000000, 0x00000000,
237 0x3232323e, 0x003e3232, 0x18181818, 0x00181818,
238 0x3e30303e, 0x003e0606, 0x3c30303e, 0x003e3030,
239 0x32323232, 0x0030303e, 0x3e06063e, 0x003e3030,
240 0x3e06063e, 0x003e2626, 0x1830303e, 0x0006060c,
241 0x3e32323e, 0x003e3232, 0x3e32323e, 0x003e3030,
242 0x00000000, 0x00060600, 0x18362600, 0x0032360c,
243 0x32323e1c, 0x0032323e, 0x1e26261e, 0x001e2626,
244 0x0606063c, 0x003c0606, 0x3232321e, 0x001e3232,
245 0x1e06063e, 0x003e0606, 0x1e06063e, 0x00060606,
246 0x3606063c, 0x001c2626, 0x3e323232, 0x00323232,
247 0x0c0c0c1e, 0x001e0c0c, 0x30303030, 0x003e3232,
248 0x0e162626, 0x00262616, 0x06060606, 0x003e0606,
249 0x6a7e7662, 0x00626262, 0x323a3e36, 0x00323232,
250 0x3232321c, 0x001c3232, 0x3e32323e, 0x00020202,
251 0x3232321c, 0x002c1a3a, 0x1e26261e, 0x00262626,
252 0x1e06063c, 0x001e3030, 0x1818187e, 0x00181818,
253 0x32323232, 0x003e3232, 0x32323232, 0x00183432,
254 0x32323232, 0x00143a32, 0x0c323232, 0x00323232,
255 0x32323232, 0x001e203c, 0x1c30303e, 0x003e0606,
256 0x00000000, 0x00000000, 0x00000000, 0x00000000,
257 0x00000000, 0x00000000, 0x00000000, 0x00000000,
258 0x00000000, 0x00000000, 0x00000000, 0x00000000,
259 0x00000000, 0x00000000, 0x00000000, 0x00000000,
260 0x00000000, 0x00000000, 0x00000000, 0x00000000,
261};
diff --git a/src/text/text.h b/src/text/text.h
index 0bcf090..ab525d4 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;
@@ -76,18 +61,15 @@ txt_puts(char *msg) {
76static inline 61static inline
77void 62void
78txt_clear_line(void) { 63txt_clear_line(void) {
79 for (size_t i = 0; i < 30; ++i) { 64 dma_fill(text_engine.buffer, 0, sizeof(text_engine.buffer), 3);
80 int x = text_engine.cursor_x;
81 int y = text_engine.cursor_y;
82 text_engine.font_tilemap[x + 32 * y] = text_engine.font_map[0];
83 }
84 text_engine.cursor_x = 0; 65 text_engine.cursor_x = 0;
66 text_engine.cursor_y = 0;
85} 67}
86 68
87// Clears the screen on the tile text mode. 69// Clears the screen on the tile text mode.
88static inline 70static inline
89void 71void
90txt_clear_screen(void) { 72txt_clear(void) {
91 for (size_t j = 0; j < 20; ++j) { 73 for (size_t j = 0; j < 20; ++j) {
92 text_engine.cursor_y = j; 74 text_engine.cursor_y = j;
93 txt_clear_line(); 75 txt_clear_line();
@@ -104,30 +86,49 @@ txt_position(size_t tile_x, size_t tile_y) {
104 text_engine.cursor_y = tile_y; 86 text_engine.cursor_y = tile_y;
105} 87}
106 88
89static inline
90void
91txt_color(u8 clr) {
92 text_engine.color = clr;
93}
94
95static inline
96void
97txt_spacing(u8 spacing) {
98 text_engine.spacing = spacing;
99}
100
101// Renders the contents of the scrollback buffer to the screen.
102void
103txt_render(void) {
104 for (size_t y = 0; y < 20; y++) {
105 for (size_t x = 0; x < 30; x++) {
106 size_t pos = x + y * 30;
107 if (text_engine.buffer[pos] == 0) {
108 continue;
109 }
110 text_engine.drawc(
111 text_engine.buffer[pos],
112 x * text_engine.spacing,
113 y * text_engine.spacing,
114 text_engine.color);
115 }
116 }
117}
118
107// Draws a message where the first character's top-left corner begins at the 119// 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 120// given x and y position. The character spacing can be adjusted, but beware of
109// color merging issues. 121// color merging issues.
110static inline 122static inline
111void 123void
112txt_draws(char *msg, size_t x, size_t y, size_t spacing, u8 clr) { 124txt_draws(char *msg, size_t x, size_t y, u8 clr) {
125 size_t i = 0;
113 while (*msg) { 126 while (*msg) {
114 char c = *msg++; 127 char c = *msg++;
115 Tile *tile = FONT_DATA; 128 text_engine.drawc(c, x + i++ * text_engine.spacing, y, clr);
116 tile += c;
117 draw_tile(x, y, tile, clr, true);
118 x += spacing;
119 } 129 }
120} 130}
121 131
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, true);
128 x += spacing;
129}
130
131// Print text to the screen with formatting. 132// Print text to the screen with formatting.
132#define txt_printf(msg, ...) \ 133#define txt_printf(msg, ...) \
133 { \ 134 { \
@@ -138,31 +139,11 @@ txt_drawc(char c, size_t x, size_t y, size_t spacing, u8 clr) {
138 139
139// Draws text to the screen with formatting starting on the x and y position and 140// Draws text to the screen with formatting starting on the x and y position and
140// with custom character spacing. 141// with custom character spacing.
141#define txt_drawf(msg, x, y, s, c, ...) \ 142#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 { \ 143 { \
153 char buf[256] = {0}; \ 144 char buf[256] = {0}; \
154 posprintf(buf, msg, ##__VA_ARGS__); \ 145 posprintf(buf, msg, ##__VA_ARGS__); \
155 for (size_t i = 0; i < 256; i++) { \ 146 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 } 147 }
167 148
168#endif // TEXT_H 149#endif // TEXT_H