aboutsummaryrefslogtreecommitdiffstats
path: root/src/ppu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ppu.c')
-rw-r--r--src/ppu.c418
1 files changed, 418 insertions, 0 deletions
diff --git a/src/ppu.c b/src/ppu.c
new file mode 100644
index 0000000..f626604
--- /dev/null
+++ b/src/ppu.c
@@ -0,0 +1,418 @@
1#include "ppu.h"
2#include "bd-font.c"
3
4/*
5Copyright (c) 2021 Devine Lu Linvega
6Copyright (c) 2021 Andrew Alderwick
7Copyright (c) 2021 Adrian "asie" Siekierka
8Copyright (c) 2021 Bad Diode
9
10Permission to use, copy, modify, and distribute this software for any
11purpose with or without fee is hereby granted, provided that the above
12copyright notice and this permission notice appear in all copies.
13
14THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15WITH REGARD TO THIS SOFTWARE.
16*/
17
18#define FG_FRONT ((u32*)(MEM_VRAM))
19#define BG_FRONT ((u32*)(MEM_VRAM + KB(20)))
20#define FG_BACK ((u32*)(MEM_VRAM + KB(44)))
21#define BG_BACK ((u32*)(MEM_VRAM + KB(64)))
22#define TILE_MAP ((u32*)(MEM_VRAM + KB(40)))
23#define FONT_DATA ((u32*)(MEM_VRAM + KB(84)))
24
25// Keyboard.
26#define SPRITE_START_IDX 640
27
28static u32 unpack_icon_lut[256] = {
29 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100,
30 0x00000101, 0x00000110, 0x00000111, 0x00001000, 0x00001001,
31 0x00001010, 0x00001011, 0x00001100, 0x00001101, 0x00001110,
32 0x00001111, 0x00010000, 0x00010001, 0x00010010, 0x00010011,
33 0x00010100, 0x00010101, 0x00010110, 0x00010111, 0x00011000,
34 0x00011001, 0x00011010, 0x00011011, 0x00011100, 0x00011101,
35 0x00011110, 0x00011111, 0x00100000, 0x00100001, 0x00100010,
36 0x00100011, 0x00100100, 0x00100101, 0x00100110, 0x00100111,
37 0x00101000, 0x00101001, 0x00101010, 0x00101011, 0x00101100,
38 0x00101101, 0x00101110, 0x00101111, 0x00110000, 0x00110001,
39 0x00110010, 0x00110011, 0x00110100, 0x00110101, 0x00110110,
40 0x00110111, 0x00111000, 0x00111001, 0x00111010, 0x00111011,
41 0x00111100, 0x00111101, 0x00111110, 0x00111111, 0x01000000,
42 0x01000001, 0x01000010, 0x01000011, 0x01000100, 0x01000101,
43 0x01000110, 0x01000111, 0x01001000, 0x01001001, 0x01001010,
44 0x01001011, 0x01001100, 0x01001101, 0x01001110, 0x01001111,
45 0x01010000, 0x01010001, 0x01010010, 0x01010011, 0x01010100,
46 0x01010101, 0x01010110, 0x01010111, 0x01011000, 0x01011001,
47 0x01011010, 0x01011011, 0x01011100, 0x01011101, 0x01011110,
48 0x01011111, 0x01100000, 0x01100001, 0x01100010, 0x01100011,
49 0x01100100, 0x01100101, 0x01100110, 0x01100111, 0x01101000,
50 0x01101001, 0x01101010, 0x01101011, 0x01101100, 0x01101101,
51 0x01101110, 0x01101111, 0x01110000, 0x01110001, 0x01110010,
52 0x01110011, 0x01110100, 0x01110101, 0x01110110, 0x01110111,
53 0x01111000, 0x01111001, 0x01111010, 0x01111011, 0x01111100,
54 0x01111101, 0x01111110, 0x01111111, 0x10000000, 0x10000001,
55 0x10000010, 0x10000011, 0x10000100, 0x10000101, 0x10000110,
56 0x10000111, 0x10001000, 0x10001001, 0x10001010, 0x10001011,
57 0x10001100, 0x10001101, 0x10001110, 0x10001111, 0x10010000,
58 0x10010001, 0x10010010, 0x10010011, 0x10010100, 0x10010101,
59 0x10010110, 0x10010111, 0x10011000, 0x10011001, 0x10011010,
60 0x10011011, 0x10011100, 0x10011101, 0x10011110, 0x10011111,
61 0x10100000, 0x10100001, 0x10100010, 0x10100011, 0x10100100,
62 0x10100101, 0x10100110, 0x10100111, 0x10101000, 0x10101001,
63 0x10101010, 0x10101011, 0x10101100, 0x10101101, 0x10101110,
64 0x10101111, 0x10110000, 0x10110001, 0x10110010, 0x10110011,
65 0x10110100, 0x10110101, 0x10110110, 0x10110111, 0x10111000,
66 0x10111001, 0x10111010, 0x10111011, 0x10111100, 0x10111101,
67 0x10111110, 0x10111111, 0x11000000, 0x11000001, 0x11000010,
68 0x11000011, 0x11000100, 0x11000101, 0x11000110, 0x11000111,
69 0x11001000, 0x11001001, 0x11001010, 0x11001011, 0x11001100,
70 0x11001101, 0x11001110, 0x11001111, 0x11010000, 0x11010001,
71 0x11010010, 0x11010011, 0x11010100, 0x11010101, 0x11010110,
72 0x11010111, 0x11011000, 0x11011001, 0x11011010, 0x11011011,
73 0x11011100, 0x11011101, 0x11011110, 0x11011111, 0x11100000,
74 0x11100001, 0x11100010, 0x11100011, 0x11100100, 0x11100101,
75 0x11100110, 0x11100111, 0x11101000, 0x11101001, 0x11101010,
76 0x11101011, 0x11101100, 0x11101101, 0x11101110, 0x11101111,
77 0x11110000, 0x11110001, 0x11110010, 0x11110011, 0x11110100,
78 0x11110101, 0x11110110, 0x11110111, 0x11111000, 0x11111001,
79 0x11111010, 0x11111011, 0x11111100, 0x11111101, 0x11111110,
80 0x11111111
81};
82
83static u32 unpack_icon_lut_flipx[256] = {
84 0x00000000, 0x10000000, 0x01000000, 0x11000000, 0x00100000,
85 0x10100000, 0x01100000, 0x11100000, 0x00010000, 0x10010000,
86 0x01010000, 0x11010000, 0x00110000, 0x10110000, 0x01110000,
87 0x11110000, 0x00001000, 0x10001000, 0x01001000, 0x11001000,
88 0x00101000, 0x10101000, 0x01101000, 0x11101000, 0x00011000,
89 0x10011000, 0x01011000, 0x11011000, 0x00111000, 0x10111000,
90 0x01111000, 0x11111000, 0x00000100, 0x10000100, 0x01000100,
91 0x11000100, 0x00100100, 0x10100100, 0x01100100, 0x11100100,
92 0x00010100, 0x10010100, 0x01010100, 0x11010100, 0x00110100,
93 0x10110100, 0x01110100, 0x11110100, 0x00001100, 0x10001100,
94 0x01001100, 0x11001100, 0x00101100, 0x10101100, 0x01101100,
95 0x11101100, 0x00011100, 0x10011100, 0x01011100, 0x11011100,
96 0x00111100, 0x10111100, 0x01111100, 0x11111100, 0x00000010,
97 0x10000010, 0x01000010, 0x11000010, 0x00100010, 0x10100010,
98 0x01100010, 0x11100010, 0x00010010, 0x10010010, 0x01010010,
99 0x11010010, 0x00110010, 0x10110010, 0x01110010, 0x11110010,
100 0x00001010, 0x10001010, 0x01001010, 0x11001010, 0x00101010,
101 0x10101010, 0x01101010, 0x11101010, 0x00011010, 0x10011010,
102 0x01011010, 0x11011010, 0x00111010, 0x10111010, 0x01111010,
103 0x11111010, 0x00000110, 0x10000110, 0x01000110, 0x11000110,
104 0x00100110, 0x10100110, 0x01100110, 0x11100110, 0x00010110,
105 0x10010110, 0x01010110, 0x11010110, 0x00110110, 0x10110110,
106 0x01110110, 0x11110110, 0x00001110, 0x10001110, 0x01001110,
107 0x11001110, 0x00101110, 0x10101110, 0x01101110, 0x11101110,
108 0x00011110, 0x10011110, 0x01011110, 0x11011110, 0x00111110,
109 0x10111110, 0x01111110, 0x11111110, 0x00000001, 0x10000001,
110 0x01000001, 0x11000001, 0x00100001, 0x10100001, 0x01100001,
111 0x11100001, 0x00010001, 0x10010001, 0x01010001, 0x11010001,
112 0x00110001, 0x10110001, 0x01110001, 0x11110001, 0x00001001,
113 0x10001001, 0x01001001, 0x11001001, 0x00101001, 0x10101001,
114 0x01101001, 0x11101001, 0x00011001, 0x10011001, 0x01011001,
115 0x11011001, 0x00111001, 0x10111001, 0x01111001, 0x11111001,
116 0x00000101, 0x10000101, 0x01000101, 0x11000101, 0x00100101,
117 0x10100101, 0x01100101, 0x11100101, 0x00010101, 0x10010101,
118 0x01010101, 0x11010101, 0x00110101, 0x10110101, 0x01110101,
119 0x11110101, 0x00001101, 0x10001101, 0x01001101, 0x11001101,
120 0x00101101, 0x10101101, 0x01101101, 0x11101101, 0x00011101,
121 0x10011101, 0x01011101, 0x11011101, 0x00111101, 0x10111101,
122 0x01111101, 0x11111101, 0x00000011, 0x10000011, 0x01000011,
123 0x11000011, 0x00100011, 0x10100011, 0x01100011, 0x11100011,
124 0x00010011, 0x10010011, 0x01010011, 0x11010011, 0x00110011,
125 0x10110011, 0x01110011, 0x11110011, 0x00001011, 0x10001011,
126 0x01001011, 0x11001011, 0x00101011, 0x10101011, 0x01101011,
127 0x11101011, 0x00011011, 0x10011011, 0x01011011, 0x11011011,
128 0x00111011, 0x10111011, 0x01111011, 0x11111011, 0x00000111,
129 0x10000111, 0x01000111, 0x11000111, 0x00100111, 0x10100111,
130 0x01100111, 0x11100111, 0x00010111, 0x10010111, 0x01010111,
131 0x11010111, 0x00110111, 0x10110111, 0x01110111, 0x11110111,
132 0x00001111, 0x10001111, 0x01001111, 0x11001111, 0x00101111,
133 0x10101111, 0x01101111, 0x11101111, 0x00011111, 0x10011111,
134 0x01011111, 0x11011111, 0x00111111, 0x10111111, 0x01111111,
135 0x11111111
136};
137
138static u32 dirty_tiles[21] = {0};
139
140void
141putcolors(u8 *addr) {
142 int i;
143 for(i = 0; i < 4; ++i) {
144 u8
145 r = (*(addr + i / 2) >> (!(i % 2) << 2)) & 0x0f,
146 g = (*(addr + 2 + i / 2) >> (!(i % 2) << 2)) & 0x0f,
147 b = (*(addr + 4 + i / 2) >> (!(i % 2) << 2)) & 0x0f;
148 PAL_BUFFER_BG[i] = rgb15(
149 (r << 1) | (r >> 3),
150 (g << 1) | (g >> 3),
151 (b << 1) | (b >> 3));
152 for (size_t j = 0; j < 16; ++j) {
153 PAL_BUFFER_SPRITES[i * 16 + j] = rgb15(
154 (r << 1) | (r >> 3),
155 (g << 1) | (g >> 3),
156 (b << 1) | (b >> 3));
157 }
158 }
159}
160
161IWRAM_CODE
162void
163putpixel(u32 *layer, u16 x, u16 y, u8 color) {
164 if (x >= SCREEN_WIDTH || y >= SCREEN_HEIGHT) return;
165 size_t tile_x = x / 8;
166 size_t tile_y = y / 8;
167 size_t start_col = x % 8;
168 size_t start_row = y % 8;
169 size_t pos = (start_row + ((tile_x + tile_y * 32) * 8));
170 size_t shift = start_col * 4;
171 layer[pos] = (layer[pos] & (~(0xF << shift))) | (color << shift);
172 dirty_tiles[tile_y] |= 1 << tile_x;
173}
174
175IWRAM_CODE
176void
177puticn(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color, u8 flipx, u8 flipy) {
178 u8 sprline;
179 u16 v;
180 u32 dirtyflag = (1 << (x >> 3)) | (1 << ((x + 7) >> 3));
181
182 u32 layerpos = ((y & 7) + (((x >> 3) + (y >> 3) * 32) * 8));
183 u32 *layerptr = &layer[layerpos];
184 u32 shift = (x & 7) << 2;
185 u32 *lut_expand = flipx ? unpack_icon_lut : unpack_icon_lut_flipx;
186
187 if (flipy) flipy = 7;
188
189 if (x >= SCREEN_WIDTH || y >= SCREEN_HEIGHT) return;
190
191 if (color != 0x05 && color != 0x0a && color != 0x0f) {
192 u64 mask = ~((u64)0xFFFFFFFF << shift);
193
194 for (v = 0; v < 8; v++, layerptr++) {
195 if ((y + v) >= SCREEN_HEIGHT) break;
196
197 sprline = sprite[v ^ flipy];
198 u64 data = (u64)(lut_expand[sprline] * (color & 3)) << shift;
199 data |= (u64)(lut_expand[sprline ^ 0xFF] * (color >> 2)) << shift;
200
201 layerptr[0] = (layerptr[0] & mask) | data;
202 layerptr[8] = (layerptr[8] & (mask >> 32)) | (data >> 32);
203
204 if (((y + v) & 7) == 7) layerptr += (32 - 1) * 8;
205 }
206 } else {
207 for (v = 0; v < 8; v++, layerptr++) {
208 if ((y + v) >= SCREEN_HEIGHT) break;
209
210 sprline = sprite[v ^ flipy];
211 u64 mask = ~((u64)(lut_expand[sprline] * 0xF) << shift);
212 u64 data = (u64)(lut_expand[sprline] * (color & 3)) << shift;
213
214 layerptr[0] = (layerptr[0] & mask) | data;
215 layerptr[8] = (layerptr[8] & (mask >> 32)) | (data >> 32);
216
217 if (((y + v) & 7) == 7) layerptr += (32 - 1) * 8;
218 }
219 }
220
221 dirty_tiles[y >> 3] |= dirtyflag;
222 dirty_tiles[(y + 7) >> 3] |= dirtyflag;
223}
224
225IWRAM_CODE
226void
227putfontchar(u32 *layer, u16 tile_x, u16 tile_y, u8 ch, u8 color) {
228 u32 pos = (tile_x + tile_y * 32) * 8;
229 u32 *tile_data = &layer[pos];
230 u32 *font_data = &FONT_DATA[8 * ch];
231 for (size_t i = 0; i < 8; ++i) {
232 tile_data[i] = font_data[i] * color;
233 }
234 dirty_tiles[tile_y] |= 1 << tile_x;
235}
236
237IWRAM_CODE
238void
239putchr(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color,
240 u8 flipx, u8 flipy) {
241 u8 sprline1, sprline2;
242 u16 v;
243 u32 dirtyflag = (1 << (x >> 3)) | (1 << ((x + 7) >> 3));
244
245 u32 layerpos = ((y & 7) + (((x >> 3) + (y >> 3) * 32) * 8));
246 u32 *layerptr = &layer[layerpos];
247 u32 shift = (x & 7) << 2;
248 u32 *lut_expand = flipx ? unpack_icon_lut : unpack_icon_lut_flipx;
249
250 if (flipy) flipy = 7;
251
252 if (x >= SCREEN_WIDTH || y >= SCREEN_HEIGHT) return;
253
254 u64 mask = ~((u64)0xFFFFFFFF << shift);
255 u32 colconst = (color >> 2) * 0x11111111;
256
257 for (v = 0; v < 8; v++, layerptr++) {
258 if ((y + v) >= SCREEN_HEIGHT) break;
259
260 sprline1 = sprite[v ^ flipy];
261 sprline2 = sprite[(v ^ flipy) | 8];
262
263 u32 data32 =
264 (lut_expand[sprline1] * (color & 3))
265 + (lut_expand[sprline2] * ((color & 1) << 1))
266 + colconst;
267 u64 data = ((u64) (data32 & 0x33333333)) << shift;
268
269 layerptr[0] = (layerptr[0] & mask) | data;
270 layerptr[8] = (layerptr[8] & (mask >> 32)) | (data >> 32);
271
272 if (((y + v) & 7) == 7) layerptr += (32 - 1) * 8;
273 }
274
275 dirty_tiles[y >> 3] |= dirtyflag;
276 dirty_tiles[(y + 7) >> 3] |= dirtyflag;
277}
278
279IWRAM_CODE
280void
281flipbuf(Ppu *p) {
282 Tile *mem_fg = FG_FRONT;
283 Tile *mem_bg = BG_FRONT;
284 for (size_t j = 0; j < 20; ++j) {
285 if (dirty_tiles[j] == 0) {
286 continue;
287 }
288
289 size_t k = 1;
290 for (size_t i = 0; i < 30; ++i, k <<= 1) {
291 if (dirty_tiles[j] & k) {
292 Tile *tile_fg = p->fg;
293 Tile *tile_bg = p->bg;
294 mem_fg[i + j * 32] = tile_fg[i + j * 32];
295 mem_bg[i + j * 32] = tile_bg[i + j * 32];
296 }
297 }
298 dirty_tiles[j] = 0;
299 }
300}
301
302typedef struct KeyboardChar {
303 int x;
304 int y;
305 u8 symbol;
306} KeyboardChar;
307
308static u8 cursor_position = 0;
309
310#define KEYBOARD_ROW_SIZE 12
311#define KEYBOARD_START_TILE_X (30 / 2 - KEYBOARD_ROW_SIZE / 2)
312#define KEYBOARD_START_TILE_Y (20 / 2 - 3)
313
314KeyboardChar keyboard[] = {
315 {0, 0, '!'}, {0, 0, '?'}, {0, 0, '@'}, {0, 0, '#'}, {0, 0, '$'}, {0, 0, '%'}, {0, 0, '^'}, {0, 0, '&'}, {0, 0, '*'}, {0, 0, '"'}, {0, 0, '\''}, {0, 0, 0x7f},
316 {0, 0, '('}, {0, 0, ')'}, {0, 0, '['}, {0, 0, ']'}, {0, 0, '{'}, {0, 0, '}'}, {0, 0, '<'}, {0, 0, '>'}, {0, 0, '+'}, {0, 0, '-'}, {0, 0, '='}, {0, 0, 0x14},
317 {0, 0, '0'}, {0, 0, '1'}, {0, 0, '2'}, {0, 0, '3'}, {0, 0, '4'}, {0, 0, '5'}, {0, 0, '6'}, {0, 0, '7'}, {0, 0, '8'}, {0, 0, '9'}, {0, 0, '~'}, {0, 0, 0x18},
318 {0, 0, 'a'}, {0, 0, 'b'}, {0, 0, 'c'}, {0, 0, 'd'}, {0, 0, 'e'}, {0, 0, 'f'}, {0, 0, 'g'}, {0, 0, 'h'}, {0, 0, 'i'}, {0, 0, 'j'}, {0, 0, '/'}, {0, 0, 0x19},
319 {0, 0, 'k'}, {0, 0, 'l'}, {0, 0, 'm'}, {0, 0, 'n'}, {0, 0, 'o'}, {0, 0, 'p'}, {0, 0, 'q'}, {0, 0, 'r'}, {0, 0, 's'}, {0, 0, 't'}, {0, 0, '\\'}, {0, 0, 0x1b},
320 {0, 0, 'u'}, {0, 0, 'v'}, {0, 0, 'w'}, {0, 0, 'x'}, {0, 0, 'y'}, {0, 0, 'z'}, {0, 0, ','}, {0, 0, '.'}, {0, 0, ';'}, {0, 0, ':'}, {0, 0, '_'}, {0, 0, 0x1a},
321};
322
323void
324toggle_keyboard(void) {
325 for (size_t i = 0; i < LEN(keyboard); ++i) {
326 OBJ_ATTR_0(i) ^= OBJ_HIDDEN;
327 }
328 OBJ_ATTR_0(127) ^= OBJ_HIDDEN;
329}
330
331void
332update_cursor(u8 pos) {
333 cursor_position = CLAMP(pos, 0, LEN(keyboard) - 1);
334 OBJ_ATTR_0(127) = (OBJ_ATTR_0(127) & ~0xFF)
335 | OBJ_Y_COORD(keyboard[cursor_position].y);
336 OBJ_ATTR_1(127) = (OBJ_ATTR_0(127) & ~0x1FF)
337 | OBJ_X_COORD(keyboard[cursor_position].x);
338}
339
340int
341initppu(Ppu *p, u8 hor, u8 ver, u8 pad) {
342 p->hor = hor;
343 p->ver = ver;
344 p->pad = pad;
345 p->width = (8 * p->hor + p->pad * 2);
346 p->height = (8 * p->ver + p->pad * 2);
347
348 // Initialize display mode and bg palette.
349 DISP_CTRL = DISP_MODE_0 | DISP_BG_0 | DISP_BG_1 | DISP_OBJ;
350
351 // Initialize backgrounds.
352 u8 cb_fg = 0;
353 u8 cb_bg = 1;
354 u8 sb_fg = 20;
355 u8 sb_bg = 21;
356 BG_CTRL(0) = BG_CHARBLOCK(cb_fg) | BG_SCREENBLOCK(sb_fg) | BG_PRIORITY(1);
357 BG_CTRL(1) = BG_CHARBLOCK(cb_bg) | BG_SCREENBLOCK(sb_bg) | BG_PRIORITY(2);
358
359 // Clear front buffer.
360 p->fg = FG_FRONT;
361 p->bg = BG_FRONT;
362
363 // Use DMA to clear VRAM.
364 u32 fill = 0;
365 dma_fill(p->fg, fill, KB(20), 3);
366 dma_fill(p->bg, fill, KB(20), 3);
367
368 // Clear back buffer.
369 p->fg = FG_BACK;
370 p->bg = BG_BACK;
371 dma_fill(p->fg, fill, KB(20), 3);
372 dma_fill(p->bg, fill, KB(20), 3);
373
374 // Initialize default palette.
375 PAL_BUFFER_BG[0] = COLOR_BLACK;
376 PAL_BUFFER_BG[1] = COLOR_WHITE;
377 PAL_BUFFER_BG[2] = COLOR_RED;
378 PAL_BUFFER_BG[3] = COLOR_BLUE;
379 for (size_t i = 0; i < 16; ++i) {
380 PAL_BUFFER_SPRITES[i] = COLOR_BLACK;
381 PAL_BUFFER_SPRITES[1 * 16] = COLOR_WHITE;
382 PAL_BUFFER_SPRITES[2 * 16] = COLOR_RED;
383 PAL_BUFFER_SPRITES[3 * 16] = COLOR_BLUE;
384 }
385
386 // Initialize background memory map.
387 u16 *mem_map_fg = SCREENBLOCK_MEM[sb_fg];
388 u16 *mem_map_bg = SCREENBLOCK_MEM[sb_bg];
389 size_t k = 0;
390 for (size_t i = 0; i < 32 * 20; ++i, ++k) {
391 mem_map_fg[i] = k;
392 mem_map_bg[i] = k + 32 * 4;
393 }
394
395 // Load font data into VRAM.
396 unpack_tiles(&bd_font, FONT_DATA, 256);
397
398 // Initialize keyboard sprites.
399 int tile_x = KEYBOARD_START_TILE_X;
400 int tile_y = KEYBOARD_START_TILE_Y;
401 for (size_t i = 0; i < sizeof(keyboard) / sizeof(keyboard[0]); ++i) {
402 keyboard[i].x = tile_x * 8;
403 keyboard[i].y = tile_y * 8;
404 OBJ_ATTR_0(i) = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(keyboard[i].y) | OBJ_HIDDEN;
405 OBJ_ATTR_1(i) = OBJ_SIZE_SMALL | OBJ_X_COORD(keyboard[i].x);
406 OBJ_ATTR_2(i) = (SPRITE_START_IDX + keyboard[i].symbol) | OBJ_PAL_BANK(0);
407 tile_x++;
408 if (tile_x - KEYBOARD_START_TILE_X >= KEYBOARD_ROW_SIZE) {
409 tile_x = KEYBOARD_START_TILE_X;
410 tile_y++;
411 }
412 }
413 OBJ_ATTR_0(127) = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(keyboard[cursor_position].y) | OBJ_HIDDEN;
414 OBJ_ATTR_1(127) = OBJ_SIZE_SMALL | OBJ_X_COORD(keyboard[cursor_position].x);
415 OBJ_ATTR_2(127) = (SPRITE_START_IDX + 0xdb) | OBJ_PAL_BANK(3);
416
417 return 1;
418}