#include #include #include "common.h" #include "bitmap.h" // #include "small-font.c" #include "bd-font.c" #include "uxn/uxn.h" #include "uxn/uxn.c" #include "uxn/devices/ppu.h" #include "uxn/devices/ppu.c" #include "uxn/roms/boot.c" #include "text.h" /* Copyright (c) 2021 Bad Diode Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ static Ppu ppu; u8 reqdraw = 0; static Device *devscreen; static Device *devctrl; time_t time_seconds; void nil_talk(Device *d, Uint8 b0, Uint8 w) { (void)d; (void)b0; (void)w; } void console_talk(Device *d, u8 b0, u8 w) { char stmp[2]; if(!w) { return; } switch(b0) { case 0x8: stmp[0] = d->dat[0x8]; stmp[1] = 0; txt_printf(stmp); break; case 0x9: txt_printf("0x%02x", d->dat[0x9]); break; case 0xb: txt_printf("0x%04x", mempeek16(d->dat, 0xa)); break; case 0xd: txt_printf("%s", &d->mem[mempeek16(d->dat, 0xc)]); break; } } void system_talk(Device *d, Uint8 b0, Uint8 w) { if(!w) { d->dat[0x2] = d->u->wst.ptr; d->dat[0x3] = d->u->rst.ptr; } else { putcolors(&ppu, &d->dat[0x8]); } (void)b0; } IWRAM_CODE void screen_talk(Device *d, Uint8 b0, Uint8 w) { if(w && b0 == 0xe) { Uint16 x = mempeek16(d->dat, 0x8); Uint16 y = mempeek16(d->dat, 0xa); Uint8 *addr = &d->mem[mempeek16(d->dat, 0xc)]; Uint8 *layer = d->dat[0xe] >> 4 & 0x1 ? ppu.fg : ppu.bg; Uint8 mode = d->dat[0xe] >> 5; if(!mode) { putpixel(&ppu, layer, x, y, d->dat[0xe] & 0x3); } else if(mode-- & 0x1) { puticn(&ppu, layer, x, y, addr, d->dat[0xe] & 0xf, mode & 0x2, mode & 0x4); } else { putchr(&ppu, layer, x, y, addr, d->dat[0xe] & 0xf, mode & 0x2, mode & 0x4); } reqdraw = 1; } } void datetime_talk(Device *d, Uint8 b0, Uint8 w) { struct tm *t = localtime(&time_seconds); t->tm_year += 1900; mempoke16(d->dat, 0x0, t->tm_year); d->dat[0x2] = t->tm_mon; d->dat[0x3] = t->tm_mday; d->dat[0x4] = t->tm_hour; d->dat[0x5] = t->tm_min; d->dat[0x6] = t->tm_sec; d->dat[0x7] = t->tm_wday; mempoke16(d->dat, 0x08, t->tm_yday); d->dat[0xa] = t->tm_isdst; (void)b0; (void)w; } void init_uxn(Uxn *u) { // Initialize PPU. initppu(&ppu, 30, 20, 0); // Init clock. time_seconds = time(NULL); // Copy rom to VM. memcpy(u->ram.dat + PAGE_PROGRAM, uxn_rom, sizeof(uxn_rom)); // Prepare devices. portuxn(u, 0x0, "system", system_talk); portuxn(u, 0x1, "console", console_talk); devscreen = portuxn(u, 0x2, "screen", screen_talk); portuxn(u, 0x3, "---", nil_talk); portuxn(u, 0x4, "---", nil_talk); portuxn(u, 0x5, "---", nil_talk); portuxn(u, 0x6, "---", nil_talk); portuxn(u, 0x7, "---", nil_talk); devctrl = portuxn(u, 0x8, "controller", nil_talk); portuxn(u, 0x9, "---", nil_talk); portuxn(u, 0xa, "---", nil_talk); portuxn(u, 0xb, "datetime", datetime_talk); portuxn(u, 0xc, "---", nil_talk); portuxn(u, 0xd, "---", nil_talk); portuxn(u, 0xe, "---", nil_talk); portuxn(u, 0xf, "---", nil_talk); mempoke16(devscreen->dat, 2, ppu.hor * 8); mempoke16(devscreen->dat, 4, ppu.ver * 8); } void handle_input(Uxn *u) { poll_keys(); // TODO: We don't need ifs if we use KEY_INPUTS directly and mayvbe just // swap some things if needed. Uint8 *flag = &devctrl->dat[2]; if (key_pressed(KEY_A) | key_hold(KEY_A)) { *flag |= 0x01; } else { *flag &= ~0x01; } if (key_pressed(KEY_B) | key_hold(KEY_B)) { *flag |= 0x02; } else { *flag &= ~0x02; } if (key_pressed(KEY_L) | key_hold(KEY_L)) { *flag |= 0x04; } else { *flag &= ~0x04; } if (key_pressed(KEY_R) | key_hold(KEY_R)) { *flag |= 0x08; } else { *flag &= ~0x08; } if (key_pressed(KEY_UP) | key_hold(KEY_UP)) { *flag |= 0x10; } else { *flag &= ~0x10; } if (key_pressed(KEY_DOWN) | key_hold(KEY_DOWN)) { *flag |= 0x20; } else { *flag &= ~0x20; } if (key_pressed(KEY_LEFT) | key_hold(KEY_LEFT)) { *flag |= 0x40; } else { *flag &= ~0x40; } if (key_pressed(KEY_RIGHT) | key_hold(KEY_RIGHT)) { *flag |= 0x80; } else { *flag &= ~0x80; } evaluxn(u, mempeek16(devctrl->dat, 0)); devctrl->dat[3] = 0; } static Uxn u; EWRAM_BSS static Uint8 umem[65536]; int main(void) { // Register interrupts. irq_init(); irs_set(IRQ_VBLANK, irs_stub); // Initialize VM. memset(&u, 0, sizeof(u)); u.ram.dat = umem; init_uxn(&u); // Initialize text engine. txt_init_hybrid( TXT_MODE_HYBRID, (Font){ .data = bd_font, .char_height = 8, .char_width = 8, }, &ppu.fg); txt_position(0,0); // Main loop. int frame_counter = 0; evaluxn(&u, 0x0100); // flipbuf(&ppu); while(true) { bios_vblank_wait(); handle_input(&u); profile_start(); evaluxn(&u, mempeek16(devscreen->dat, 0)); int eval_cycles = profile_stop(); flipbuf(&ppu); txt_position(0,0); txt_printf("FRAME: %d\n", frame_counter); txt_printf("EVAL: %d\n", eval_cycles); time_seconds++; frame_counter++; } return 0; }