mirror of https://github.com/bsnes-emu/bsnes.git
183 lines
4.0 KiB
C++
183 lines
4.0 KiB
C++
#include <gb/gb.hpp>
|
|
|
|
namespace GameBoy {
|
|
|
|
PPU ppu;
|
|
#include "io.cpp"
|
|
#include "dmg.cpp"
|
|
#include "cgb.cpp"
|
|
#include "serialization.cpp"
|
|
|
|
auto PPU::Enter() -> void {
|
|
while(true) scheduler.synchronize(), ppu.main();
|
|
}
|
|
|
|
auto PPU::main() -> void {
|
|
if(!status.displayEnable) {
|
|
for(uint n : range(160 * 144)) screen[n] = Model::GameBoy() ? 0 : 0x7fff;
|
|
Thread::step(154 * 456);
|
|
synchronize(cpu);
|
|
scheduler.exit(Scheduler::Event::Frame);
|
|
return;
|
|
}
|
|
|
|
status.lx = 0;
|
|
if(Model::SuperGameBoy()) superGameBoy->lcdScanline();
|
|
|
|
if(status.ly <= 143) {
|
|
status.mode = 2;
|
|
scanline();
|
|
step(92);
|
|
|
|
status.mode = 3;
|
|
for(auto n : range(160)) {
|
|
run();
|
|
step(1);
|
|
}
|
|
|
|
status.mode = 0;
|
|
cpu.hblank();
|
|
step(204);
|
|
} else {
|
|
status.mode = 1;
|
|
step(456);
|
|
}
|
|
|
|
status.ly++;
|
|
|
|
if(status.ly == 144) {
|
|
cpu.raise(CPU::Interrupt::Vblank);
|
|
scheduler.exit(Scheduler::Event::Frame);
|
|
}
|
|
|
|
if(status.ly == 154) {
|
|
status.ly = 0;
|
|
}
|
|
}
|
|
|
|
auto PPU::stat() -> void {
|
|
bool irq = status.irq;
|
|
|
|
status.irq = status.interruptHblank && status.mode == 0;
|
|
status.irq |= status.interruptVblank && status.mode == 1;
|
|
status.irq |= status.interruptOAM && status.mode == 2;
|
|
status.irq |= status.interruptLYC && coincidence();
|
|
|
|
if(!irq && status.irq) cpu.raise(CPU::Interrupt::Stat);
|
|
}
|
|
|
|
auto PPU::coincidence() -> bool {
|
|
uint ly = status.ly;
|
|
if(ly == 153 && status.lx >= 92) ly = 0; //LYC=0 triggers early during LY=153
|
|
return ly == status.lyc;
|
|
}
|
|
|
|
auto PPU::refresh() -> void {
|
|
if(!Model::SuperGameBoy()) Emulator::video.refresh(screen, 160 * sizeof(uint32), 160, 144);
|
|
}
|
|
|
|
auto PPU::step(uint clocks) -> void {
|
|
while(clocks--) {
|
|
stat();
|
|
if(status.dmaActive) {
|
|
uint hi = status.dmaClock++;
|
|
uint lo = hi & (cpu.status.speedDouble ? 1 : 3);
|
|
hi >>= cpu.status.speedDouble ? 1 : 2;
|
|
if(lo == 0) {
|
|
if(hi == 0) {
|
|
//warm-up
|
|
} else if(hi == 161) {
|
|
//cool-down; disable
|
|
status.dmaActive = false;
|
|
} else {
|
|
oam[hi - 1] = bus.read(status.dmaBank << 8 | hi - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
status.lx++;
|
|
Thread::step(1);
|
|
synchronize(cpu);
|
|
}
|
|
}
|
|
|
|
auto PPU::hflip(uint data) const -> uint {
|
|
return (data & 0x8080) >> 7 | (data & 0x4040) >> 5
|
|
| (data & 0x2020) >> 3 | (data & 0x1010) >> 1
|
|
| (data & 0x0808) << 1 | (data & 0x0404) << 3
|
|
| (data & 0x0202) << 5 | (data & 0x0101) << 7;
|
|
}
|
|
|
|
auto PPU::power() -> void {
|
|
create(Enter, 4 * 1024 * 1024);
|
|
|
|
if(Model::GameBoyColor()) {
|
|
scanline = {&PPU::scanlineCGB, this};
|
|
run = {&PPU::runCGB, this};
|
|
} else {
|
|
scanline = {&PPU::scanlineDMG, this};
|
|
run = {&PPU::runDMG, this};
|
|
}
|
|
|
|
for(uint n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
|
|
for(uint n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM
|
|
|
|
bus.mmio[0xff40] = this; //LCDC
|
|
bus.mmio[0xff41] = this; //STAT
|
|
bus.mmio[0xff42] = this; //SCY
|
|
bus.mmio[0xff43] = this; //SCX
|
|
bus.mmio[0xff44] = this; //LY
|
|
bus.mmio[0xff45] = this; //LYC
|
|
bus.mmio[0xff46] = this; //DMA
|
|
bus.mmio[0xff47] = this; //BGP
|
|
bus.mmio[0xff48] = this; //OBP0
|
|
bus.mmio[0xff49] = this; //OBP1
|
|
bus.mmio[0xff4a] = this; //WY
|
|
bus.mmio[0xff4b] = this; //WX
|
|
|
|
if(Model::GameBoyColor()) {
|
|
bus.mmio[0xff4f] = this; //VBK
|
|
bus.mmio[0xff68] = this; //BGPI
|
|
bus.mmio[0xff69] = this; //BGPD
|
|
bus.mmio[0xff6a] = this; //OBPI
|
|
bus.mmio[0xff6b] = this; //OBPD
|
|
}
|
|
|
|
for(auto& n : vram) n = 0x00;
|
|
for(auto& n : oam) n = 0x00;
|
|
for(auto& n : bgp) n = 0x00;
|
|
for(auto& n : obp[0]) n = 3;
|
|
for(auto& n : obp[1]) n = 3;
|
|
for(auto& n : bgpd) n = 0x0000;
|
|
for(auto& n : obpd) n = 0x0000;
|
|
|
|
memory::fill(&status, sizeof(Status));
|
|
|
|
for(auto& n : screen) n = 0;
|
|
|
|
bg.color = 0;
|
|
bg.palette = 0;
|
|
bg.priority = 0;
|
|
|
|
ob.color = 0;
|
|
ob.palette = 0;
|
|
ob.priority = 0;
|
|
|
|
for(auto& s : sprite) {
|
|
s.x = 0;
|
|
s.y = 0;
|
|
s.tile = 0;
|
|
s.attr = 0;
|
|
s.data = 0;
|
|
}
|
|
sprites = 0;
|
|
|
|
background.attr = 0;
|
|
background.data = 0;
|
|
|
|
window.attr = 0;
|
|
window.data = 0;
|
|
}
|
|
|
|
}
|