aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c527
1 files changed, 70 insertions, 457 deletions
diff --git a/src/main.c b/src/main.c
index 1acae1b..e688bb3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,474 +1,79 @@
1/* 1/*
2Copyright (c) 2021 Bad Diode 2 Copyright (c) 2021 Bad Diode
3 3
4Permission to use, copy, modify, and distribute this software for any 4 Permission to use, copy, modify, and distribute this software for any
5purpose with or without fee is hereby granted, provided that the above 5 purpose with or without fee is hereby granted, provided that the above
6copyright notice and this permission notice appear in all copies. 6 copyright notice and this permission notice appear in all copies.
7 7
8THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9WITH REGARD TO THIS SOFTWARE. 9 WITH REGARD TO THIS SOFTWARE.
10*/ 10*/
11 11
12#include <string.h> 12#include <string.h>
13#include <time.h> 13#include <time.h>
14 14
15#include "common.h" 15#include "common.h"
16#include "bitmap.h"
17#include "filesystem.c" 16#include "filesystem.c"
18 17
18#include "uxn-core.c"
19
19#include "rom.c" 20#include "rom.c"
20#include "uxn.c"
21#include "ppu.c" 21#include "ppu.c"
22#include "apu.c" 22#include "apu.c"
23#include "file.c" 23#include "file.c"
24#include "text.h" 24#include "text.h"
25 25
26// 26#include "config.c"
27// Config parameters. 27#include "profiling.c"
28// 28#include "input.c"
29 29#include "devices.c"
30#if !defined(TEXT_MODE) || TEXT_MODE == 0 30#include "debug.c"
31#define TEXT_LAYER ppu.fg
32#else
33#define TEXT_LAYER ppu.bg
34#endif
35
36#ifndef CONTROL_METHODS
37#define CONTROL_METHODS CONTROL_CONTROLLER,CONTROL_MOUSE,CONTROL_KEYBOARD
38#endif
39
40#ifdef PROF_ENABLE
41#if PROF_ENABLE == 0
42#define PROF(F,VAR) (profile_start(),(F),(VAR) = profile_stop())
43#elif PROF_ENABLE == 1
44#define PROF(F,VAR) (profile_start(),(F),(VAR) = MAX(profile_stop(), (VAR)))
45#endif
46#ifndef PROF_SHOW_X
47#define PROF_SHOW_X 0
48#endif
49#ifndef PROF_SHOW_Y
50#define PROF_SHOW_Y 0
51#endif
52#define PROF_SHOW() \
53 do { \
54 txt_position((PROF_SHOW_X), (PROF_SHOW_Y));\
55 txt_printf("INPUT: %lu ", input_cycles);\
56 txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+1);\
57 txt_printf("EVAL: %lu ", eval_cycles);\
58 txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+2);\
59 txt_printf("FLIP: %lu ", flip_cycles);\
60 txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+3);\
61 txt_printf("MIX: %lu ", mix_cycles);\
62 } while (0)
63#define PROF_INIT() \
64 u32 flip_cycles = 0;\
65 u32 eval_cycles = 0;\
66 u32 input_cycles = 0;\
67 u32 mix_cycles = 0;
68#else
69#define PROF(F,VAR) (F)
70#define PROF_SHOW()
71#define PROF_INIT()
72#endif
73
74static time_t seconds = 0;
75
76typedef enum {
77 CONTROL_CONTROLLER,
78 CONTROL_MOUSE,
79 CONTROL_KEYBOARD,
80} ControlMethod;
81
82const ControlMethod ctrl_methods[] = {
83 CONTROL_METHODS
84};
85static ControlMethod ctrl_idx = 0;
86
87#define MOUSE_DELTA 1
88typedef struct Mouse {
89 int x;
90 int y;
91} Mouse;
92
93static Ppu ppu;
94static Device *devscreen;
95static Device *devctrl;
96static Device *devmouse;
97static Device *devaudio;
98
99static Mouse mouse = {SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2};
100
101void
102nil_talk(Device *d, u8 b0, u8 w) {
103 (void)d;
104 (void)b0;
105 (void)w;
106}
107
108void
109console_talk(Device *d, u8 b0, u8 w) {
110 char stmp[2];
111 if(!w) {
112 return;
113 }
114 switch(b0) {
115 case 0x8: stmp[0] = d->dat[0x8]; stmp[1] = 0; txt_printf(stmp); break;
116 case 0x9: txt_printf("0x%02x", d->dat[0x9]); break;
117 case 0xb: txt_printf("0x%04x", mempeek16(d->dat, 0xa)); break;
118 case 0xd: txt_printf("%s", &d->mem[mempeek16(d->dat, 0xc)]); break;
119 }
120}
121
122void
123system_talk(Device *d, u8 b0, u8 w) {
124 if(!w) {
125 d->dat[0x2] = d->u->wst.ptr;
126 d->dat[0x3] = d->u->rst.ptr;
127 } else {
128 putcolors(&d->dat[0x8]);
129 }
130 (void)b0;
131}
132
133IWRAM_CODE
134void
135screen_talk(Device *d, u8 b0, u8 w) {
136 if (w) {
137 switch (b0) {
138 case 0x1: {
139 d->vector = mempeek16(d->dat, 0x0);
140 } break;
141 case 0xe: {
142 u16 x, y;
143 u8 layer = d->dat[0xe] & 0x40;
144 DEVPEEK16(x, 0x8);
145 DEVPEEK16(y, 0xa);
146 ppu_pixel(layer ? ppu.fg : ppu.bg, x, y, d->dat[0xe] & 0x3);
147 if(d->dat[0x6] & 0x01) DEVPOKE16(0x8, x + 1); /* auto x+1 */
148 if(d->dat[0x6] & 0x02) DEVPOKE16(0xa, y + 1); /* auto y+1 */
149 } break;
150 case 0xf: {
151 u16 x, y, dx, dy, addr;
152 u8 twobpp = !!(d->dat[0xf] & 0x80);
153 DEVPEEK16(x, 0x8);
154 DEVPEEK16(y, 0xa);
155 DEVPEEK16(addr, 0xc);
156 u8 n = d->dat[0x6] >> 4;
157 dx = (d->dat[0x6] & 0x01) << 3;
158 dy = (d->dat[0x6] & 0x02) << 2;
159 if(addr > 0x10000 - ((n + 1) << (3 + twobpp))) {
160 return;
161 }
162 u8 *layer = (d->dat[0xf] & 0x40) ? ppu.fg : ppu.bg;
163 u8 color = d->dat[0xf] & 0xf;
164 u8 flipx = d->dat[0xf] & 0x10;
165 u8 flipy = d->dat[0xf] & 0x20;
166 for(size_t i = 0; i <= n; i++) {
167 u8 *sprite = &d->mem[addr];
168 if (twobpp) {
169 ppu_2bpp(layer, x + dy * i, y + dx * i, sprite, color, flipx, flipy);
170 } else {
171 ppu_1bpp(layer, x + dy * i, y + dx * i, sprite, color, flipx, flipy);
172 }
173 addr += (d->dat[0x6] & 0x04) << (1 + twobpp);
174 }
175 DEVPOKE16(0xc, addr); /* auto addr+length */
176 DEVPOKE16(0x8, x + dx); /* auto x+8 */
177 DEVPOKE16(0xa, y + dy); /* auto y+8 */
178 } break;
179 default: break;
180 }
181 }
182}
183
184static void
185audio_talk(Device *d, u8 b0, u8 w) {
186 AudioChannel *c = &channels[d - devaudio];
187 if(!w) {
188 if(b0 == 0x2) {
189 mempoke16(d->dat, 0x2, c->pos);
190 c->pos <<= 12; // fixed point.
191 } else if(b0 == 0x4) {
192 // d->dat[0x4] = apu_get_vu(c);
193 }
194 } else if(b0 == 0xf) {
195 u16 length = mempeek16(d->dat, 0xa);
196 u8 *data = &d->mem[mempeek16(d->dat, 0xc)];
197 u8 pitch = d->dat[0xf] & 0x7f;
198 u16 adsr = mempeek16(d->dat, 0x8);
199 u32 vol = MAX(d->dat[0xe] >> 4, d->dat[0xe] & 0xf) * 4 / 3;
200 bool loop = !(d->dat[0xf] & 0x80);
201 update_channel(c, data, length, pitch, adsr, vol, loop);
202 }
203}
204
205void
206datetime_talk(Device *d, u8 b0, u8 w) {
207 (void)b0;
208 (void)w;
209 struct tm *t = gmtime(&seconds);
210 t->tm_year += 1900;
211 DEVPOKE16(0x0, t->tm_year);
212 d->dat[0x2] = t->tm_mon;
213 d->dat[0x3] = t->tm_mday;
214 d->dat[0x4] = t->tm_hour;
215 d->dat[0x5] = t->tm_min;
216 d->dat[0x6] = t->tm_sec;
217 d->dat[0x7] = t->tm_wday;
218 DEVPOKE16(0x08, t->tm_yday);
219 d->dat[0xa] = t->tm_isdst;
220}
221
222void
223file_talk(Device *d, u8 b0, u8 w) {
224 if (w) {
225 u16 a, b, res;
226 switch(b0) {
227 case 0x5: {
228 DEVPEEK16(a, 0x4);
229 DEVPEEK16(b, 0xa);
230 if(b > 0x10000 - a) {
231 b = 0x10000 - a;
232 }
233 res = file_stat(&d->mem[a], b);
234 DEVPOKE16(0x2, res);
235 } break;
236 case 0x6: {
237 // res = file_delete();
238 // DEVPOKE16(0x2, res);
239 } break;
240 case 0x9: {
241 DEVPEEK16(a, 0x8);
242 res = file_init(&d->mem[a]);
243 DEVPOKE16(0x2, res);
244 } break;
245 case 0xd: {
246 DEVPEEK16(a, 0xc);
247 DEVPEEK16(b, 0xa);
248 if(b > 0x10000 - a) {
249 b = 0x10000 - a;
250 }
251 res = file_read(&d->mem[a], b);
252 DEVPOKE16(0x2, res);
253 } break;
254 case 0xf: {
255 DEVPEEK16(a, 0xe);
256 DEVPEEK16(b, 0xa);
257 if(b > 0x10000 - a) {
258 b = 0x10000 - a;
259 }
260 res = file_write(&d->mem[a], b, d->dat[0x7]);
261 DEVPOKE16(0x2, res);
262 } break;
263 }
264 }
265}
266
267void
268init_uxn(Uxn *u) {
269 // Initialize PPU.
270 initppu(&ppu, 30, 20, 0);
271
272 // Enable sound.
273 init_sound();
274
275 // Copy rom to VM.
276 memcpy(u->ram.dat + PAGE_PROGRAM, uxn_rom, sizeof(uxn_rom));
277
278 // Prepare devices.
279 uxn_port(u, 0x0, "system", system_talk);
280 uxn_port(u, 0x1, "console", console_talk);
281 devscreen = uxn_port(u, 0x2, "screen", screen_talk);
282 devaudio = uxn_port(u, 0x3, "audio0", audio_talk);
283 uxn_port(u, 0x4, "audio1", audio_talk);
284 uxn_port(u, 0x5, "audio2", audio_talk);
285 uxn_port(u, 0x6, "audio3", audio_talk);
286 uxn_port(u, 0x7, "---", nil_talk);
287 devctrl = uxn_port(u, 0x8, "controller", nil_talk);
288 devmouse = uxn_port(u, 0x9, "mouse", nil_talk);
289 uxn_port(u, 0xa, "file1", file_talk);
290 uxn_port(u, 0xb, "file2", file_talk); // TODO: support second file device
291 uxn_port(u, 0xc, "datetime", datetime_talk);
292 uxn_port(u, 0xd, "---", nil_talk);
293 uxn_port(u, 0xe, "---", nil_talk);
294 uxn_port(u, 0xf, "---", nil_talk);
295 mempoke16(devscreen->dat, 2, ppu.hor * 8);
296 mempoke16(devscreen->dat, 4, ppu.ver * 8);
297}
298 31
299IWRAM_CODE
300void 32void
301handle_input(Uxn *u) { 33init_uxn() {
302 poll_keys(); 34 // Initialize uxn.
303 if (key_tap(KEY_SELECT)) { 35 u32 fill = 0;
304 // Reset control variables on method switch. 36 dma_fill(uxn_ram, fill, sizeof(uxn_ram), 3);
305 switch (ctrl_methods[ctrl_idx]) { 37 uxn_rom = _binary_build_uxn_rom_start;
306 case CONTROL_CONTROLLER: { 38 uxn_rom_size = (size_t)_binary_build_uxn_rom_size;
307 devctrl->dat[2] = 0; 39 size_t size = MIN(0x10000 - PAGE_PROGRAM, uxn_rom_size);
308 uxn_eval(u, mempeek16(devctrl->dat, 0)); 40 memcpy(uxn_ram + PAGE_PROGRAM, uxn_rom, size);
309 devctrl->dat[3] = 0; 41
310 } break; 42 // Initialize stack and device memory.
311 case CONTROL_MOUSE: { 43 for (size_t i = 0; i < 256; i++) {
312 devmouse->dat[6] = 0; 44 wst[i] = 0;
313 devmouse->dat[7] = 0; 45 rst[i] = 0;
314 mempoke16(devmouse->dat, 0x2, -10); 46 device_data[i] = 0;
315 mempoke16(devmouse->dat, 0x4, -10);
316 uxn_eval(u, mempeek16(devmouse->dat, 0));
317 } break;
318 case CONTROL_KEYBOARD: {
319 toggle_keyboard();
320 } break;
321 }
322
323 // Update ctrl_idx.
324 ctrl_idx = (ctrl_idx + 1 > (int)LEN(ctrl_methods) - 1) ? 0 : ctrl_idx + 1;
325
326 // Initialize controller variables here.
327 if (ctrl_methods[ctrl_idx] == CONTROL_KEYBOARD) {
328 toggle_keyboard();
329 }
330 } 47 }
331 48
332 if (ctrl_methods[ctrl_idx] == CONTROL_CONTROLLER) { 49 // Setup deo/dei maps.
333 // TODO: We don't need ifs if we use KEY_INPUTS directly and mayvbe just 50 for (size_t i = 0; i < 16; i++) {
334 // swap some things if needed. 51 deo_map[i] = (uintptr_t) deo_stub;
335 u8 *flag = &devctrl->dat[2]; 52 dei_map[i] = (uintptr_t) dei_stub;
336 if (key_pressed(KEY_A)) {
337 *flag |= 0x01;
338 } else {
339 *flag &= ~0x01;
340 }
341 if (key_pressed(KEY_B)) {
342 *flag |= 0x02;
343 } else {
344 *flag &= ~0x02;
345 }
346 if (key_pressed(KEY_L)) {
347 *flag |= 0x04;
348 } else {
349 *flag &= ~0x04;
350 }
351 if (key_pressed(KEY_R)) {
352 *flag |= 0x08;
353 } else {
354 *flag &= ~0x08;
355 }
356 if (key_pressed(KEY_UP)) {
357 *flag |= 0x10;
358 } else {
359 *flag &= ~0x10;
360 }
361 if (key_pressed(KEY_DOWN)) {
362 *flag |= 0x20;
363 } else {
364 *flag &= ~0x20;
365 }
366 if (key_pressed(KEY_LEFT)) {
367 *flag |= 0x40;
368 } else {
369 *flag &= ~0x40;
370 }
371 if (key_pressed(KEY_RIGHT)) {
372 *flag |= 0x80;
373 } else {
374 *flag &= ~0x80;
375 }
376
377 uxn_eval(u, mempeek16(devctrl->dat, 0));
378 devctrl->dat[3] = 0;
379 } else if (ctrl_methods[ctrl_idx] == CONTROL_MOUSE) {
380 // Detect "mouse key press".
381 u8 flag = devmouse->dat[6];
382 if (key_tap(KEY_B)) {
383 flag |= 0x01;
384 } else if (key_released(KEY_B)) {
385 flag &= ~0x01;
386 }
387 if (key_tap(KEY_A)) {
388 flag |= 0x10;
389 } else if (key_released(KEY_A)) {
390 flag &= ~0x10;
391 }
392
393 // Handle chording.
394 devmouse->dat[6] = flag;
395 if(flag == 0x10 && (devmouse->dat[6] & 0x01)) {
396 devmouse->dat[7] = 0x01;
397 }
398 if(flag == 0x01 && (devmouse->dat[6] & 0x10)) {
399 devmouse->dat[7] = 0x10;
400 }
401
402 // Detect mouse movement.
403 if (key_pressed(KEY_UP)) {
404 mouse.y = CLAMP(mouse.y - MOUSE_DELTA, 0, SCREEN_HEIGHT - 8);
405 } else if (key_pressed(KEY_DOWN)) {
406 mouse.y = CLAMP(mouse.y + MOUSE_DELTA, 0, SCREEN_HEIGHT - 8);
407 }
408 if (key_pressed(KEY_LEFT)) {
409 mouse.x = CLAMP(mouse.x - MOUSE_DELTA, 0, SCREEN_WIDTH - 8);
410 } else if (key_pressed(KEY_RIGHT)) {
411 mouse.x = CLAMP(mouse.x + MOUSE_DELTA, 0, SCREEN_WIDTH - 8);
412 }
413
414 // Eval mouse.
415 mempoke16(devmouse->dat, 0x2, mouse.x);
416 mempoke16(devmouse->dat, 0x4, mouse.y);
417 uxn_eval(u, mempeek16(devmouse->dat, 0));
418 } else if (ctrl_methods[ctrl_idx] == CONTROL_KEYBOARD) {
419 if (key_tap(KEY_LEFT)) {
420 update_cursor(cursor_position - 1);
421 } else if (key_tap(KEY_RIGHT)) {
422 update_cursor(cursor_position + 1);
423 }
424 if (key_tap(KEY_UP) && cursor_position >= KEYBOARD_ROW_SIZE) {
425 update_cursor(cursor_position - KEYBOARD_ROW_SIZE);
426 } else if (key_tap(KEY_DOWN)
427 && cursor_position < LEN(keyboard) - KEYBOARD_ROW_SIZE) {
428 update_cursor(cursor_position + KEYBOARD_ROW_SIZE);
429 }
430 if (key_tap(KEY_B)) {
431 u8 symbol = keyboard[cursor_position].symbol;
432 switch (symbol) {
433 case 0x7f: {
434 // Backspace.
435 devctrl->dat[3] = 0x08;
436 } break;
437 case 0x14: {
438 // New line.
439 devctrl->dat[3] = 0x0d;
440 } break;
441 case 0x18: {
442 // Arrow up.
443 devctrl->dat[2] = 0x10;
444 } break;
445 case 0x19: {
446 // Arrow down.
447 devctrl->dat[2] = 0x20;
448 } break;
449 case 0x1b: {
450 // Arrow left.
451 devctrl->dat[2] = 0x40;
452 } break;
453 case 0x1a: {
454 // Arrow right.
455 devctrl->dat[2] = 0x80;
456 } break;
457 default: {
458 devctrl->dat[3] = symbol;
459 } break;
460 }
461 uxn_eval(u, mempeek16(devctrl->dat, 0));
462 devctrl->dat[3] = 0;
463 }
464 } 53 }
54 deo_map[0x0] = (uintptr_t) deo_system;
55 dei_map[0x0] = (uintptr_t) dei_system;
56 deo_map[0x1] = (uintptr_t) deo_console;
57 deo_map[0x2] = (uintptr_t) deo_screen;
58 dei_map[0x2] = (uintptr_t) dei_screen;
59 deo_map[0x3] = (uintptr_t) deo_audio;
60 dei_map[0x3] = (uintptr_t) dei_audio;
61 deo_map[0x4] = (uintptr_t) deo_audio;
62 dei_map[0x4] = (uintptr_t) dei_audio;
63 deo_map[0x5] = (uintptr_t) deo_audio;
64 dei_map[0x5] = (uintptr_t) dei_audio;
65 deo_map[0x6] = (uintptr_t) deo_audio;
66 dei_map[0x6] = (uintptr_t) dei_audio;
67 deo_map[0xa] = (uintptr_t) deo_file;
68 dei_map[0xa] = (uintptr_t) dei_file;
69 deo_map[0xb] = (uintptr_t) deo_file;
70 dei_map[0xb] = (uintptr_t) dei_file;
71 dei_map[0xc] = (uintptr_t) dei_datetime;
72 dei_map[0x9] = (uintptr_t) dei_mouse;
465} 73}
466 74
467static Uxn u; 75int
468EWRAM_BSS 76main(void) {
469static u8 umem[KB(65)];
470
471int main(void) {
472 // Adjust system wait times. 77 // Adjust system wait times.
473 SYSTEM_WAIT = SYSTEM_WAIT_CARTRIDGE; 78 SYSTEM_WAIT = SYSTEM_WAIT_CARTRIDGE;
474 79
@@ -479,34 +84,42 @@ int main(void) {
479 irq_init(); 84 irq_init();
480 irs_set(IRQ_VBLANK, sound_vsync); 85 irs_set(IRQ_VBLANK, sound_vsync);
481 86
482 // Initialize VM. 87 // Initialize PPU.
483 dma_fill(&u, 0, sizeof(u), 3); 88 video_init();
484 u.ram.dat = umem;
485 init_uxn(&u);
486 89
487 // Initialize text engine. 90 // Initialize text engine.
91#if !defined(TEXT_DISABLE) || TEXT_DISABLE == 0 || PROF_ENABLE > 0
488 txt_init(1, TEXT_LAYER); 92 txt_init(1, TEXT_LAYER);
489 txt_position(0,0); 93 txt_position(0,0);
94#endif
95
96 // Initialize UXN.
97 init_uxn();
490 98
491 // Initialize sound mixer. 99 // Enable sound.
100#if !defined(SOUND_DISABLE) || SOUND_DISABLE == 0
492 init_sound(); 101 init_sound();
102#endif
493 103
494 // Main loop. 104 // Main loop.
495 uxn_eval(&u, PAGE_PROGRAM);
496 PROF_INIT();
497 u8 frame_counter = 0; 105 u8 frame_counter = 0;
106 PROF(uxn_eval_asm(PAGE_PROGRAM), eval_cycles);
498 while(true) { 107 while(true) {
499 bios_vblank_wait(); 108 bios_vblank_wait();
500 PROF(handle_input(&u), input_cycles); 109 FRAME_START();
501 PROF(uxn_eval(&u, mempeek16(devscreen->dat, 0)), eval_cycles); 110 PROF(handle_input(), input_cycles);
111 PROF(uxn_eval_asm(PEEK2(&device_data[0x20])), eval_cycles);
112#if !defined(SOUND_DISABLE) || SOUND_DISABLE == 0
502 PROF(sound_mix(), mix_cycles); 113 PROF(sound_mix(), mix_cycles);
114#endif
503 PROF_SHOW(); 115 PROF_SHOW();
504 PROF(flipbuf(&ppu), flip_cycles); 116 PROF(flipbuf(), flip_cycles);
505 frame_counter++; 117 frame_counter++;
506 if (frame_counter == 60) { 118 if (frame_counter == 60) {
507 seconds++; 119 seconds++;
508 frame_counter = 0; 120 frame_counter = 0;
509 } 121 }
122 FRAME_END();
510 } 123 }
511 124
512 return 0; 125 return 0;