mirror of https://github.com/bsnes-emu/bsnes.git
189 lines
4.3 KiB
C++
189 lines
4.3 KiB
C++
#include <gba/gba.hpp>
|
|
|
|
//pixel: 4 cycles
|
|
|
|
//hdraw: 240 pixels ( 960 cycles)
|
|
//hblank: 68 pixels ( 272 cycles)
|
|
//scanline: 308 pixels (1232 cycles)
|
|
|
|
//vdraw: 160 scanlines (197120 cycles)
|
|
//vblank: 68 scanlines ( 83776 cycles)
|
|
//frame: 228 scanlines (280896 cycles)
|
|
|
|
namespace GameBoyAdvance {
|
|
|
|
PPU ppu;
|
|
#include "background.cpp"
|
|
#include "object.cpp"
|
|
#include "mosaic.cpp"
|
|
#include "screen.cpp"
|
|
#include "io.cpp"
|
|
#include "memory.cpp"
|
|
#include "serialization.cpp"
|
|
|
|
PPU::PPU() {
|
|
output = new uint32[240 * 160];
|
|
|
|
regs.bg[0].id = BG0;
|
|
regs.bg[1].id = BG1;
|
|
regs.bg[2].id = BG2;
|
|
regs.bg[3].id = BG3;
|
|
}
|
|
|
|
PPU::~PPU() {
|
|
delete[] output;
|
|
}
|
|
|
|
auto PPU::Enter() -> void {
|
|
while(true) scheduler.synchronize(), ppu.main();
|
|
}
|
|
|
|
auto PPU::main() -> void {
|
|
scanline();
|
|
}
|
|
|
|
auto PPU::step(uint clocks) -> void {
|
|
Thread::step(clocks);
|
|
synchronize(cpu);
|
|
}
|
|
|
|
auto PPU::power() -> void {
|
|
create(PPU::Enter, 16'777'216);
|
|
|
|
for(uint n = 0; n < 240 * 160; n++) output[n] = 0;
|
|
|
|
for(uint n = 0; n < 1024; n += 2) writePRAM(n, Half, 0x0000);
|
|
for(uint n = 0; n < 1024; n += 2) writeOAM(n, Half, 0x0000);
|
|
|
|
regs.control.bgmode = 0;
|
|
regs.control.cgbmode = 0;
|
|
regs.control.frame = 0;
|
|
regs.control.hblank = 0;
|
|
regs.control.objmapping = 0;
|
|
regs.control.forceblank = 0;
|
|
for(auto& enable : regs.control.enable) enable = 0;
|
|
for(auto& enablewindow : regs.control.enablewindow) enablewindow = 0;
|
|
regs.greenswap = 0;
|
|
regs.status.vblank = 0;
|
|
regs.status.hblank = 0;
|
|
regs.status.vcoincidence = 0;
|
|
regs.status.irqvblank = 0;
|
|
regs.status.irqhblank = 0;
|
|
regs.status.irqvcoincidence = 0;
|
|
regs.status.vcompare = 0;
|
|
regs.vcounter = 0;
|
|
for(auto& bg : regs.bg) {
|
|
bg.control.priority = 0;
|
|
bg.control.characterbaseblock = 0;
|
|
bg.control.unused = 0;
|
|
bg.control.mosaic = 0;
|
|
bg.control.colormode = 0;
|
|
bg.control.screenbaseblock = 0;
|
|
bg.control.affinewrap = 0;
|
|
bg.control.screensize = 0;
|
|
bg.hoffset = 0;
|
|
bg.voffset = 0;
|
|
bg.pa = 0;
|
|
bg.pb = 0;
|
|
bg.pc = 0;
|
|
bg.pd = 0;
|
|
bg.x = 0;
|
|
bg.y = 0;
|
|
bg.lx = 0;
|
|
bg.ly = 0;
|
|
}
|
|
for(auto& w : regs.window) {
|
|
w.x1 = 0;
|
|
w.x2 = 0;
|
|
w.y1 = 0;
|
|
w.y2 = 0;
|
|
}
|
|
for(auto& flags : regs.windowflags) {
|
|
for(auto& enable : flags.enable) enable = 0;
|
|
}
|
|
regs.mosaic.bghsize = 0;
|
|
regs.mosaic.bgvsize = 0;
|
|
regs.mosaic.objhsize = 0;
|
|
regs.mosaic.objvsize = 0;
|
|
for(auto& above : regs.blend.control.above) above = 0;
|
|
regs.blend.control.mode = 0;
|
|
for(auto& below : regs.blend.control.below) below = 0;
|
|
regs.blend.eva = 0;
|
|
regs.blend.evb = 0;
|
|
regs.blend.evy = 0;
|
|
|
|
for(uint n = 0x000; n <= 0x055; n++) bus.io[n] = this;
|
|
}
|
|
|
|
auto PPU::scanline() -> void {
|
|
cpu.keypadRun();
|
|
|
|
regs.status.vblank = regs.vcounter >= 160 && regs.vcounter <= 226;
|
|
regs.status.vcoincidence = regs.vcounter == regs.status.vcompare;
|
|
|
|
if(regs.vcounter == 0) {
|
|
frame();
|
|
|
|
regs.bg[2].lx = regs.bg[2].x;
|
|
regs.bg[2].ly = regs.bg[2].y;
|
|
|
|
regs.bg[3].lx = regs.bg[3].x;
|
|
regs.bg[3].ly = regs.bg[3].y;
|
|
}
|
|
|
|
if(regs.vcounter == 160) {
|
|
if(regs.status.irqvblank) cpu.regs.irq.flag |= CPU::Interrupt::VBlank;
|
|
cpu.dmaVblank();
|
|
}
|
|
|
|
if(regs.status.irqvcoincidence) {
|
|
if(regs.status.vcoincidence) cpu.regs.irq.flag |= CPU::Interrupt::VCoincidence;
|
|
}
|
|
|
|
if(regs.vcounter < 160) {
|
|
if(regs.control.forceblank || cpu.regs.mode == CPU::Registers::Mode::Stop) {
|
|
renderForceBlank();
|
|
} else {
|
|
for(auto x : range(240)) {
|
|
windowmask[0][x] = false;
|
|
windowmask[1][x] = false;
|
|
windowmask[2][x] = false;
|
|
layer[OBJ][x].write(false);
|
|
layer[BG0][x].write(false);
|
|
layer[BG1][x].write(false);
|
|
layer[BG2][x].write(false);
|
|
layer[BG3][x].write(false);
|
|
layer[SFX][x].write(true, 3, pram[0]);
|
|
}
|
|
renderWindow(0);
|
|
renderWindow(1);
|
|
renderObjects();
|
|
renderBackgrounds();
|
|
renderScreen();
|
|
}
|
|
}
|
|
|
|
step(960);
|
|
regs.status.hblank = 1;
|
|
if(regs.status.irqhblank) cpu.regs.irq.flag |= CPU::Interrupt::HBlank;
|
|
if(regs.vcounter < 160) cpu.dmaHblank();
|
|
|
|
step(240);
|
|
regs.status.hblank = 0;
|
|
if(regs.vcounter < 160) cpu.dmaHDMA();
|
|
|
|
step(32);
|
|
if(++regs.vcounter == 228) regs.vcounter = 0;
|
|
}
|
|
|
|
auto PPU::frame() -> void {
|
|
player.frame();
|
|
scheduler.exit(Scheduler::Event::Frame);
|
|
}
|
|
|
|
auto PPU::refresh() -> void {
|
|
Emulator::video.refresh(output, 240 * sizeof(uint32), 240, 160);
|
|
}
|
|
|
|
}
|