/* Varvara plugin for rockbox. I have no idea what I'm doing. copyright (c) 2021 Devine Lu Linvega copyright (c) 2021 nihilazo 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. TODO proper support for greyscale displays TODO rom loading TODO other varvara devices TODO clean up */ #include "uxn.h" #include "devices/ppu.h" #include "plugin.h" #include #include #include const Uint8 hwrom[] = { /* hello world ROM */ 0x20, 0x01, 0x0e, 0x94, 0x80, 0x18, 0x17, 0x21, 0x94, 0x80, 0xf7, 0x0d, 0x22, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x55, 0x78, 0x6e, 0x21, 0x21, }; const Uint8 uxn_rom[] = { /* screen test ROM */ 0x20, 0x01, 0x43, 0x80, 0x20, 0x37, 0x20, 0xf0, 0x7f, 0x80, 0x08, 0x37, 0x20, 0xf0, 0xe0, 0x80, 0x0a, 0x37, 0x20, 0xf0, 0xc0, 0x80, 0x0c, 0x37, 0x80, 0x22, 0x36, 0x80, 0x01, 0x3f, 0x20, 0x00, 0x20, 0x39, 0x80, 0x02, 0x31, 0x80, 0x24, 0x36, 0x80, 0x01, 0x3f, 0x80, 0x04, 0x31, 0x20, 0x01, 0xd4, 0x2e, 0x20, 0x02, 0x2f, 0x2e, 0x20, 0x02, 0x71, 0x2e, 0x20, 0x02, 0xae, 0x2e, 0x20, 0x03, 0x3a, 0x2e, 0x00, 0x80, 0x00, 0x30, 0x21, 0x23, 0x80, 0x00, 0x31, 0x80, 0x02, 0x30, 0x20, 0x00, 0x48, 0x38, 0x80, 0x28, 0x37, 0x80, 0x04, 0x30, 0x20, 0x00, 0x50, 0x39, 0x80, 0x2a, 0x37, 0x80, 0x01, 0x0f, 0x05, 0x03, 0x80, 0x04, 0x1f, 0x80, 0x00, 0x05, 0x80, 0x30, 0x3f, 0x20, 0x03, 0x91, 0x38, 0x80, 0x2c, 0x37, 0xcf, 0x80, 0x2f, 0x17, 0x80, 0x0f, 0x1c, 0x80, 0x00, 0x05, 0x80, 0x30, 0x3f, 0x20, 0x03, 0x91, 0x38, 0x80, 0x2c, 0x37, 0x80, 0x28, 0x36, 0x20, 0x00, 0x08, 0x38, 0x80, 0x28, 0x37, 0xcf, 0x80, 0x2f, 0x17, 0x03, 0x80, 0x04, 0x1f, 0x80, 0x00, 0x05, 0x80, 0x30, 0x3f, 0x20, 0x03, 0x91, 0x38, 0x80, 0x2c, 0x37, 0x80, 0x28, 0x36, 0x20, 0x00, 0x08, 0x38, 0x80, 0x28, 0x37, 0xcf, 0x80, 0x2f, 0x17, 0x80, 0x0f, 0x1c, 0x80, 0x00, 0x05, 0x80, 0x30, 0x3f, 0x20, 0x03, 0x91, 0x38, 0x80, 0x2c, 0x37, 0x80, 0x28, 0x36, 0x20, 0x00, 0x08, 0x38, 0x80, 0x28, 0x37, 0x4f, 0x80, 0x2f, 0x17, 0x00, 0x80, 0x10, 0x80, 0x00, 0x03, 0x80, 0x30, 0x1f, 0x80, 0x00, 0x05, 0x20, 0x03, 0x91, 0x38, 0x80, 0x2c, 0x37, 0x03, 0x80, 0x30, 0x1f, 0x80, 0x00, 0x05, 0x80, 0x02, 0x30, 0x20, 0x00, 0x40, 0x39, 0x38, 0x80, 0x28, 0x37, 0x80, 0x04, 0x30, 0x20, 0x00, 0x50, 0x39, 0x80, 0x2a, 0x37, 0x80, 0x01, 0x80, 0x2f, 0x17, 0x03, 0x80, 0x30, 0x1f, 0x80, 0x00, 0x05, 0x80, 0x04, 0x30, 0x20, 0x00, 0x40, 0x39, 0x38, 0x80, 0x2a, 0x37, 0x80, 0x02, 0x30, 0x20, 0x00, 0x50, 0x39, 0x80, 0x28, 0x37, 0x80, 0x01, 0x80, 0x2f, 0x17, 0x01, 0x8a, 0x80, 0xab, 0x0d, 0x22, 0x6c, 0x20, 0x03, 0x81, 0x80, 0x2c, 0x37, 0x80, 0x00, 0x80, 0x00, 0x03, 0x80, 0x0f, 0x1c, 0x80, 0x40, 0x1f, 0x80, 0x01, 0x1f, 0x80, 0x00, 0x05, 0x80, 0x02, 0x30, 0x20, 0x00, 0x40, 0x39, 0x38, 0x80, 0x28, 0x37, 0x03, 0x80, 0xf0, 0x1c, 0x80, 0x01, 0x1f, 0x80, 0x00, 0x05, 0x80, 0x04, 0x30, 0x20, 0x00, 0x40, 0x39, 0x38, 0x80, 0x2a, 0x37, 0x03, 0x80, 0x2f, 0x17, 0x01, 0x89, 0x80, 0xca, 0x0d, 0x22, 0x6c, 0x80, 0x10, 0x80, 0x00, 0x8f, 0x03, 0x80, 0x02, 0x1f, 0x80, 0x00, 0x05, 0x80, 0x40, 0x3f, 0x80, 0x04, 0x30, 0x20, 0x00, 0x40, 0x39, 0x38, 0x2f, 0x03, 0x80, 0x03, 0x1c, 0x80, 0x00, 0x05, 0x80, 0x40, 0x3f, 0x20, 0x00, 0x40, 0x38, 0x80, 0x02, 0x30, 0x20, 0x00, 0x08, 0x38, 0x38, 0x6f, 0x4f, 0x80, 0x00, 0x20, 0x02, 0xe7, 0x2e, 0x01, 0x8a, 0x80, 0xc9, 0x0d, 0x22, 0x6c, 0x80, 0x10, 0x80, 0x00, 0x8f, 0x03, 0x80, 0x02, 0x1f, 0x80, 0x00, 0x05, 0x80, 0x40, 0x3f, 0x80, 0x04, 0x30, 0x38, 0x2f, 0x03, 0x80, 0x03, 0x1c, 0x80, 0x00, 0x05, 0x80, 0x40, 0x3f, 0x20, 0x00, 0x40, 0x38, 0x80, 0x02, 0x30, 0x20, 0x00, 0x08, 0x38, 0x38, 0x6f, 0x4f, 0x80, 0x80, 0x20, 0x02, 0xe7, 0x2e, 0x01, 0x8a, 0x80, 0xcd, 0x0d, 0x22, 0x6c, 0x18, 0x0f, 0x20, 0x03, 0x81, 0x80, 0x2c, 0x37, 0x80, 0x2a, 0x37, 0x80, 0x28, 0x37, 0x80, 0x00, 0xcf, 0x18, 0x80, 0x2f, 0x17, 0x80, 0x28, 0x36, 0x20, 0x00, 0x08, 0x38, 0x80, 0x28, 0x37, 0x80, 0x10, 0xcf, 0x18, 0x80, 0x2f, 0x17, 0x80, 0x28, 0x36, 0x20, 0x00, 0x08, 0x39, 0x80, 0x28, 0x37, 0x80, 0x2a, 0x36, 0x20, 0x00, 0x08, 0x38, 0x80, 0x2a, 0x37, 0x80, 0x20, 0xcf, 0x18, 0x80, 0x2f, 0x17, 0x80, 0x28, 0x36, 0x20, 0x00, 0x08, 0x38, 0x80, 0x28, 0x37, 0x80, 0x30, 0x4f, 0x18, 0x80, 0x2f, 0x17, 0x6c, 0x80, 0x04, 0x30, 0x20, 0x00, 0x40, 0x39, 0x80, 0x2a, 0x37, 0x80, 0x02, 0x30, 0x20, 0x00, 0x48, 0x38, 0x80, 0x28, 0x37, 0x80, 0x00, 0x80, 0x2e, 0x17, 0x80, 0x02, 0x30, 0x20, 0x00, 0x49, 0x38, 0x80, 0x28, 0x37, 0x80, 0x01, 0x80, 0x2e, 0x17, 0x80, 0x02, 0x30, 0x20, 0x00, 0x4a, 0x38, 0x80, 0x28, 0x37, 0x80, 0x02, 0x80, 0x2e, 0x17, 0x80, 0x02, 0x30, 0x20, 0x00, 0x4b, 0x38, 0x80, 0x28, 0x37, 0x80, 0x03, 0x80, 0x2e, 0x17, 0x6c, 0x0f, 0x38, 0x67, 0x5f, 0xdf, 0xbf, 0xbf, 0xbf, 0x00, 0x07, 0x18, 0x20, 0x23, 0x44, 0x48, 0x48, 0x00, 0x7c, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7c, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x7c, 0x82, 0x02, 0x7c, 0x80, 0x80, 0xfe, 0x00, 0x7c, 0x82, 0x02, 0x1c, 0x02, 0x82, 0x7c, 0x00, 0x0c, 0x14, 0x24, 0x44, 0x84, 0xfe, 0x04, 0x00, 0xfe, 0x80, 0x80, 0x7c, 0x02, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x80, 0xfc, 0x82, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x02, 0x1e, 0x02, 0x02, 0x02, 0x00, 0x7c, 0x82, 0x82, 0x7c, 0x82, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x82, 0x7e, 0x02, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x02, 0x7e, 0x82, 0x82, 0x7e, 0x00, 0xfc, 0x82, 0x82, 0xfc, 0x82, 0x82, 0xfc, 0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0x82, 0x7c, 0x00, 0xfc, 0x82, 0x82, 0x82, 0x82, 0x82, 0xfc, 0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x80, 0x80, 0x80, }; const Uint8 vector_rom[] = { // screen vector test ROM 0x20, 0x01, 0x19, 0x80, 0x20, 0x37, 0x20, 0x0f, 0x7f, 0x80, 0x08, 0x37, 0x20, 0x0f, 0xe0, 0x80, 0x0a, 0x37, 0x20, 0x0f, 0xc0, 0x80, 0x0c, 0x37, 0x00, 0x80, 0x00, 0x30, 0x21, 0x23, 0x80, 0x00, 0x31, 0x20, 0x00, 0x00, 0x80, 0x28, 0x37, 0x20, 0x00, 0x00, 0x80, 0x2a, 0x37, 0x80, 0x01, 0x0f, 0x05, 0x03, 0x80, 0x04, 0x1f, 0x80, 0x00, 0x05, 0x80, 0x30, 0x3f, 0x20, 0x01, 0xb2, 0x38, 0x80, 0x2c, 0x37, 0xcf, 0x80, 0x2f, 0x17, 0x80, 0x0f, 0x1c, 0x80, 0x00, 0x05, 0x80, 0x30, 0x3f, 0x20, 0x01, 0xb2, 0x38, 0x80, 0x2c, 0x37, 0x80, 0x28, 0x36, 0x20, 0x00, 0x08, 0x38, 0x80, 0x28, 0x37, 0xcf, 0x80, 0x2f, 0x17, 0x03, 0x80, 0x04, 0x1f, 0x80, 0x00, 0x05, 0x80, 0x30, 0x3f, 0x20, 0x01, 0xb2, 0x38, 0x80, 0x2c, 0x37, 0x80, 0x28, 0x36, 0x20, 0x00, 0x08, 0x38, 0x80, 0x28, 0x37, 0xcf, 0x80, 0x2f, 0x17, 0x80, 0x0f, 0x1c, 0x80, 0x00, 0x05, 0x80, 0x30, 0x3f, 0x20, 0x01, 0xb2, 0x38, 0x80, 0x2c, 0x37, 0x80, 0x28, 0x36, 0x20, 0x00, 0x08, 0x38, 0x80, 0x28, 0x37, 0x4f, 0x80, 0x2f, 0x17, 0x00, 0x0f, 0x38, 0x67, 0x5f, 0xdf, 0xbf, 0xbf, 0xbf, 0x00, 0x07, 0x18, 0x20, 0x23, 0x44, 0x48, 0x48, 0x00, 0x7c, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7c, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x7c, 0x82, 0x02, 0x7c, 0x80, 0x80, 0xfe, 0x00, 0x7c, 0x82, 0x02, 0x1c, 0x02, 0x82, 0x7c, 0x00, 0x0c, 0x14, 0x24, 0x44, 0x84, 0xfe, 0x04, 0x00, 0xfe, 0x80, 0x80, 0x7c, 0x02, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x80, 0xfc, 0x82, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x02, 0x1e, 0x02, 0x02, 0x02, 0x00, 0x7c, 0x82, 0x82, 0x7c, 0x82, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x82, 0x7e, 0x02, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x02, 0x7e, 0x82, 0x82, 0x7e, 0x00, 0xfc, 0x82, 0x82, 0xfc, 0x82, 0x82, 0xfc, 0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0x82, 0x7c, 0x00, 0xfc, 0x82, 0x82, 0x82, 0x82, 0x82, 0xfc, 0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x80, 0x80, 0x80, }; static Uxn u; static Ppu ppu; static Device *devsystem, *devconsole, *devscreen; unsigned int palette[3]; static Uint8 framebuffer[LCD_HEIGHT * LCD_WIDTH * 4]; static void memzero8(void *src, uint64_t n) { uint8_t * ptr = src; for (size_t i = 0; i < n; i++) { ptr[i] = 0; } } /* taken from uxncli */ static void inspect(Stack *s, char *name) { Uint8 x, y; DEBUGF("\n%s\n", name); for(y = 0; y < 0x04; ++y) { for(x = 0; x < 0x08; ++x) { Uint8 p = y * 0x08 + x; DEBUGF(p == s->ptr ? "[%02x]" : " %02x ", s->dat[p]); } DEBUGF("\n"); } } static void set_palette(Uint8 *addr) { #if LCD_HAS_COLOR int i; for(i = 0; i < 4; ++i) { Uint8 r = (*(addr + i / 2) >> (!(i % 2) << 2)) & 0x0f, g = (*(addr + 2 + i / 2) >> (!(i % 2) << 2)) & 0x0f, b = (*(addr + 4 + i / 2) >> (!(i % 2) << 2)) & 0x0f; palette[i] = LCD_RGBPACK(r*8,g*8,b*8); } #elif LCD_DEPTH > 1 // greyscale int i; for(i = 0; i < 4; ++i) { Uint8 sum = (*(addr + i / 2) >> (!(i % 2) << 2)) & 0x0f; sum += (*(addr + 2 + i / 2) >> (!(i % 2) << 2)) & 0x0f; sum += (*(addr + 4 + i / 2) >> (!(i % 2) << 2)) & 0x0f; palette[i] = sum; } #else int i; for(i = 0; i < 4; ++i) { Uint8 sum = (*(addr + i / 2) >> (!(i % 2) << 2)) & 0x0f; sum += (*(addr + 2 + i / 2) >> (!(i % 2) << 2)) & 0x0f; sum += (*(addr + 4 + i / 2) >> (!(i % 2) << 2)) & 0x0f; palette[i] = sum > 0x17; } #endif } /* taken from uxncli */ static int system_talk(Device *d, Uint8 b0, Uint8 w) { if(!w) { /* read */ switch(b0) { case 0x2: d->dat[0x2] = d->u->wst.ptr; break; case 0x3: d->dat[0x3] = d->u->rst.ptr; break; } } else { /* write */ switch(b0) { case 0x2: d->u->wst.ptr = d->dat[0x2]; break; case 0x3: d->u->rst.ptr = d->dat[0x3]; break; case 0xe: inspect(&d->u->wst, "Working-stack"); inspect(&d->u->rst, "Return-stack"); break; case 0xf: return 0; } if(b0 > 0x7 && b0 < 0xe) set_palette(&d->dat[0x8]); } return 1; } /* taken from uxncli */ static int console_talk(Device *d, Uint8 b0, Uint8 w) { if(b0 == 0x1) d->vector = peek16(d->dat, 0x0); if(w && b0 > 0x7) DEBUGF("%c",(char *)&d->dat[b0]); return 1; } /* taken from uxnemu */ static int screen_talk(Device *d, Uint8 b0, Uint8 w) { if(!w) switch(b0) { case 0x2: d->dat[0x2] = ppu.width >> 8; break; case 0x3: d->dat[0x3] = ppu.width; break; case 0x4: d->dat[0x4] = ppu.height >> 8; break; case 0x5: d->dat[0x5] = ppu.height; break; } else switch(b0) { case 0x1: d->vector = peek16(d->dat, 0x0); break; case 0x5: break; case 0xe: { Uint16 x = peek16(d->dat, 0x8); Uint16 y = peek16(d->dat, 0xa); Uint8 layer = d->dat[0xe] & 0x40; ppu_write(&ppu, !!layer, x, y, d->dat[0xe] & 0x3); if(d->dat[0x6] & 0x01) poke16(d->dat, 0x8, x + 1); /* auto x+1 */ if(d->dat[0x6] & 0x02) poke16(d->dat, 0xa, y + 1); /* auto y+1 */ break; } case 0xf: { Uint16 x = peek16(d->dat, 0x8); Uint16 y = peek16(d->dat, 0xa); Uint8 layer = d->dat[0xf] & 0x40; Uint8 *addr = &d->mem[peek16(d->dat, 0xc)]; if(d->dat[0xf] & 0x80) { ppu_2bpp(&ppu, !!layer, x, y, addr, d->dat[0xf] & 0xf, d->dat[0xf] & 0x10, d->dat[0xf] & 0x20); if(d->dat[0x6] & 0x04) poke16(d->dat, 0xc, peek16(d->dat, 0xc) + 16); /* auto addr+16 */ } else { ppu_1bpp(&ppu, !!layer, x, y, addr, d->dat[0xf] & 0xf, d->dat[0xf] & 0x10, d->dat[0xf] & 0x20); if(d->dat[0x6] & 0x04) poke16(d->dat, 0xc, peek16(d->dat, 0xc) + 8); /* auto addr+8 */ } if(d->dat[0x6] & 0x01) poke16(d->dat, 0x8, x + 8); /* auto x+8 */ if(d->dat[0x6] & 0x02) poke16(d->dat, 0xa, y + 8); /* auto y+8 */ break; } } return 1; } static int nil_talk(Device *d, Uint8 b0, Uint8 w) { (void)d; (void)b0; (void)w; return 1; } // TODO optimise, proper greyscale static void redraw(void) { rb->lcd_clear_display(); Uint16 x, y; for(y = 0; y < ppu.height; ++y) for(x = 0; x < ppu.width; ++x) { #if LCD_HAS_COLOR rb->lcd_set_foreground(palette[ppu_read(&ppu, x, y)]); rb->lcd_drawpixel(x, y); #elif LCD_DEPTH > 1 rb->lcd_set_foreground(palette[ppu_read(&ppu, x, y)]); rb->lcd_drawpixel(x, y); #else if(palette[ppu_read(&ppu, x, y)]) { rb->lcd_drawpixel(x, y); } #endif } rb->lcd_update(); ppu.reqdraw = 0; } int uxn_halt(Uxn *u, Uint8 error, char *name, int id) { rb->splash(HZ, "Halted\n"); return 0; } static void run() { while(!devsystem->dat[0xf]) { uxn_eval(&u, devscreen->vector); if(ppu.reqdraw || devsystem->dat[0xe]) redraw(); switch(rb->button_get(false)) { // TODO implement other buttons case BUTTON_NONE: break; default: return; } rb->sleep(HZ/60); } } /* this is the plugin entry point */ enum plugin_status plugin_start(const void* parameter) { (void)parameter; DEBUGF("Setting PPU size\n"); ppu_init(&ppu, LCD_WIDTH, LCD_HEIGHT, framebuffer); DEBUGF("UXN init\n"); // Clear RAM and copy rom to VM. DEBUGF("zeroing\n"); memzero8(&u, sizeof(Uxn)); DEBUGF("copying ROM\n"); memcpy(u.ram.dat + PAGE_PROGRAM, uxn_rom, sizeof(uxn_rom)); DEBUGF("registering ports\n"); // Register ports /* system */ devsystem = uxn_port(&u, 0x0, system_talk); /* console */ devconsole = uxn_port(&u, 0x1, console_talk); /* screen */ devscreen = uxn_port(&u, 0x2, screen_talk); /* audio0 */ uxn_port(&u, 0x3, nil_talk); /* audio1 */ uxn_port(&u, 0x4, nil_talk); /* audio2 */ uxn_port(&u, 0x5, nil_talk); /* audio3 */ uxn_port(&u, 0x6, nil_talk); /* empty */ uxn_port(&u, 0x7, nil_talk); /* control */ uxn_port(&u, 0x8, nil_talk); /* mouse */ uxn_port(&u, 0x9, nil_talk); /* file */ uxn_port(&u, 0xa, nil_talk); /* datetime */ uxn_port(&u, 0xb, nil_talk); /* empty */ uxn_port(&u, 0xc, nil_talk); /* empty */ uxn_port(&u, 0xd, nil_talk); /* empty */ uxn_port(&u, 0xe, nil_talk); /* empty */ uxn_port(&u, 0xf, nil_talk); DEBUGF("eval\n"); uxn_eval(&u, 0x0100); run(); return PLUGIN_OK; }