bsnes/bsnes/gba/ppu/ppu.cpp

150 lines
3.1 KiB
C++
Raw Normal View History

#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 GBA {
#include "registers.cpp"
#include "background.cpp"
#include "object.cpp"
#include "screen.cpp"
#include "mmio.cpp"
#include "memory.cpp"
PPU ppu;
void PPU::Enter() { ppu.enter(); }
void PPU::enter() {
while(true) {
scanline();
}
}
void PPU::step(unsigned clocks) {
clock += clocks;
if(clock >= 0) co_switch(cpu.thread);
}
void PPU::power() {
create(PPU::Enter, 16777216);
//for(unsigned n = 0; n < vram.size; n++) vram.data[n] = 0;
for(unsigned n = 0; n < 240 * 160; n++) output[n] = 0;
for(unsigned n = 0; n < 1024; n += 2) pram_write(n, Half, 0x0000);
for(unsigned n = 0; n < 1024; n += 2) oam_write(n, Half, 0x0000);
regs.control = 0;
regs.greenswap = 0;
regs.status = 0;
regs.vcounter = 0;
for(auto &bg : regs.bg) {
bg.control = 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;
w.in = 0;
w.out = 0;
}
regs.windowobj.in = 0;
regs.mosaic.bghsize = 0;
regs.mosaic.bgvsize = 0;
regs.mosaic.objhsize = 0;
regs.mosaic.objvsize = 0;
regs.blend.control = 0;
regs.blend.eva = 0;
regs.blend.evb = 0;
regs.blend.evy = 0;
for(unsigned n = 0x000; n <= 0x055; n++) bus.mmio[n] = this;
}
void PPU::scanline() {
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.vblank = 1;
cpu.pending.dma.vblank = true;
}
if(regs.status.irqvcoincidence) {
if(regs.status.vcoincidence) cpu.regs.irq.flag.vcoincidence = 1;
}
if(regs.vcounter < 160) {
for(unsigned x = 0; x < 240; x++) {
layer[0][x].exists = false;
layer[1][x].exists = false;
layer[2][x].exists = false;
layer[3][x].exists = false;
}
if(regs.control.forceblank) {
render_forceblank();
} else {
render_backgrounds();
render_objects();
render_screen();
}
}
step(960);
regs.status.hblank = 1;
if(regs.status.irqhblank) cpu.regs.irq.flag.hblank = 1;
if(regs.vcounter < 160) cpu.pending.dma.hblank = true;
step(240);
regs.status.hblank = 0;
if(regs.vcounter < 160) cpu.pending.dma.hdma = true;
step(32);
if(++regs.vcounter == 228) regs.vcounter = 0;
}
void PPU::frame() {
interface->videoRefresh(output);
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
PPU::PPU() {
output = new uint16[240 * 160];
}
PPU::~PPU() {
delete[] output;
}
}