From 9fc5ff0b00e1ca72c75721d3bdc4ff2287b48fc2 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Sun, 27 Aug 2023 19:47:34 +0200 Subject: Testing barebones uxn_eval implementation in ASM --- src/main.c | 806 +++++++++++++++++++++++++++++++-------------------------- src/uxn-core.s | 567 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1005 insertions(+), 368 deletions(-) create mode 100644 src/uxn-core.s diff --git a/src/main.c b/src/main.c index a2a43d4..21c8e0e 100644 --- a/src/main.c +++ b/src/main.c @@ -15,7 +15,7 @@ #include "common.h" #include "filesystem.c" -#include "uxn.c" +#include "uxn.h" #include "ppu.c" #include "apu.c" #include "file.c" @@ -37,6 +37,7 @@ #define CONTROL_METHODS CONTROL_CONTROLLER,CONTROL_MOUSE,CONTROL_KEYBOARD #endif +#define PROF_ENABLE 0 #ifndef PROF_ENABLE #define PROF_ENABLE 0 #endif @@ -214,130 +215,130 @@ typedef struct Mouse { } Mouse; EWRAM_BSS -static u8 umem[0x10300]; +u8 uxn_ram[0x10300]; -static Uxn u; +// static Uxn u; static Mouse mouse = {SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2}; -int -uxn_halt(Uxn *u, u8 instr, u8 err, u16 addr) { - (void)u; - txt_printf("HALTED\n"); - txt_printf("I: %lu\n", instr); - txt_printf("E: %lu\n", err); - txt_printf("A: %lu\n", addr); - while (true); -} +// int +// uxn_halt(Uxn *u, u8 instr, u8 err, u16 addr) { +// (void)u; +// txt_printf("HALTED\n"); +// txt_printf("I: %lu\n", instr); +// txt_printf("E: %lu\n", err); +// txt_printf("A: %lu\n", addr); +// while (true); +// } IWRAM_CODE u8 screen_dei(u8 *d, u8 port) { - switch(port) { - case 0x2: return (SCREEN_WIDTH >> 8); - case 0x3: return (SCREEN_WIDTH); - case 0x4: return (SCREEN_HEIGHT >> 8); - case 0x5: return (SCREEN_HEIGHT); - default: return d[port]; - } + // switch(port) { + // case 0x2: return (SCREEN_WIDTH >> 8); + // case 0x3: return (SCREEN_WIDTH); + // case 0x4: return (SCREEN_HEIGHT >> 8); + // case 0x5: return (SCREEN_HEIGHT); + // default: return d[port]; + // } } IWRAM_CODE void screen_deo(u8 *ram, u8 *d, u8 port) { - switch(port) { - case 0xe: { - u8 ctrl = d[0xe]; - u8 color = ctrl & 0x3; - u16 x0 = PEEK2(d + 0x8); - u16 y0 = PEEK2(d + 0xa); - u8 *layer = (ctrl & 0x40) ? FG_BACK : BG_BACK; - if(ctrl & 0x80) { - u16 x1 = SCREEN_WIDTH - 1; - u16 y1 = SCREEN_HEIGHT - 1; - if(ctrl & 0x10) x1 = x0, x0 = 0; - if(ctrl & 0x20) y1 = y0, y0 = 0; - PROF(screen_fill(layer, x0, y0, x1, y1, color), ppu_fill_cycles); - } else { - PROF(ppu_pixel(layer, x0, y0, color), ppu_pixel_cycles); - if(d[0x6] & 0x1) POKE2(d + 0x8, x0 + 1); /* auto x+1 */ - if(d[0x6] & 0x2) POKE2(d + 0xa, y0 + 1); /* auto y+1 */ - } - break; - } - case 0xf: { - u16 x, y, dx, dy, addr; - u8 n, twobpp = !!(d[0xf] & 0x80); - x = PEEK2(d + 0x8); - y = PEEK2(d + 0xa); - addr = PEEK2(d + 0xc); - n = d[0x6] >> 4; - dx = (d[0x6] & 0x01) << 3; - dy = (d[0x6] & 0x02) << 2; - if(addr > 0x10000 - ((n + 1) << (3 + twobpp))) { - return; - } - u8 *layer = (d[0xf] & 0x40) ? FG_BACK : BG_BACK; - u8 color = d[0xf] & 0xf; - u8 flipx = d[0xf] & 0x10; - u8 flipy = d[0xf] & 0x20; - for(size_t i = 0; i <= n; i++) { - u8 *sprite = &ram[addr]; - if (twobpp) { - PROF(ppu_2bpp(layer, - x + dy * i, - y + dx * i, - sprite, - color, - flipx, flipy), ppu_chr_cycles); - } else { - PROF(ppu_1bpp(layer, - x + dy * i, - y + dx * i, - sprite, - color, - flipx, flipy), ppu_icn_cycles); - } - addr += (d[0x6] & 0x04) << (1 + twobpp); - } - POKE2(d + 0xc, addr); /* auto addr+length */ - POKE2(d + 0x8, x + dx); /* auto x+8 */ - POKE2(d + 0xa, y + dy); /* auto y+8 */ - break; - } - } + // switch(port) { + // case 0xe: { + // u8 ctrl = d[0xe]; + // u8 color = ctrl & 0x3; + // u16 x0 = PEEK2(d + 0x8); + // u16 y0 = PEEK2(d + 0xa); + // u8 *layer = (ctrl & 0x40) ? FG_BACK : BG_BACK; + // if(ctrl & 0x80) { + // u16 x1 = SCREEN_WIDTH - 1; + // u16 y1 = SCREEN_HEIGHT - 1; + // if(ctrl & 0x10) x1 = x0, x0 = 0; + // if(ctrl & 0x20) y1 = y0, y0 = 0; + // PROF(screen_fill(layer, x0, y0, x1, y1, color), ppu_fill_cycles); + // } else { + // PROF(ppu_pixel(layer, x0, y0, color), ppu_pixel_cycles); + // if(d[0x6] & 0x1) POKE2(d + 0x8, x0 + 1); /* auto x+1 */ + // if(d[0x6] & 0x2) POKE2(d + 0xa, y0 + 1); /* auto y+1 */ + // } + // break; + // } + // case 0xf: { + // u16 x, y, dx, dy, addr; + // u8 n, twobpp = !!(d[0xf] & 0x80); + // x = PEEK2(d + 0x8); + // y = PEEK2(d + 0xa); + // addr = PEEK2(d + 0xc); + // n = d[0x6] >> 4; + // dx = (d[0x6] & 0x01) << 3; + // dy = (d[0x6] & 0x02) << 2; + // if(addr > 0x10000 - ((n + 1) << (3 + twobpp))) { + // return; + // } + // u8 *layer = (d[0xf] & 0x40) ? FG_BACK : BG_BACK; + // u8 color = d[0xf] & 0xf; + // u8 flipx = d[0xf] & 0x10; + // u8 flipy = d[0xf] & 0x20; + // for(size_t i = 0; i <= n; i++) { + // u8 *sprite = &ram[addr]; + // if (twobpp) { + // PROF(ppu_2bpp(layer, + // x + dy * i, + // y + dx * i, + // sprite, + // color, + // flipx, flipy), ppu_chr_cycles); + // } else { + // PROF(ppu_1bpp(layer, + // x + dy * i, + // y + dx * i, + // sprite, + // color, + // flipx, flipy), ppu_icn_cycles); + // } + // addr += (d[0x6] & 0x04) << (1 + twobpp); + // } + // POKE2(d + 0xc, addr); /* auto addr+length */ + // POKE2(d + 0x8, x + dx); /* auto x+8 */ + // POKE2(d + 0xa, y + dy); /* auto y+8 */ + // break; + // } + // } } u8 audio_dei(int instance, u8 *d, u8 port) { - AudioChannel *c = &channels[instance]; - switch(port) { - // case 0x4: return apu_get_vu(instance); - case 0x2: { - POKE2(d + 0x2, c->pos); - c->pos <<= 12; // fixed point. - break; - } - } + // AudioChannel *c = &channels[instance]; + // switch(port) { + // // case 0x4: return apu_get_vu(instance); + // case 0x2: { + // POKE2(d + 0x2, c->pos); + // c->pos <<= 12; // fixed point. + // break; + // } + // } return d[port]; } void audio_deo(int instance, u8 *d, u8 port, Uxn *u) { - AudioChannel *c = &channels[instance]; - if (port == 0xf) { - u16 length = 0; - u16 adsr = 0; - u16 addr = 0; - u8 pitch = d[0xf] & 0x7f; - adsr = PEEK2(d + 0x8); - length = PEEK2(d + 0xa); - addr = PEEK2(d + 0xc); - u8 *data = &u->ram[addr]; - u32 vol = MAX(d[0xe] >> 4, d[0xe] & 0xf) * 4 / 3; - bool loop = !(d[0xf] & 0x80); - update_channel(c, data, length, pitch, adsr, vol, loop); - } + // AudioChannel *c = &channels[instance]; + // if (port == 0xf) { + // u16 length = 0; + // u16 adsr = 0; + // u16 addr = 0; + // u8 pitch = d[0xf] & 0x7f; + // adsr = PEEK2(d + 0x8); + // length = PEEK2(d + 0xa); + // addr = PEEK2(d + 0xc); + // u8 *data = &u->ram[addr]; + // u32 vol = MAX(d[0xe] >> 4, d[0xe] & 0xf) * 4 / 3; + // bool loop = !(d[0xf] & 0x80); + // update_channel(c, data, length, pitch, adsr, vol, loop); + // } } u8 @@ -361,75 +362,75 @@ datetime_dei(u8 *d, u8 port) { u8 file_dei(u8 id, u8 *d, u8 port) { - UxnFile *c = &uxn_file[id]; - u16 res; - switch(port) { - case 0xc: - case 0xd: { - res = file_read(c, &d[port], 1); - POKE2(d + 0x2, res); - break; - } - } + // UxnFile *c = &uxn_file[id]; + // u16 res; + // switch(port) { + // case 0xc: + // case 0xd: { + // res = file_read(c, &d[port], 1); + // POKE2(d + 0x2, res); + // break; + // } + // } return d[port]; } void file_deo(u8 id, u8 *ram, u8 *d, u8 port) { - u16 a, b, res; - UxnFile *f = &uxn_file[id]; - switch(port) { - case 0x5: { - a = PEEK2(d + 0x4); - b = PEEK2(d + 0xa); - if(b > 0x10000 - a) { - b = 0x10000 - a; - } - res = file_stat(f, &ram[a], b); - POKE2(d + 0x2, res); - } break; - case 0x6: { - // TODO: no file deletion for now - // res = file_delete(); - // POKE2(d + 0x2, res); - } break; - case 0x9: { - a = PEEK2(d + 0x8); - res = file_init(f, &ram[a]); - POKE2(d + 0x2, res); - } break; - case 0xd: { - a = PEEK2(d + 0xc); - b = PEEK2(d + 0xa); - if(b > 0x10000 - a) { - b = 0x10000 - a; - } - res = file_read(f, &ram[a], b); - POKE2(d + 0x2, res); - } break; - case 0xf: { - a = PEEK2(d + 0xe); - b = PEEK2(d + 0xa); - if(b > 0x10000 - a) { - b = 0x10000 - a; - } - res = file_write(f, &ram[a], b, d[0x7]); - POKE2(d + 0x2, res); - } break; - } + // u16 a, b, res; + // UxnFile *f = &uxn_file[id]; + // switch(port) { + // case 0x5: { + // a = PEEK2(d + 0x4); + // b = PEEK2(d + 0xa); + // if(b > 0x10000 - a) { + // b = 0x10000 - a; + // } + // res = file_stat(f, &ram[a], b); + // POKE2(d + 0x2, res); + // } break; + // case 0x6: { + // // TODO: no file deletion for now + // // res = file_delete(); + // // POKE2(d + 0x2, res); + // } break; + // case 0x9: { + // a = PEEK2(d + 0x8); + // res = file_init(f, &ram[a]); + // POKE2(d + 0x2, res); + // } break; + // case 0xd: { + // a = PEEK2(d + 0xc); + // b = PEEK2(d + 0xa); + // if(b > 0x10000 - a) { + // b = 0x10000 - a; + // } + // res = file_read(f, &ram[a], b); + // POKE2(d + 0x2, res); + // } break; + // case 0xf: { + // a = PEEK2(d + 0xe); + // b = PEEK2(d + 0xa); + // if(b > 0x10000 - a) { + // b = 0x10000 - a; + // } + // res = file_write(f, &ram[a], b, d[0x7]); + // POKE2(d + 0x2, res); + // } break; + // } } void console_deo(u8 *d, u8 port) { - switch(port) { - case 0x8: - txt_putc(d[port]); - return; - case 0x9: - txt_printf("ERROR: %c"); - txt_putc(d[port]); - return; - } + // switch(port) { + // case 0x8: + // txt_putc(d[port]); + // return; + // case 0x9: + // txt_printf("ERROR: %c"); + // txt_putc(d[port]); + // return; + // } } #define RAM_PAGES 0x10 @@ -464,11 +465,11 @@ system_cmd(u8 *ram, u16 addr) { void system_deo(Uxn *u, u8 *d, u8 port) { - switch(port) { - case 0x3: { - system_cmd(u->ram, PEEK2(d + 2)); - } break; - } + // switch(port) { + // case 0x3: { + // system_cmd(u->ram, PEEK2(d + 2)); + // } break; + // } } u8 @@ -509,201 +510,221 @@ uxn_deo(Uxn *u, u8 addr) { } void -init_uxn(Uxn *u) { +init_uxn() { // Initialize uxn. u32 fill = 0; - dma_fill(umem, fill, 0x10300, 3); - uxn_boot(u, umem, uxn_dei, uxn_deo); + dma_fill(uxn_ram, fill, 0x10300, 3); + // uxn_boot(u, uxn_ram, uxn_dei, uxn_deo); // Copy rom to VM. - memcpy(u->ram + PAGE_PROGRAM, uxn_rom, sizeof(uxn_rom)); + u8 uxn_rom[] = { + // Hello world (h) + // 0x80, 0x68, 0x80, 0x18, 0x17, + // + // ADD test + 0x80, 0x04, 0x80, 0x03, 0x80, 0x02, 0x80, 0x01, 0x18 + }; + memcpy(uxn_ram + PAGE_PROGRAM, uxn_rom, sizeof(uxn_rom)); } IWRAM_CODE void handle_input(Uxn *u) { - poll_keys(); - if (key_tap(KEY_SELECT)) { - // Reset control variables on method switch. - switch (ctrl_methods[ctrl_idx]) { - case CONTROL_CONTROLLER: { - u8 *d = &u->dev[0x80]; - d[2] = 0; - uxn_eval(u, PEEK2(d)); - d[3] = 0; - } break; - case CONTROL_MOUSE: { - u8 *d = &u->dev[0x90]; - d[6] = 0; - d[7] = 0; - POKE2(d + 0x2, -10); - POKE2(d + 0x4, -10); - uxn_eval(u, PEEK2(d)); - } break; - case CONTROL_KEYBOARD: { - toggle_keyboard(); - } break; - } - - // Update ctrl_idx. - ctrl_idx = (ctrl_idx + 1 > (int)LEN(ctrl_methods) - 1) ? 0 : ctrl_idx + 1; - - // Initialize controller variables here. - if (ctrl_methods[ctrl_idx] == CONTROL_KEYBOARD) { - toggle_keyboard(); - } - } - - if (ctrl_methods[ctrl_idx] == CONTROL_CONTROLLER) { - u8 *d = &u->dev[0x80]; - // TODO: We don't need ifs if we use KEY_INPUTS directly and maybe just - // swap some things if needed. - u8 *flag = &d[2]; - if (key_tap(KEY_A)) { - *flag |= 0x01; - } else { - *flag &= ~0x01; - } - if (key_tap(KEY_B)) { - *flag |= 0x02; - } else { - *flag &= ~0x02; - } - if (key_tap(KEY_L)) { - *flag |= 0x04; - } else { - *flag &= ~0x04; - } - if (key_tap(KEY_R)) { - *flag |= 0x08; - } else { - *flag &= ~0x08; - } - if (key_tap(KEY_UP)) { - *flag |= 0x10; - } else { - *flag &= ~0x10; - } - if (key_tap(KEY_DOWN)) { - *flag |= 0x20; - } else { - *flag &= ~0x20; - } - if (key_tap(KEY_LEFT)) { - *flag |= 0x40; - } else { - *flag &= ~0x40; - } - if (key_tap(KEY_RIGHT)) { - *flag |= 0x80; - } else { - *flag &= ~0x80; - } - - if (key_prev != key_curr) { - uxn_eval(u, PEEK2(d)); - } - d[3] = 0; - } else if (ctrl_methods[ctrl_idx] == CONTROL_MOUSE) { - u8 *d = &u->dev[0x90]; - // Detect "mouse key press". - u8 flag = d[6]; - bool event = false; - if (key_tap(KEY_B)) { - event = true; - flag |= 0x01; - } else if (key_released(KEY_B)) { - event = true; - flag &= ~0x01; - } - if (key_tap(KEY_A)) { - event = true; - flag |= 0x10; - } else if (key_released(KEY_A)) { - event = true; - flag &= ~0x10; - } + // poll_keys(); + // if (key_tap(KEY_SELECT)) { + // // Reset control variables on method switch. + // switch (ctrl_methods[ctrl_idx]) { + // case CONTROL_CONTROLLER: { + // u8 *d = &u->dev[0x80]; + // d[2] = 0; + // uxn_eval(u, PEEK2(d)); + // d[3] = 0; + // } break; + // case CONTROL_MOUSE: { + // u8 *d = &u->dev[0x90]; + // d[6] = 0; + // d[7] = 0; + // POKE2(d + 0x2, -10); + // POKE2(d + 0x4, -10); + // uxn_eval(u, PEEK2(d)); + // } break; + // case CONTROL_KEYBOARD: { + // toggle_keyboard(); + // } break; + // } + + // // Update ctrl_idx. + // ctrl_idx = (ctrl_idx + 1 > (int)LEN(ctrl_methods) - 1) ? 0 : ctrl_idx + 1; + + // // Initialize controller variables here. + // if (ctrl_methods[ctrl_idx] == CONTROL_KEYBOARD) { + // toggle_keyboard(); + // } + // } + + // if (ctrl_methods[ctrl_idx] == CONTROL_CONTROLLER) { + // u8 *d = &u->dev[0x80]; + // // TODO: We don't need ifs if we use KEY_INPUTS directly and maybe just + // // swap some things if needed. + // u8 *flag = &d[2]; + // if (key_tap(KEY_A)) { + // *flag |= 0x01; + // } else { + // *flag &= ~0x01; + // } + // if (key_tap(KEY_B)) { + // *flag |= 0x02; + // } else { + // *flag &= ~0x02; + // } + // if (key_tap(KEY_L)) { + // *flag |= 0x04; + // } else { + // *flag &= ~0x04; + // } + // if (key_tap(KEY_R)) { + // *flag |= 0x08; + // } else { + // *flag &= ~0x08; + // } + // if (key_tap(KEY_UP)) { + // *flag |= 0x10; + // } else { + // *flag &= ~0x10; + // } + // if (key_tap(KEY_DOWN)) { + // *flag |= 0x20; + // } else { + // *flag &= ~0x20; + // } + // if (key_tap(KEY_LEFT)) { + // *flag |= 0x40; + // } else { + // *flag &= ~0x40; + // } + // if (key_tap(KEY_RIGHT)) { + // *flag |= 0x80; + // } else { + // *flag &= ~0x80; + // } + + // if (key_prev != key_curr) { + // uxn_eval(u, PEEK2(d)); + // } + // d[3] = 0; + // } else if (ctrl_methods[ctrl_idx] == CONTROL_MOUSE) { + // u8 *d = &u->dev[0x90]; + // // Detect "mouse key press". + // u8 flag = d[6]; + // bool event = false; + // if (key_tap(KEY_B)) { + // event = true; + // flag |= 0x01; + // } else if (key_released(KEY_B)) { + // event = true; + // flag &= ~0x01; + // } + // if (key_tap(KEY_A)) { + // event = true; + // flag |= 0x10; + // } else if (key_released(KEY_A)) { + // event = true; + // flag &= ~0x10; + // } + + // // Handle chording. + // d[6] = flag; + // if(flag == 0x10 && (d[6] & 0x01)) { + // d[7] = 0x01; + // } + // if(flag == 0x01 && (d[6] & 0x10)) { + // d[7] = 0x10; + // } + + // // Detect mouse movement. + // if (key_pressed(KEY_UP)) { + // event = true; + // mouse.y = CLAMP(mouse.y - MOUSE_DELTA, 0, SCREEN_HEIGHT - 8); + // } else if (key_pressed(KEY_DOWN)) { + // event = true; + // mouse.y = CLAMP(mouse.y + MOUSE_DELTA, 0, SCREEN_HEIGHT - 8); + // } + // if (key_pressed(KEY_LEFT)) { + // event = true; + // mouse.x = CLAMP(mouse.x - MOUSE_DELTA, 0, SCREEN_WIDTH - 8); + // } else if (key_pressed(KEY_RIGHT)) { + // event = true; + // mouse.x = CLAMP(mouse.x + MOUSE_DELTA, 0, SCREEN_WIDTH - 8); + // } + + // // Eval mouse. + // POKE2(d + 0x2, mouse.x); + // POKE2(d + 0x4, mouse.y); + // if (event) { + // uxn_eval(u, PEEK2(d)); + // } + // } else if (ctrl_methods[ctrl_idx] == CONTROL_KEYBOARD) { + // u8 *d = &u->dev[0x80]; + // if (key_tap(KEY_LEFT)) { + // update_cursor(cursor_position - 1); + // } else if (key_tap(KEY_RIGHT)) { + // update_cursor(cursor_position + 1); + // } + // if (key_tap(KEY_UP) && cursor_position >= KEYBOARD_ROW_SIZE) { + // update_cursor(cursor_position - KEYBOARD_ROW_SIZE); + // } else if (key_tap(KEY_DOWN) + // && cursor_position < LEN(keyboard) - KEYBOARD_ROW_SIZE) { + // update_cursor(cursor_position + KEYBOARD_ROW_SIZE); + // } + // if (key_tap(KEY_B)) { + // u8 symbol = keyboard[cursor_position].symbol; + // switch (symbol) { + // case 0x7f: { + // // Backspace. + // d[3] = 0x08; + // } break; + // case 0x14: { + // // New line. + // d[3] = 0x0d; + // } break; + // case 0x18: { + // // Arrow up. + // d[2] = 0x10; + // } break; + // case 0x19: { + // // Arrow down. + // d[2] = 0x20; + // } break; + // case 0x1b: { + // // Arrow left. + // d[2] = 0x40; + // } break; + // case 0x1a: { + // // Arrow right. + // d[2] = 0x80; + // } break; + // default: { + // d[3] = symbol; + // } break; + // } + // uxn_eval(u, PEEK2(d)); + // d[3] = 0; + // } + // } +} - // Handle chording. - d[6] = flag; - if(flag == 0x10 && (d[6] & 0x01)) { - d[7] = 0x01; - } - if(flag == 0x01 && (d[6] & 0x10)) { - d[7] = 0x10; - } +#define STACK_SIZE 16 +u8 stack[STACK_SIZE] = {0}; +extern void uxn_init_asm(void); +extern void uxn_eval_asm(u16 pc); - // Detect mouse movement. - if (key_pressed(KEY_UP)) { - event = true; - mouse.y = CLAMP(mouse.y - MOUSE_DELTA, 0, SCREEN_HEIGHT - 8); - } else if (key_pressed(KEY_DOWN)) { - event = true; - mouse.y = CLAMP(mouse.y + MOUSE_DELTA, 0, SCREEN_HEIGHT - 8); - } - if (key_pressed(KEY_LEFT)) { - event = true; - mouse.x = CLAMP(mouse.x - MOUSE_DELTA, 0, SCREEN_WIDTH - 8); - } else if (key_pressed(KEY_RIGHT)) { - event = true; - mouse.x = CLAMP(mouse.x + MOUSE_DELTA, 0, SCREEN_WIDTH - 8); - } - - // Eval mouse. - POKE2(d + 0x2, mouse.x); - POKE2(d + 0x4, mouse.y); - if (event) { - uxn_eval(u, PEEK2(d)); - } - } else if (ctrl_methods[ctrl_idx] == CONTROL_KEYBOARD) { - u8 *d = &u->dev[0x80]; - if (key_tap(KEY_LEFT)) { - update_cursor(cursor_position - 1); - } else if (key_tap(KEY_RIGHT)) { - update_cursor(cursor_position + 1); - } - if (key_tap(KEY_UP) && cursor_position >= KEYBOARD_ROW_SIZE) { - update_cursor(cursor_position - KEYBOARD_ROW_SIZE); - } else if (key_tap(KEY_DOWN) - && cursor_position < LEN(keyboard) - KEYBOARD_ROW_SIZE) { - update_cursor(cursor_position + KEYBOARD_ROW_SIZE); - } - if (key_tap(KEY_B)) { - u8 symbol = keyboard[cursor_position].symbol; - switch (symbol) { - case 0x7f: { - // Backspace. - d[3] = 0x08; - } break; - case 0x14: { - // New line. - d[3] = 0x0d; - } break; - case 0x18: { - // Arrow up. - d[2] = 0x10; - } break; - case 0x19: { - // Arrow down. - d[2] = 0x20; - } break; - case 0x1b: { - // Arrow left. - d[2] = 0x40; - } break; - case 0x1a: { - // Arrow right. - d[2] = 0x80; - } break; - default: { - d[3] = symbol; - } break; - } - uxn_eval(u, PEEK2(d)); - d[3] = 0; - } - } -} +// TODO: This should be on the IWRAM for maximum speed, as these are operations +// we will use very often. +extern u8 wst[256]; +extern u8 rst[256]; +extern u8 io_ports[256]; +extern uintptr_t wst_ptr; +extern uintptr_t rst_ptr; int main(void) { @@ -721,38 +742,87 @@ main(void) { video_init(); // Initialize text engine. -#ifdef TEXT_ENABLE +// #ifdef TEXT_ENABLE txt_init(1, TEXT_LAYER); txt_position(0,0); -#endif +// #endif // Initialize UXN. - init_uxn(&u); + init_uxn(); // Enable sound. init_sound(); // Main loop. - uxn_eval(&u, PAGE_PROGRAM); + // uxn_eval(&u, PAGE_PROGRAM); u8 frame_counter = 0; // NOTE: A VBLANK is 83776 cycles, anything other than that will make it so // we fail to render at 60FPS. + + // TODO: Initialize memory. + // Initialize stack. + for (size_t i = 0; i < 256; i++) { + wst[i] = 0; + rst[i] = 0; + io_ports[i] = 0; + } + uxn_init_asm(); + + txt_printf("ROM\n\n"); + for (size_t i = 0; i < 8; i++) { + txt_printf("%02x ", uxn_ram[i + PAGE_PROGRAM]); + } + txt_printf("\n\n"); + + txt_printf("STACK (BEFORE)\n"); + txt_printf("PTR: %d\n", wst_ptr - (uintptr_t)wst); + for (size_t i = 0; i < STACK_SIZE; i++) { + if (i % 4 == 0) { + txt_printf("\n"); + } + txt_printf("%02x ", wst[i]); + } + txt_printf("\n\n"); + + uxn_eval_asm(PAGE_PROGRAM); + txt_printf("STACK (AFTER)\n"); + txt_printf("PTR: %d\n", wst_ptr - (uintptr_t)wst); + for (size_t i = 0; i < STACK_SIZE; i++) { + if (i % 4 == 0) { + txt_printf("\n"); + } + txt_printf("%02x ", wst[i]); + } + + uintptr_t stack_base = wst; + uintptr_t stack_size = wst_ptr - stack_base; while(true) { + txt_position(0,0); + // bios_vblank_wait(); + // FRAME_START(); + // // PROF(handle_input(&u), input_cycles); + // // PROF(uxn_eval(&u, PEEK2(&u.dev[0x20])), eval_cycles); + // // PROF(sound_mix(), mix_cycles); + // // // TODO: allow configuration to do VSYNC at 15 or 30 fps to avoid too + // // // much memory copying on demanding uxn roms. + // // PROF_SHOW(); + // // PROF(flipbuf(), flip_cycles); + // // frame_counter++; + // // if (frame_counter == 60) { + // // seconds++; + // // frame_counter = 0; + // // } + // FRAME_END(); bios_vblank_wait(); FRAME_START(); - PROF(handle_input(&u), input_cycles); - PROF(uxn_eval(&u, PEEK2(&u.dev[0x20])), eval_cycles); - PROF(sound_mix(), mix_cycles); - // TODO: allow configuration to do VSYNC at 15 or 30 fps to avoid too - // much memory copying on demanding uxn roms. - PROF_SHOW(); - PROF(flipbuf(), flip_cycles); - frame_counter++; - if (frame_counter == 60) { - seconds++; - frame_counter = 0; - } + flipbuf(); + // screen_fill(0); + // txt_printf("WST: 0x%08x\n", wst); + // txt_printf("PTR: 0x%08x\n", wst_ptr); FRAME_END(); + PROF_SHOW(); + // txt_render(); + // txt_clear(); } return 0; diff --git a/src/uxn-core.s b/src/uxn-core.s new file mode 100644 index 0000000..2e7a165 --- /dev/null +++ b/src/uxn-core.s @@ -0,0 +1,567 @@ +.file "uxn-core.s" +.section .iwram, "ax", %progbits +.extern uxn_ram +.align + +@ +@ Core variables +@ + +.global wst +wst: .space 256 +.global wst_ptr +wst_ptr: .word wst + +.global rst +rst: .space 256 +.global rst_ptr +rst_ptr: .word rst + +.global io_ports +io_ports: .space 256 + +@ +@ r0 is our custom stack pointer +@ r1 is the value we want to insert/extract +@ +@ When the stack grows downwards: +@ push {r1} == str r1, [sp, #-4]! +@ pop {r1} == ldr r1, [sp], #4 +@ +@ In our case the stack grow upwards: +@ +@ 8bit: +@ push {r1} == strb r1, [sp], #1 +@ pop {r1} == ldrb r1, [sp, #-1]! +@ +@ 32bit: +@ push {r1} == str r1, [sp], #4 +@ pop {r1} == ldr r1, [sp, #-4]! +@ + +.global uxn_init_asm +uxn_init_asm: + @ DEBUG: Initialize the stack for testing purposes. + ldr r0, wst_ptr + mov r1, #4 + strb r1, [r0], #1 + sub r1, r1, #1 + strb r1, [r0], #1 + sub r1, r1, #1 + strb r1, [r0], #1 + sub r1, r1, #1 + strb r1, [r0], #1 + adr r1, wst_ptr + str r0, [r1] + bx lr + +@ UXN evaluation function. +@ +@ r0: PC pointer (argument is offset from RAM). +@ r1: Stack pointer (wst/rst). +@ r2: Ram ptr. +@ r3-r7: Scratch registers. +.global uxn_eval_asm +uxn_eval_asm: + @ Initialization. + push {r4-r7} + ldr r1, wst_ptr + ldr r2, =uxn_ram + add r0, r0, r2 + +uxn_decode: + ldrb r3, [r0], #1 @ current OP value / table index + + @ TODO: Setup wst/rst? or fill operations accordingly? + @ str r3, [r1], #1 @ Push to stack: Debugging + + @ Decode OP based on table lookup. + adr r4, op_table @ decoding table + ldr r4, [r4, r3, lsl #2] @ op_table[idx * 4] + bx r4 @ op_table[idx * 4]() + +uxn_ret: + @ Update stack pointer and return. + adr r0, wst_ptr + str r1, [r0] + pop {r4-r7} + bx lr + +@ +@ OP implementations. +@ + +brk: + b uxn_ret + +jci: + b uxn_ret + +jmi: + b uxn_ret + +jsi: + b uxn_ret + +litr: + ldrb r3, [r0], #1 + strb r3, [r1], #1 + b uxn_decode + +lit2r: + b uxn_ret + +inc: + b uxn_ret + +inc2: + b uxn_ret + +pop: + b uxn_ret + +pop2: + b uxn_ret + +nip: + b uxn_ret + +nip2: + b uxn_ret + +swp: + b uxn_ret + +swp2: + b uxn_ret + +rot: + b uxn_ret + +rot2: + b uxn_ret + +dup: + b uxn_ret + +dup2: + b uxn_ret + +ovr: + b uxn_ret + +ovr2: + b uxn_ret + +equ: + b uxn_ret + +equ2: + b uxn_ret + +neq: + b uxn_ret + +neq2: + b uxn_ret + +gth: + b uxn_ret + +gth2: + b uxn_ret + +lth: + b uxn_ret + +lth2: + b uxn_ret + +jmp: + b uxn_ret + +jmp2: + b uxn_ret + +jcn: + b uxn_ret + +jcn2: + b uxn_ret + +jsr: + b uxn_ret + +jsr2: + b uxn_ret + +sth: + b uxn_ret + +sth2: + b uxn_ret + +ldz: + b uxn_ret + +ldz2: + b uxn_ret + +stz: + b uxn_ret + +stz2: + b uxn_ret + +ldr: + b uxn_ret + +ldr2: + b uxn_ret + +str: + b uxn_ret + +str2: + b uxn_ret + +lda: + b uxn_ret + +lda2: + b uxn_ret + +sta: + b uxn_ret + +sta2: + b uxn_ret + +dei: + b uxn_ret + +dei2: + b uxn_ret + +deo: + b uxn_ret + +deo2: + b uxn_ret + +add: + ldr r3, [r1, #-1]! + ldr r4, [r1, #-1]! + add r3, r3, r4 + strb r3, [r1], #1 + b uxn_ret @ NOTE: This should return to the next loop iteration. + +add2: + b uxn_ret + +sub: + @ ldr r2, [r0, #-1]! + @ ldr r1, [r0, #-1]! + @ sub r1, r1, r2 + @ strb r1, [r0], #1 + b uxn_ret @ NOTE: This should return to the next loop iteration. + +sub2: + b uxn_ret + +mul: + b uxn_ret + +mul2: + b uxn_ret + +div: + b uxn_ret + +div2: + b uxn_ret + +and: + b uxn_ret + +and2: + b uxn_ret + +ora: + b uxn_ret + +ora2: + b uxn_ret + +eor: + b uxn_ret + +eor2: + b uxn_ret + +sft: + b uxn_ret + +sft2: + b uxn_ret + +@ OP table +op_table: + .word brk @ 0x00 + .word inc @ 0x01 + .word pop @ 0x02 + .word nip @ 0x03 + .word swp @ 0x04 + .word rot @ 0x05 + .word dup @ 0x06 + .word ovr @ 0x07 + .word equ @ 0x08 + .word neq @ 0x09 + .word gth @ 0x0a + .word lth @ 0x0b + .word jmp @ 0x0c + .word jcn @ 0x0d + .word jsr @ 0x0e + .word sth @ 0x0f + .word ldz @ 0x00 + .word stz @ 0x11 + .word ldr @ 0x12 + .word str @ 0x13 + .word lda @ 0x14 + .word sta @ 0x15 + .word dei @ 0x16 + .word deo @ 0x17 + .word add @ 0x18 + .word sub @ 0x19 + .word mul @ 0x1a + .word div @ 0x1b + .word and @ 0x1c + .word ora @ 0x1d + .word eor @ 0x1e + .word sft @ 0x1f + .word brk @ 0x20 + .word inc2 @ 0x21 + .word pop2 @ 0x22 + .word nip2 @ 0x23 + .word swp2 @ 0x24 + .word rot2 @ 0x25 + .word dup2 @ 0x26 + .word ovr2 @ 0x27 + .word equ2 @ 0x28 + .word neq2 @ 0x29 + .word gth2 @ 0x2a + .word lth2 @ 0x2b + .word jmp2 @ 0x2c + .word jcn2 @ 0x2d + .word jsr2 @ 0x2e + .word sth2 @ 0x2f + .word ldz2 @ 0x30 + .word stz2 @ 0x31 + .word ldr2 @ 0x32 + .word str2 @ 0x33 + .word lda2 @ 0x34 + .word sta2 @ 0x35 + .word dei2 @ 0x36 + .word deo2 @ 0x37 + .word add2 @ 0x38 + .word sub2 @ 0x39 + .word mul2 @ 0x3a + .word div2 @ 0x3b + .word and2 @ 0x3c + .word ora2 @ 0x3d + .word eor2 @ 0x3e + .word sft2 @ 0x3f +@ TODO: Can we mask this instead of having empty space? + .word brk @ 0x40 + .word brk @ 0x41 + .word brk @ 0x42 + .word brk @ 0x43 + .word brk @ 0x44 + .word brk @ 0x45 + .word brk @ 0x46 + .word brk @ 0x47 + .word brk @ 0x48 + .word brk @ 0x49 + .word brk @ 0x4a + .word brk @ 0x4b + .word brk @ 0x4c + .word brk @ 0x4d + .word brk @ 0x4e + .word brk @ 0x4f + .word brk @ 0x50 + .word brk @ 0x51 + .word brk @ 0x52 + .word brk @ 0x53 + .word brk @ 0x54 + .word brk @ 0x55 + .word brk @ 0x56 + .word brk @ 0x57 + .word brk @ 0x58 + .word brk @ 0x59 + .word brk @ 0x5a + .word brk @ 0x5b + .word brk @ 0x5c + .word brk @ 0x5d + .word brk @ 0x5e + .word brk @ 0x5f + .word brk @ 0x60 + .word brk @ 0x61 + .word brk @ 0x62 + .word brk @ 0x63 + .word brk @ 0x64 + .word brk @ 0x65 + .word brk @ 0x66 + .word brk @ 0x67 + .word brk @ 0x68 + .word brk @ 0x69 + .word brk @ 0x6a + .word brk @ 0x6b + .word brk @ 0x6c + .word brk @ 0x6d + .word brk @ 0x6e + .word brk @ 0x6f + .word brk @ 0x70 + .word brk @ 0x71 + .word brk @ 0x72 + .word brk @ 0x73 + .word brk @ 0x74 + .word brk @ 0x75 + .word brk @ 0x76 + .word brk @ 0x77 + .word brk @ 0x78 + .word brk @ 0x79 + .word brk @ 0x7a + .word brk @ 0x7b + .word brk @ 0x7c + .word brk @ 0x7d + .word brk @ 0x7e + .word brk @ 0x7f + .word litr @ 0x80 + .word brk @ 0x81 + .word brk @ 0x82 + .word brk @ 0x83 + .word brk @ 0x84 + .word brk @ 0x85 + .word brk @ 0x86 + .word brk @ 0x87 + .word brk @ 0x88 + .word brk @ 0x89 + .word brk @ 0x8a + .word brk @ 0x8b + .word brk @ 0x8c + .word brk @ 0x8d + .word brk @ 0x8e + .word brk @ 0x8f + .word brk @ 0x90 + .word brk @ 0x91 + .word brk @ 0x92 + .word brk @ 0x93 + .word brk @ 0x94 + .word brk @ 0x95 + .word brk @ 0x96 + .word brk @ 0x97 + .word brk @ 0x98 + .word brk @ 0x99 + .word brk @ 0x9a + .word brk @ 0x9b + .word brk @ 0x9c + .word brk @ 0x9d + .word brk @ 0x9e + .word brk @ 0x9f + .word brk @ 0xa0 + .word brk @ 0xa1 + .word brk @ 0xa2 + .word brk @ 0xa3 + .word brk @ 0xa4 + .word brk @ 0xa5 + .word brk @ 0xa6 + .word brk @ 0xa7 + .word brk @ 0xa8 + .word brk @ 0xa9 + .word brk @ 0xaa + .word brk @ 0xab + .word brk @ 0xac + .word brk @ 0xad + .word brk @ 0xae + .word brk @ 0xaf + .word brk @ 0xb0 + .word brk @ 0xb1 + .word brk @ 0xb2 + .word brk @ 0xb3 + .word brk @ 0xb4 + .word brk @ 0xb5 + .word brk @ 0xb6 + .word brk @ 0xb7 + .word brk @ 0xb8 + .word brk @ 0xb9 + .word brk @ 0xba + .word brk @ 0xbb + .word brk @ 0xbc + .word brk @ 0xbd + .word brk @ 0xbe + .word brk @ 0xbf + .word brk @ 0xc0 + .word brk @ 0xc1 + .word brk @ 0xc2 + .word brk @ 0xc3 + .word brk @ 0xc4 + .word brk @ 0xc5 + .word brk @ 0xc6 + .word brk @ 0xc7 + .word brk @ 0xc8 + .word brk @ 0xc9 + .word brk @ 0xca + .word brk @ 0xcb + .word brk @ 0xcc + .word brk @ 0xcd + .word brk @ 0xce + .word brk @ 0xcf + .word brk @ 0xd0 + .word brk @ 0xd1 + .word brk @ 0xd2 + .word brk @ 0xd3 + .word brk @ 0xd4 + .word brk @ 0xd5 + .word brk @ 0xd6 + .word brk @ 0xd7 + .word brk @ 0xd8 + .word brk @ 0xd9 + .word brk @ 0xda + .word brk @ 0xdb + .word brk @ 0xdc + .word brk @ 0xdd + .word brk @ 0xde + .word brk @ 0xdf + .word brk @ 0xe0 + .word brk @ 0xe1 + .word brk @ 0xe2 + .word brk @ 0xe3 + .word brk @ 0xe4 + .word brk @ 0xe5 + .word brk @ 0xe6 + .word brk @ 0xe7 + .word brk @ 0xe8 + .word brk @ 0xe9 + .word brk @ 0xea + .word brk @ 0xeb + .word brk @ 0xec + .word brk @ 0xed + .word brk @ 0xee + .word brk @ 0xef + .word brk @ 0xf0 + .word brk @ 0xf1 + .word brk @ 0xf2 + .word brk @ 0xf3 + .word brk @ 0xf4 + .word brk @ 0xf5 + .word brk @ 0xf6 + .word brk @ 0xf7 + .word brk @ 0xf8 +@ TODO: Can we mask this instead of having empty space? + .word lit2r @ 0xf9 + .word litr @ 0xfa + .word lit2r @ 0xfb + .word litr @ 0xfc + .word jsi @ 0xfd + .word jmi @ 0xfe + .word jci @ 0xff -- cgit v1.2.1