static time_t seconds = 1693475007; #define RAM_PAGES 0x10 void deo_console(u8 *dev, u8 port) { switch(port) { case 0x8: txt_putc(dev[port]); return; case 0x9: txt_printf("ERROR: %c"); txt_putc(dev[port]); return; } } u16 dei_screen(u8 *dev, u8 port) { switch(port) { case 0x0: case 0x8: case 0xa: case 0xc: return PEEK2(dev + port); case 0x2: return SCREEN_WIDTH; case 0x4: return SCREEN_HEIGHT; default: return dev[port]; } } u16 dei_mouse(u8 *dev, u8 port) { switch(port) { case 0x0: case 0x2: case 0x4: case 0xa: case 0xc: return PEEK2(dev + port); default: return dev[port]; } } void deo_screen(u8 *dev, u8 port) { switch(port) { case 0xe: { u8 ctrl = dev[0xe]; u8 color = ctrl & 0x3; u16 x0 = PEEK2(dev + 0x8); u16 y0 = PEEK2(dev + 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(dev[0x6] & 0x1) POKE2(dev + 0x8, x0 + 1); /* auto x+1 */ if(dev[0x6] & 0x2) POKE2(dev + 0xa, y0 + 1); /* auto y+1 */ } break; } case 0xf: { u8 ctrl = dev[0xf]; u8 move = dev[0x6]; u8 length = move >> 4; u8 twobpp = !!(ctrl & 0x80); u8 *layer = (dev[0xf] & 0x40) ? FG_BACK : BG_BACK; u8 color = ctrl & 0xf; u16 x = PEEK2(dev + 0x8), dx = (move & 0x1) << 3; u16 y = PEEK2(dev + 0xa), dy = (move & 0x2) << 2; u16 addr = PEEK2(dev + 0xc), addr_incr = (move & 0x4) << (1 + twobpp); int flipx = (ctrl & 0x10), fx = flipx ? -1 : 1; int flipy = (ctrl & 0x20), fy = flipy ? -1 : 1; for(size_t i = 0; i <= length; i++) { u8 *sprite = &uxn_ram[addr]; if (twobpp) { PROF(ppu_2bpp(layer, x + dy * i * fx, y + dx * i * fy, sprite, color, flipx, flipy), ppu_chr_cycles); } else { PROF(ppu_1bpp(layer, x + dy * i * fx, y + dx * i * fy, sprite, color, flipx, flipy), ppu_icn_cycles); } addr += addr_incr; } if(move & 0x1) POKE2(dev + 0x8, x + dx * fx); /* auto x+8 */ if(move & 0x2) POKE2(dev + 0xa, y + dy * fy); /* auto y+8 */ if(move & 0x4) POKE2(dev + 0xc, addr); /* auto addr+length */ break; } } } void deo_system(u8 *dev, u8 port) { switch(port) { case 0x2: { // Rom bank switching. u16 addr = PEEK2(dev + port); if(uxn_ram[addr] == 0x01) { u16 length = PEEK2(uxn_ram + addr + 1); u16 a_page = PEEK2(uxn_ram + addr + 1 + 2); u16 a_addr = PEEK2(uxn_ram + addr + 1 + 4); u16 b_page = PEEK2(uxn_ram + addr + 1 + 6); u16 b_addr = PEEK2(uxn_ram + addr + 1 + 8); u8 *ram = uxn_ram + (b_page % RAM_PAGES) * 0x10000; u8 *rom = uxn_rom + (a_page % RAM_PAGES) * 0x10000 - PAGE_PROGRAM; for(size_t i = 0; i < length; i++) { ram[b_addr + i] = rom[a_addr + i]; } } } break; case 0x4: { // TODO: Set wst_ptr, but is it the offset instead? } break; case 0x5: { // TODO: Set rst_ptr, but is it the offset instead? } break; case 0x8: case 0x9: case 0xa: case 0xb: case 0xc: case 0xd: { // Setup RGB palette. putcolors(&dev[0x8]); } break; case 0xe: { // TODO: System inspect. } break; } } u16 dei_system(u8 *dev, u8 port) { switch (port) { case 0x0: case 0x2: case 0x6: case 0x8: case 0xa: case 0xc: return PEEK2(dev + port); case 0x4: { // TODO: Return wst_ptr, but is it the offset instead? } break; case 0x5: { // TODO: Return rst_ptr, but is it the offset instead? } break; } return dev[port]; } u16 dei_datetime(u8 *dev, u8 port) { struct tm *t = gmtime(&seconds); switch(port) { case 0x0: return (t->tm_year + 1900); case 0x1: return (t->tm_year + 1900) >> 8; 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; case 0x9: return t->tm_yday >> 8; case 0xa: return t->tm_isdst; } return dev[port]; } u16 dei_audio(u8 *dev, u8 port) { size_t idx = (dev - (device_data + 0x30)) / 16; AudioChannel *c = &channels[idx]; switch(port) { case 0x0: case 0x8: case 0xa: case 0x2: // TODO: return the position case 0xc: return PEEK2(dev + port); // case 0x2: { // POKE2(d + 0x2, c->pos); // c->pos <<= 12; // fixed point. // break; // } // case 0x4: return apu_get_vu(idx); // TODO: return the current envelope loudness (0x00-0xff). default: return dev[port]; } return dev[port]; } void deo_audio(u8 *dev, u8 port) { size_t idx = (dev - (device_data + 0x30)) / 16; txt_printf("IDX: %d\n", idx); AudioChannel *c = &channels[idx]; if (port == 0xf) { u16 length = 0; u16 adsr = 0; u16 addr = 0; u8 pitch = dev[0xf] & 0x7f; adsr = PEEK2(dev + 0x8); length = PEEK2(dev + 0xa); addr = PEEK2(dev + 0xc); u8 *data = &uxn_ram[addr]; u32 vol = MAX(dev[0xe] >> 4, dev[0xe] & 0xf) * 4 / 3; bool loop = !(dev[0xf] & 0x80); update_channel(c, data, length, pitch, adsr, vol, loop); } } u16 dei_file(u8 *dev, u8 port) { size_t idx = (dev - (device_data + 0xa0)) / 16; UxnFile *c = &uxn_file[idx]; switch(port) { case 0x0: case 0x2: case 0x4: case 0x8: case 0xa: case 0xe: return PEEK2(dev + port); case 0xc: case 0xd: { u16 res = file_read(c, &dev[port], 1); POKE2(dev + 0x2, res); return res; } } return dev[port]; } void deo_file(u8 *dev, u8 port) { size_t idx = (dev - (device_data + 0xa0)) / 16; u16 a, b, res; UxnFile *f = &uxn_file[idx]; switch(port) { case 0x5: { a = PEEK2(dev + 0x4); b = PEEK2(dev + 0xa); if(b > 0x10000 - a) { b = 0x10000 - a; } res = file_stat(f, &uxn_ram[a], b); POKE2(dev + 0x2, res); } break; case 0x6: { // TODO: no file deletion for now // res = file_delete(); // POKE2(dev + 0x2, res); } break; case 0x9: { a = PEEK2(dev + 0x8); res = file_init(f, &uxn_ram[a]); POKE2(dev + 0x2, res); } break; case 0xd: { a = PEEK2(dev + 0xc); b = PEEK2(dev + 0xa); if(b > 0x10000 - a) { b = 0x10000 - a; } res = file_read(f, &uxn_ram[a], b); POKE2(dev + 0x2, res); } break; case 0xf: { a = PEEK2(dev + 0xe); b = PEEK2(dev + 0xa); if(b > 0x10000 - a) { b = 0x10000 - a; } res = file_write(f, &uxn_ram[a], b, dev[0x7]); POKE2(dev + 0x2, res); } break; } } void deo_stub(u8 *dev, u8 port) { (void)dev; (void)port; } u16 dei_stub(u8 *dev, u8 port) { if (port == 0) { return PEEK2(dev); } return dev[port]; }