From 1716fe1e9867b9015c6651b8e75e9856f88fc9a1 Mon Sep 17 00:00:00 2001 From: MadMaurice Date: Tue, 12 Sep 2023 00:07:33 +0200 Subject: [PATCH] lcd - Initial work on the LCD/PPU --- Makeconf | 4 +- lcd/lcd.cpp | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ lcd/lcd.h | 55 +++++++++++++++++++++++ lcd/palette.cpp | 36 +++++++++++++++ lcd/palette.h | 21 +++++++++ 5 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 lcd/lcd.cpp create mode 100644 lcd/lcd.h create mode 100644 lcd/palette.cpp create mode 100644 lcd/palette.h diff --git a/Makeconf b/Makeconf index 5d8a27e..4e62aab 100644 --- a/Makeconf +++ b/Makeconf @@ -17,7 +17,9 @@ modules_libemu.a := memory/device.o \ cpu/cpu.o \ cpu/decoder.o \ cartridge/mbc/mbc1.o \ - cartridge/cartridge.o + cartridge/cartridge.o \ + lcd/lcd.o \ + lcd/palette.o \ modules_vgbc := main.o libemu.a verb_vgbc := link diff --git a/lcd/lcd.cpp b/lcd/lcd.cpp new file mode 100644 index 0000000..ea331e4 --- /dev/null +++ b/lcd/lcd.cpp @@ -0,0 +1,117 @@ +#include +#include + +LCD::LCD(Cpu& cpu) + : screenbuffer(), regLY(0), regLYC(0), + intHBlank(false), intVBlank(false), intOAM(false), intLYC(false), + cpu(cpu), bgp(false), obp{Palette(true),Palette(true)}, + vram_dirty(true) +{ + screenbuffer.create(160,144); + screenbuffer.clear(); + + std::memset(vram_raw, 0, 0x2000); + + for(int i = 0; i < 256; i++) + tilemap[i].create(8,8); +} + + +void LCD::write8(u16 addr, u8 data) { + // VRAM access + if (Range(0x8000,0x9FFF).contains(addr)) + { + vram_raw[addr-0x8000] = data; + vram_dirty = true; + return; + } + + switch(addr) + { + case 0xFF40: // LCDC + // TODO + break; + case 0xFF41: // STAT + intHBlank = data & IntSourceHBlank; + intVBlank = data & IntSourceVBlank; + intOAM = data & IntSourceOAM; + intLYC = data & IntSourceLYC; + break; + case 0xFF42: // SCY + // TODO + break; + case 0xFF43: // SCX + // TODO + break; + case 0xFF44: // LY + // Ignore + break; + case 0xFF45: // LYC + regLYC = data; + break; + case 0xFF46: // DMA (OAM DMA source address) + // TODO + break; + case 0xFF47: // BGP + bgp.setRegValue(data); + vram_dirty = true; + break; + case 0xFF48: // OBP0 + obp[0].setRegValue(data); + vram_dirty = true; + break; + case 0xFF49: // OBP1 + obp[1].setRegValue(data); + vram_dirty = true; + break; + case 0xFF4A: // WY + case 0xFF4B: // WX + // TODO + break; + } +} + +u8 LCD::read8(u16 addr) { + // VRAM access + if (Range(0x8000,0x9FFF).contains(addr)) + return vram_raw[addr-0x8000]; + + switch(addr) + { + case 0xFF40: // LCDC + // TODO + return 0x00; + case 0xFF41: // STAT + return (currentMode | + (regLY == regLYC ? LycEqual : 0) | + (intHBlank ? IntSourceHBlank : 0) | + (intVBlank ? IntSourceVBlank : 0) | + (intOAM ? IntSourceOAM : 0) | + (intLYC ? IntSourceLYC : 0)); + case 0xFF42: // SCY + // TODO + return 0x00; + case 0xFF43: // SCX + // TODO + return 0x00; + case 0xFF44: // LY + return regLY; + case 0xFF45: // LYC + return regLYC; + case 0xFF46: // DMA (OAM DMA source address) + // TODO + return 0x00; + case 0xFF47: // BGP + return bgp.getRegValue(); + case 0xFF48: // OBP0 + return obp[0].getRegValue(); + case 0xFF49: // OBP1 + return obp[1].getRegValue(); + case 0xFF4A: // WY + case 0xFF4B: // WY + // TODO + return 0x00; + default: + return 0xFF; + } +} diff --git a/lcd/lcd.h b/lcd/lcd.h new file mode 100644 index 0000000..0ff6b41 --- /dev/null +++ b/lcd/lcd.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include + +class LCD; + +enum LCDMode : u8 { + ModeHBlank = 0b000000000, + ModeVBlank = 0b000000001, + ModeOAMSearch = 0b000000010, + ModeTransferring = 0b000000011, +}; + +enum RegStat : u8 { + LycEqual = 0b000000100, + IntSourceHBlank = 0b000001000, + IntSourceVBlank = 0b000010000, + IntSourceOAM = 0b000100000, + IntSourceLYC = 0b001000000, +}; + +class LCD : public Mem_device { +private: + // Graphics + sf::RenderTexture screenbuffer; + sf::Image tilemap[256]; + + // Emulated device + u8 regLY; + u8 regLYC; + bool intHBlank; + bool intVBlank; + bool intOAM; + bool intLYC; + LCDMode currentMode; + Palette bgp; + Palette obp[2]; + + u8 vram_raw[0x2000]; + + // misc + Cpu& cpu; + bool enabled; + bool vram_dirty; + +public: + LCD(Cpu& cpu); + + virtual void write8(u16 addr, u8 data); + virtual u8 read8(u16 addr); +}; diff --git a/lcd/palette.cpp b/lcd/palette.cpp new file mode 100644 index 0000000..2918299 --- /dev/null +++ b/lcd/palette.cpp @@ -0,0 +1,36 @@ +#include + +const sf::Color Palette::colors[] = { + sf::Color(255,255,255), + sf::Color(170,170,170), + sf::Color(85,85,85), + sf::Color(0,0,0), +}; + +const sf::Color Palette::transparent(0,0,0,255); + +Palette::Palette(bool transparent0) + : transparent0(transparent0) +{ + setRegValue(0); +} + +void Palette::setRegValue(u8 regval) +{ + for(int i = 0; i < 4; i++, regval >>= 2) + idx_to_color[i] = regval & 0x3; +} + +u8 Palette::getRegValue() +{ + u8 regval = 0; + for(int i = 0; i < 4; i++, regval <<= 2) + regval |= idx_to_color[i] & 0x3; + return regval; +} + +const sf::Color& Palette::getColorByIdx(u8 idx) +{ + if(idx == 0 && transparent0) return transparent; + return colors[idx_to_color[idx]]; +} diff --git a/lcd/palette.h b/lcd/palette.h new file mode 100644 index 0000000..8888e2d --- /dev/null +++ b/lcd/palette.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +class Palette { +private: + static const sf::Color colors[4]; + static const sf::Color transparent; + + u8 idx_to_color[4]; + bool transparent0; +public: + Palette(bool transparent0); + + void setRegValue(u8 regval); + u8 getRegValue(); + + const sf::Color& getColorByIdx(u8 idx); + +};