diff --git a/Makeconf b/Makeconf index d4b9d3d..580c870 100644 --- a/Makeconf +++ b/Makeconf @@ -1,4 +1,4 @@ -TARGETS := libemu.a vgbc vgbc.test vgbc.inspect vgbc.gbdif +TARGETS := libemu.a vgbc vgbc.test vgbc.inspect vgbc.gbdif vgbc.lcdtest sfml_packages := sfml-graphics sfml_CXXFLAGS := $(shell pkg-config --cflags $(sfml_packages)) @@ -39,3 +39,7 @@ verb_vgbc.inspect := link # Game Boy Doctor Interface modules_vgbc.gbdif := misc/gbdif.o libemu.a verb_vgbc.gbdif := link + +modules_vgbc.lcdtest := lcd/lcdtest.o libemu.a +verb_vgbc.lcdtest := link +LDFLAGS_vgbc.lcdtest := $(sfml_LDFLAGS) diff --git a/lcd/lcd.cpp b/lcd/lcd.cpp index ea331e4..c8a144d 100644 --- a/lcd/lcd.cpp +++ b/lcd/lcd.cpp @@ -1,21 +1,47 @@ #include #include +#include + LCD::LCD(Cpu& cpu) - : screenbuffer(), regLY(0), regLYC(0), + : screenbuffer(), regLY(90), regLYC(0), intHBlank(false), intVBlank(false), intOAM(false), intLYC(false), cpu(cpu), bgp(false), obp{Palette(true),Palette(true)}, - vram_dirty(true) + vram_dirty(true), currentMode(ModeVBlank) { screenbuffer.create(160,144); screenbuffer.clear(); std::memset(vram_raw, 0, 0x2000); - for(int i = 0; i < 256; i++) - tilemap[i].create(8,8); + for(int i = 0; i < 384; i++) + { + tiles[i].create(8,8,sf::Color::Red); + } } +void LCD::generate_tile(int idx) +{ + std::cerr << "Updating tile " << idx << std::endl; + for(int y = 0, addr = idx << 4; y < 8; y++, addr+=2) + { + u8 a = vram_raw[addr]; + u8 b = vram_raw[addr+1]; + for(int x = 7; x >= 0; x--, a>>=1, b>>=1) + { + tiles[idx].setPixel(x,y,bgp.getColorByIdx( ((a&0x1) << 1) | (b&0x1))); + //tiles[idx].setPixel(x,y,sf::Color::Red); + } + } +} + +void LCD::vram_write(u16 addr, u8 data) +{ + if (Range(0x8000, 0x97FF).contains(addr)) + { + generate_tile((addr & 0x1FFF) >> 4); + } +} void LCD::write8(u16 addr, u8 data) { // VRAM access @@ -23,6 +49,7 @@ void LCD::write8(u16 addr, u8 data) { { vram_raw[addr-0x8000] = data; vram_dirty = true; + vram_write(addr, data); return; } @@ -115,3 +142,20 @@ u8 LCD::read8(u16 addr) { return 0xFF; } } + +void LCD::render(sf::RenderTarget& target) +{ + cpu.signalInterrupt(INT_VBlank); + for(int row = 0; row < 16; row++) + for(int col = 0; col < 12; col++) + { + unsigned int idx = row*12+col; + sf::Texture txt; + sf::Sprite spr; + + txt.loadFromImage(tiles[idx]); + spr.setTexture(txt,true); + spr.setPosition(col*8,row*8); + target.draw(spr); + } +} diff --git a/lcd/lcd.h b/lcd/lcd.h index 0ff6b41..539f76c 100644 --- a/lcd/lcd.h +++ b/lcd/lcd.h @@ -27,7 +27,7 @@ class LCD : public Mem_device { private: // Graphics sf::RenderTexture screenbuffer; - sf::Image tilemap[256]; + sf::Image tiles[384]; // Emulated device u8 regLY; @@ -47,9 +47,13 @@ private: bool enabled; bool vram_dirty; + void vram_write(u16 addr, u8 data); + void generate_tile(int idx); public: LCD(Cpu& cpu); virtual void write8(u16 addr, u8 data); virtual u8 read8(u16 addr); + + void render(sf::RenderTarget& target); }; diff --git a/lcd/lcdtest.cpp b/lcd/lcdtest.cpp new file mode 100644 index 0000000..4e0da18 --- /dev/null +++ b/lcd/lcdtest.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if(argc < 2) + return 1; + + std::ifstream file(argv[1]); + Cartridge cart(file); + + if(cart.type() != CT_MBC1 && cart.type() != CT_ROM_ONLY) + { + std::cerr << "Wrong cart type (" << (int)cart.type() << ")" << std::endl; + return 2; + } + + sf::RenderWindow window(sf::VideoMode(12*8,16*8), "VGBC tiles view"); + + RAM wram1(0x1000); + RAM wram2(0x1000); + RAM hram(0x100); + RAM oam(0x100); + + MBC1 mbc1(cart); + + Bus b; + Cpu cpu(&b); + + LCD lcd(cpu); + + b.map_device(0x0000, 0x7FFF, &mbc1); + b.map_device(0x8000, 0x9FFF, &lcd, 0x8000); + b.map_device(0xA000, 0xBFFF, &mbc1, 0xA000); + b.map_device(0xC000, 0xCFFF, &wram1); + b.map_device(0xD000, 0xDFFF, &wram2); + b.map_device(0xE000, 0xFDFF, &wram1); + b.map_device(0xFE00, 0xFE9F, &oam); + b.map_device(0xFF40, 0xFF4F, &lcd, 0xFF40); + b.map_device(0xFF80, 0xFFFE, &hram); + + + BoundRegister ie_mapped(cpu.state.IE); + BoundRegister if_mapped(cpu.state.IF); + b.map_device(0xFFFF, 0xFFFF, &ie_mapped); + b.map_device(0xFF0F, 0xFF0F, &if_mapped); + + cpu.state.A = 0x1; + cpu.state.carry = true; + cpu.state.halfcarry = true; + cpu.state.subtract = false; + cpu.state.zero = true; + + cpu.state.B = 0x00; + cpu.state.C = 0x13; + cpu.state.D = 0x00; + cpu.state.E = 0xD8; + cpu.state.H = 0x01; + cpu.state.L = 0x4D; + + cpu.state.SP = 0xFFFE; + cpu.state.PC = 0x100; + + const std::chrono::milliseconds delay(1); + + try { + while(!cpu.state.stopped) + { + + cpu.run(4194304/4000); + + window.clear(sf::Color::Black); + lcd.render(window); + window.display(); + + std::this_thread::sleep_for(delay); + } + } catch(CpuException& e) { + std::cerr << e.what() << std::endl; + } + +}