#include "ppu.h" /* Copyright (c) 2021 Devine Lu Linvega Copyright (c) 2021 Andrew Alderwick Copyright (c) 2021 Adrian "asie" Siekierka 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 FG_FRONT ((u32*)(MEM_VRAM)) #define BG_FRONT ((u32*)(MEM_VRAM + KB(20))) #define FG_BACK ((u32*)(MEM_VRAM + KB(44))) #define BG_BACK ((u32*)(MEM_VRAM + KB(64))) #define TILE_MAP ((u32*)(MEM_VRAM + KB(40))) static u32 unpack_icon_lut[256] = { 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100, 0x00000101, 0x00000110, 0x00000111, 0x00001000, 0x00001001, 0x00001010, 0x00001011, 0x00001100, 0x00001101, 0x00001110, 0x00001111, 0x00010000, 0x00010001, 0x00010010, 0x00010011, 0x00010100, 0x00010101, 0x00010110, 0x00010111, 0x00011000, 0x00011001, 0x00011010, 0x00011011, 0x00011100, 0x00011101, 0x00011110, 0x00011111, 0x00100000, 0x00100001, 0x00100010, 0x00100011, 0x00100100, 0x00100101, 0x00100110, 0x00100111, 0x00101000, 0x00101001, 0x00101010, 0x00101011, 0x00101100, 0x00101101, 0x00101110, 0x00101111, 0x00110000, 0x00110001, 0x00110010, 0x00110011, 0x00110100, 0x00110101, 0x00110110, 0x00110111, 0x00111000, 0x00111001, 0x00111010, 0x00111011, 0x00111100, 0x00111101, 0x00111110, 0x00111111, 0x01000000, 0x01000001, 0x01000010, 0x01000011, 0x01000100, 0x01000101, 0x01000110, 0x01000111, 0x01001000, 0x01001001, 0x01001010, 0x01001011, 0x01001100, 0x01001101, 0x01001110, 0x01001111, 0x01010000, 0x01010001, 0x01010010, 0x01010011, 0x01010100, 0x01010101, 0x01010110, 0x01010111, 0x01011000, 0x01011001, 0x01011010, 0x01011011, 0x01011100, 0x01011101, 0x01011110, 0x01011111, 0x01100000, 0x01100001, 0x01100010, 0x01100011, 0x01100100, 0x01100101, 0x01100110, 0x01100111, 0x01101000, 0x01101001, 0x01101010, 0x01101011, 0x01101100, 0x01101101, 0x01101110, 0x01101111, 0x01110000, 0x01110001, 0x01110010, 0x01110011, 0x01110100, 0x01110101, 0x01110110, 0x01110111, 0x01111000, 0x01111001, 0x01111010, 0x01111011, 0x01111100, 0x01111101, 0x01111110, 0x01111111, 0x10000000, 0x10000001, 0x10000010, 0x10000011, 0x10000100, 0x10000101, 0x10000110, 0x10000111, 0x10001000, 0x10001001, 0x10001010, 0x10001011, 0x10001100, 0x10001101, 0x10001110, 0x10001111, 0x10010000, 0x10010001, 0x10010010, 0x10010011, 0x10010100, 0x10010101, 0x10010110, 0x10010111, 0x10011000, 0x10011001, 0x10011010, 0x10011011, 0x10011100, 0x10011101, 0x10011110, 0x10011111, 0x10100000, 0x10100001, 0x10100010, 0x10100011, 0x10100100, 0x10100101, 0x10100110, 0x10100111, 0x10101000, 0x10101001, 0x10101010, 0x10101011, 0x10101100, 0x10101101, 0x10101110, 0x10101111, 0x10110000, 0x10110001, 0x10110010, 0x10110011, 0x10110100, 0x10110101, 0x10110110, 0x10110111, 0x10111000, 0x10111001, 0x10111010, 0x10111011, 0x10111100, 0x10111101, 0x10111110, 0x10111111, 0x11000000, 0x11000001, 0x11000010, 0x11000011, 0x11000100, 0x11000101, 0x11000110, 0x11000111, 0x11001000, 0x11001001, 0x11001010, 0x11001011, 0x11001100, 0x11001101, 0x11001110, 0x11001111, 0x11010000, 0x11010001, 0x11010010, 0x11010011, 0x11010100, 0x11010101, 0x11010110, 0x11010111, 0x11011000, 0x11011001, 0x11011010, 0x11011011, 0x11011100, 0x11011101, 0x11011110, 0x11011111, 0x11100000, 0x11100001, 0x11100010, 0x11100011, 0x11100100, 0x11100101, 0x11100110, 0x11100111, 0x11101000, 0x11101001, 0x11101010, 0x11101011, 0x11101100, 0x11101101, 0x11101110, 0x11101111, 0x11110000, 0x11110001, 0x11110010, 0x11110011, 0x11110100, 0x11110101, 0x11110110, 0x11110111, 0x11111000, 0x11111001, 0x11111010, 0x11111011, 0x11111100, 0x11111101, 0x11111110, 0x11111111 }; static u32 unpack_icon_lut_flipx[256] = { 0x00000000, 0x10000000, 0x01000000, 0x11000000, 0x00100000, 0x10100000, 0x01100000, 0x11100000, 0x00010000, 0x10010000, 0x01010000, 0x11010000, 0x00110000, 0x10110000, 0x01110000, 0x11110000, 0x00001000, 0x10001000, 0x01001000, 0x11001000, 0x00101000, 0x10101000, 0x01101000, 0x11101000, 0x00011000, 0x10011000, 0x01011000, 0x11011000, 0x00111000, 0x10111000, 0x01111000, 0x11111000, 0x00000100, 0x10000100, 0x01000100, 0x11000100, 0x00100100, 0x10100100, 0x01100100, 0x11100100, 0x00010100, 0x10010100, 0x01010100, 0x11010100, 0x00110100, 0x10110100, 0x01110100, 0x11110100, 0x00001100, 0x10001100, 0x01001100, 0x11001100, 0x00101100, 0x10101100, 0x01101100, 0x11101100, 0x00011100, 0x10011100, 0x01011100, 0x11011100, 0x00111100, 0x10111100, 0x01111100, 0x11111100, 0x00000010, 0x10000010, 0x01000010, 0x11000010, 0x00100010, 0x10100010, 0x01100010, 0x11100010, 0x00010010, 0x10010010, 0x01010010, 0x11010010, 0x00110010, 0x10110010, 0x01110010, 0x11110010, 0x00001010, 0x10001010, 0x01001010, 0x11001010, 0x00101010, 0x10101010, 0x01101010, 0x11101010, 0x00011010, 0x10011010, 0x01011010, 0x11011010, 0x00111010, 0x10111010, 0x01111010, 0x11111010, 0x00000110, 0x10000110, 0x01000110, 0x11000110, 0x00100110, 0x10100110, 0x01100110, 0x11100110, 0x00010110, 0x10010110, 0x01010110, 0x11010110, 0x00110110, 0x10110110, 0x01110110, 0x11110110, 0x00001110, 0x10001110, 0x01001110, 0x11001110, 0x00101110, 0x10101110, 0x01101110, 0x11101110, 0x00011110, 0x10011110, 0x01011110, 0x11011110, 0x00111110, 0x10111110, 0x01111110, 0x11111110, 0x00000001, 0x10000001, 0x01000001, 0x11000001, 0x00100001, 0x10100001, 0x01100001, 0x11100001, 0x00010001, 0x10010001, 0x01010001, 0x11010001, 0x00110001, 0x10110001, 0x01110001, 0x11110001, 0x00001001, 0x10001001, 0x01001001, 0x11001001, 0x00101001, 0x10101001, 0x01101001, 0x11101001, 0x00011001, 0x10011001, 0x01011001, 0x11011001, 0x00111001, 0x10111001, 0x01111001, 0x11111001, 0x00000101, 0x10000101, 0x01000101, 0x11000101, 0x00100101, 0x10100101, 0x01100101, 0x11100101, 0x00010101, 0x10010101, 0x01010101, 0x11010101, 0x00110101, 0x10110101, 0x01110101, 0x11110101, 0x00001101, 0x10001101, 0x01001101, 0x11001101, 0x00101101, 0x10101101, 0x01101101, 0x11101101, 0x00011101, 0x10011101, 0x01011101, 0x11011101, 0x00111101, 0x10111101, 0x01111101, 0x11111101, 0x00000011, 0x10000011, 0x01000011, 0x11000011, 0x00100011, 0x10100011, 0x01100011, 0x11100011, 0x00010011, 0x10010011, 0x01010011, 0x11010011, 0x00110011, 0x10110011, 0x01110011, 0x11110011, 0x00001011, 0x10001011, 0x01001011, 0x11001011, 0x00101011, 0x10101011, 0x01101011, 0x11101011, 0x00011011, 0x10011011, 0x01011011, 0x11011011, 0x00111011, 0x10111011, 0x01111011, 0x11111011, 0x00000111, 0x10000111, 0x01000111, 0x11000111, 0x00100111, 0x10100111, 0x01100111, 0x11100111, 0x00010111, 0x10010111, 0x01010111, 0x11010111, 0x00110111, 0x10110111, 0x01110111, 0x11110111, 0x00001111, 0x10001111, 0x01001111, 0x11001111, 0x00101111, 0x10101111, 0x01101111, 0x11101111, 0x00011111, 0x10011111, 0x01011111, 0x11011111, 0x00111111, 0x10111111, 0x01111111, 0x11111111 }; static u32 dirty_tiles[21] = {0}; void putcolors(u8 *addr) { int i; for(i = 0; i < 4; ++i) { u8 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; PAL_BUFFER_BG[i] = rgb15( (r << 1) | (r >> 3), (g << 1) | (g >> 3), (b << 1) | (b >> 3)); } } IWRAM_CODE void putpixel(u32 *layer, u16 x, u16 y, u8 color) { if (x >= SCREEN_WIDTH || y >= SCREEN_HEIGHT) return; size_t tile_x = x / 8; size_t tile_y = y / 8; size_t start_col = x % 8; size_t start_row = y % 8; size_t pos = (start_row + ((tile_x + tile_y * 32) * 8)); size_t shift = start_col * 4; layer[pos] = (layer[pos] & (~(0xF << shift))) | (color << shift); dirty_tiles[tile_y] |= 1 << tile_x; } IWRAM_CODE void puticn(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color, u8 flipx, u8 flipy) { u8 sprline; u16 v; u32 dirtyflag = (1 << (x >> 3)) | (1 << ((x + 7) >> 3)); u32 layerpos = ((y & 7) + (((x >> 3) + (y >> 3) * 32) * 8)); u32 *layerptr = &layer[layerpos]; u32 shift = (x & 7) << 2; u32 *lut_expand = flipx ? unpack_icon_lut : unpack_icon_lut_flipx; if (flipy) flipy = 7; if (x >= SCREEN_WIDTH || y >= SCREEN_HEIGHT) return; if (color != 0x05 && color != 0x0a && color != 0x0f) { u64 mask = ~((u64)0xFFFFFFFF << shift); for (v = 0; v < 8; v++, layerptr++) { if ((y + v) >= SCREEN_HEIGHT) break; sprline = sprite[v ^ flipy]; u64 data = (u64)(lut_expand[sprline] * (color & 3)) << shift; data |= (u64)(lut_expand[sprline ^ 0xFF] * (color >> 2)) << shift; layerptr[0] = (layerptr[0] & mask) | data; layerptr[8] = (layerptr[8] & (mask >> 32)) | (data >> 32); if (((y + v) & 7) == 7) layerptr += (32 - 1) * 8; } } else { for (v = 0; v < 8; v++, layerptr++) { if ((y + v) >= SCREEN_HEIGHT) break; sprline = sprite[v ^ flipy]; u64 mask = ~((u64)(lut_expand[sprline] * 0xF) << shift); u64 data = (u64)(lut_expand[sprline] * (color & 3)) << shift; layerptr[0] = (layerptr[0] & mask) | data; layerptr[8] = (layerptr[8] & (mask >> 32)) | (data >> 32); if (((y + v) & 7) == 7) layerptr += (32 - 1) * 8; } } dirty_tiles[y >> 3] |= dirtyflag; dirty_tiles[(y + 7) >> 3] |= dirtyflag; } IWRAM_CODE void putchr(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color, u8 flipx, u8 flipy) { u8 sprline1, sprline2; u16 v; u32 dirtyflag = (1 << (x >> 3)) | (1 << ((x + 7) >> 3)); u32 layerpos = ((y & 7) + (((x >> 3) + (y >> 3) * 32) * 8)); u32 *layerptr = &layer[layerpos]; u32 shift = (x & 7) << 2; u32 *lut_expand = flipx ? unpack_icon_lut : unpack_icon_lut_flipx; if (flipy) flipy = 7; if (x >= SCREEN_WIDTH || y >= SCREEN_HEIGHT) return; u64 mask = ~((u64)0xFFFFFFFF << shift); u32 colconst = (color >> 2) * 0x11111111; for (v = 0; v < 8; v++, layerptr++) { if ((y + v) >= SCREEN_HEIGHT) break; sprline1 = sprite[v ^ flipy]; sprline2 = sprite[(v ^ flipy) | 8]; u32 data32 = (lut_expand[sprline1] * (color & 3)) + (lut_expand[sprline2] * ((color & 1) << 1)) + colconst; u64 data = ((u64) (data32 & 0x33333333)) << shift; layerptr[0] = (layerptr[0] & mask) | data; layerptr[8] = (layerptr[8] & (mask >> 32)) | (data >> 32); if (((y + v) & 7) == 7) layerptr += (32 - 1) * 8; } dirty_tiles[y >> 3] |= dirtyflag; dirty_tiles[(y + 7) >> 3] |= dirtyflag; } IWRAM_CODE void flipbuf(Ppu *p) { Tile *mem_fg = FG_FRONT; Tile *mem_bg = BG_FRONT; for (size_t j = 0; j < 20; ++j) { if (dirty_tiles[j] == 0) { continue; } size_t k = 1; for (size_t i = 0; i < 30; ++i, k <<= 1) { if (dirty_tiles[j] & k) { Tile *tile_fg = p->fg; Tile *tile_bg = p->bg; mem_fg[i + j * 32] = tile_fg[i + j * 32]; mem_bg[i + j * 32] = tile_bg[i + j * 32]; } } dirty_tiles[j] = 0; } } int initppu(Ppu *p, u8 hor, u8 ver, u8 pad) { p->hor = hor; p->ver = ver; p->pad = pad; p->width = (8 * p->hor + p->pad * 2); p->height = (8 * p->ver + p->pad * 2); // Initialize display mode and bg palette. DISP_CTRL = DISP_MODE_0 | DISP_BG_0 | DISP_BG_1; // Initialize backgrounds. u8 cb_fg = 0; u8 cb_bg = 1; u8 sb_fg = 20; u8 sb_bg = 21; BG_CTRL(0) = BG_CHARBLOCK(cb_fg) | BG_SCREENBLOCK(sb_fg) | BG_PRIORITY(1); BG_CTRL(1) = BG_CHARBLOCK(cb_bg) | BG_SCREENBLOCK(sb_bg) | BG_PRIORITY(2); // Clear front buffer. p->fg = FG_FRONT; p->bg = BG_FRONT; // Use DMA to clear VRAM. u32 fill = 0; dma_fill(p->fg, fill, KB(20), 3); dma_fill(p->bg, fill, KB(20), 3); // Clear back buffer. p->fg = FG_BACK; p->bg = BG_BACK; dma_fill(p->fg, fill, KB(20), 3); dma_fill(p->bg, fill, KB(20), 3); // Initialize default palette. PAL_BUFFER_BG[0] = COLOR_BLACK; PAL_BUFFER_BG[1] = COLOR_WHITE; PAL_BUFFER_BG[2] = COLOR_RED; PAL_BUFFER_BG[3] = COLOR_BLUE; // Initialize memory map. u16 *mem_map_fg = SCREENBLOCK_MEM[sb_fg]; u16 *mem_map_bg = SCREENBLOCK_MEM[sb_bg]; size_t k = 0; for (size_t i = 0; i < 32 * 20; ++i, ++k) { mem_map_fg[i] = k; mem_map_bg[i] = k + 32 * 4; } return 1; }