From 7654bb013814d7cf317f7dfd69238abe6dc30ef1 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Wed, 8 Sep 2021 13:28:56 +0200 Subject: Add initial PPU implementation for UXN --- src/main.c | 124 +++++++++++++++++++++++++------------------------------------ src/ppu.c | 85 +++++++++++++++++++++++++++++++++++++++--- src/ppu.h | 25 +++++++++++++ 3 files changed, 156 insertions(+), 78 deletions(-) create mode 100644 src/ppu.h diff --git a/src/main.c b/src/main.c index f2ffa5b..fc073c9 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,7 @@ #include "rom.c" static Uxn u; +static Ppu ppu; static Device *devscreen; static Device *devctrl; static Device *devmouse; @@ -24,6 +25,8 @@ console_talk(Device *d, u8 b0, u8 w) { } switch(b0) { case 0x8: stmp[0] = d->dat[0x8]; stmp[1] = 0; uart_puts(stmp); break; + // TODO: implement printf for the uart to be able to format + // numbers. // 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; @@ -32,72 +35,55 @@ console_talk(Device *d, u8 b0, u8 w) { static void docolors(Device *d) { - for(size_t i = 0; i < 4; ++i) { - u8 r = ((d->dat[0x8 + i / 2] >> (!(i % 2) << 2)) & 0x0f) * 0x11; - u8 g = ((d->dat[0xa + i / 2] >> (!(i % 2) << 2)) & 0x0f) * 0x11; - u8 b = ((d->dat[0xc + i / 2] >> (!(i % 2) << 2)) & 0x0f) * 0x11; - palette[i] = (b << 16) | (g << 8) | r; - } + for(size_t i = 0; i < 4; ++i) { + u8 r = ((d->dat[0x8 + i / 2] >> (!(i % 2) << 2)) & 0x0f) * 0x11; + u8 g = ((d->dat[0xa + i / 2] >> (!(i % 2) << 2)) & 0x0f) * 0x11; + u8 b = ((d->dat[0xc + i / 2] >> (!(i % 2) << 2)) & 0x0f) * 0x11; + palette[i] = (b << 16) | (g << 8) | r; + } + for(size_t i = 4; i < 16; ++i) { + palette[i] = palette[i / 4]; + } } void system_talk(Device *d, u8 b0, u8 w) { - // uart_puts("system_talk\n"); - // TODO: Implement... - 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 0xf: d->u->ram.ptr = 0x0000; break; - } - if(b0 > 0x7 && b0 < 0xe) { - docolors(d); + 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 0xf: d->u->ram.ptr = 0x0000; break; } - } + if(b0 > 0x7 && b0 < 0xe) { + docolors(d); + } + } (void)b0; } void screen_talk(Device *d, u8 b0, u8 w) { // uart_puts("screen_talk\n"); - // TODO: Implement... - // if(w && b0 == 0xe) { - // u16 x = mempeek16(d->dat, 0x8); - // u16 y = mempeek16(d->dat, 0xa); - // u8 *addr = &d->mem[mempeek16(d->dat, 0xc)]; - // u8 *layer = d->dat[0xe] >> 4 & 0x1 ? ppu.fg : ppu.bg; - // u8 mode = d->dat[0xe] >> 5; - // u8 color = d->dat[0xf] & 0xf; - // if(!mode) { - // ppu_pixel(layer, x, y, d->dat[0xe] & 0x3); - // } else if(mode-- & 0x1) { - // u8 flipx = mode & 0x2; - // u8 flipy = mode & 0x4; - // ppu_1bpp(layer, x, y, addr, color, flipx, flipy); - // } else { - // u8 flipx = mode & 0x2; - // u8 flipy = mode & 0x4; - // ppu_2bpp(layer, x, y, addr, color, flipx, flipy); - // } - // } else if(w && b0 == 0xf) { - // u16 x = mempeek16(d->dat, 0x8); - // u16 y = mempeek16(d->dat, 0xa); - // u8 *addr = &d->mem[mempeek16(d->dat, 0xc)]; - // u8 *layer = d->dat[0xf] >> 6 & 0x1 ? ppu.fg : ppu.bg; - // u8 color = d->dat[0xf] & 0xf; - // u8 flipx = (d->dat[0xf] >> 0x4) & 0x1; - // u8 flipy = (d->dat[0xf] >> 0x5) & 0x1; - // if(d->dat[0xf] >> 0x7 & 0x1) { - // ppu_2bpp(layer, x, y, addr, color, flipx, flipy); - // } else { - // ppu_1bpp(layer, x, y, addr, color, flipx, flipy); - // } - // } + if(w && b0 == 0xe) { + Uint16 x = mempeek16(d->dat, 0x8); + Uint16 y = mempeek16(d->dat, 0xa); + Uint8 layer = d->dat[0xe] >> 4 & 0x1; + ppu_pixel(&ppu, layer, x, y, d->dat[0xe] & 0x3); + } else if(w && b0 == 0xf) { + Uint16 x = mempeek16(d->dat, 0x8); + Uint16 y = mempeek16(d->dat, 0xa); + Uint8 layer = d->dat[0xf] >> 0x6 & 0x1; + Uint8 *addr = &d->mem[mempeek16(d->dat, 0xc)]; + if(d->dat[0xf] >> 0x7 & 0x1) + ppu_2bpp(&ppu, layer, x, y, addr, d->dat[0xf] & 0xf, d->dat[0xf] >> 0x4 & 0x1, d->dat[0xf] >> 0x5 & 0x1); + else + ppu_1bpp(&ppu, layer, x, y, addr, d->dat[0xf] & 0xf, d->dat[0xf] >> 0x4 & 0x1, d->dat[0xf] >> 0x5 & 0x1); + } } static void @@ -111,8 +97,8 @@ audio_talk(Device *d, u8 b0, u8 w) { void datetime_talk(Device *d, u8 b0, u8 w) { - // uart_puts("datetime_talk\n"); // TODO: Implement... + // uart_puts("datetime_talk\n"); (void)d; (void)b0; (void)w; @@ -120,8 +106,8 @@ datetime_talk(Device *d, u8 b0, u8 w) { void file_talk(Device *d, u8 b0, u8 w) { - // uart_puts("file_talk\n"); // TODO: Implement... + // uart_puts("file_talk\n"); (void)d; (void)b0; (void)w; @@ -131,7 +117,7 @@ void init_uxn() { uart_puts("Initializing UXN.\n"); uart_init(); - ppu_init(); + ppu_init(&ppu, SCREEN_WIDTH / 8, SCREEN_HEIGHT / 8); // Copy rom to VM. memcpy(u.ram.dat + PAGE_PROGRAM, uxn_rom, sizeof(uxn_rom)); @@ -153,8 +139,8 @@ init_uxn() { uxn_port(&u, 0xd, "---", nil_talk); uxn_port(&u, 0xe, "---", nil_talk); uxn_port(&u, 0xf, "---", nil_talk); - // mempoke16(devscreen->dat, 2, ppu.hor * 8); - // mempoke16(devscreen->dat, 4, ppu.ver * 8); + mempoke16(devscreen->dat, 2, ppu.width); + mempoke16(devscreen->dat, 4, ppu.height); } void main(void) { @@ -166,19 +152,11 @@ void main(void) { // Echo input to standard output. uxn_eval(&u, mempeek16(devscreen->dat, 0)); - // DEBUG: testing fb drawing. - u32 lines = SCREEN_WIDTH * SCREEN_HEIGHT / 4; - for (size_t i = 0; i < lines; i++) { - framebuffer[i] = palette[0]; - } - for (size_t i = lines; i < lines * 2; i++) { - framebuffer[i] = palette[1]; - } - for (size_t i = lines * 2; i < lines * 3; i++) { - framebuffer[i] = palette[2]; - } - for (size_t i = lines * 3; i < lines * 4; i++) { - framebuffer[i] = palette[3]; + // Blit ppu.pixels to the framebuffer. + // NOTE: This is very inefficient, we likely want to keep track of the + // lines/tiles that have changed and only copy those. + for (size_t i = 0; i < SCREEN_WIDTH * SCREEN_HEIGHT; i++) { + framebuffer[i] = palette[ppu.pixels[i]]; } } } diff --git a/src/ppu.c b/src/ppu.c index 2e0404f..aeb3699 100644 --- a/src/ppu.c +++ b/src/ppu.c @@ -1,14 +1,85 @@ #include "common.h" +#include "ppu.h" + + +/* +Copyright (c) 2021 Devine Lu Linvega +Copyright (c) 2021 Andrew Alderwick +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. +*/ + +#define SCREEN_WIDTH 64 * 8 / 2 +#define SCREEN_HEIGHT 40 * 8 / 2 static u32 *framebuffer = 0; -#define SCREEN_WIDTH 800 -#define SCREEN_HEIGHT 600 +static u32 palette[16]; +static u8 pixels_buf[SCREEN_WIDTH * SCREEN_HEIGHT]; -static u32 palette[4]; +static Uint8 blending[5][16] = { + {0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0}, + {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, + {1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1}, + {2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2}, + {1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}}; void -ppu_init(void) { +ppu_pixel(Ppu *p, Uint8 layer, Uint16 x, Uint16 y, Uint8 color) +{ + size_t idx = y * p->width + x; + Uint8 *pixel = &p->pixels[idx], shift = layer * 2; + if(x < p->width && y < p->height) { + *pixel = (*pixel & ~(0x3 << shift)) | (color << shift); + // framebuffer[idx] = palette[*pixel]; + } +} + +void +ppu_1bpp(Ppu *p, Uint8 layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy) +{ + Uint16 v, h; + for(v = 0; v < 8; v++) + for(h = 0; h < 8; h++) { + Uint8 ch1 = (sprite[v] >> (7 - h)) & 0x1; + if(ch1 || blending[4][color]) + ppu_pixel(p, + layer, + x + (flipx ? 7 - h : h), + y + (flipy ? 7 - v : v), + blending[ch1][color]); + } +} + +void +ppu_2bpp(Ppu *p, Uint8 layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy) +{ + Uint16 v, h; + for(v = 0; v < 8; v++) + for(h = 0; h < 8; h++) { + Uint8 ch1 = ((sprite[v] >> (7 - h)) & 0x1); + Uint8 ch2 = ((sprite[v + 8] >> (7 - h)) & 0x1); + Uint8 ch = ch1 + ch2 * 2; + if(ch || blending[4][color]) + ppu_pixel(p, + layer, + x + (flipx ? 7 - h : h), + y + (flipy ? 7 - v : v), + blending[ch][color]); + } +} + +int +ppu_init(Ppu *p, Uint8 hor, Uint8 ver) { + p->width = 8 * hor; + p->height = 8 * ver; + // Initialize the framebuffer. static MboxFramebufferRequest fb_request = { .buf_size = 96, @@ -53,12 +124,16 @@ ppu_init(void) { framebuffer = (u32*)((uintptr_t)fb_request.framebuffer_tag.fb_addr); } else { uart_puts("Unable initialize framebuffer\n"); - return; + return 0; } + p->pixels = pixels_buf; + // Initialize default palette. palette[0] = 0x00444444; palette[1] = 0x00ffffff; palette[2] = 0x007777ff; palette[3] = 0x00ff7777; + + return 1; } diff --git a/src/ppu.h b/src/ppu.h new file mode 100644 index 0000000..45af70b --- /dev/null +++ b/src/ppu.h @@ -0,0 +1,25 @@ +/* +Copyright (c) 2021 Devine Lu Linvega +Copyright (c) 2021 Andrew Alderwick + +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. +*/ + +typedef unsigned char Uint8; +typedef unsigned short Uint16; +typedef unsigned int Uint32; + +typedef struct Ppu { + Uint16 width, height; + Uint8 *pixels; +} Ppu; + +int ppu_init(Ppu *p, Uint8 hor, Uint8 ver); +void ppu_pixel(Ppu *p, Uint8 layer, Uint16 x, Uint16 y, Uint8 color); +void ppu_1bpp(Ppu *p, Uint8 layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy); +void ppu_2bpp(Ppu *p, Uint8 layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy); -- cgit v1.2.1