From 8357708a6f73401886bc2bdf405cb19adf419cbc Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Sun, 27 Aug 2023 22:52:43 +0200 Subject: Adjust add/sub instructions slightly --- src/config.c | 11 + src/devices.c | 497 +++++++++++++++++++++++++++++++++++++ src/main.c | 701 ++-------------------------------------------------- src/profiling.c | 157 ++++++++++++ src/uxn-core.s | 747 ++++++++++++++++++++++++++++---------------------------- 5 files changed, 1053 insertions(+), 1060 deletions(-) create mode 100644 src/config.c create mode 100644 src/devices.c create mode 100644 src/profiling.c diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..d100233 --- /dev/null +++ b/src/config.c @@ -0,0 +1,11 @@ +#if !defined(TEXT_MODE) || TEXT_MODE == 0 +#define TEXT_LAYER FG_BACK +#else +#define TEXT_LAYER BG_BACK +#endif + +#ifndef CONTROL_METHODS +#define CONTROL_METHODS CONTROL_CONTROLLER,CONTROL_MOUSE,CONTROL_KEYBOARD +#endif + +#define PROF_ENABLE 0 diff --git a/src/devices.c b/src/devices.c new file mode 100644 index 0000000..48d062d --- /dev/null +++ b/src/devices.c @@ -0,0 +1,497 @@ +static time_t seconds = 0; + +typedef enum { + CONTROL_CONTROLLER, + CONTROL_MOUSE, + CONTROL_KEYBOARD, +} ControlMethod; + +const ControlMethod ctrl_methods[] = { + CONTROL_METHODS +}; +static ControlMethod ctrl_idx = 0; + +#define MOUSE_DELTA 1 +typedef struct Mouse { + int x; + int y; +} Mouse; + +// 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); +} + +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]; + // } +} + +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; + // } + // } +} + +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; + // } + // } + 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); + // } +} + +u8 +datetime_dei(u8 *d, u8 port) { + struct tm *t = gmtime(&seconds); + switch(port) { + case 0x0: return (t->tm_year + 1900) >> 8; + case 0x1: return (t->tm_year + 1900); + case 0x2: return t->tm_mon; + case 0x3: return t->tm_mday; + case 0x4: return t->tm_hour; + case 0x5: return t->tm_min; + case 0x6: return t->tm_sec; + case 0x7: return t->tm_wday; + case 0x8: return t->tm_yday >> 8; + case 0x9: return t->tm_yday; + case 0xa: return t->tm_isdst; + default: return d[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; + // } + // } + 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; + // } +} + +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; + // } +} + +#define RAM_PAGES 0x10 + +static void +system_cmd(u8 *ram, u16 addr) { + if(ram[addr] == 0x01) { + // NOTE: Handle rom paging on a case by case basis if a rom has to be + // split in multiple chunks. The GBA compiler doesn't like allocating + // big arrays, but it's fine if we split it into chunks of 64KB, for + // example. + // + // u16 i, length = PEEK2(ram + addr + 1); + // u16 a_page = PEEK2(ram + addr + 1 + 2); + // u16 a_addr = PEEK2(ram + addr + 1 + 4); + // u16 b_addr = PEEK2(ram + addr + 1 + 8); + // u8 *rom = uxn_rom; + // for(i = 0; i < length; i++) { + // switch (a_page % RAM_PAGES) { + // case 0: { rom = uxn_rom; } break; + // case 1: { rom = uxn_rom_2; } break; + // case 2: { rom = uxn_rom_3; } break; + // case 3: { rom = uxn_rom_4; } break; + // case 4: { rom = uxn_rom_5; } break; + // case 5: { rom = uxn_rom_6; } break; + // case 6: { rom = uxn_rom_7; } break; + // } + // ram[(u16)(b_addr + i)] = rom[(u16)(a_addr + i)]; + // } + } +} + +void +system_deo(Uxn *u, u8 *d, u8 port) { + // switch(port) { + // case 0x3: { + // system_cmd(u->ram, PEEK2(d + 2)); + // } break; + // } +} + +u8 +uxn_dei(Uxn *u, u8 addr) { + u8 p = addr & 0x0f, d = addr & 0xf0; + switch(d) { + case 0x20: return screen_dei(&u->dev[d], p); + case 0x30: return audio_dei(0, &u->dev[d], p); + case 0x40: return audio_dei(1, &u->dev[d], p); + case 0x50: return audio_dei(2, &u->dev[d], p); + case 0x60: return audio_dei(3, &u->dev[d], p); + case 0xa0: return file_dei(0, &u->dev[d], p); + case 0xb0: return file_dei(1, &u->dev[d], p); + case 0xc0: return datetime_dei(&u->dev[d], p); + } + return u->dev[addr]; +} + +void +uxn_deo(Uxn *u, u8 addr) { + u8 p = addr & 0x0f, d = addr & 0xf0; + switch(d) { + case 0x00: + system_deo(u, &u->dev[d], p); + if(p > 0x7 && p < 0xe) { + putcolors(&u->dev[0x8]); + } + break; + case 0x10: console_deo(&u->dev[d], p); break; + case 0x20: screen_deo(u->ram, &u->dev[d], p); break; + case 0x30: audio_deo(0, &u->dev[d], p, u); break; + case 0x40: audio_deo(1, &u->dev[d], p, u); break; + case 0x50: audio_deo(2, &u->dev[d], p, u); break; + case 0x60: audio_deo(3, &u->dev[d], p, u); break; + case 0xa0: file_deo(0, u->ram, &u->dev[d], p); break; + case 0xb0: file_deo(1, u->ram, &u->dev[d], p); break; + } +} + +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; + // } + + // // 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; + // } + // } +} + diff --git a/src/main.c b/src/main.c index e10c8f0..4a4b58c 100644 --- a/src/main.c +++ b/src/main.c @@ -15,499 +15,31 @@ #include "common.h" #include "filesystem.c" -#include "uxn.h" +#include "uxn.c" #include "ppu.c" #include "apu.c" #include "file.c" #include "text.h" #include "rom.c" +#include "config.c" +#include "profiling.c" +#include "devices.c" -// -// Config parameters. -// - -#if !defined(TEXT_MODE) || TEXT_MODE == 0 -#define TEXT_LAYER FG_BACK -#else -#define TEXT_LAYER BG_BACK -#endif - -#ifndef CONTROL_METHODS -#define CONTROL_METHODS CONTROL_CONTROLLER,CONTROL_MOUSE,CONTROL_KEYBOARD -#endif - -#define PROF_ENABLE 0 -#ifndef PROF_ENABLE -#define PROF_ENABLE 0 -#endif - -#if PROF_ENABLE > 0 && PROF_ENABLE < 3 - -#ifndef PROF_N_FRAMES -#define PROF_N_FRAMES 15 -#endif - -// Profile method 1: Average per N frames. -#if PROF_ENABLE == 1 -#define TEXT_ENABLE 1 -#define PROF(F,VAR) \ - do { \ - u32 __tmp_prof = profile_measure();\ - (F);\ - (VAR) += profile_measure() - __tmp_prof;\ - } while (0) - -// Profile method 2: Maximum in N frames. -#elif PROF_ENABLE == 2 -#define TEXT_ENABLE 1 -#define PROF(F,VAR) \ - do { \ - u32 __tmp_prof = profile_measure();\ - (F);\ - (VAR) = MAX(profile_measure() - __tmp_prof, (VAR));\ - } while (0) -#endif - -#ifndef PROF_SHOW_X -#define PROF_SHOW_X 0 -#endif -#ifndef PROF_SHOW_Y -#define PROF_SHOW_Y 0 -#endif - -#define PROF_SHOW() \ - do { \ - txt_position((PROF_SHOW_X), (PROF_SHOW_Y));\ - txt_printf("INPUT %.8lu\n", avg_input_cycles);\ - txt_printf("EVAL %.8lu\n", avg_eval_cycles);\ - txt_printf("VIDEO\n");\ - txt_printf(">PIX %.8lu\n", avg_ppu_pixel_cycles);\ - txt_printf(">FILL %.8lu\n", avg_ppu_fill_cycles);\ - txt_printf(">1BPP %.8lu\n", avg_ppu_icn_cycles);\ - txt_printf(">2BPP %.8lu\n", avg_ppu_chr_cycles);\ - txt_printf(">FLIP %.8lu\n", avg_flip_cycles);\ - txt_printf("AUDIO %.8lu\n", avg_mix_cycles);\ - txt_printf("TOTAL %.8lu\n", avg_frame_cycles);\ - u32 frame_time =\ - FP_DIV(\ - FP_NUM(avg_frame_cycles + 1, 2),\ - FP_NUM(2809, 2),\ - 2) * 166;\ - u32 fps =\ - FP_DIV(\ - FP_NUM(280896 * 60, 2),\ - FP_NUM(avg_frame_cycles + 1, 2),\ - 2);\ - txt_printf("TIME %.8lu\n", frame_time >> 2);\ - txt_printf("FPS %.8lu\n", (fps >> 2) + 1);\ - screen_fill(BG_BACK, 0, 0, 8 * 16, 8 * 12, 2);\ - } while (0) - -static u32 prof_frame_counter = 0; - -static u32 frame_cycles = 0; -static u32 ppu_pixel_cycles = 0; -static u32 ppu_fill_cycles = 0; -static u32 ppu_chr_cycles = 0; -static u32 ppu_icn_cycles = 0; -static u32 flip_cycles = 0; -static u32 eval_cycles = 0; -static u32 input_cycles = 0; -static u32 mix_cycles = 0; - -static u32 avg_ppu_pixel_cycles = 0; -static u32 avg_ppu_fill_cycles = 0; -static u32 avg_ppu_chr_cycles = 0; -static u32 avg_ppu_icn_cycles = 0; -static u32 avg_flip_cycles = 0; -static u32 avg_eval_cycles = 0; -static u32 avg_input_cycles = 0; -static u32 avg_mix_cycles = 0; -static u32 avg_frame_cycles = 0; - -#if PROF_ENABLE == 1 -#define FRAME_START()\ - do { \ - if (prof_frame_counter == PROF_N_FRAMES) {\ - avg_ppu_pixel_cycles = ppu_pixel_cycles / prof_frame_counter;\ - avg_ppu_fill_cycles = ppu_fill_cycles / prof_frame_counter;\ - avg_ppu_chr_cycles = ppu_chr_cycles / prof_frame_counter;\ - avg_ppu_icn_cycles = ppu_icn_cycles / prof_frame_counter;\ - avg_flip_cycles = flip_cycles / prof_frame_counter;\ - avg_eval_cycles = eval_cycles / prof_frame_counter;\ - avg_input_cycles = input_cycles / prof_frame_counter;\ - avg_mix_cycles = mix_cycles / prof_frame_counter;\ - avg_frame_cycles = frame_cycles / prof_frame_counter;\ - prof_frame_counter = 0;\ - frame_cycles = 0;\ - ppu_pixel_cycles = 0;\ - ppu_fill_cycles = 0;\ - ppu_chr_cycles = 0;\ - ppu_icn_cycles = 0;\ - flip_cycles = 0;\ - eval_cycles = 0;\ - input_cycles = 0;\ - mix_cycles = 0;\ - }\ - profile_start();\ - } while (0) -#elif PROF_ENABLE == 2 -#define FRAME_START()\ - do { \ - if (prof_frame_counter == PROF_N_FRAMES) {\ - avg_ppu_pixel_cycles = ppu_pixel_cycles;\ - avg_ppu_fill_cycles = ppu_fill_cycles;\ - avg_ppu_chr_cycles = ppu_chr_cycles;\ - avg_ppu_icn_cycles = ppu_icn_cycles;\ - avg_flip_cycles = flip_cycles;\ - avg_eval_cycles = eval_cycles;\ - avg_input_cycles = input_cycles;\ - avg_mix_cycles = mix_cycles;\ - avg_frame_cycles = frame_cycles / prof_frame_counter;\ - prof_frame_counter = 0;\ - frame_cycles = 0;\ - ppu_pixel_cycles = 0;\ - ppu_fill_cycles = 0;\ - ppu_chr_cycles = 0;\ - ppu_icn_cycles = 0;\ - flip_cycles = 0;\ - eval_cycles = 0;\ - input_cycles = 0;\ - mix_cycles = 0;\ - }\ - profile_start();\ - } while (0) -#endif - -#define FRAME_END() \ - do { \ - prof_frame_counter++;\ - frame_cycles += profile_stop();\ - } while (0) - -#else - -// No profiling. -#define PROF(F,VAR) (F) -#define PROF_SHOW() -#define FRAME_START() -#define FRAME_END() -#endif - -static time_t seconds = 0; - -typedef enum { - CONTROL_CONTROLLER, - CONTROL_MOUSE, - CONTROL_KEYBOARD, -} ControlMethod; - -const ControlMethod ctrl_methods[] = { - CONTROL_METHODS -}; -static ControlMethod ctrl_idx = 0; +#define STACK_SIZE 16 +u8 stack[STACK_SIZE] = {0}; +extern void uxn_eval_asm(u16 pc); -#define MOUSE_DELTA 1 -typedef struct Mouse { - int x; - int y; -} Mouse; +// 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; EWRAM_BSS -u8 uxn_ram[0x10300]; - -// 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); -// } - -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]; - // } -} - -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; - // } - // } -} - -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; - // } - // } - 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); - // } -} - -u8 -datetime_dei(u8 *d, u8 port) { - struct tm *t = gmtime(&seconds); - switch(port) { - case 0x0: return (t->tm_year + 1900) >> 8; - case 0x1: return (t->tm_year + 1900); - case 0x2: return t->tm_mon; - case 0x3: return t->tm_mday; - case 0x4: return t->tm_hour; - case 0x5: return t->tm_min; - case 0x6: return t->tm_sec; - case 0x7: return t->tm_wday; - case 0x8: return t->tm_yday >> 8; - case 0x9: return t->tm_yday; - case 0xa: return t->tm_isdst; - default: return d[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; - // } - // } - 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; - // } -} - -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; - // } -} - -#define RAM_PAGES 0x10 - -static void -system_cmd(u8 *ram, u16 addr) { - if(ram[addr] == 0x01) { - // NOTE: Handle rom paging on a case by case basis if a rom has to be - // split in multiple chunks. The GBA compiler doesn't like allocating - // big arrays, but it's fine if we split it into chunks of 64KB, for - // example. - // - // u16 i, length = PEEK2(ram + addr + 1); - // u16 a_page = PEEK2(ram + addr + 1 + 2); - // u16 a_addr = PEEK2(ram + addr + 1 + 4); - // u16 b_addr = PEEK2(ram + addr + 1 + 8); - // u8 *rom = uxn_rom; - // for(i = 0; i < length; i++) { - // switch (a_page % RAM_PAGES) { - // case 0: { rom = uxn_rom; } break; - // case 1: { rom = uxn_rom_2; } break; - // case 2: { rom = uxn_rom_3; } break; - // case 3: { rom = uxn_rom_4; } break; - // case 4: { rom = uxn_rom_5; } break; - // case 5: { rom = uxn_rom_6; } break; - // case 6: { rom = uxn_rom_7; } break; - // } - // ram[(u16)(b_addr + i)] = rom[(u16)(a_addr + i)]; - // } - } -} - -void -system_deo(Uxn *u, u8 *d, u8 port) { - // switch(port) { - // case 0x3: { - // system_cmd(u->ram, PEEK2(d + 2)); - // } break; - // } -} - -u8 -uxn_dei(Uxn *u, u8 addr) { - u8 p = addr & 0x0f, d = addr & 0xf0; - switch(d) { - case 0x20: return screen_dei(&u->dev[d], p); - case 0x30: return audio_dei(0, &u->dev[d], p); - case 0x40: return audio_dei(1, &u->dev[d], p); - case 0x50: return audio_dei(2, &u->dev[d], p); - case 0x60: return audio_dei(3, &u->dev[d], p); - case 0xa0: return file_dei(0, &u->dev[d], p); - case 0xb0: return file_dei(1, &u->dev[d], p); - case 0xc0: return datetime_dei(&u->dev[d], p); - } - return u->dev[addr]; -} - -void -uxn_deo(Uxn *u, u8 addr) { - u8 p = addr & 0x0f, d = addr & 0xf0; - switch(d) { - case 0x00: - system_deo(u, &u->dev[d], p); - if(p > 0x7 && p < 0xe) { - putcolors(&u->dev[0x8]); - } - break; - case 0x10: console_deo(&u->dev[d], p); break; - case 0x20: screen_deo(u->ram, &u->dev[d], p); break; - case 0x30: audio_deo(0, &u->dev[d], p, u); break; - case 0x40: audio_deo(1, &u->dev[d], p, u); break; - case 0x50: audio_deo(2, &u->dev[d], p, u); break; - case 0x60: audio_deo(3, &u->dev[d], p, u); break; - case 0xa0: file_deo(0, u->ram, &u->dev[d], p); break; - case 0xb0: file_deo(1, u->ram, &u->dev[d], p); break; - } -} +u8 uxn_ram[KB(64) * 2]; void init_uxn() { @@ -532,8 +64,9 @@ init_uxn() { // #0004 #0008 ADD // 0xa0, 0x00, 0x04, 0xa0, 0x00, 0x08, 0x18, // #0004 #0008 ADD2 - 0x80, 0xFF, 0xff, - 0xa0, 0x00, 0x08, 0xa0, 0x00, 0x04, 0x38, + // 0x80, 0xFF, 0xff, + // 0xa0, 0x00, 0x08, 0xa0, 0x00, 0x04, 0x38, + // 0xa0, 0x00, 0x08, 0xa0, 0x00, 0x04, 0x38, // 0xa0, 0x00, 0x08, 0xa0, 0x00, 0x03, 0x39, // 0xa0, 0x00, 0x01, 0x38, // @@ -553,204 +86,6 @@ init_uxn() { 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; - // } - - // // 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; - // } - // } -} - -#define STACK_SIZE 16 -u8 stack[STACK_SIZE] = {0}; -extern void uxn_eval_asm(u16 pc); - -// 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) { // Adjust system wait times. diff --git a/src/profiling.c b/src/profiling.c new file mode 100644 index 0000000..2d230df --- /dev/null +++ b/src/profiling.c @@ -0,0 +1,157 @@ +#ifndef PROF_ENABLE +#define PROF_ENABLE 0 +#endif + +#if PROF_ENABLE > 0 && PROF_ENABLE < 3 + +#ifndef PROF_N_FRAMES +#define PROF_N_FRAMES 15 +#endif + +// Profile method 1: Average per N frames. +#if PROF_ENABLE == 1 +#define TEXT_ENABLE 1 +#define PROF(F,VAR) \ + do { \ + u32 __tmp_prof = profile_measure();\ + (F);\ + (VAR) += profile_measure() - __tmp_prof;\ + } while (0) + +// Profile method 2: Maximum in N frames. +#elif PROF_ENABLE == 2 +#define TEXT_ENABLE 1 +#define PROF(F,VAR) \ + do { \ + u32 __tmp_prof = profile_measure();\ + (F);\ + (VAR) = MAX(profile_measure() - __tmp_prof, (VAR));\ + } while (0) +#endif + +#ifndef PROF_SHOW_X +#define PROF_SHOW_X 0 +#endif +#ifndef PROF_SHOW_Y +#define PROF_SHOW_Y 0 +#endif + +#define PROF_SHOW() \ + do { \ + txt_position((PROF_SHOW_X), (PROF_SHOW_Y));\ + txt_printf("INPUT %.8lu\n", avg_input_cycles);\ + txt_printf("EVAL %.8lu\n", avg_eval_cycles);\ + txt_printf("VIDEO\n");\ + txt_printf(">PIX %.8lu\n", avg_ppu_pixel_cycles);\ + txt_printf(">FILL %.8lu\n", avg_ppu_fill_cycles);\ + txt_printf(">1BPP %.8lu\n", avg_ppu_icn_cycles);\ + txt_printf(">2BPP %.8lu\n", avg_ppu_chr_cycles);\ + txt_printf(">FLIP %.8lu\n", avg_flip_cycles);\ + txt_printf("AUDIO %.8lu\n", avg_mix_cycles);\ + txt_printf("TOTAL %.8lu\n", avg_frame_cycles);\ + u32 frame_time =\ + FP_DIV(\ + FP_NUM(avg_frame_cycles + 1, 2),\ + FP_NUM(2809, 2),\ + 2) * 166;\ + u32 fps =\ + FP_DIV(\ + FP_NUM(280896 * 60, 2),\ + FP_NUM(avg_frame_cycles + 1, 2),\ + 2);\ + txt_printf("TIME %.8lu\n", frame_time >> 2);\ + txt_printf("FPS %.8lu\n", (fps >> 2) + 1);\ + screen_fill(BG_BACK, 0, 0, 8 * 16, 8 * 12, 2);\ + } while (0) + +static u32 prof_frame_counter = 0; + +static u32 frame_cycles = 0; +static u32 ppu_pixel_cycles = 0; +static u32 ppu_fill_cycles = 0; +static u32 ppu_chr_cycles = 0; +static u32 ppu_icn_cycles = 0; +static u32 flip_cycles = 0; +static u32 eval_cycles = 0; +static u32 input_cycles = 0; +static u32 mix_cycles = 0; + +static u32 avg_ppu_pixel_cycles = 0; +static u32 avg_ppu_fill_cycles = 0; +static u32 avg_ppu_chr_cycles = 0; +static u32 avg_ppu_icn_cycles = 0; +static u32 avg_flip_cycles = 0; +static u32 avg_eval_cycles = 0; +static u32 avg_input_cycles = 0; +static u32 avg_mix_cycles = 0; +static u32 avg_frame_cycles = 0; + +#if PROF_ENABLE == 1 +#define FRAME_START()\ + do { \ + if (prof_frame_counter == PROF_N_FRAMES) {\ + avg_ppu_pixel_cycles = ppu_pixel_cycles / prof_frame_counter;\ + avg_ppu_fill_cycles = ppu_fill_cycles / prof_frame_counter;\ + avg_ppu_chr_cycles = ppu_chr_cycles / prof_frame_counter;\ + avg_ppu_icn_cycles = ppu_icn_cycles / prof_frame_counter;\ + avg_flip_cycles = flip_cycles / prof_frame_counter;\ + avg_eval_cycles = eval_cycles / prof_frame_counter;\ + avg_input_cycles = input_cycles / prof_frame_counter;\ + avg_mix_cycles = mix_cycles / prof_frame_counter;\ + avg_frame_cycles = frame_cycles / prof_frame_counter;\ + prof_frame_counter = 0;\ + frame_cycles = 0;\ + ppu_pixel_cycles = 0;\ + ppu_fill_cycles = 0;\ + ppu_chr_cycles = 0;\ + ppu_icn_cycles = 0;\ + flip_cycles = 0;\ + eval_cycles = 0;\ + input_cycles = 0;\ + mix_cycles = 0;\ + }\ + profile_start();\ + } while (0) +#elif PROF_ENABLE == 2 +#define FRAME_START()\ + do { \ + if (prof_frame_counter == PROF_N_FRAMES) {\ + avg_ppu_pixel_cycles = ppu_pixel_cycles;\ + avg_ppu_fill_cycles = ppu_fill_cycles;\ + avg_ppu_chr_cycles = ppu_chr_cycles;\ + avg_ppu_icn_cycles = ppu_icn_cycles;\ + avg_flip_cycles = flip_cycles;\ + avg_eval_cycles = eval_cycles;\ + avg_input_cycles = input_cycles;\ + avg_mix_cycles = mix_cycles;\ + avg_frame_cycles = frame_cycles / prof_frame_counter;\ + prof_frame_counter = 0;\ + frame_cycles = 0;\ + ppu_pixel_cycles = 0;\ + ppu_fill_cycles = 0;\ + ppu_chr_cycles = 0;\ + ppu_icn_cycles = 0;\ + flip_cycles = 0;\ + eval_cycles = 0;\ + input_cycles = 0;\ + mix_cycles = 0;\ + }\ + profile_start();\ + } while (0) +#endif + +#define FRAME_END() \ + do { \ + prof_frame_counter++;\ + frame_cycles += profile_stop();\ + } while (0) + +#else + +// No profiling. +#define PROF(F,VAR) (F) +#define PROF_SHOW() +#define FRAME_START() +#define FRAME_END() +#endif + diff --git a/src/uxn-core.s b/src/uxn-core.s index a9fc3f9..32ec26b 100644 --- a/src/uxn-core.s +++ b/src/uxn-core.s @@ -30,532 +30,525 @@ io_ports: .space 256 .global uxn_eval_asm uxn_eval_asm: @ Initialization. - push {r4-r7} - ldr r1, wst_ptr - ldr r2, =uxn_ram - add r0, r0, r2 + 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 + ldrb r3, [r0], #1 @ current OP value / table index @ 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]() + 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 + adr r0, wst_ptr + str r1, [r0] + pop {r4-r7} + bx lr @ @ OP implementations. @ brk: - b uxn_ret + b uxn_ret jci: - b uxn_decode + b uxn_decode jmi: - b uxn_decode + b uxn_decode jsi: - b uxn_decode + b uxn_decode litr: - ldrb r3, [r0], #1 - strb r3, [r1], #1 - b uxn_decode + ldrb r3, [r0], #1 + strb r3, [r1], #1 + b uxn_decode lit2r: - ldrb r3, [r0], #1 - ldrb r4, [r0], #1 - strb r3, [r1], #1 - strb r4, [r1], #1 - b uxn_decode + ldrb r3, [r0], #1 + ldrb r4, [r0], #1 + strb r3, [r1], #1 + strb r4, [r1], #1 + b uxn_decode inc: - b uxn_decode + b uxn_decode inc2: - b uxn_decode + b uxn_decode pop: - b uxn_decode + b uxn_decode pop2: - b uxn_decode + b uxn_decode nip: - b uxn_decode + b uxn_decode nip2: - b uxn_decode + b uxn_decode swp: - b uxn_decode + b uxn_decode swp2: - b uxn_decode + b uxn_decode rot: - b uxn_decode + b uxn_decode rot2: - b uxn_decode + b uxn_decode dup: - b uxn_decode + b uxn_decode dup2: - b uxn_decode + b uxn_decode ovr: - b uxn_decode + b uxn_decode ovr2: - b uxn_decode + b uxn_decode equ: - b uxn_decode + b uxn_decode equ2: - b uxn_decode + b uxn_decode neq: - b uxn_decode + b uxn_decode neq2: - b uxn_decode + b uxn_decode gth: - b uxn_decode + b uxn_decode gth2: - b uxn_decode + b uxn_decode lth: - b uxn_decode + b uxn_decode lth2: - b uxn_decode + b uxn_decode jmp: - b uxn_decode + b uxn_decode jmp2: - b uxn_decode + b uxn_decode jcn: - b uxn_decode + b uxn_decode jcn2: - b uxn_decode + b uxn_decode jsr: - b uxn_decode + b uxn_decode jsr2: - b uxn_decode + b uxn_decode sth: - b uxn_decode + b uxn_decode sth2: - b uxn_decode + b uxn_decode ldz: - b uxn_decode + b uxn_decode ldz2: - b uxn_decode + b uxn_decode stz: - b uxn_decode + b uxn_decode stz2: - b uxn_decode + b uxn_decode ldr: - b uxn_decode + b uxn_decode ldr2: - b uxn_decode + b uxn_decode str: - b uxn_decode + b uxn_decode str2: - b uxn_decode + b uxn_decode lda: - b uxn_decode + b uxn_decode lda2: - b uxn_decode + b uxn_decode sta: - b uxn_decode + b uxn_decode sta2: - b uxn_decode + b uxn_decode dei: - b uxn_decode + b uxn_decode dei2: - b uxn_decode + b uxn_decode deo: - b uxn_decode + b uxn_decode deo2: - b uxn_decode + b uxn_decode add: - ldr r3, [r1, #-1]! - ldr r4, [r1, #-1]! - add r3, r3, r4 - strb r3, [r1], #1 - b uxn_decode + ldrb r3, [r1, #-1]! + ldrb r4, [r1, #-1]! + add r3, r3, r4 + strb r3, [r1], #1 + b uxn_decode add2: - ldrb r3, [r1, #-1]! - lsl r3, #8 - ldrb r5, [r1, #-1]! - add r3, r5 - ldrb r4, [r1, #-1]! - ldrb r5, [r1, #-1]! - lsl r4, #8 - add r4, r5 - add r3, r3, r4 - strb r3, [r1], #1 - lsr r3, #8 - strb r3, [r1], #1 - b uxn_decode + ldrb r3, [r1, #-1]! + ldrb r5, [r1, #-1]! + orr r3, r5, r3, lsl #8 + ldrb r4, [r1, #-1]! + ldrb r5, [r1, #-1]! + orr r4, r5, r4, lsl #8 + add r3, r3, r4 + strb r3, [r1], #1 + lsr r3, #8 + strb r3, [r1], #1 + b uxn_decode sub: - ldr r3, [r1, #-1]! - ldr r4, [r1, #-1]! - sub r4, r4, r3 - strb r4, [r1], #1 - b uxn_decode + ldr r3, [r1, #-1]! + ldr r4, [r1, #-1]! + sub r4, r4, r3 + strb r4, [r1], #1 + b uxn_decode sub2: - ldrb r3, [r1, #-1]! - ldrb r5, [r1, #-1]! - lsl r3, #8 - add r3, r5 - ldrb r4, [r1, #-1]! - ldrb r5, [r1, #-1]! - lsl r4, #8 - add r4, r5 - sub r3, r4, r3 - strb r3, [r1], #1 - lsr r3, #8 - strb r3, [r1], #1 - b uxn_decode + ldrb r3, [r1, #-1]! + ldrb r5, [r1, #-1]! + orr r3, r5, r3, lsl #8 + ldrb r4, [r1, #-1]! + ldrb r5, [r1, #-1]! + orr r4, r5, r4, lsl #8 + sub r3, r4, r3 + strb r3, [r1], #1 + lsr r3, #8 + strb r3, [r1], #1 + b uxn_decode mul: - b uxn_decode + b uxn_decode mul2: - b uxn_decode + b uxn_decode div: - b uxn_decode + b uxn_decode div2: - b uxn_decode + b uxn_decode and: - b uxn_decode + b uxn_decode and2: - b uxn_decode + b uxn_decode ora: - b uxn_decode + b uxn_decode ora2: - b uxn_decode + b uxn_decode eor: - b uxn_decode + b uxn_decode eor2: - b uxn_decode + b uxn_decode sft: - b uxn_decode + b uxn_decode sft2: - b uxn_decode + b uxn_decode @ 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 + .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 lit2r @ 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 + .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 lit2r @ 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 + .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