#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 #define SCREEN_HEIGHT 40 * 8 static u32 *framebuffer = 0; static u32 palette[16]; static u8 pixels_buf[SCREEN_WIDTH * SCREEN_HEIGHT]; static u8 dirty_lines[SCREEN_HEIGHT]; static u8 reqdraw = 0; static u32 rgb_order; 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_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); } dirty_lines[y] |= 1; } 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]); } } void redraw_screen(void) { for (size_t j = 0; j < SCREEN_HEIGHT; j++) { dirty_lines[j] = 1; } } void fb_init(void) { mbox[0] = 25 * 4; mbox[1] = MBOX_REQUEST; // Screen request. mbox[2] = MBOX_TAG_SET_SCREEN; mbox[3] = 8; mbox[4] = 0; mbox[5] = SCREEN_WIDTH; mbox[6] = SCREEN_HEIGHT; // Virtual screen. mbox[7] = MBOX_TAG_SET_VSCREEN; mbox[8] = 8; mbox[9] = 0; mbox[10] = SCREEN_WIDTH; mbox[11] = SCREEN_HEIGHT; // Bit depth. mbox[12] = MBOX_TAG_SET_DEPTH; mbox[13] = 4; mbox[14] = 0; mbox[15] = 32; // RGB order. mbox[16] = MBOX_TAG_SET_RGB; mbox[17] = 4; mbox[18] = 0; mbox[19] = 1; // Framebuffer request. mbox[20] = MBOX_TAG_GET_FB; mbox[21] = 8; mbox[22] = 0; mbox[23] = 0; mbox[24] = 0; // End tag. mbox[25] = MBOX_TAG_END; if (mb_call(MBOX_CH_PROP) && mbox[15] == 32 && mbox[23] != 0) { framebuffer = (u32*)((uintptr_t)(mbox[23]) & 0x3FFFFFFF); rgb_order = mbox[19]; uart_puts("Framebuffer initialized\n"); } else { uart_puts("Unable to initialize framebuffer\n"); } } int ppu_init(Ppu *p, Uint8 hor, Uint8 ver) { p->width = 8 * hor; p->height = 8 * ver; fb_init(); p->pixels = pixels_buf; // Initialize default palette. palette[0] = 0x444444; palette[1] = 0xffffff; palette[2] = 0x7777ff; palette[3] = 0xff7777; // Clear pixel buffer memory. memzero8(pixels_buf, sizeof(pixels_buf)); // Make sure we perform an initial screen drawing. reqdraw = 1; redraw_screen(); return 1; } void blit_framebuffer(Ppu *p) { if (reqdraw == 0) { return; } for (size_t j = 0; j < SCREEN_HEIGHT; j++) { if (dirty_lines[j] != 0) { for (size_t i = 0; i < SCREEN_WIDTH; i++) { size_t idx = i + j * SCREEN_WIDTH; framebuffer[idx] = palette[p->pixels[idx] % 16]; } } dirty_lines[j] = 0; } reqdraw = 0; }