lcd - Initial work on the LCD/PPU

This commit is contained in:
madmaurice 2023-09-12 00:07:33 +02:00
parent dc81fcb4e5
commit 1716fe1e98
5 changed files with 232 additions and 1 deletions

View file

@ -17,7 +17,9 @@ modules_libemu.a := memory/device.o \
cpu/cpu.o \ cpu/cpu.o \
cpu/decoder.o \ cpu/decoder.o \
cartridge/mbc/mbc1.o \ cartridge/mbc/mbc1.o \
cartridge/cartridge.o cartridge/cartridge.o \
lcd/lcd.o \
lcd/palette.o \
modules_vgbc := main.o libemu.a modules_vgbc := main.o libemu.a
verb_vgbc := link verb_vgbc := link

117
lcd/lcd.cpp Normal file
View file

@ -0,0 +1,117 @@
#include <lcd/lcd.h>
#include <cstring>
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;
}
}

55
lcd/lcd.h Normal file
View file

@ -0,0 +1,55 @@
#pragma once
#include <SFML/Graphics.hpp>
#include <memory/device.h>
#include <misc/types.h>
#include <lcd/palette.h>
#include <cpu/cpu.h>
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);
};

36
lcd/palette.cpp Normal file
View file

@ -0,0 +1,36 @@
#include <lcd/palette.h>
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]];
}

21
lcd/palette.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include <SFML/Graphics.hpp>
#include <misc/types.h>
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);
};