mirror of https://github.com/bsnes-emu/bsnes.git
Update to v099r13 release.
byuu says: Changelog: - GB core code cleanup completed - GBA core code cleanup completed - some more cleanup on missed processor/arm functions/variables - fixed FC loading icarus bug - "Load ROM File" icarus functionality restored - minor code unification efforts all around (not perfect yet) - MMIO->IO - mmio.cpp->io.cpp - read,write->readIO,writeIO It's been a very long work in progress ... starting all the way back with v094r09, but the major part of the higan code cleanup is now completed! Of course, it's very important to note that this is only for the basic style: - under_score functions and variables are now camelCase - return-type function-name() are now auto function-name() -> return-type - Natural<T>/Integer<T> replace (u)intT_n types where possible - signed/unsigned are now int/uint - most of the x==true,x==false tests changed to x,!x A lot of spot improvements to consistency, simplicity and quality have gone in along the way, of course. But we'll probably never fully finishing beautifying every last line of code in the entire codebase. Still, this is a really great start. Going forward, WIP diffs should start being smaller and of higher quality once again. I know the joke is, "until my coding style changes again", but ... this was way too stressful, way too time consuming, and way too risky. I'm too old and tired now for extreme upheavel like this again. The only major change I'm slowly mulling over would be renaming the using Natural<T>/Integer<T> = (u)intT; shorthand to something that isn't as easily confused with the (u)int_t types ... but we'll see. I'll definitely continue to change small things all the time, but for the larger picture, I need to just accept the style I have and live with it.
This commit is contained in:
parent
7a68059f78
commit
67457fade4
|
@ -11,7 +11,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "099.12";
|
||||
static const string Version = "099.13";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -17,7 +17,7 @@ auto Cartridge::main() -> void {
|
|||
auto Cartridge::load() -> bool {
|
||||
if(auto pathID = interface->load(ID::Famicom, "Famicom", "fc")) {
|
||||
information.pathID = pathID();
|
||||
}
|
||||
} else return false;
|
||||
|
||||
if(auto fp = interface->open(pathID(), "manifest.bml", File::Read, File::Required)) {
|
||||
information.manifest = fp->reads();
|
||||
|
|
|
@ -9,7 +9,7 @@ auto PPU::writeCIRAM(uint11 addr, uint8 data) -> void {
|
|||
auto PPU::readCGRAM(uint5 addr) -> uint8 {
|
||||
if((addr & 0x13) == 0x10) addr &= ~0x10;
|
||||
uint8 data = cgram[addr];
|
||||
if(r.grayscale) data &= 0x30;
|
||||
if(io.grayscale) data &= 0x30;
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -25,36 +25,36 @@ auto PPU::readIO(uint16 addr) -> uint8 {
|
|||
|
||||
//PPUSTATUS
|
||||
case 2:
|
||||
result |= r.mdr.bits(0,4);
|
||||
result |= r.spriteOverflow << 5;
|
||||
result |= r.spriteZeroHit << 6;
|
||||
result |= r.nmiFlag << 7;
|
||||
r.v.latch = 0;
|
||||
r.nmiHold = 0;
|
||||
cpu.nmiLine(r.nmiFlag = 0);
|
||||
result |= io.mdr.bits(0,4);
|
||||
result |= io.spriteOverflow << 5;
|
||||
result |= io.spriteZeroHit << 6;
|
||||
result |= io.nmiFlag << 7;
|
||||
io.v.latch = 0;
|
||||
io.nmiHold = 0;
|
||||
cpu.nmiLine(io.nmiFlag = 0);
|
||||
break;
|
||||
|
||||
//OAMDATA
|
||||
case 4:
|
||||
result = oam[r.oamAddress];
|
||||
result = oam[io.oamAddress];
|
||||
break;
|
||||
|
||||
//PPUDATA
|
||||
case 7:
|
||||
if(enable() && (r.ly <= 240 || r.ly == 261)) return 0x00;
|
||||
if(enable() && (io.ly <= 240 || io.ly == 261)) return 0x00;
|
||||
|
||||
addr = (uint14)r.v.address;
|
||||
addr = (uint14)io.v.address;
|
||||
if(addr <= 0x1fff) {
|
||||
result = r.busData;
|
||||
r.busData = cartridge.readCHR(addr);
|
||||
result = io.busData;
|
||||
io.busData = cartridge.readCHR(addr);
|
||||
} else if(addr <= 0x3eff) {
|
||||
result = r.busData;
|
||||
r.busData = cartridge.readCHR(addr);
|
||||
result = io.busData;
|
||||
io.busData = cartridge.readCHR(addr);
|
||||
} else if(addr <= 0x3fff) {
|
||||
result = readCGRAM(addr);
|
||||
r.busData = cartridge.readCHR(addr);
|
||||
io.busData = cartridge.readCHR(addr);
|
||||
}
|
||||
r.v.address += r.vramIncrement;
|
||||
io.v.address += io.vramIncrement;
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -63,30 +63,30 @@ auto PPU::readIO(uint16 addr) -> uint8 {
|
|||
}
|
||||
|
||||
auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
||||
r.mdr = data;
|
||||
io.mdr = data;
|
||||
|
||||
switch(addr.bits(0,2)) {
|
||||
|
||||
//PPUCTRL
|
||||
case 0:
|
||||
r.t.nametable = data.bits(0,1);
|
||||
r.vramIncrement = data.bit (2) ? 32 : 1;
|
||||
r.spriteAddress = data.bit (3) ? 0x1000 : 0x0000;
|
||||
r.bgAddress = data.bit (4) ? 0x1000 : 0x0000;
|
||||
r.spriteHeight = data.bit (5) ? 16 : 8;
|
||||
r.masterSelect = data.bit (6);
|
||||
r.nmiEnable = data.bit (7);
|
||||
cpu.nmiLine(r.nmiEnable && r.nmiHold && r.nmiFlag);
|
||||
io.t.nametable = data.bits(0,1);
|
||||
io.vramIncrement = data.bit (2) ? 32 : 1;
|
||||
io.spriteAddress = data.bit (3) ? 0x1000 : 0x0000;
|
||||
io.bgAddress = data.bit (4) ? 0x1000 : 0x0000;
|
||||
io.spriteHeight = data.bit (5) ? 16 : 8;
|
||||
io.masterSelect = data.bit (6);
|
||||
io.nmiEnable = data.bit (7);
|
||||
cpu.nmiLine(io.nmiEnable && io.nmiHold && io.nmiFlag);
|
||||
break;
|
||||
|
||||
//PPUMASK
|
||||
case 1:
|
||||
r.grayscale = data.bit (0);
|
||||
r.bgEdgeEnable = data.bit (1);
|
||||
r.spriteEdgeEnable = data.bit (2);
|
||||
r.bgEnable = data.bit (3);
|
||||
r.spriteEnable = data.bit (4);
|
||||
r.emphasis = data.bits(5,7);
|
||||
io.grayscale = data.bit (0);
|
||||
io.bgEdgeEnable = data.bit (1);
|
||||
io.spriteEdgeEnable = data.bit (2);
|
||||
io.bgEnable = data.bit (3);
|
||||
io.spriteEnable = data.bit (4);
|
||||
io.emphasis = data.bits(5,7);
|
||||
break;
|
||||
|
||||
//PPUSTATUS
|
||||
|
@ -95,41 +95,41 @@ auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
|||
|
||||
//OAMADDR
|
||||
case 3:
|
||||
r.oamAddress = data;
|
||||
io.oamAddress = data;
|
||||
break;
|
||||
|
||||
//OAMDATA
|
||||
case 4:
|
||||
if(r.oamAddress.bits(0,1) == 2) data.bits(2,4) = 0; //clear non-existent bits (always read back as 0)
|
||||
oam[r.oamAddress++] = data;
|
||||
if(io.oamAddress.bits(0,1) == 2) data.bits(2,4) = 0; //clear non-existent bits (always read back as 0)
|
||||
oam[io.oamAddress++] = data;
|
||||
break;
|
||||
|
||||
//PPUSCROLL
|
||||
case 5:
|
||||
if(r.v.latch++ == 0) {
|
||||
r.v.fineX = data.bits(0,2);
|
||||
r.t.tileX = data.bits(3,7);
|
||||
if(io.v.latch++ == 0) {
|
||||
io.v.fineX = data.bits(0,2);
|
||||
io.t.tileX = data.bits(3,7);
|
||||
} else {
|
||||
r.t.fineY = data.bits(0,2);
|
||||
r.t.tileY = data.bits(3,7);
|
||||
io.t.fineY = data.bits(0,2);
|
||||
io.t.tileY = data.bits(3,7);
|
||||
}
|
||||
break;
|
||||
|
||||
//PPUADDR
|
||||
case 6:
|
||||
if(r.v.latch++ == 0) {
|
||||
r.t.addressHi = data.bits(0,5);
|
||||
if(io.v.latch++ == 0) {
|
||||
io.t.addressHi = data.bits(0,5);
|
||||
} else {
|
||||
r.t.addressLo = data.bits(0,7);
|
||||
r.v.address = r.t.address;
|
||||
io.t.addressLo = data.bits(0,7);
|
||||
io.v.address = io.t.address;
|
||||
}
|
||||
break;
|
||||
|
||||
//PPUDATA
|
||||
case 7:
|
||||
if(enable() && (r.ly <= 240 || r.ly == 261)) return;
|
||||
if(enable() && (io.ly <= 240 || io.ly == 261)) return;
|
||||
|
||||
addr = (uint14)r.v.address;
|
||||
addr = (uint14)io.v.address;
|
||||
if(addr <= 0x1fff) {
|
||||
cartridge.writeCHR(addr, data);
|
||||
} else if(addr <= 0x3eff) {
|
||||
|
@ -137,7 +137,7 @@ auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
|||
} else if(addr <= 0x3fff) {
|
||||
writeCGRAM(addr, data);
|
||||
}
|
||||
r.v.address += r.vramIncrement;
|
||||
io.v.address += io.vramIncrement;
|
||||
break;
|
||||
|
||||
}
|
||||
|
|
|
@ -17,34 +17,34 @@ auto PPU::main() -> void {
|
|||
|
||||
auto PPU::step(uint clocks) -> void {
|
||||
while(clocks--) {
|
||||
if(r.ly == 240 && r.lx == 340) r.nmiHold = 1;
|
||||
if(r.ly == 241 && r.lx == 0) r.nmiFlag = r.nmiHold;
|
||||
if(r.ly == 241 && r.lx == 2) cpu.nmiLine(r.nmiEnable && r.nmiFlag);
|
||||
if(io.ly == 240 && io.lx == 340) io.nmiHold = 1;
|
||||
if(io.ly == 241 && io.lx == 0) io.nmiFlag = io.nmiHold;
|
||||
if(io.ly == 241 && io.lx == 2) cpu.nmiLine(io.nmiEnable && io.nmiFlag);
|
||||
|
||||
if(r.ly == 260 && r.lx == 340) r.spriteZeroHit = 0, r.spriteOverflow = 0;
|
||||
if(io.ly == 260 && io.lx == 340) io.spriteZeroHit = 0, io.spriteOverflow = 0;
|
||||
|
||||
if(r.ly == 260 && r.lx == 340) r.nmiHold = 0;
|
||||
if(r.ly == 261 && r.lx == 0) r.nmiFlag = r.nmiHold;
|
||||
if(r.ly == 261 && r.lx == 2) cpu.nmiLine(r.nmiEnable && r.nmiFlag);
|
||||
if(io.ly == 260 && io.lx == 340) io.nmiHold = 0;
|
||||
if(io.ly == 261 && io.lx == 0) io.nmiFlag = io.nmiHold;
|
||||
if(io.ly == 261 && io.lx == 2) cpu.nmiLine(io.nmiEnable && io.nmiFlag);
|
||||
|
||||
clock += 4;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
|
||||
r.lx++;
|
||||
io.lx++;
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::scanline() -> void {
|
||||
r.lx = 0;
|
||||
if(++r.ly == 262) {
|
||||
r.ly = 0;
|
||||
io.lx = 0;
|
||||
if(++io.ly == 262) {
|
||||
io.ly = 0;
|
||||
frame();
|
||||
}
|
||||
cartridge.scanline(r.ly);
|
||||
cartridge.scanline(io.ly);
|
||||
}
|
||||
|
||||
auto PPU::frame() -> void {
|
||||
r.field++;
|
||||
io.field++;
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
|
||||
|
@ -58,9 +58,9 @@ auto PPU::power() -> void {
|
|||
auto PPU::reset() -> void {
|
||||
create(PPU::Enter, 21'477'272);
|
||||
|
||||
memory::fill(&r, sizeof(Registers));
|
||||
memory::fill(&l, sizeof(Latches));
|
||||
r.vramIncrement = 1;
|
||||
memory::fill(&io, sizeof(IO));
|
||||
memory::fill(&latch, sizeof(Latches));
|
||||
io.vramIncrement = 1;
|
||||
|
||||
for(auto& n : ciram ) n = 0;
|
||||
for(auto& n : cgram ) n = 0;
|
||||
|
|
|
@ -31,7 +31,7 @@ struct PPU : Thread {
|
|||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct Registers {
|
||||
struct IO {
|
||||
//internal
|
||||
uint8 mdr;
|
||||
|
||||
|
@ -81,7 +81,7 @@ struct PPU : Thread {
|
|||
|
||||
//$2003
|
||||
uint8 oamAddress;
|
||||
} r;
|
||||
} io;
|
||||
|
||||
struct OAM {
|
||||
//serialization.cpp
|
||||
|
@ -108,7 +108,7 @@ struct PPU : Thread {
|
|||
|
||||
OAM oam[8]; //primary
|
||||
OAM soam[8]; //secondary
|
||||
} l;
|
||||
} latch;
|
||||
|
||||
uint8 ciram[2048];
|
||||
uint8 cgram[32];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
auto PPU::enable() const -> bool {
|
||||
return r.bgEnable || r.spriteEnable;
|
||||
return io.bgEnable || io.spriteEnable;
|
||||
}
|
||||
|
||||
auto PPU::loadCHR(uint16 addr) -> uint8 {
|
||||
|
@ -7,44 +7,44 @@ auto PPU::loadCHR(uint16 addr) -> uint8 {
|
|||
}
|
||||
|
||||
auto PPU::renderPixel() -> void {
|
||||
uint32* output = buffer + r.ly * 256;
|
||||
uint32* output = buffer + io.ly * 256;
|
||||
|
||||
uint x = r.lx - 1;
|
||||
uint mask = 0x8000 >> (r.v.fineX + (x & 7));
|
||||
uint x = io.lx - 1;
|
||||
uint mask = 0x8000 >> (io.v.fineX + (x & 7));
|
||||
uint palette = 0;
|
||||
uint objectPalette = 0;
|
||||
bool objectPriority = 0;
|
||||
|
||||
palette |= l.tiledataLo & mask ? 1 : 0;
|
||||
palette |= l.tiledataHi & mask ? 2 : 0;
|
||||
palette |= latch.tiledataLo & mask ? 1 : 0;
|
||||
palette |= latch.tiledataHi & mask ? 2 : 0;
|
||||
if(palette) {
|
||||
uint attr = l.attribute;
|
||||
uint attr = latch.attribute;
|
||||
if(mask >= 256) attr >>= 2;
|
||||
palette |= (attr & 3) << 2;
|
||||
}
|
||||
|
||||
if(!r.bgEnable) palette = 0;
|
||||
if(!r.bgEdgeEnable && x < 8) palette = 0;
|
||||
if(!io.bgEnable) palette = 0;
|
||||
if(!io.bgEdgeEnable && x < 8) palette = 0;
|
||||
|
||||
if(r.spriteEnable)
|
||||
if(io.spriteEnable)
|
||||
for(int sprite = 7; sprite >= 0; sprite--) {
|
||||
if(!r.spriteEdgeEnable && x < 8) continue;
|
||||
if(l.oam[sprite].id == 64) continue;
|
||||
if(!io.spriteEdgeEnable && x < 8) continue;
|
||||
if(latch.oam[sprite].id == 64) continue;
|
||||
|
||||
uint spriteX = x - l.oam[sprite].x;
|
||||
uint spriteX = x - latch.oam[sprite].x;
|
||||
if(spriteX >= 8) continue;
|
||||
|
||||
if(l.oam[sprite].attr & 0x40) spriteX ^= 7;
|
||||
if(latch.oam[sprite].attr & 0x40) spriteX ^= 7;
|
||||
uint mask = 0x80 >> spriteX;
|
||||
uint spritePalette = 0;
|
||||
spritePalette |= l.oam[sprite].tiledataLo & mask ? 1 : 0;
|
||||
spritePalette |= l.oam[sprite].tiledataHi & mask ? 2 : 0;
|
||||
spritePalette |= latch.oam[sprite].tiledataLo & mask ? 1 : 0;
|
||||
spritePalette |= latch.oam[sprite].tiledataHi & mask ? 2 : 0;
|
||||
if(spritePalette == 0) continue;
|
||||
|
||||
if(l.oam[sprite].id == 0 && palette && x != 255) r.spriteZeroHit = 1;
|
||||
spritePalette |= (l.oam[sprite].attr & 3) << 2;
|
||||
if(latch.oam[sprite].id == 0 && palette && x != 255) io.spriteZeroHit = 1;
|
||||
spritePalette |= (latch.oam[sprite].attr & 3) << 2;
|
||||
|
||||
objectPriority = l.oam[sprite].attr & 0x20;
|
||||
objectPriority = latch.oam[sprite].attr & 0x20;
|
||||
objectPalette = 16 + spritePalette;
|
||||
}
|
||||
|
||||
|
@ -53,23 +53,23 @@ auto PPU::renderPixel() -> void {
|
|||
}
|
||||
|
||||
if(!enable()) palette = 0;
|
||||
output[x] = r.emphasis << 6 | readCGRAM(palette);
|
||||
output[x] = io.emphasis << 6 | readCGRAM(palette);
|
||||
}
|
||||
|
||||
auto PPU::renderSprite() -> void {
|
||||
if(!enable()) return;
|
||||
|
||||
uint n = l.oamIterator++;
|
||||
int ly = r.ly == 261 ? -1 : r.ly;
|
||||
uint n = latch.oamIterator++;
|
||||
int ly = io.ly == 261 ? -1 : io.ly;
|
||||
uint y = ly - oam[n * 4 + 0];
|
||||
|
||||
if(y >= r.spriteHeight) return;
|
||||
if(l.oamCounter == 8) {
|
||||
r.spriteOverflow = 1;
|
||||
if(y >= io.spriteHeight) return;
|
||||
if(latch.oamCounter == 8) {
|
||||
io.spriteOverflow = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
auto& o = l.soam[l.oamCounter++];
|
||||
auto& o = latch.soam[latch.oamCounter++];
|
||||
o.id = n;
|
||||
o.y = oam[n * 4 + 0];
|
||||
o.tile = oam[n * 4 + 1];
|
||||
|
@ -79,34 +79,34 @@ auto PPU::renderSprite() -> void {
|
|||
|
||||
auto PPU::renderScanline() -> void {
|
||||
//Vblank
|
||||
if(r.ly >= 240 && r.ly <= 260) return step(341), scanline();
|
||||
if(io.ly >= 240 && io.ly <= 260) return step(341), scanline();
|
||||
|
||||
l.oamIterator = 0;
|
||||
l.oamCounter = 0;
|
||||
latch.oamIterator = 0;
|
||||
latch.oamCounter = 0;
|
||||
|
||||
for(auto n : range(8)) l.soam[n] = {};
|
||||
for(auto n : range(8)) latch.soam[n] = {};
|
||||
|
||||
// 0
|
||||
step(1);
|
||||
|
||||
// 1-256
|
||||
for(uint tile : range(32)) {
|
||||
uint nametable = loadCHR(0x2000 | (uint12)r.v.address);
|
||||
uint tileaddr = r.bgAddress | nametable << 4 | r.v.fineY;
|
||||
uint nametable = loadCHR(0x2000 | (uint12)io.v.address);
|
||||
uint tileaddr = io.bgAddress | nametable << 4 | io.v.fineY;
|
||||
renderPixel();
|
||||
step(1);
|
||||
|
||||
renderPixel();
|
||||
step(1);
|
||||
|
||||
uint attribute = loadCHR(0x23c0 | r.v.nametable << 10 | (r.v.tileY >> 2) << 3 | r.v.tileX >> 2);
|
||||
if(r.v.tileY & 2) attribute >>= 4;
|
||||
if(r.v.tileX & 2) attribute >>= 2;
|
||||
uint attribute = loadCHR(0x23c0 | io.v.nametable << 10 | (io.v.tileY >> 2) << 3 | io.v.tileX >> 2);
|
||||
if(io.v.tileY & 2) attribute >>= 4;
|
||||
if(io.v.tileX & 2) attribute >>= 2;
|
||||
renderPixel();
|
||||
step(1);
|
||||
|
||||
if(enable() && ++r.v.tileX == 0) r.v.nametableX++;
|
||||
if(enable() && tile == 31 && ++r.v.fineY == 0 && ++r.v.tileY == 30) r.v.nametableY++, r.v.tileY = 0;
|
||||
if(enable() && ++io.v.tileX == 0) io.v.nametableX++;
|
||||
if(enable() && tile == 31 && ++io.v.fineY == 0 && ++io.v.tileY == 30) io.v.nametableY++, io.v.tileY = 0;
|
||||
renderPixel();
|
||||
renderSprite();
|
||||
step(1);
|
||||
|
@ -126,60 +126,60 @@ auto PPU::renderScanline() -> void {
|
|||
renderSprite();
|
||||
step(1);
|
||||
|
||||
l.nametable = l.nametable << 8 | nametable;
|
||||
l.attribute = l.attribute << 2 | (attribute & 3);
|
||||
l.tiledataLo = l.tiledataLo << 8 | tiledataLo;
|
||||
l.tiledataHi = l.tiledataHi << 8 | tiledataHi;
|
||||
latch.nametable = latch.nametable << 8 | nametable;
|
||||
latch.attribute = latch.attribute << 2 | (attribute & 3);
|
||||
latch.tiledataLo = latch.tiledataLo << 8 | tiledataLo;
|
||||
latch.tiledataHi = latch.tiledataHi << 8 | tiledataHi;
|
||||
}
|
||||
|
||||
for(auto n : range(8)) l.oam[n] = l.soam[n];
|
||||
for(auto n : range(8)) latch.oam[n] = latch.soam[n];
|
||||
|
||||
//257-320
|
||||
for(uint sprite : range(8)) {
|
||||
uint nametable = loadCHR(0x2000 | (uint12)r.v.address);
|
||||
uint nametable = loadCHR(0x2000 | (uint12)io.v.address);
|
||||
step(1);
|
||||
|
||||
if(enable() && sprite == 0) {
|
||||
//258
|
||||
r.v.nametableX = r.t.nametableX;
|
||||
r.v.tileX = r.t.tileX;
|
||||
io.v.nametableX = io.t.nametableX;
|
||||
io.v.tileX = io.t.tileX;
|
||||
}
|
||||
step(1);
|
||||
|
||||
uint attribute = loadCHR(0x23c0 | r.v.nametable << 10 | (r.v.tileY >> 2) << 3 | r.v.tileX >> 2);
|
||||
uint tileaddr = r.spriteHeight == 8
|
||||
? r.spriteAddress + l.oam[sprite].tile * 16
|
||||
: (l.oam[sprite].tile & ~1) * 16 + (l.oam[sprite].tile & 1) * 0x1000;
|
||||
uint attribute = loadCHR(0x23c0 | io.v.nametable << 10 | (io.v.tileY >> 2) << 3 | io.v.tileX >> 2);
|
||||
uint tileaddr = io.spriteHeight == 8
|
||||
? io.spriteAddress + latch.oam[sprite].tile * 16
|
||||
: (latch.oam[sprite].tile & ~1) * 16 + (latch.oam[sprite].tile & 1) * 0x1000;
|
||||
step(2);
|
||||
|
||||
uint spriteY = (r.ly - l.oam[sprite].y) & (r.spriteHeight - 1);
|
||||
if(l.oam[sprite].attr & 0x80) spriteY ^= r.spriteHeight - 1;
|
||||
uint spriteY = (io.ly - latch.oam[sprite].y) & (io.spriteHeight - 1);
|
||||
if(latch.oam[sprite].attr & 0x80) spriteY ^= io.spriteHeight - 1;
|
||||
tileaddr += spriteY + (spriteY & 8);
|
||||
|
||||
l.oam[sprite].tiledataLo = loadCHR(tileaddr + 0);
|
||||
latch.oam[sprite].tiledataLo = loadCHR(tileaddr + 0);
|
||||
step(2);
|
||||
|
||||
l.oam[sprite].tiledataHi = loadCHR(tileaddr + 8);
|
||||
latch.oam[sprite].tiledataHi = loadCHR(tileaddr + 8);
|
||||
step(2);
|
||||
|
||||
if(enable() && sprite == 6 && r.ly == 261) {
|
||||
if(enable() && sprite == 6 && io.ly == 261) {
|
||||
//305
|
||||
r.v.address = r.t.address;
|
||||
io.v.address = io.t.address;
|
||||
}
|
||||
}
|
||||
|
||||
//321-336
|
||||
for(uint tile : range(2)) {
|
||||
uint nametable = loadCHR(0x2000 | (uint12)r.v.address);
|
||||
uint tileaddr = r.bgAddress | nametable << 4 | r.v.fineY;
|
||||
uint nametable = loadCHR(0x2000 | (uint12)io.v.address);
|
||||
uint tileaddr = io.bgAddress | nametable << 4 | io.v.fineY;
|
||||
step(2);
|
||||
|
||||
uint attribute = loadCHR(0x23c0 | r.v.nametable << 10 | (r.v.tileY >> 2) << 3 | r.v.tileX >> 2);
|
||||
if(r.v.tileY & 2) attribute >>= 4;
|
||||
if(r.v.tileX & 2) attribute >>= 2;
|
||||
uint attribute = loadCHR(0x23c0 | io.v.nametable << 10 | (io.v.tileY >> 2) << 3 | io.v.tileX >> 2);
|
||||
if(io.v.tileY & 2) attribute >>= 4;
|
||||
if(io.v.tileX & 2) attribute >>= 2;
|
||||
step(1);
|
||||
|
||||
if(enable() && ++r.v.tileX == 0) r.v.nametableX++;
|
||||
if(enable() && ++io.v.tileX == 0) io.v.nametableX++;
|
||||
step(1);
|
||||
|
||||
uint tiledataLo = loadCHR(tileaddr + 0);
|
||||
|
@ -188,20 +188,20 @@ auto PPU::renderScanline() -> void {
|
|||
uint tiledataHi = loadCHR(tileaddr + 8);
|
||||
step(2);
|
||||
|
||||
l.nametable = l.nametable << 8 | nametable;
|
||||
l.attribute = l.attribute << 2 | (attribute & 3);
|
||||
l.tiledataLo = l.tiledataLo << 8 | tiledataLo;
|
||||
l.tiledataHi = l.tiledataHi << 8 | tiledataHi;
|
||||
latch.nametable = latch.nametable << 8 | nametable;
|
||||
latch.attribute = latch.attribute << 2 | (attribute & 3);
|
||||
latch.tiledataLo = latch.tiledataLo << 8 | tiledataLo;
|
||||
latch.tiledataHi = latch.tiledataHi << 8 | tiledataHi;
|
||||
}
|
||||
|
||||
//337-338
|
||||
loadCHR(0x2000 | (uint12)r.v.address);
|
||||
loadCHR(0x2000 | (uint12)io.v.address);
|
||||
step(1);
|
||||
bool skip = enable() && r.field == 1 && r.ly == 261;
|
||||
bool skip = enable() && io.field == 1 && io.ly == 261;
|
||||
step(1);
|
||||
|
||||
//339
|
||||
loadCHR(0x2000 | (uint12)r.v.address);
|
||||
loadCHR(0x2000 | (uint12)io.v.address);
|
||||
step(1);
|
||||
|
||||
//340
|
||||
|
|
|
@ -1,49 +1,49 @@
|
|||
auto PPU::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
|
||||
s.integer(r.mdr);
|
||||
s.integer(io.mdr);
|
||||
|
||||
s.integer(r.field);
|
||||
s.integer(r.lx);
|
||||
s.integer(r.ly);
|
||||
s.integer(io.field);
|
||||
s.integer(io.lx);
|
||||
s.integer(io.ly);
|
||||
|
||||
s.integer(r.busData);
|
||||
s.integer(io.busData);
|
||||
|
||||
s.integer(r.v.value);
|
||||
s.integer(r.t.value);
|
||||
s.integer(io.v.value);
|
||||
s.integer(io.t.value);
|
||||
|
||||
s.integer(r.nmiHold);
|
||||
s.integer(r.nmiFlag);
|
||||
s.integer(io.nmiHold);
|
||||
s.integer(io.nmiFlag);
|
||||
|
||||
s.integer(r.vramIncrement);
|
||||
s.integer(r.spriteAddress);
|
||||
s.integer(r.bgAddress);
|
||||
s.integer(r.spriteHeight);
|
||||
s.integer(r.masterSelect);
|
||||
s.integer(r.nmiEnable);
|
||||
s.integer(io.vramIncrement);
|
||||
s.integer(io.spriteAddress);
|
||||
s.integer(io.bgAddress);
|
||||
s.integer(io.spriteHeight);
|
||||
s.integer(io.masterSelect);
|
||||
s.integer(io.nmiEnable);
|
||||
|
||||
s.integer(r.grayscale);
|
||||
s.integer(r.bgEdgeEnable);
|
||||
s.integer(r.spriteEdgeEnable);
|
||||
s.integer(r.bgEnable);
|
||||
s.integer(r.spriteEnable);
|
||||
s.integer(r.emphasis);
|
||||
s.integer(io.grayscale);
|
||||
s.integer(io.bgEdgeEnable);
|
||||
s.integer(io.spriteEdgeEnable);
|
||||
s.integer(io.bgEnable);
|
||||
s.integer(io.spriteEnable);
|
||||
s.integer(io.emphasis);
|
||||
|
||||
s.integer(r.spriteOverflow);
|
||||
s.integer(r.spriteZeroHit);
|
||||
s.integer(io.spriteOverflow);
|
||||
s.integer(io.spriteZeroHit);
|
||||
|
||||
s.integer(r.oamAddress);
|
||||
s.integer(io.oamAddress);
|
||||
|
||||
s.integer(l.nametable);
|
||||
s.integer(l.attribute);
|
||||
s.integer(l.tiledataLo);
|
||||
s.integer(l.tiledataHi);
|
||||
s.integer(latch.nametable);
|
||||
s.integer(latch.attribute);
|
||||
s.integer(latch.tiledataLo);
|
||||
s.integer(latch.tiledataHi);
|
||||
|
||||
s.integer(l.oamIterator);
|
||||
s.integer(l.oamCounter);
|
||||
s.integer(latch.oamIterator);
|
||||
s.integer(latch.oamCounter);
|
||||
|
||||
for(auto& o : l.oam) o.serialize(s);
|
||||
for(auto& o : l.soam) o.serialize(s);
|
||||
for(auto& o : latch.oam) o.serialize(s);
|
||||
for(auto& o : latch.soam) o.serialize(s);
|
||||
|
||||
s.array(ciram);
|
||||
s.array(cgram);
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
namespace GameBoy {
|
||||
|
||||
#include "sequencer/sequencer.cpp"
|
||||
#include "square1/square1.cpp"
|
||||
#include "square2/square2.cpp"
|
||||
#include "wave/wave.cpp"
|
||||
#include "noise/noise.cpp"
|
||||
#include "sequencer.cpp"
|
||||
#include "square1.cpp"
|
||||
#include "square2.cpp"
|
||||
#include "wave.cpp"
|
||||
#include "noise.cpp"
|
||||
#include "serialization.cpp"
|
||||
APU apu;
|
||||
|
||||
|
|
|
@ -11,17 +11,163 @@ struct APU : Thread, MMIO {
|
|||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
#include "square1/square1.hpp"
|
||||
#include "square2/square2.hpp"
|
||||
#include "wave/wave.hpp"
|
||||
#include "noise/noise.hpp"
|
||||
#include "sequencer/sequencer.hpp"
|
||||
//square1.cpp
|
||||
struct Square1 {
|
||||
auto dacEnable() const -> bool;
|
||||
|
||||
Square1 square1;
|
||||
Square2 square2;
|
||||
Wave wave;
|
||||
Noise noise;
|
||||
Sequencer sequencer;
|
||||
auto run() -> void;
|
||||
auto sweep(bool update) -> void;
|
||||
auto clockLength() -> void;
|
||||
auto clockSweep() -> void;
|
||||
auto clockEnvelope() -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power(bool initializeLength = true) -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
uint3 sweepFrequency;
|
||||
bool sweepDirection;
|
||||
uint3 sweepShift;
|
||||
bool sweepNegate;
|
||||
uint2 duty;
|
||||
uint length;
|
||||
uint4 envelopeVolume;
|
||||
bool envelopeDirection;
|
||||
uint3 envelopeFrequency;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
bool dutyOutput;
|
||||
uint3 phase;
|
||||
uint period;
|
||||
uint3 envelopePeriod;
|
||||
uint3 sweepPeriod;
|
||||
int frequencyShadow;
|
||||
bool sweepEnable;
|
||||
uint4 volume;
|
||||
} square1;
|
||||
|
||||
//square2.cpp
|
||||
struct Square2 {
|
||||
auto dacEnable() const -> bool;
|
||||
|
||||
auto run() -> void;
|
||||
auto clockLength() -> void;
|
||||
auto clockEnvelope() -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power(bool initializeLength = true) -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
uint2 duty;
|
||||
uint length;
|
||||
uint4 envelopeVolume;
|
||||
bool envelopeDirection;
|
||||
uint3 envelopeFrequency;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
bool dutyOutput;
|
||||
uint3 phase;
|
||||
uint period;
|
||||
uint3 envelopePeriod;
|
||||
uint4 volume;
|
||||
} square2;
|
||||
|
||||
struct Wave {
|
||||
auto getPattern(uint5 offset) const -> uint4;
|
||||
|
||||
auto run() -> void;
|
||||
auto clockLength() -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power(bool initializeLength = true) -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
bool dacEnable;
|
||||
uint2 volume;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
uint8 pattern[16];
|
||||
|
||||
int16 output;
|
||||
uint length;
|
||||
uint period;
|
||||
uint5 patternOffset;
|
||||
uint4 patternSample;
|
||||
uint patternHold;
|
||||
} wave;
|
||||
|
||||
struct Noise {
|
||||
auto dacEnable() const -> bool;
|
||||
auto getPeriod() const -> uint;
|
||||
|
||||
auto run() -> void;
|
||||
auto clockLength() -> void;
|
||||
auto clockEnvelope() -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power(bool initializeLength = true) -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
uint4 envelopeVolume;
|
||||
bool envelopeDirection;
|
||||
uint3 envelopeFrequency;
|
||||
uint4 frequency;
|
||||
bool narrow;
|
||||
uint3 divisor;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
uint length;
|
||||
uint3 envelopePeriod;
|
||||
uint4 volume;
|
||||
uint period;
|
||||
uint15 lfsr;
|
||||
} noise;
|
||||
|
||||
struct Sequencer {
|
||||
auto run() -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool leftEnable;
|
||||
uint3 leftVolume;
|
||||
bool rightEnable;
|
||||
uint3 rightVolume;
|
||||
|
||||
struct Channel {
|
||||
bool leftEnable;
|
||||
bool rightEnable;
|
||||
} square1, square2, wave, noise;
|
||||
|
||||
bool enable;
|
||||
|
||||
int16 center;
|
||||
int16 left;
|
||||
int16 right;
|
||||
|
||||
int64 centerBias;
|
||||
int64 leftBias;
|
||||
int64 rightBias;
|
||||
} sequencer;
|
||||
|
||||
uint3 phase; //high 3-bits of clock counter
|
||||
uint12 cycle; //low 12-bits of clock counter
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
struct Noise {
|
||||
auto dacEnable() const -> bool;
|
||||
auto getPeriod() const -> uint;
|
||||
|
||||
auto run() -> void;
|
||||
auto clockLength() -> void;
|
||||
auto clockEnvelope() -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power(bool initializeLength = true) -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
uint4 envelopeVolume;
|
||||
bool envelopeDirection;
|
||||
uint3 envelopeFrequency;
|
||||
uint4 frequency;
|
||||
bool narrow;
|
||||
uint3 divisor;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
uint length;
|
||||
uint3 envelopePeriod;
|
||||
uint4 volume;
|
||||
uint period;
|
||||
uint15 lfsr;
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
struct Sequencer {
|
||||
auto run() -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool leftEnable;
|
||||
uint3 leftVolume;
|
||||
bool rightEnable;
|
||||
uint3 rightVolume;
|
||||
|
||||
struct Channel {
|
||||
bool leftEnable;
|
||||
bool rightEnable;
|
||||
} square1, square2, wave, noise;
|
||||
|
||||
bool enable;
|
||||
|
||||
int16 center;
|
||||
int16 left;
|
||||
int16 right;
|
||||
|
||||
int64 centerBias;
|
||||
int64 leftBias;
|
||||
int64 rightBias;
|
||||
};
|
|
@ -1,38 +0,0 @@
|
|||
struct Square1 {
|
||||
auto dacEnable() const -> bool;
|
||||
|
||||
auto run() -> void;
|
||||
auto sweep(bool update) -> void;
|
||||
auto clockLength() -> void;
|
||||
auto clockSweep() -> void;
|
||||
auto clockEnvelope() -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power(bool initializeLength = true) -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
uint3 sweepFrequency;
|
||||
bool sweepDirection;
|
||||
uint3 sweepShift;
|
||||
bool sweepNegate;
|
||||
uint2 duty;
|
||||
uint length;
|
||||
uint4 envelopeVolume;
|
||||
bool envelopeDirection;
|
||||
uint3 envelopeFrequency;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
bool dutyOutput;
|
||||
uint3 phase;
|
||||
uint period;
|
||||
uint3 envelopePeriod;
|
||||
uint3 sweepPeriod;
|
||||
int frequencyShadow;
|
||||
bool sweepEnable;
|
||||
uint4 volume;
|
||||
};
|
|
@ -1,29 +0,0 @@
|
|||
struct Square2 {
|
||||
auto dacEnable() const -> bool;
|
||||
|
||||
auto run() -> void;
|
||||
auto clockLength() -> void;
|
||||
auto clockEnvelope() -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power(bool initializeLength = true) -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
uint2 duty;
|
||||
uint length;
|
||||
uint4 envelopeVolume;
|
||||
bool envelopeDirection;
|
||||
uint3 envelopeFrequency;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
|
||||
int16 output;
|
||||
bool dutyOutput;
|
||||
uint3 phase;
|
||||
uint period;
|
||||
uint3 envelopePeriod;
|
||||
uint4 volume;
|
||||
};
|
|
@ -1,26 +0,0 @@
|
|||
struct Wave {
|
||||
auto getPattern(uint5 offset) const -> uint4;
|
||||
|
||||
auto run() -> void;
|
||||
auto clockLength() -> void;
|
||||
auto read(uint16 addr) -> uint8;
|
||||
auto write(uint16 addr, uint8 data) -> void;
|
||||
auto power(bool initializeLength = true) -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool enable;
|
||||
|
||||
bool dacEnable;
|
||||
uint2 volume;
|
||||
uint11 frequency;
|
||||
bool counter;
|
||||
uint8 pattern[16];
|
||||
|
||||
int16 output;
|
||||
uint length;
|
||||
uint period;
|
||||
uint5 patternOffset;
|
||||
uint4 patternSample;
|
||||
uint patternHold;
|
||||
};
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace GameBoy {
|
||||
|
||||
#include "mmio.cpp"
|
||||
#include "io.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "timing.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
|
|
@ -10,7 +10,7 @@ struct CPU : Processor::LR35902, Thread, MMIO {
|
|||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//mmio.cpp
|
||||
//io.cpp
|
||||
auto wramAddress(uint16 addr) const -> uint;
|
||||
auto joypPoll() -> void;
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
//0x08: VRAM bank#
|
||||
//0x07: palette#
|
||||
|
||||
auto PPU::cgb_read_tile(bool select, uint x, uint y, uint& attr, uint& data) -> void {
|
||||
auto PPU::readTileCGB(bool select, uint x, uint y, uint& attr, uint& data) -> void {
|
||||
uint tmaddr = 0x1800 + (select << 10);
|
||||
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
|
||||
|
||||
|
@ -20,7 +20,7 @@ auto PPU::cgb_read_tile(bool select, uint x, uint y, uint& attr, uint& data) ->
|
|||
attr = vram[0x2000 + tmaddr];
|
||||
|
||||
uint tdaddr = attr & 0x08 ? 0x2000 : 0x0000;
|
||||
if(status.bg_tiledata_select == 0) {
|
||||
if(status.bgTiledataSelect == 0) {
|
||||
tdaddr += 0x1000 + ((int8)tile << 4);
|
||||
} else {
|
||||
tdaddr += 0x0000 + (tile << 4);
|
||||
|
@ -35,11 +35,11 @@ auto PPU::cgb_read_tile(bool select, uint x, uint y, uint& attr, uint& data) ->
|
|||
if(attr & 0x20) data = hflip(data);
|
||||
}
|
||||
|
||||
auto PPU::cgb_scanline() -> void {
|
||||
auto PPU::scanlineCGB() -> void {
|
||||
px = 0;
|
||||
if(!enabled()) return;
|
||||
|
||||
const uint Height = (status.ob_size == 0 ? 8 : 16);
|
||||
const uint Height = (status.obSize == 0 ? 8 : 16);
|
||||
sprites = 0;
|
||||
|
||||
//find first ten sprites on this scanline
|
||||
|
@ -47,7 +47,7 @@ auto PPU::cgb_scanline() -> void {
|
|||
Sprite& s = sprite[sprites];
|
||||
s.y = oam[n + 0] - 16;
|
||||
s.x = oam[n + 1] - 8;
|
||||
s.tile = oam[n + 2] & ~status.ob_size;
|
||||
s.tile = oam[n + 2] & ~status.obSize;
|
||||
s.attr = oam[n + 3];
|
||||
|
||||
s.y = status.ly - s.y;
|
||||
|
@ -63,22 +63,22 @@ auto PPU::cgb_scanline() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::cgb_run() -> void {
|
||||
auto PPU::runCGB() -> void {
|
||||
ob.color = 0;
|
||||
ob.palette = 0;
|
||||
ob.priority = 0;
|
||||
|
||||
uint color = 0x7fff;
|
||||
if(enabled()) {
|
||||
cgb_run_bg();
|
||||
if(status.window_display_enable) cgb_run_window();
|
||||
if(status.ob_enable) cgb_run_ob();
|
||||
runBackgroundCGB();
|
||||
if(status.windowDisplayEnable) runWindowCGB();
|
||||
if(status.obEnable) runObjectsCGB();
|
||||
|
||||
if(ob.palette == 0) {
|
||||
color = bg.color;
|
||||
} else if(bg.palette == 0) {
|
||||
color = ob.color;
|
||||
} else if(status.bg_enable == 0) {
|
||||
} else if(status.bgEnable == 0) {
|
||||
color = ob.color;
|
||||
} else if(bg.priority) {
|
||||
color = bg.color;
|
||||
|
@ -93,11 +93,11 @@ auto PPU::cgb_run() -> void {
|
|||
*output = color;
|
||||
}
|
||||
|
||||
auto PPU::cgb_run_bg() -> void {
|
||||
auto PPU::runBackgroundCGB() -> void {
|
||||
uint scrolly = (status.ly + status.scy) & 255;
|
||||
uint scrollx = (px + status.scx) & 255;
|
||||
uint tx = scrollx & 7;
|
||||
if(tx == 0 || px == 0) cgb_read_tile(status.bg_tilemap_select, scrollx, scrolly, background.attr, background.data);
|
||||
if(tx == 0 || px == 0) readTileCGB(status.bgTilemapSelect, scrollx, scrolly, background.attr, background.data);
|
||||
|
||||
uint index = 0;
|
||||
index |= (background.data & (0x0080 >> tx)) ? 1 : 0;
|
||||
|
@ -113,13 +113,13 @@ auto PPU::cgb_run_bg() -> void {
|
|||
bg.priority = background.attr & 0x80;
|
||||
}
|
||||
|
||||
auto PPU::cgb_run_window() -> void {
|
||||
auto PPU::runWindowCGB() -> void {
|
||||
uint scrolly = status.ly - status.wy;
|
||||
uint scrollx = px + 7 - status.wx;
|
||||
if(scrolly >= 144u) return; //also matches underflow (scrolly < 0)
|
||||
if(scrollx >= 160u) return; //also matches underflow (scrollx < 0)
|
||||
uint tx = scrollx & 7;
|
||||
if(tx == 0 || px == 0) cgb_read_tile(status.window_tilemap_select, scrollx, scrolly, window.attr, window.data);
|
||||
if(tx == 0 || px == 0) readTileCGB(status.windowTilemapSelect, scrollx, scrolly, window.attr, window.data);
|
||||
|
||||
uint index = 0;
|
||||
index |= (window.data & (0x0080 >> tx)) ? 1 : 0;
|
||||
|
@ -135,7 +135,7 @@ auto PPU::cgb_run_window() -> void {
|
|||
bg.priority = window.attr & 0x80;
|
||||
}
|
||||
|
||||
auto PPU::cgb_run_ob() -> void {
|
||||
auto PPU::runObjectsCGB() -> void {
|
||||
//render backwards, so that first sprite has priority
|
||||
for(int n = sprites - 1; n >= 0; n--) {
|
||||
Sprite& s = sprite[n];
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
//0x20: horizontal flip
|
||||
//0x10: palette#
|
||||
|
||||
auto PPU::dmg_read_tile(bool select, uint x, uint y, uint& data) -> void {
|
||||
auto PPU::readTileDMG(bool select, uint x, uint y, uint& data) -> void {
|
||||
uint tmaddr = 0x1800 + (select << 10), tdaddr;
|
||||
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
|
||||
if(status.bg_tiledata_select == 0) {
|
||||
if(status.bgTiledataSelect == 0) {
|
||||
tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4);
|
||||
} else {
|
||||
tdaddr = 0x0000 + (vram[tmaddr] << 4);
|
||||
|
@ -17,11 +17,11 @@ auto PPU::dmg_read_tile(bool select, uint x, uint y, uint& data) -> void {
|
|||
data |= vram[tdaddr + 1] << 8;
|
||||
}
|
||||
|
||||
auto PPU::dmg_scanline() -> void {
|
||||
auto PPU::scanlineDMG() -> void {
|
||||
px = 0;
|
||||
if(!enabled()) return;
|
||||
|
||||
const uint Height = (status.ob_size == 0 ? 8 : 16);
|
||||
const uint Height = (status.obSize == 0 ? 8 : 16);
|
||||
sprites = 0;
|
||||
|
||||
//find first ten sprites on this scanline
|
||||
|
@ -29,7 +29,7 @@ auto PPU::dmg_scanline() -> void {
|
|||
Sprite& s = sprite[sprites];
|
||||
s.y = oam[n + 0] - 16;
|
||||
s.x = oam[n + 1] - 8;
|
||||
s.tile = oam[n + 2] & ~status.ob_size;
|
||||
s.tile = oam[n + 2] & ~status.obSize;
|
||||
s.attr = oam[n + 3];
|
||||
|
||||
s.y = status.ly - s.y;
|
||||
|
@ -52,7 +52,7 @@ auto PPU::dmg_scanline() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::dmg_run() -> void {
|
||||
auto PPU::runDMG() -> void {
|
||||
bg.color = 0;
|
||||
bg.palette = 0;
|
||||
|
||||
|
@ -61,9 +61,9 @@ auto PPU::dmg_run() -> void {
|
|||
|
||||
uint color = 0;
|
||||
if(enabled()) {
|
||||
if(status.bg_enable) dmg_run_bg();
|
||||
if(status.window_display_enable) dmg_run_window();
|
||||
if(status.ob_enable) dmg_run_ob();
|
||||
if(status.bgEnable) runBackgroundDMG();
|
||||
if(status.windowDisplayEnable) runWindowDMG();
|
||||
if(status.obEnable) runObjectsDMG();
|
||||
|
||||
if(ob.palette == 0) {
|
||||
color = bg.color;
|
||||
|
@ -81,11 +81,11 @@ auto PPU::dmg_run() -> void {
|
|||
interface->lcdOutput(color); //Super Game Boy notification
|
||||
}
|
||||
|
||||
auto PPU::dmg_run_bg() -> void {
|
||||
auto PPU::runBackgroundDMG() -> void {
|
||||
uint scrolly = (status.ly + status.scy) & 255;
|
||||
uint scrollx = (px + status.scx) & 255;
|
||||
uint tx = scrollx & 7;
|
||||
if(tx == 0 || px == 0) dmg_read_tile(status.bg_tilemap_select, scrollx, scrolly, background.data);
|
||||
if(tx == 0 || px == 0) readTileDMG(status.bgTilemapSelect, scrollx, scrolly, background.data);
|
||||
|
||||
uint index = 0;
|
||||
index |= (background.data & (0x0080 >> tx)) ? 1 : 0;
|
||||
|
@ -95,13 +95,13 @@ auto PPU::dmg_run_bg() -> void {
|
|||
bg.palette = index;
|
||||
}
|
||||
|
||||
auto PPU::dmg_run_window() -> void {
|
||||
auto PPU::runWindowDMG() -> void {
|
||||
uint scrolly = status.ly - status.wy;
|
||||
uint scrollx = px + 7 - status.wx;
|
||||
if(scrolly >= 144u) return; //also matches underflow (scrolly < 0)
|
||||
if(scrollx >= 160u) return; //also matches underflow (scrollx < 0)
|
||||
uint tx = scrollx & 7;
|
||||
if(tx == 0 || px == 0) dmg_read_tile(status.window_tilemap_select, scrollx, scrolly, window.data);
|
||||
if(tx == 0 || px == 0) readTileDMG(status.windowTilemapSelect, scrollx, scrolly, window.data);
|
||||
|
||||
uint index = 0;
|
||||
index |= (window.data & (0x0080 >> tx)) ? 1 : 0;
|
||||
|
@ -111,7 +111,7 @@ auto PPU::dmg_run_window() -> void {
|
|||
bg.palette = index;
|
||||
}
|
||||
|
||||
auto PPU::dmg_run_ob() -> void {
|
||||
auto PPU::runObjectsDMG() -> void {
|
||||
//render backwards, so that first sprite has priority
|
||||
for(int n = sprites - 1; n >= 0; n--) {
|
||||
Sprite& s = sprite[n];
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
auto PPU::vram_addr(uint16 addr) const -> uint {
|
||||
return (status.vram_bank * 0x2000) + (addr & 0x1fff);
|
||||
auto PPU::vramAddress(uint16 addr) const -> uint {
|
||||
return status.vramBank << 13 | (uint13)addr;
|
||||
}
|
||||
|
||||
auto PPU::readIO(uint16 addr) -> uint8 {
|
||||
if(addr >= 0x8000 && addr <= 0x9fff) {
|
||||
return vram[vram_addr(addr)];
|
||||
return vram[vramAddress(addr)];
|
||||
}
|
||||
|
||||
if(addr >= 0xfe00 && addr <= 0xfe9f) {
|
||||
if(status.dma_active && status.dma_clock >= 8) return 0xff;
|
||||
if(status.dmaActive && status.dmaClock >= 8) return 0xff;
|
||||
return oam[addr & 0xff];
|
||||
}
|
||||
|
||||
if(addr == 0xff40) { //LCDC
|
||||
return (status.display_enable << 7)
|
||||
| (status.window_tilemap_select << 6)
|
||||
| (status.window_display_enable << 5)
|
||||
| (status.bg_tiledata_select << 4)
|
||||
| (status.bg_tilemap_select << 3)
|
||||
| (status.ob_size << 2)
|
||||
| (status.ob_enable << 1)
|
||||
| (status.bg_enable << 0);
|
||||
return (status.displayEnable << 7)
|
||||
| (status.windowTilemapSelect << 6)
|
||||
| (status.windowDisplayEnable << 5)
|
||||
| (status.bgTiledataSelect << 4)
|
||||
| (status.bgTilemapSelect << 3)
|
||||
| (status.obSize << 2)
|
||||
| (status.obEnable << 1)
|
||||
| (status.bgEnable << 0);
|
||||
}
|
||||
|
||||
if(addr == 0xff41) { //STAT
|
||||
return (status.interrupt_lyc << 6)
|
||||
| (status.interrupt_oam << 5)
|
||||
| (status.interrupt_vblank << 4)
|
||||
| (status.interrupt_hblank << 3)
|
||||
return (status.interruptLYC << 6)
|
||||
| (status.interruptOAM << 5)
|
||||
| (status.interruptVblank << 4)
|
||||
| (status.interruptHblank << 3)
|
||||
| ((status.ly == status.lyc) << 2)
|
||||
| (status.mode << 0);
|
||||
}
|
||||
|
@ -78,11 +78,11 @@ auto PPU::readIO(uint16 addr) -> uint8 {
|
|||
}
|
||||
|
||||
if(addr == 0xff4f) { //VBK
|
||||
return status.vram_bank;
|
||||
return status.vramBank;
|
||||
}
|
||||
|
||||
if(addr == 0xff68) { //BGPI
|
||||
return status.bgpi_increment << 7 | status.bgpi;
|
||||
return status.bgpiIncrement << 7 | status.bgpi;
|
||||
}
|
||||
|
||||
if(addr == 0xff69) { //BGPD
|
||||
|
@ -90,7 +90,7 @@ auto PPU::readIO(uint16 addr) -> uint8 {
|
|||
}
|
||||
|
||||
if(addr == 0xff6a) { //OBPI
|
||||
return status.obpi_increment << 7 | status.obpi;
|
||||
return status.obpiIncrement << 7 | status.obpi;
|
||||
}
|
||||
|
||||
if(addr == 0xff6b) { //OBPD
|
||||
|
@ -102,18 +102,18 @@ auto PPU::readIO(uint16 addr) -> uint8 {
|
|||
|
||||
auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
||||
if(addr >= 0x8000 && addr <= 0x9fff) {
|
||||
vram[vram_addr(addr)] = data;
|
||||
vram[vramAddress(addr)] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr >= 0xfe00 && addr <= 0xfe9f) {
|
||||
if(status.dma_active && status.dma_clock >= 8) return;
|
||||
if(status.dmaActive && status.dmaClock >= 8) return;
|
||||
oam[addr & 0xff] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff40) { //LCDC
|
||||
if(status.display_enable == false && (data & 0x80)) {
|
||||
if(!status.displayEnable && (data & 0x80)) {
|
||||
status.ly = 0;
|
||||
status.lx = 0;
|
||||
|
||||
|
@ -123,22 +123,22 @@ auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
|||
this->clock = clock;
|
||||
}
|
||||
|
||||
status.display_enable = data & 0x80;
|
||||
status.window_tilemap_select = data & 0x40;
|
||||
status.window_display_enable = data & 0x20;
|
||||
status.bg_tiledata_select = data & 0x10;
|
||||
status.bg_tilemap_select = data & 0x08;
|
||||
status.ob_size = data & 0x04;
|
||||
status.ob_enable = data & 0x02;
|
||||
status.bg_enable = data & 0x01;
|
||||
status.displayEnable = data & 0x80;
|
||||
status.windowTilemapSelect = data & 0x40;
|
||||
status.windowDisplayEnable = data & 0x20;
|
||||
status.bgTiledataSelect = data & 0x10;
|
||||
status.bgTilemapSelect = data & 0x08;
|
||||
status.obSize = data & 0x04;
|
||||
status.obEnable = data & 0x02;
|
||||
status.bgEnable = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff41) { //STAT
|
||||
status.interrupt_lyc = data & 0x40;
|
||||
status.interrupt_oam = data & 0x20;
|
||||
status.interrupt_vblank = data & 0x10;
|
||||
status.interrupt_hblank = data & 0x08;
|
||||
status.interruptLYC = data & 0x40;
|
||||
status.interruptOAM = data & 0x20;
|
||||
status.interruptVblank = data & 0x10;
|
||||
status.interruptHblank = data & 0x08;
|
||||
|
||||
//hardware bug: writes to STAT on DMG,SGB during vblank triggers STAT IRQ
|
||||
//note: this behavior isn't entirely correct; more research is needed ...
|
||||
|
@ -170,9 +170,9 @@ auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
|||
}
|
||||
|
||||
if(addr == 0xff46) { //DMA
|
||||
status.dma_active = true;
|
||||
status.dma_clock = 0;
|
||||
status.dma_bank = data;
|
||||
status.dmaActive = true;
|
||||
status.dmaClock = 0;
|
||||
status.dmaBank = data;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -211,29 +211,29 @@ auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
|||
}
|
||||
|
||||
if(addr == 0xff4f) { //VBK
|
||||
status.vram_bank = data & 1;
|
||||
status.vramBank = data & 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff68) { //BGPI
|
||||
status.bgpi_increment = data & 0x80;
|
||||
status.bgpiIncrement = data & 0x80;
|
||||
status.bgpi = data & 0x3f;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff69) { //BGPD
|
||||
bgpd[status.bgpi] = data;
|
||||
if(status.bgpi_increment) status.bgpi++;
|
||||
if(status.bgpiIncrement) status.bgpi++;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0xff6a) { //OBPI
|
||||
status.obpi_increment = data & 0x80;
|
||||
status.obpiIncrement = data & 0x80;
|
||||
status.obpi = data & 0x3f;
|
||||
}
|
||||
|
||||
if(addr == 0xff6b) { //OBPD
|
||||
obpd[status.obpi] = data;
|
||||
if(status.obpi_increment) status.obpi++;
|
||||
if(status.obpiIncrement) status.obpi++;
|
||||
}
|
||||
}
|
|
@ -3,12 +3,12 @@
|
|||
namespace GameBoy {
|
||||
|
||||
PPU ppu;
|
||||
#include "mmio.cpp"
|
||||
#include "io.cpp"
|
||||
#include "dmg.cpp"
|
||||
#include "cgb.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto PPU::enabled() const -> bool { return status.display_enable; }
|
||||
auto PPU::enabled() const -> bool { return status.displayEnable; }
|
||||
|
||||
auto PPU::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), ppu.main();
|
||||
|
@ -21,20 +21,20 @@ auto PPU::main() -> void {
|
|||
if(status.ly <= 143) {
|
||||
mode(2);
|
||||
scanline();
|
||||
wait(92);
|
||||
step(92);
|
||||
|
||||
mode(3);
|
||||
for(auto n : range(160)) {
|
||||
run();
|
||||
wait(1);
|
||||
step(1);
|
||||
}
|
||||
|
||||
mode(0);
|
||||
if(enabled()) cpu.hblank();
|
||||
wait(204);
|
||||
step(204);
|
||||
} else {
|
||||
mode(1);
|
||||
wait(456);
|
||||
step(456);
|
||||
}
|
||||
|
||||
status.ly++;
|
||||
|
@ -56,10 +56,10 @@ auto PPU::mode(uint mode) -> void {
|
|||
auto PPU::stat() -> void {
|
||||
bool irq = status.irq;
|
||||
|
||||
status.irq = status.interrupt_hblank && status.mode == 0;
|
||||
status.irq |= status.interrupt_vblank && status.mode == 1;
|
||||
status.irq |= status.interrupt_oam && status.mode == 2;
|
||||
status.irq |= status.interrupt_lyc && coincidence();
|
||||
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);
|
||||
}
|
||||
|
@ -74,11 +74,11 @@ auto PPU::refresh() -> void {
|
|||
if(!system.sgb()) Emulator::video.refresh(screen, 160 * sizeof(uint32), 160, 144);
|
||||
}
|
||||
|
||||
auto PPU::wait(uint clocks) -> void {
|
||||
auto PPU::step(uint clocks) -> void {
|
||||
while(clocks--) {
|
||||
stat();
|
||||
if(status.dma_active) {
|
||||
uint hi = status.dma_clock++;
|
||||
if(status.dmaActive) {
|
||||
uint hi = status.dmaClock++;
|
||||
uint lo = hi & (cpu.status.speedDouble ? 1 : 3);
|
||||
hi >>= cpu.status.speedDouble ? 1 : 2;
|
||||
if(lo == 0) {
|
||||
|
@ -86,9 +86,9 @@ auto PPU::wait(uint clocks) -> void {
|
|||
//warm-up
|
||||
} else if(hi == 161) {
|
||||
//cool-down; disable
|
||||
status.dma_active = false;
|
||||
status.dmaActive = false;
|
||||
} else {
|
||||
oam[hi - 1] = bus.read(status.dma_bank << 8 | hi - 1);
|
||||
oam[hi - 1] = bus.read(status.dmaBank << 8 | hi - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,11 +110,11 @@ auto PPU::power() -> void {
|
|||
create(Enter, 4 * 1024 * 1024);
|
||||
|
||||
if(system.cgb()) {
|
||||
scanline = {&PPU::cgb_scanline, this};
|
||||
run = {&PPU::cgb_run, this};
|
||||
scanline = {&PPU::scanlineCGB, this};
|
||||
run = {&PPU::runCGB, this};
|
||||
} else {
|
||||
scanline = {&PPU::dmg_scanline, this};
|
||||
run = {&PPU::dmg_run, this};
|
||||
scanline = {&PPU::scanlineDMG, this};
|
||||
run = {&PPU::runDMG, this};
|
||||
}
|
||||
|
||||
for(uint n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
|
||||
|
@ -149,43 +149,7 @@ auto PPU::power() -> void {
|
|||
for(auto& n : bgpd) n = 0x0000;
|
||||
for(auto& n : obpd) n = 0x0000;
|
||||
|
||||
status.irq = false;
|
||||
status.lx = 0;
|
||||
|
||||
status.display_enable = 0;
|
||||
status.window_tilemap_select = 0;
|
||||
status.window_display_enable = 0;
|
||||
status.bg_tiledata_select = 0;
|
||||
status.bg_tilemap_select = 0;
|
||||
status.ob_size = 0;
|
||||
status.ob_enable = 0;
|
||||
status.bg_enable = 0;
|
||||
|
||||
status.interrupt_lyc = 0;
|
||||
status.interrupt_oam = 0;
|
||||
status.interrupt_vblank = 0;
|
||||
status.interrupt_hblank = 0;
|
||||
status.mode = 0;
|
||||
|
||||
status.scy = 0;
|
||||
status.scx = 0;
|
||||
status.ly = 0;
|
||||
status.lyc = 0;
|
||||
|
||||
status.dma_active = false;
|
||||
status.dma_clock = 0;
|
||||
status.dma_bank = 0;
|
||||
|
||||
status.wy = 0;
|
||||
status.wx = 0;
|
||||
|
||||
status.vram_bank = 0;
|
||||
|
||||
status.bgpi_increment = 0;
|
||||
status.bgpi = 0;
|
||||
|
||||
status.obpi_increment = 0;
|
||||
status.obpi = 0;
|
||||
memory::fill(&status, sizeof(Status));
|
||||
|
||||
for(auto& n : screen) n = 0;
|
||||
|
||||
|
|
|
@ -7,30 +7,30 @@ struct PPU : Thread, MMIO {
|
|||
auto stat() -> void;
|
||||
auto coincidence() -> bool;
|
||||
auto refresh() -> void;
|
||||
auto wait(uint clocks) -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto hflip(uint data) const -> uint;
|
||||
|
||||
//mmio.cpp
|
||||
auto vram_addr(uint16 addr) const -> uint;
|
||||
//io.cpp
|
||||
auto vramAddress(uint16 addr) const -> uint;
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
|
||||
//dmg.cpp
|
||||
auto dmg_read_tile(bool select, uint x, uint y, uint& data) -> void;
|
||||
auto dmg_scanline() -> void;
|
||||
auto dmg_run() -> void;
|
||||
auto dmg_run_bg() -> void;
|
||||
auto dmg_run_window() -> void;
|
||||
auto dmg_run_ob() -> void;
|
||||
auto readTileDMG(bool select, uint x, uint y, uint& data) -> void;
|
||||
auto scanlineDMG() -> void;
|
||||
auto runDMG() -> void;
|
||||
auto runBackgroundDMG() -> void;
|
||||
auto runWindowDMG() -> void;
|
||||
auto runObjectsDMG() -> void;
|
||||
|
||||
//cgb.cpp
|
||||
auto cgb_read_tile(bool select, uint x, uint y, uint& attr, uint& data) -> void;
|
||||
auto cgb_scanline() -> void;
|
||||
auto cgb_run() -> void;
|
||||
auto cgb_run_bg() -> void;
|
||||
auto cgb_run_window() -> void;
|
||||
auto cgb_run_ob() -> void;
|
||||
auto readTileCGB(bool select, uint x, uint y, uint& attr, uint& data) -> void;
|
||||
auto scanlineCGB() -> void;
|
||||
auto runCGB() -> void;
|
||||
auto runBackgroundCGB() -> void;
|
||||
auto runWindowCGB() -> void;
|
||||
auto runObjectsCGB() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
|
@ -51,20 +51,20 @@ struct PPU : Thread, MMIO {
|
|||
uint lx;
|
||||
|
||||
//$ff40 LCDC
|
||||
bool display_enable;
|
||||
bool window_tilemap_select;
|
||||
bool window_display_enable;
|
||||
bool bg_tiledata_select;
|
||||
bool bg_tilemap_select;
|
||||
bool ob_size;
|
||||
bool ob_enable;
|
||||
bool bg_enable;
|
||||
bool displayEnable;
|
||||
bool windowTilemapSelect;
|
||||
bool windowDisplayEnable;
|
||||
bool bgTiledataSelect;
|
||||
bool bgTilemapSelect;
|
||||
bool obSize;
|
||||
bool obEnable;
|
||||
bool bgEnable;
|
||||
|
||||
//$ff41 STAT
|
||||
bool interrupt_lyc;
|
||||
bool interrupt_oam;
|
||||
bool interrupt_vblank;
|
||||
bool interrupt_hblank;
|
||||
bool interruptLYC;
|
||||
bool interruptOAM;
|
||||
bool interruptVblank;
|
||||
bool interruptHblank;
|
||||
uint2 mode;
|
||||
|
||||
//$ff42 SCY
|
||||
|
@ -80,9 +80,9 @@ struct PPU : Thread, MMIO {
|
|||
uint8 lyc;
|
||||
|
||||
//$ff46 DMA
|
||||
bool dma_active;
|
||||
uint dma_clock;
|
||||
uint8 dma_bank;
|
||||
bool dmaActive;
|
||||
uint dmaClock;
|
||||
uint8 dmaBank;
|
||||
|
||||
//$ff4a WY
|
||||
uint8 wy;
|
||||
|
@ -91,14 +91,14 @@ struct PPU : Thread, MMIO {
|
|||
uint8 wx;
|
||||
|
||||
//$ff4f VBK
|
||||
bool vram_bank;
|
||||
bool vramBank;
|
||||
|
||||
//$ff68 BGPI
|
||||
bool bgpi_increment;
|
||||
bool bgpiIncrement;
|
||||
uint6 bgpi;
|
||||
|
||||
//$ff6a OBPI
|
||||
bool obpi_increment;
|
||||
bool obpiIncrement;
|
||||
uint8 obpi;
|
||||
} status;
|
||||
|
||||
|
|
|
@ -12,19 +12,19 @@ auto PPU::serialize(serializer& s) -> void {
|
|||
s.integer(status.irq);
|
||||
s.integer(status.lx);
|
||||
|
||||
s.integer(status.display_enable);
|
||||
s.integer(status.window_tilemap_select);
|
||||
s.integer(status.window_display_enable);
|
||||
s.integer(status.bg_tiledata_select);
|
||||
s.integer(status.bg_tilemap_select);
|
||||
s.integer(status.ob_size);
|
||||
s.integer(status.ob_enable);
|
||||
s.integer(status.bg_enable);
|
||||
s.integer(status.displayEnable);
|
||||
s.integer(status.windowTilemapSelect);
|
||||
s.integer(status.windowDisplayEnable);
|
||||
s.integer(status.bgTiledataSelect);
|
||||
s.integer(status.bgTilemapSelect);
|
||||
s.integer(status.obSize);
|
||||
s.integer(status.obEnable);
|
||||
s.integer(status.bgEnable);
|
||||
|
||||
s.integer(status.interrupt_lyc);
|
||||
s.integer(status.interrupt_oam);
|
||||
s.integer(status.interrupt_vblank);
|
||||
s.integer(status.interrupt_hblank);
|
||||
s.integer(status.interruptLYC);
|
||||
s.integer(status.interruptOAM);
|
||||
s.integer(status.interruptVblank);
|
||||
s.integer(status.interruptHblank);
|
||||
s.integer(status.mode);
|
||||
|
||||
s.integer(status.scy);
|
||||
|
@ -33,19 +33,19 @@ auto PPU::serialize(serializer& s) -> void {
|
|||
s.integer(status.ly);
|
||||
s.integer(status.lyc);
|
||||
|
||||
s.integer(status.dma_active);
|
||||
s.integer(status.dma_clock);
|
||||
s.integer(status.dma_bank);
|
||||
s.integer(status.dmaActive);
|
||||
s.integer(status.dmaClock);
|
||||
s.integer(status.dmaBank);
|
||||
|
||||
s.integer(status.wy);
|
||||
s.integer(status.wx);
|
||||
|
||||
s.integer(status.vram_bank);
|
||||
s.integer(status.vramBank);
|
||||
|
||||
s.integer(status.bgpi_increment);
|
||||
s.integer(status.bgpiIncrement);
|
||||
s.integer(status.bgpi);
|
||||
|
||||
s.integer(status.obpi_increment);
|
||||
s.integer(status.obpiIncrement);
|
||||
s.integer(status.obpi);
|
||||
|
||||
s.array(screen);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace GameBoyAdvance {
|
||||
|
||||
#include "mmio.cpp"
|
||||
#include "io.cpp"
|
||||
#include "square.cpp"
|
||||
#include "square1.cpp"
|
||||
#include "square2.cpp"
|
||||
|
@ -87,7 +87,7 @@ auto APU::power() -> void {
|
|||
regs.bias.amplitude = 0;
|
||||
regs.bias.level = 0x200;
|
||||
|
||||
for(uint n = 0x060; n <= 0x0a7; n++) bus.mmio[n] = this;
|
||||
for(uint n = 0x060; n <= 0x0a7; n++) bus.io[n] = this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct APU : Thread, MMIO {
|
||||
struct APU : Thread, IO {
|
||||
shared_pointer<Emulator::Stream> stream;
|
||||
|
||||
#include "registers.hpp"
|
||||
|
@ -7,8 +7,8 @@ struct APU : Thread, MMIO {
|
|||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto read(uint32 addr) -> uint8;
|
||||
auto write(uint32 addr, uint8 byte) -> void;
|
||||
auto readIO(uint32 addr) -> uint8;
|
||||
auto writeIO(uint32 addr, uint8 byte) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto runsequencer() -> void;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto APU::read(uint32 addr) -> uint8 {
|
||||
auto APU::readIO(uint32 addr) -> uint8 {
|
||||
switch(addr) {
|
||||
|
||||
//NR10
|
||||
|
@ -111,7 +111,7 @@ auto APU::read(uint32 addr) -> uint8 {
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto APU::write(uint32 addr, uint8 data) -> void {
|
||||
auto APU::writeIO(uint32 addr, uint8 data) -> void {
|
||||
switch(addr) {
|
||||
|
||||
//NR10
|
|
@ -42,7 +42,7 @@ auto Cartridge::FLASH::write(uint16 addr, uint8 byte) -> void {
|
|||
if(byte == 0x10 && addr == 0x5555) {
|
||||
if(erasemode) {
|
||||
erasemode = false;
|
||||
for(unsigned n = 0; n < size; n++) data[n] = 0xff;
|
||||
for(uint n : range(size)) data[n] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,8 +50,8 @@ auto Cartridge::FLASH::write(uint16 addr, uint8 byte) -> void {
|
|||
//command only valid for non-Atmel chips
|
||||
if(erasemode && id != 0x3d1f) {
|
||||
erasemode = false;
|
||||
unsigned offset = bank << 16 | (addr & ~4095);
|
||||
for(unsigned n = 0; n < 4096; n++) data[offset++] = 0xff;
|
||||
uint offset = bank << 16 | (addr & ~4095);
|
||||
for(uint n : range(4096)) data[offset++] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,63 +1,63 @@
|
|||
auto CPU::busIdle() -> void {
|
||||
prefetch_step(1);
|
||||
auto CPU::_idle() -> void {
|
||||
prefetchStep(1);
|
||||
}
|
||||
|
||||
auto CPU::busRead(uint mode, uint32 addr) -> uint32 {
|
||||
uint wait = busWait(mode, addr);
|
||||
auto CPU::_read(uint mode, uint32 addr) -> uint32 {
|
||||
uint wait = this->wait(mode, addr);
|
||||
uint word = pipeline.fetch.instruction;
|
||||
|
||||
if(addr >= 0x1000'0000) {
|
||||
prefetch_step(wait);
|
||||
prefetchStep(wait);
|
||||
} else if(addr & 0x0800'0000) {
|
||||
if(mode & Prefetch && regs.wait.control.prefetch) {
|
||||
prefetch_sync(addr);
|
||||
word = prefetch_read();
|
||||
if(mode & Word) word |= prefetch_read() << 16;
|
||||
prefetchSync(addr);
|
||||
word = prefetchRead();
|
||||
if(mode & Word) word |= prefetchRead() << 16;
|
||||
} else {
|
||||
if(!active.dma) prefetch_wait();
|
||||
if(!active.dma) prefetchWait();
|
||||
step(wait - 1);
|
||||
word = cartridge.read(mode, addr);
|
||||
step(1);
|
||||
}
|
||||
} else {
|
||||
prefetch_step(wait - 1);
|
||||
prefetchStep(wait - 1);
|
||||
if(addr < 0x0200'0000) word = bios.read(mode, addr);
|
||||
else if(addr < 0x0300'0000) word = ewram_read(mode, addr);
|
||||
else if(addr < 0x0400'0000) word = iwram_read(mode, addr);
|
||||
else if(addr >= 0x0700'0000) word = ppu.oam_read(mode, addr);
|
||||
else if(addr >= 0x0600'0000) word = ppu.vram_read(mode, addr);
|
||||
else if(addr >= 0x0500'0000) word = ppu.pram_read(mode, addr);
|
||||
else if((addr & 0xffff'fc00) == 0x0400'0000) word = bus.mmio[addr & 0x3ff]->read(mode, addr);
|
||||
else if((addr & 0xff00'ffff) == 0x0400'0800) word = ((MMIO*)this)->read(mode, 0x0400'0800 | (addr & 3));
|
||||
prefetch_step(1);
|
||||
else if(addr < 0x0300'0000) word = readEWRAM(mode, addr);
|
||||
else if(addr < 0x0400'0000) word = readIWRAM(mode, addr);
|
||||
else if(addr >= 0x0700'0000) word = ppu.readOAM(mode, addr);
|
||||
else if(addr >= 0x0600'0000) word = ppu.readVRAM(mode, addr);
|
||||
else if(addr >= 0x0500'0000) word = ppu.readPRAM(mode, addr);
|
||||
else if((addr & 0xffff'fc00) == 0x0400'0000) word = bus.io[addr & 0x3ff]->readIO(mode, addr);
|
||||
else if((addr & 0xff00'ffff) == 0x0400'0800) word = ((IO*)this)->readIO(mode, 0x0400'0800 | (addr & 3));
|
||||
prefetchStep(1);
|
||||
}
|
||||
|
||||
return word;
|
||||
}
|
||||
|
||||
auto CPU::busWrite(uint mode, uint32 addr, uint32 word) -> void {
|
||||
uint wait = busWait(mode, addr);
|
||||
auto CPU::_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
uint wait = this->wait(mode, addr);
|
||||
|
||||
if(addr >= 0x1000'0000) {
|
||||
prefetch_step(wait);
|
||||
prefetchStep(wait);
|
||||
} else if(addr & 0x0800'0000) {
|
||||
if(!active.dma) prefetch_wait();
|
||||
if(!active.dma) prefetchWait();
|
||||
step(wait);
|
||||
cartridge.write(mode, addr, word);
|
||||
} else {
|
||||
prefetch_step(wait);
|
||||
prefetchStep(wait);
|
||||
if(addr < 0x0200'0000);
|
||||
else if(addr < 0x0300'0000) ewram_write(mode, addr, word);
|
||||
else if(addr < 0x0400'0000) iwram_write(mode, addr, word);
|
||||
else if(addr >= 0x0700'0000) ppu.oam_write(mode, addr, word);
|
||||
else if(addr >= 0x0600'0000) ppu.vram_write(mode, addr, word);
|
||||
else if(addr >= 0x0500'0000) ppu.pram_write(mode, addr, word);
|
||||
else if((addr & 0xffff'fc00) == 0x0400'0000) bus.mmio[addr & 0x3ff]->write(mode, addr, word);
|
||||
else if((addr & 0xff00'ffff) == 0x0400'0800) ((MMIO*)this)->write(mode, 0x0400'0800 | (addr & 3), word);
|
||||
else if(addr < 0x0300'0000) writeEWRAM(mode, addr, word);
|
||||
else if(addr < 0x0400'0000) writeIWRAM(mode, addr, word);
|
||||
else if(addr >= 0x0700'0000) ppu.writeOAM(mode, addr, word);
|
||||
else if(addr >= 0x0600'0000) ppu.writeVRAM(mode, addr, word);
|
||||
else if(addr >= 0x0500'0000) ppu.writePRAM(mode, addr, word);
|
||||
else if((addr & 0xffff'fc00) == 0x0400'0000) bus.io[addr & 0x3ff]->writeIO(mode, addr, word);
|
||||
else if((addr & 0xff00'ffff) == 0x0400'0800) ((IO*)this)->writeIO(mode, 0x0400'0800 | (addr & 3), word);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::busWait(uint mode, uint32 addr) -> uint {
|
||||
auto CPU::wait(uint mode, uint32 addr) -> uint {
|
||||
if(addr >= 0x1000'0000) return 1; //unmapped
|
||||
if(addr < 0x0200'0000) return 1;
|
||||
if(addr < 0x0300'0000) return (16 - regs.memory.control.ewramwait) * (mode & Word ? 2 : 1);
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace GameBoyAdvance {
|
|||
|
||||
#include "prefetch.cpp"
|
||||
#include "bus.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "io.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "dma.cpp"
|
||||
#include "timer.cpp"
|
||||
|
@ -56,14 +56,14 @@ auto CPU::main() -> void {
|
|||
|
||||
if(regs.mode == Registers::Mode::Stop) {
|
||||
if(!(regs.irq.enable & regs.irq.flag & Interrupt::Keypad)) {
|
||||
sync_step(16); //STOP does not advance timers
|
||||
syncStep(16); //STOP does not advance timers
|
||||
} else {
|
||||
regs.mode = Registers::Mode::Normal;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
dma_run();
|
||||
dmaRun();
|
||||
|
||||
if(regs.mode == Registers::Mode::Halt) {
|
||||
if(!(regs.irq.enable & regs.irq.flag)) {
|
||||
|
@ -78,11 +78,11 @@ auto CPU::main() -> void {
|
|||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
timer_step(clocks);
|
||||
sync_step(clocks);
|
||||
timerStep(clocks);
|
||||
syncStep(clocks);
|
||||
}
|
||||
|
||||
auto CPU::sync_step(uint clocks) -> void {
|
||||
auto CPU::syncStep(uint clocks) -> void {
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
|
@ -90,7 +90,7 @@ auto CPU::sync_step(uint clocks) -> void {
|
|||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
}
|
||||
|
||||
auto CPU::keypad_run() -> void {
|
||||
auto CPU::keypadRun() -> void {
|
||||
//lookup table to convert button indexes to Emulator::Interface indexes
|
||||
static const uint lookup[] = {5, 4, 8, 9, 3, 2, 0, 1, 7, 6};
|
||||
|
||||
|
@ -166,13 +166,13 @@ auto CPU::power() -> void {
|
|||
|
||||
active.dma = false;
|
||||
|
||||
for(uint n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this; //DMA
|
||||
for(uint n = 0x100; n <= 0x10f; n++) bus.mmio[n] = this; //Timers
|
||||
for(uint n = 0x120; n <= 0x12b; n++) bus.mmio[n] = this; //Serial
|
||||
for(uint n = 0x130; n <= 0x133; n++) bus.mmio[n] = this; //Keypad
|
||||
for(uint n = 0x134; n <= 0x159; n++) bus.mmio[n] = this; //Serial
|
||||
for(uint n = 0x200; n <= 0x209; n++) bus.mmio[n] = this; //System
|
||||
for(uint n = 0x300; n <= 0x301; n++) bus.mmio[n] = this; //System
|
||||
for(uint n = 0x0b0; n <= 0x0df; n++) bus.io[n] = this; //DMA
|
||||
for(uint n = 0x100; n <= 0x10f; n++) bus.io[n] = this; //Timers
|
||||
for(uint n = 0x120; n <= 0x12b; n++) bus.io[n] = this; //Serial
|
||||
for(uint n = 0x130; n <= 0x133; n++) bus.io[n] = this; //Keypad
|
||||
for(uint n = 0x134; n <= 0x159; n++) bus.io[n] = this; //Serial
|
||||
for(uint n = 0x200; n <= 0x209; n++) bus.io[n] = this; //System
|
||||
for(uint n = 0x300; n <= 0x301; n++) bus.io[n] = this; //System
|
||||
//0x080-0x083 mirrored via gba/memory/memory.cpp //System
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct CPU : Processor::ARM, Thread, MMIO {
|
||||
struct CPU : Processor::ARM, Thread, IO {
|
||||
using ARM::read;
|
||||
using ARM::write;
|
||||
|
||||
|
@ -34,37 +34,37 @@ struct CPU : Processor::ARM, Thread, MMIO {
|
|||
|
||||
auto step(uint clocks) -> void override;
|
||||
|
||||
auto sync_step(uint clocks) -> void;
|
||||
auto keypad_run() -> void;
|
||||
auto syncStep(uint clocks) -> void;
|
||||
auto keypadRun() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//bus.cpp
|
||||
auto busIdle() -> void override;
|
||||
auto busRead(uint mode, uint32 addr) -> uint32 override;
|
||||
auto busWrite(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
auto busWait(uint mode, uint32 addr) -> uint;
|
||||
auto _idle() -> void override;
|
||||
auto _read(uint mode, uint32 addr) -> uint32 override;
|
||||
auto _write(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
auto wait(uint mode, uint32 addr) -> uint;
|
||||
|
||||
//mmio.cpp
|
||||
auto read(uint32 addr) -> uint8;
|
||||
auto write(uint32 addr, uint8 byte) -> void;
|
||||
//io.cpp
|
||||
auto readIO(uint32 addr) -> uint8;
|
||||
auto writeIO(uint32 addr, uint8 byte) -> void;
|
||||
|
||||
auto iwram_read(uint mode, uint32 addr) -> uint32;
|
||||
auto iwram_write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
auto readIWRAM(uint mode, uint32 addr) -> uint32;
|
||||
auto writeIWRAM(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto ewram_read(uint mode, uint32 addr) -> uint32;
|
||||
auto ewram_write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
auto readEWRAM(uint mode, uint32 addr) -> uint32;
|
||||
auto writeEWRAM(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
//dma.cpp
|
||||
auto dma_run() -> void;
|
||||
auto dma_exec(Registers::DMA& dma) -> void;
|
||||
auto dma_vblank() -> void;
|
||||
auto dma_hblank() -> void;
|
||||
auto dma_hdma() -> void;
|
||||
auto dmaRun() -> void;
|
||||
auto dmaExecute(Registers::DMA& dma) -> void;
|
||||
auto dmaVblank() -> void;
|
||||
auto dmaHblank() -> void;
|
||||
auto dmaHDMA() -> void;
|
||||
|
||||
//timer.cpp
|
||||
auto timer_step(uint clocks) -> void;
|
||||
auto timer_increment(uint n) -> void;
|
||||
auto timer_fifo_run(uint n) -> void;
|
||||
auto timerStep(uint clocks) -> void;
|
||||
auto timerIncrement(uint n) -> void;
|
||||
auto timerRunFIFO(uint n) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto CPU::dma_run() -> void {
|
||||
auto CPU::dmaRun() -> void {
|
||||
active.dma = true;
|
||||
|
||||
while(true) {
|
||||
|
@ -6,7 +6,7 @@ auto CPU::dma_run() -> void {
|
|||
for(auto n : range(4)) {
|
||||
auto& dma = regs.dma[n];
|
||||
if(dma.pending) {
|
||||
dma_exec(dma);
|
||||
dmaExecute(dma);
|
||||
if(dma.control.irq) regs.irq.flag |= Interrupt::DMA0 << n;
|
||||
if(dma.control.drq && n == 3) regs.irq.flag |= Interrupt::Cartridge;
|
||||
transferred = true;
|
||||
|
@ -19,7 +19,7 @@ auto CPU::dma_run() -> void {
|
|||
active.dma = false;
|
||||
}
|
||||
|
||||
auto CPU::dma_exec(Registers::DMA& dma) -> void {
|
||||
auto CPU::dmaExecute(Registers::DMA& dma) -> void {
|
||||
uint seek = dma.control.size ? 4 : 2;
|
||||
uint mode = dma.control.size ? Word : Half;
|
||||
mode |= dma.run.length == dma.length ? Nonsequential : Sequential;
|
||||
|
@ -39,7 +39,7 @@ auto CPU::dma_exec(Registers::DMA& dma) -> void {
|
|||
uint32 addr = dma.run.source;
|
||||
if(mode & Word) addr &= ~3;
|
||||
if(mode & Half) addr &= ~1;
|
||||
dma.data = busRead(mode, addr);
|
||||
dma.data = _read(mode, addr);
|
||||
}
|
||||
|
||||
if(dma.run.target < 0x0200'0000) {
|
||||
|
@ -48,7 +48,7 @@ auto CPU::dma_exec(Registers::DMA& dma) -> void {
|
|||
uint32 addr = dma.run.target;
|
||||
if(mode & Word) addr &= ~3;
|
||||
if(mode & Half) addr &= ~1;
|
||||
busWrite(mode, addr, dma.data);
|
||||
_write(mode, addr, dma.data);
|
||||
}
|
||||
|
||||
switch(dma.control.sourcemode) {
|
||||
|
@ -70,19 +70,19 @@ auto CPU::dma_exec(Registers::DMA& dma) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto CPU::dma_vblank() -> void {
|
||||
auto CPU::dmaVblank() -> void {
|
||||
for(auto& dma : regs.dma) {
|
||||
if(dma.control.enable && dma.control.timingmode == 1) dma.pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::dma_hblank() -> void {
|
||||
auto CPU::dmaHblank() -> void {
|
||||
for(auto& dma : regs.dma) {
|
||||
if(dma.control.enable && dma.control.timingmode == 2) dma.pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::dma_hdma() -> void {
|
||||
auto CPU::dmaHDMA() -> void {
|
||||
auto& dma = regs.dma[3];
|
||||
if(dma.control.enable && dma.control.timingmode == 3) dma.pending = true;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto CPU::read(uint32 addr) -> uint8 {
|
||||
auto CPU::readIO(uint32 addr) -> uint8 {
|
||||
auto dma = [&]() -> Registers::DMA& { return regs.dma[addr / 12 & 3]; };
|
||||
auto timer = [&]() -> Registers::Timer& { return regs.timer[addr.bits(2,3)]; };
|
||||
|
||||
|
@ -196,7 +196,7 @@ auto CPU::read(uint32 addr) -> uint8 {
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto CPU::write(uint32 addr, uint8 data) -> void {
|
||||
auto CPU::writeIO(uint32 addr, uint8 data) -> void {
|
||||
auto dma = [&]() -> Registers::DMA& { return regs.dma[addr / 12 & 3]; };
|
||||
auto timer = [&]() -> Registers::Timer& { return regs.timer[addr.bits(2,3)]; };
|
||||
|
|
@ -1,53 +1,53 @@
|
|||
auto CPU::iwram_read(uint mode, uint32 addr) -> uint32 {
|
||||
auto CPU::readIWRAM(uint mode, uint32 addr) -> uint32 {
|
||||
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
|
||||
|
||||
if(mode & Word) return iwram_read(Half, addr &~ 2) << 0 | iwram_read(Half, addr | 2) << 16;
|
||||
if(mode & Half) return iwram_read(Byte, addr &~ 1) << 0 | iwram_read(Byte, addr | 1) << 8;
|
||||
if(mode & Word) return readIWRAM(Half, addr &~ 2) << 0 | readIWRAM(Half, addr | 2) << 16;
|
||||
if(mode & Half) return readIWRAM(Byte, addr &~ 1) << 0 | readIWRAM(Byte, addr | 1) << 8;
|
||||
|
||||
return iwram[addr & 0x7fff];
|
||||
}
|
||||
|
||||
auto CPU::iwram_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
auto CPU::writeIWRAM(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(regs.memory.control.disable) return;
|
||||
|
||||
if(mode & Word) {
|
||||
iwram_write(Half, addr &~2, word >> 0);
|
||||
iwram_write(Half, addr | 2, word >> 16);
|
||||
writeIWRAM(Half, addr &~2, word >> 0);
|
||||
writeIWRAM(Half, addr | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mode & Half) {
|
||||
iwram_write(Byte, addr &~1, word >> 0);
|
||||
iwram_write(Byte, addr | 1, word >> 8);
|
||||
writeIWRAM(Byte, addr &~1, word >> 0);
|
||||
writeIWRAM(Byte, addr | 1, word >> 8);
|
||||
return;
|
||||
}
|
||||
|
||||
iwram[addr & 0x7fff] = word;
|
||||
}
|
||||
|
||||
auto CPU::ewram_read(uint mode, uint32 addr) -> uint32 {
|
||||
auto CPU::readEWRAM(uint mode, uint32 addr) -> uint32 {
|
||||
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
|
||||
if(!regs.memory.control.ewram) return iwram_read(mode, addr);
|
||||
if(!regs.memory.control.ewram) return readIWRAM(mode, addr);
|
||||
|
||||
if(mode & Word) return ewram_read(Half, addr &~ 2) << 0 | ewram_read(Half, addr | 2) << 16;
|
||||
if(mode & Half) return ewram_read(Byte, addr &~ 1) << 0 | ewram_read(Byte, addr | 1) << 8;
|
||||
if(mode & Word) return readEWRAM(Half, addr &~ 2) << 0 | readEWRAM(Half, addr | 2) << 16;
|
||||
if(mode & Half) return readEWRAM(Byte, addr &~ 1) << 0 | readEWRAM(Byte, addr | 1) << 8;
|
||||
|
||||
return ewram[addr & 0x3ffff];
|
||||
}
|
||||
|
||||
auto CPU::ewram_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
auto CPU::writeEWRAM(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(regs.memory.control.disable) return;
|
||||
if(!regs.memory.control.ewram) return iwram_write(mode, addr, word);
|
||||
if(!regs.memory.control.ewram) return writeIWRAM(mode, addr, word);
|
||||
|
||||
if(mode & Word) {
|
||||
ewram_write(Half, addr &~2, word >> 0);
|
||||
ewram_write(Half, addr | 2, word >> 16);
|
||||
writeEWRAM(Half, addr &~2, word >> 0);
|
||||
writeEWRAM(Half, addr | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mode & Half) {
|
||||
ewram_write(Byte, addr &~1, word >> 0);
|
||||
ewram_write(Byte, addr | 1, word >> 8);
|
||||
writeEWRAM(Byte, addr &~1, word >> 0);
|
||||
writeEWRAM(Byte, addr | 1, word >> 8);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
auto CPU::prefetch_sync(uint32 addr) -> void {
|
||||
auto CPU::prefetchSync(uint32 addr) -> void {
|
||||
if(addr == prefetch.addr) return;
|
||||
|
||||
prefetch.addr = addr;
|
||||
prefetch.load = addr;
|
||||
prefetch.wait = busWait(Half | Nonsequential, prefetch.load);
|
||||
prefetch.wait = wait(Half | Nonsequential, prefetch.load);
|
||||
}
|
||||
|
||||
auto CPU::prefetch_step(uint clocks) -> void {
|
||||
auto CPU::prefetchStep(uint clocks) -> void {
|
||||
step(clocks);
|
||||
if(!regs.wait.control.prefetch || active.dma) return;
|
||||
|
||||
|
@ -14,22 +14,22 @@ auto CPU::prefetch_step(uint clocks) -> void {
|
|||
if(--prefetch.wait) continue;
|
||||
prefetch.slot[prefetch.load >> 1 & 7] = cartridge.read(Half, prefetch.load);
|
||||
prefetch.load += 2;
|
||||
prefetch.wait = busWait(Half | Sequential, prefetch.load);
|
||||
prefetch.wait = wait(Half | Sequential, prefetch.load);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::prefetch_wait() -> void {
|
||||
auto CPU::prefetchWait() -> void {
|
||||
if(!regs.wait.control.prefetch || active.dma || prefetch.full()) return;
|
||||
|
||||
prefetch_step(prefetch.wait);
|
||||
prefetch.wait = busWait(Half | Nonsequential, prefetch.load);
|
||||
prefetchStep(prefetch.wait);
|
||||
prefetch.wait = wait(Half | Nonsequential, prefetch.load);
|
||||
}
|
||||
|
||||
auto CPU::prefetch_read() -> uint16 {
|
||||
if(prefetch.empty()) prefetch_step(prefetch.wait);
|
||||
else prefetch_step(1);
|
||||
auto CPU::prefetchRead() -> uint16 {
|
||||
if(prefetch.empty()) prefetchStep(prefetch.wait);
|
||||
else prefetchStep(1);
|
||||
|
||||
if(prefetch.full()) prefetch.wait = busWait(Half | Sequential, prefetch.load);
|
||||
if(prefetch.full()) prefetch.wait = wait(Half | Sequential, prefetch.load);
|
||||
|
||||
uint16 half = prefetch.slot[prefetch.addr >> 1 & 7];
|
||||
prefetch.addr += 2;
|
||||
|
|
|
@ -8,7 +8,7 @@ struct {
|
|||
auto full() const { return load - addr == 16; }
|
||||
} prefetch;
|
||||
|
||||
auto prefetch_sync(uint32 addr) -> void;
|
||||
auto prefetch_step(uint clocks) -> void;
|
||||
auto prefetch_wait() -> void;
|
||||
auto prefetch_read() -> uint16;
|
||||
auto prefetchSync(uint32 addr) -> void;
|
||||
auto prefetchStep(uint clocks) -> void;
|
||||
auto prefetchWait() -> void;
|
||||
auto prefetchRead() -> uint16;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto CPU::timer_step(uint clocks) -> void {
|
||||
auto CPU::timerStep(uint clocks) -> void {
|
||||
for(auto c : range(clocks)) {
|
||||
for(auto n : range(4)) {
|
||||
auto& timer = regs.timer[n];
|
||||
|
@ -11,11 +11,11 @@ auto CPU::timer_step(uint clocks) -> void {
|
|||
continue;
|
||||
}
|
||||
|
||||
if(timer.control.enable == false || timer.control.cascade == true) continue;
|
||||
if(!timer.control.enable || timer.control.cascade) continue;
|
||||
|
||||
static uint mask[] = {0, 63, 255, 1023};
|
||||
if((regs.clock & mask[timer.control.frequency]) == 0) {
|
||||
timer_increment(n);
|
||||
timerIncrement(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,23 +23,23 @@ auto CPU::timer_step(uint clocks) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto CPU::timer_increment(uint n) -> void {
|
||||
auto CPU::timerIncrement(uint n) -> void {
|
||||
auto& timer = regs.timer[n];
|
||||
if(++timer.period == 0) {
|
||||
timer.period = timer.reload;
|
||||
|
||||
if(timer.control.irq) regs.irq.flag |= Interrupt::Timer0 << n;
|
||||
|
||||
if(apu.fifo[0].timer == n) timer_fifo_run(0);
|
||||
if(apu.fifo[1].timer == n) timer_fifo_run(1);
|
||||
if(apu.fifo[0].timer == n) timerRunFIFO(0);
|
||||
if(apu.fifo[1].timer == n) timerRunFIFO(1);
|
||||
|
||||
if(n < 3 && regs.timer[n + 1].control.enable && regs.timer[n + 1].control.cascade) {
|
||||
timer_increment(n + 1);
|
||||
timerIncrement(n + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::timer_fifo_run(uint n) -> void {
|
||||
auto CPU::timerRunFIFO(uint n) -> void {
|
||||
apu.fifo[n].read();
|
||||
if(apu.fifo[n].size > 16) return;
|
||||
|
||||
|
|
|
@ -2,15 +2,50 @@
|
|||
|
||||
namespace GameBoyAdvance {
|
||||
|
||||
#include "mmio.cpp"
|
||||
Bus bus;
|
||||
|
||||
struct UnmappedMemory : Memory {
|
||||
auto read(uint mode, uint32 addr) -> uint32 override { return 0; }
|
||||
auto write(uint mode, uint32 addr, uint32 word) -> void override {}
|
||||
auto IO::readIO(uint mode, uint32 addr) -> uint32 {
|
||||
uint32 word = 0;
|
||||
|
||||
if(mode & Word) {
|
||||
addr &= ~3;
|
||||
word |= readIO(addr + 0) << 0;
|
||||
word |= readIO(addr + 1) << 8;
|
||||
word |= readIO(addr + 2) << 16;
|
||||
word |= readIO(addr + 3) << 24;
|
||||
} else if(mode & Half) {
|
||||
addr &= ~1;
|
||||
word |= readIO(addr + 0) << 0;
|
||||
word |= readIO(addr + 1) << 8;
|
||||
} else if(mode & Byte) {
|
||||
word |= readIO(addr + 0) << 0;
|
||||
}
|
||||
|
||||
return word;
|
||||
}
|
||||
|
||||
auto IO::writeIO(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) {
|
||||
addr &= ~3;
|
||||
writeIO(addr + 0, word >> 0);
|
||||
writeIO(addr + 1, word >> 8);
|
||||
writeIO(addr + 2, word >> 16);
|
||||
writeIO(addr + 3, word >> 24);
|
||||
} else if(mode & Half) {
|
||||
addr &= ~1;
|
||||
writeIO(addr + 0, word >> 0);
|
||||
writeIO(addr + 1, word >> 8);
|
||||
} else if(mode & Byte) {
|
||||
writeIO(addr + 0, word >> 0);
|
||||
}
|
||||
}
|
||||
|
||||
struct UnmappedIO : IO {
|
||||
auto readIO(uint32 addr) -> uint8 override { return 0; }
|
||||
auto writeIO(uint32 addr, uint8 byte) -> void override {}
|
||||
};
|
||||
|
||||
static UnmappedMemory unmappedMemory;
|
||||
static UnmappedIO unmappedIO;
|
||||
|
||||
auto Bus::mirror(uint32 addr, uint32 size) -> uint32 {
|
||||
uint32 base = 0;
|
||||
|
@ -31,7 +66,7 @@ auto Bus::mirror(uint32 addr, uint32 size) -> uint32 {
|
|||
}
|
||||
|
||||
auto Bus::power() -> void {
|
||||
for(auto n : range(0x400)) mmio[n] = &unmappedMemory;
|
||||
for(auto n : range(0x400)) io[n] = &unmappedIO;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
struct Memory {
|
||||
virtual auto read(uint mode, uint32 addr) -> uint32 = 0;
|
||||
virtual auto write(uint mode, uint32 addr, uint32 word) -> void = 0;
|
||||
};
|
||||
|
||||
struct MMIO : Memory {
|
||||
virtual auto read(uint32 addr) -> uint8 = 0;
|
||||
virtual auto write(uint32 addr, uint8 data) -> void = 0;
|
||||
auto read(uint mode, uint32 addr) -> uint32 final;
|
||||
auto write(uint mode, uint32 addr, uint32 word) -> void final;
|
||||
struct IO {
|
||||
virtual auto readIO(uint32 addr) -> uint8 = 0;
|
||||
virtual auto writeIO(uint32 addr, uint8 data) -> void = 0;
|
||||
auto readIO(uint mode, uint32 addr) -> uint32;
|
||||
auto writeIO(uint mode, uint32 addr, uint32 word) -> void;
|
||||
};
|
||||
|
||||
struct Bus {
|
||||
|
@ -15,7 +10,7 @@ struct Bus {
|
|||
|
||||
auto power() -> void;
|
||||
|
||||
Memory* mmio[0x400] = {nullptr};
|
||||
IO* io[0x400] = {nullptr};
|
||||
};
|
||||
|
||||
extern Bus bus;
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
auto MMIO::read(uint mode, uint32 addr) -> uint32 {
|
||||
uint32 word = 0;
|
||||
|
||||
if(mode & Word) {
|
||||
addr &= ~3;
|
||||
word |= read(addr + 0) << 0;
|
||||
word |= read(addr + 1) << 8;
|
||||
word |= read(addr + 2) << 16;
|
||||
word |= read(addr + 3) << 24;
|
||||
} else if(mode & Half) {
|
||||
addr &= ~1;
|
||||
word |= read(addr + 0) << 0;
|
||||
word |= read(addr + 1) << 8;
|
||||
} else if(mode & Byte) {
|
||||
word |= read(addr + 0) << 0;
|
||||
}
|
||||
|
||||
return word;
|
||||
}
|
||||
|
||||
auto MMIO::write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) {
|
||||
addr &= ~3;
|
||||
write(addr + 0, word >> 0);
|
||||
write(addr + 1, word >> 8);
|
||||
write(addr + 2, word >> 16);
|
||||
write(addr + 3, word >> 24);
|
||||
} else if(mode & Half) {
|
||||
addr &= ~1;
|
||||
write(addr + 0, word >> 0);
|
||||
write(addr + 1, word >> 8);
|
||||
} else if(mode & Byte) {
|
||||
write(addr + 0, word >> 0);
|
||||
}
|
||||
}
|
|
@ -1,27 +1,27 @@
|
|||
auto PPU::render_backgrounds() -> void {
|
||||
auto PPU::renderBackgrounds() -> void {
|
||||
switch(regs.control.bgmode) {
|
||||
case 0:
|
||||
render_background_linear(regs.bg[3]);
|
||||
render_background_linear(regs.bg[2]);
|
||||
render_background_linear(regs.bg[1]);
|
||||
render_background_linear(regs.bg[0]);
|
||||
renderBackgroundLinear(regs.bg[3]);
|
||||
renderBackgroundLinear(regs.bg[2]);
|
||||
renderBackgroundLinear(regs.bg[1]);
|
||||
renderBackgroundLinear(regs.bg[0]);
|
||||
break;
|
||||
case 1:
|
||||
render_background_affine(regs.bg[2]);
|
||||
render_background_linear(regs.bg[1]);
|
||||
render_background_linear(regs.bg[0]);
|
||||
renderBackgroundAffine(regs.bg[2]);
|
||||
renderBackgroundLinear(regs.bg[1]);
|
||||
renderBackgroundLinear(regs.bg[0]);
|
||||
break;
|
||||
case 2:
|
||||
render_background_affine(regs.bg[3]);
|
||||
render_background_affine(regs.bg[2]);
|
||||
renderBackgroundAffine(regs.bg[3]);
|
||||
renderBackgroundAffine(regs.bg[2]);
|
||||
break;
|
||||
case 3: case 4: case 5:
|
||||
render_background_bitmap(regs.bg[2]);
|
||||
renderBackgroundBitmap(regs.bg[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::render_background_linear(Registers::Background& bg) -> void {
|
||||
auto PPU::renderBackgroundLinear(Registers::Background& bg) -> void {
|
||||
if(regs.control.enable[bg.id] == false) return;
|
||||
auto& output = layer[bg.id];
|
||||
|
||||
|
@ -48,7 +48,7 @@ auto PPU::render_background_linear(Registers::Background& bg) -> void {
|
|||
if(bg.control.screensize & 1) if(tx & 32) offset += 32 * 32;
|
||||
if(bg.control.screensize & 2) if(ty & 32) offset += 32 * 32 * (1 + (bg.control.screensize & 1));
|
||||
offset = basemap + offset * 2;
|
||||
uint16 mapdata = vram_read(Half, offset);
|
||||
uint16 mapdata = readVRAM(Half, offset);
|
||||
|
||||
tile.character = mapdata >> 0;
|
||||
tile.hflip = mapdata >> 10;
|
||||
|
@ -57,12 +57,12 @@ auto PPU::render_background_linear(Registers::Background& bg) -> void {
|
|||
|
||||
if(bg.control.colormode == 0) {
|
||||
offset = basechr + tile.character * 32 + (py ^ (tile.vflip ? 7 : 0)) * 4;
|
||||
uint32 word = vram_read(Word, offset);
|
||||
uint32 word = readVRAM(Word, offset);
|
||||
for(auto n : range(8)) data[n] = (word >> (n * 4)) & 15;
|
||||
} else {
|
||||
offset = basechr + tile.character * 64 + (py ^ (tile.vflip ? 7 : 0)) * 8;
|
||||
uint32 wordlo = vram_read(Word, offset + 0);
|
||||
uint32 wordhi = vram_read(Word, offset + 4);
|
||||
uint32 wordlo = readVRAM(Word, offset + 0);
|
||||
uint32 wordhi = readVRAM(Word, offset + 4);
|
||||
for(auto n : range(4)) data[0 + n] = (wordlo >> (n * 8)) & 255;
|
||||
for(auto n : range(4)) data[4 + n] = (wordhi >> (n * 8)) & 255;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ auto PPU::render_background_linear(Registers::Background& bg) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::render_background_affine(Registers::Background& bg) -> void {
|
||||
auto PPU::renderBackgroundAffine(Registers::Background& bg) -> void {
|
||||
if(regs.control.enable[bg.id] == false) return;
|
||||
auto& output = layer[bg.id];
|
||||
|
||||
|
@ -113,7 +113,7 @@ auto PPU::render_background_affine(Registers::Background& bg) -> void {
|
|||
bg.ly += bg.pd;
|
||||
}
|
||||
|
||||
auto PPU::render_background_bitmap(Registers::Background& bg) -> void {
|
||||
auto PPU::renderBackgroundBitmap(Registers::Background& bg) -> void {
|
||||
if(regs.control.enable[bg.id] == false) return;
|
||||
auto& output = layer[bg.id];
|
||||
|
||||
|
@ -138,7 +138,7 @@ auto PPU::render_background_bitmap(Registers::Background& bg) -> void {
|
|||
|
||||
if(px < width && py < height) {
|
||||
uint offset = py * width + px;
|
||||
uint color = vram_read(mode, basemap + (offset << depth));
|
||||
uint color = readVRAM(mode, basemap + (offset << depth));
|
||||
|
||||
if(depth || color) { //8bpp color 0 is transparent; 15bpp color is always opaque
|
||||
if(depth == 0) color = pram[color];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto PPU::read(uint32 addr) -> uint8 {
|
||||
auto PPU::readIO(uint32 addr) -> uint8 {
|
||||
auto bgcnt = [&]() -> Registers::Background::Control& { return regs.bg[addr.bits(1,2)].control; };
|
||||
auto wf = [&]() -> Registers::WindowFlags& {
|
||||
static uint id[] = {In0, In1, Out, Obj};
|
||||
|
@ -102,7 +102,7 @@ auto PPU::read(uint32 addr) -> uint8 {
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto PPU::write(uint32 addr, uint8 data) -> void {
|
||||
auto PPU::writeIO(uint32 addr, uint8 data) -> void {
|
||||
auto bgcnt = [&]() -> Registers::Background::Control& { return regs.bg[addr.bits(1,2)].control; };
|
||||
auto bgofs = [&]() -> Registers::Background& { return regs.bg[addr.bits(2,3)]; };
|
||||
auto bg = [&]() -> Registers::Background& { return regs.bg[addr.bits(4,5)]; };
|
|
@ -1,4 +1,4 @@
|
|||
auto PPU::vram_read(uint mode, uint32 addr) -> uint32 {
|
||||
auto PPU::readVRAM(uint mode, uint32 addr) -> uint32 {
|
||||
addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff;
|
||||
|
||||
if(mode & Word) {
|
||||
|
@ -14,7 +14,7 @@ auto PPU::vram_read(uint mode, uint32 addr) -> uint32 {
|
|||
return 0; //should never occur
|
||||
}
|
||||
|
||||
auto PPU::vram_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
auto PPU::writeVRAM(uint mode, uint32 addr, uint32 word) -> void {
|
||||
addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff;
|
||||
|
||||
if(mode & Word) {
|
||||
|
@ -38,30 +38,30 @@ auto PPU::vram_write(uint mode, uint32 addr, uint32 word) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::pram_read(uint mode, uint32 addr) -> uint32 {
|
||||
if(mode & Word) return pram_read(Half, addr & ~2) << 0 | pram_read(Half, addr | 2) << 16;
|
||||
if(mode & Byte) return pram_read(Half, addr) >> ((addr & 1) * 8);
|
||||
auto PPU::readPRAM(uint mode, uint32 addr) -> uint32 {
|
||||
if(mode & Word) return readPRAM(Half, addr & ~2) << 0 | readPRAM(Half, addr | 2) << 16;
|
||||
if(mode & Byte) return readPRAM(Half, addr) >> ((addr & 1) * 8);
|
||||
return pram[addr >> 1 & 511];
|
||||
}
|
||||
|
||||
auto PPU::pram_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
auto PPU::writePRAM(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) {
|
||||
pram_write(Half, addr & ~2, word >> 0);
|
||||
pram_write(Half, addr | 2, word >> 16);
|
||||
writePRAM(Half, addr & ~2, word >> 0);
|
||||
writePRAM(Half, addr | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mode & Byte) {
|
||||
word = (uint8)word;
|
||||
return pram_write(Half, addr, word << 8 | word << 0);
|
||||
return writePRAM(Half, addr, word << 8 | word << 0);
|
||||
}
|
||||
|
||||
pram[addr >> 1 & 511] = (uint16)word;
|
||||
}
|
||||
|
||||
auto PPU::oam_read(uint mode, uint32 addr) -> uint32 {
|
||||
if(mode & Word) return oam_read(Half, addr & ~2) << 0 | oam_read(Half, addr | 2) << 16;
|
||||
if(mode & Byte) return oam_read(Half, addr) >> ((addr & 1) * 8);
|
||||
auto PPU::readOAM(uint mode, uint32 addr) -> uint32 {
|
||||
if(mode & Word) return readOAM(Half, addr & ~2) << 0 | readOAM(Half, addr | 2) << 16;
|
||||
if(mode & Byte) return readOAM(Half, addr) >> ((addr & 1) * 8);
|
||||
|
||||
auto& obj = object[addr >> 3 & 127];
|
||||
auto& par = objectparam[addr >> 5 & 31];
|
||||
|
@ -103,10 +103,10 @@ auto PPU::oam_read(uint mode, uint32 addr) -> uint32 {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::oam_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
auto PPU::writeOAM(uint mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) {
|
||||
oam_write(Half, addr & ~2, word >> 0);
|
||||
oam_write(Half, addr | 2, word >> 16);
|
||||
writeOAM(Half, addr & ~2, word >> 0);
|
||||
writeOAM(Half, addr | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto PPU::render_mosaic_background(uint id) -> void {
|
||||
auto PPU::renderMosaicBackground(uint id) -> void {
|
||||
if(regs.mosaic.bghsize == 0) return;
|
||||
uint width = 1 + regs.mosaic.bghsize;
|
||||
auto& buffer = layer[id];
|
||||
|
@ -12,7 +12,7 @@ auto PPU::render_mosaic_background(uint id) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::render_mosaic_object() -> void {
|
||||
auto PPU::renderMosaicObject() -> void {
|
||||
if(regs.mosaic.objhsize == 0) return;
|
||||
uint width = 1 + regs.mosaic.objhsize;
|
||||
auto& buffer = layer[OBJ];
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
auto PPU::render_objects() -> void {
|
||||
auto PPU::renderObjects() -> void {
|
||||
if(regs.control.enable[OBJ] == false) return;
|
||||
for(auto n : range(128)) render_object(object[n]);
|
||||
for(auto n : range(128)) renderObject(object[n]);
|
||||
}
|
||||
|
||||
//px,py = pixel coordinates within sprite [0,0 - width,height)
|
||||
//fx,fy = affine pixel coordinates
|
||||
//pa,pb,pc,pd = affine pixel adjustments
|
||||
//x,y = adjusted coordinates within sprite (linear = vflip/hflip, affine = rotation/zoom)
|
||||
auto PPU::render_object(Object& obj) -> void {
|
||||
auto PPU::renderObject(Object& obj) -> void {
|
||||
uint8 py = regs.vcounter - obj.y;
|
||||
if(obj.affine == 0 && obj.affinesize == 1) return; //hidden
|
||||
if(py >= obj.height << obj.affinesize) return; //offscreen
|
||||
|
@ -55,7 +55,7 @@ auto PPU::render_object(Object& obj) -> void {
|
|||
uint offset = (y / 8) * rowsize + (x / 8);
|
||||
offset = offset * 64 + (y & 7) * 8 + (x & 7);
|
||||
|
||||
uint8 color = object_vram_read(baseaddr + (offset >> !obj.colors));
|
||||
uint8 color = readObjectVRAM(baseaddr + (offset >> !obj.colors));
|
||||
if(obj.colors == 0) color = (x & 1) ? color >> 4 : color & 15;
|
||||
if(color) {
|
||||
if(obj.mode & 2) {
|
||||
|
@ -72,7 +72,7 @@ auto PPU::render_object(Object& obj) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::object_vram_read(uint addr) const -> uint8 {
|
||||
auto PPU::readObjectVRAM(uint addr) const -> uint8 {
|
||||
if(regs.control.bgmode == 3 || regs.control.bgmode == 4 || regs.control.bgmode == 5) {
|
||||
if(addr <= 0x3fff) return 0u;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ PPU ppu;
|
|||
#include "object.cpp"
|
||||
#include "mosaic.cpp"
|
||||
#include "screen.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "io.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
|
@ -52,8 +52,8 @@ auto PPU::power() -> void {
|
|||
|
||||
for(uint n = 0; n < 240 * 160; n++) output[n] = 0;
|
||||
|
||||
for(uint n = 0; n < 1024; n += 2) pram_write(n, Half, 0x0000);
|
||||
for(uint n = 0; n < 1024; n += 2) oam_write(n, Half, 0x0000);
|
||||
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;
|
||||
|
@ -112,11 +112,11 @@ auto PPU::power() -> void {
|
|||
regs.blend.evb = 0;
|
||||
regs.blend.evy = 0;
|
||||
|
||||
for(uint n = 0x000; n <= 0x055; n++) bus.mmio[n] = this;
|
||||
for(uint n = 0x000; n <= 0x055; n++) bus.io[n] = this;
|
||||
}
|
||||
|
||||
auto PPU::scanline() -> void {
|
||||
cpu.keypad_run();
|
||||
cpu.keypadRun();
|
||||
|
||||
regs.status.vblank = regs.vcounter >= 160 && regs.vcounter <= 226;
|
||||
regs.status.vcoincidence = regs.vcounter == regs.status.vcompare;
|
||||
|
@ -133,7 +133,7 @@ auto PPU::scanline() -> void {
|
|||
|
||||
if(regs.vcounter == 160) {
|
||||
if(regs.status.irqvblank) cpu.regs.irq.flag |= CPU::Interrupt::VBlank;
|
||||
cpu.dma_vblank();
|
||||
cpu.dmaVblank();
|
||||
}
|
||||
|
||||
if(regs.status.irqvcoincidence) {
|
||||
|
@ -142,7 +142,7 @@ auto PPU::scanline() -> void {
|
|||
|
||||
if(regs.vcounter < 160) {
|
||||
if(regs.control.forceblank || cpu.regs.mode == CPU::Registers::Mode::Stop) {
|
||||
render_forceblank();
|
||||
renderForceBlank();
|
||||
} else {
|
||||
for(auto x : range(240)) {
|
||||
windowmask[0][x] = false;
|
||||
|
@ -155,22 +155,22 @@ auto PPU::scanline() -> void {
|
|||
layer[BG3][x].write(false);
|
||||
layer[SFX][x].write(true, 3, pram[0]);
|
||||
}
|
||||
render_window(0);
|
||||
render_window(1);
|
||||
render_objects();
|
||||
render_backgrounds();
|
||||
render_screen();
|
||||
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.dma_hblank();
|
||||
if(regs.vcounter < 160) cpu.dmaHblank();
|
||||
|
||||
step(240);
|
||||
regs.status.hblank = 0;
|
||||
if(regs.vcounter < 160) cpu.dma_hdma();
|
||||
if(regs.vcounter < 160) cpu.dmaHDMA();
|
||||
|
||||
step(32);
|
||||
if(++regs.vcounter == 228) regs.vcounter = 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct PPU : Thread, MMIO {
|
||||
struct PPU : Thread, IO {
|
||||
#include "registers.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
|
@ -14,33 +14,33 @@ struct PPU : Thread, MMIO {
|
|||
auto frame() -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
auto read(uint32 addr) -> uint8;
|
||||
auto write(uint32 addr, uint8 byte) -> void;
|
||||
auto readIO(uint32 addr) -> uint8;
|
||||
auto writeIO(uint32 addr, uint8 byte) -> void;
|
||||
|
||||
auto vram_read(uint mode, uint32 addr) -> uint32;
|
||||
auto vram_write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
auto readVRAM(uint mode, uint32 addr) -> uint32;
|
||||
auto writeVRAM(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto pram_read(uint mode, uint32 addr) -> uint32;
|
||||
auto pram_write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
auto readPRAM(uint mode, uint32 addr) -> uint32;
|
||||
auto writePRAM(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto oam_read(uint mode, uint32 addr) -> uint32;
|
||||
auto oam_write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
auto readOAM(uint mode, uint32 addr) -> uint32;
|
||||
auto writeOAM(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto render_backgrounds() -> void;
|
||||
auto render_background_linear(Registers::Background&) -> void;
|
||||
auto render_background_affine(Registers::Background&) -> void;
|
||||
auto render_background_bitmap(Registers::Background&) -> void;
|
||||
auto renderBackgrounds() -> void;
|
||||
auto renderBackgroundLinear(Registers::Background&) -> void;
|
||||
auto renderBackgroundAffine(Registers::Background&) -> void;
|
||||
auto renderBackgroundBitmap(Registers::Background&) -> void;
|
||||
|
||||
auto render_objects() -> void;
|
||||
auto render_object(Object&) -> void;
|
||||
auto object_vram_read(uint addr) const -> uint8;
|
||||
auto renderObjects() -> void;
|
||||
auto renderObject(Object&) -> void;
|
||||
auto readObjectVRAM(uint addr) const -> uint8;
|
||||
|
||||
auto render_mosaic_background(uint id) -> void;
|
||||
auto render_mosaic_object() -> void;
|
||||
auto renderMosaicBackground(uint id) -> void;
|
||||
auto renderMosaicObject() -> void;
|
||||
|
||||
auto render_forceblank() -> void;
|
||||
auto render_screen() -> void;
|
||||
auto render_window(uint window) -> void;
|
||||
auto renderForceBlank() -> void;
|
||||
auto renderScreen() -> void;
|
||||
auto renderWindow(uint window) -> void;
|
||||
auto blend(uint above, uint eva, uint below, uint evb) -> uint;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
auto PPU::render_forceblank() -> void {
|
||||
auto PPU::renderForceBlank() -> void {
|
||||
uint32* line = output + regs.vcounter * 240;
|
||||
for(auto x : range(240)) line[x] = 0x7fff;
|
||||
}
|
||||
|
||||
auto PPU::render_screen() -> void {
|
||||
auto PPU::renderScreen() -> void {
|
||||
uint32* line = output + regs.vcounter * 240;
|
||||
|
||||
if(regs.bg[0].control.mosaic) render_mosaic_background(BG0);
|
||||
if(regs.bg[1].control.mosaic) render_mosaic_background(BG1);
|
||||
if(regs.bg[2].control.mosaic) render_mosaic_background(BG2);
|
||||
if(regs.bg[3].control.mosaic) render_mosaic_background(BG3);
|
||||
render_mosaic_object();
|
||||
if(regs.bg[0].control.mosaic) renderMosaicBackground(BG0);
|
||||
if(regs.bg[1].control.mosaic) renderMosaicBackground(BG1);
|
||||
if(regs.bg[2].control.mosaic) renderMosaicBackground(BG2);
|
||||
if(regs.bg[3].control.mosaic) renderMosaicBackground(BG3);
|
||||
renderMosaicObject();
|
||||
|
||||
for(auto x : range(240)) {
|
||||
Registers::WindowFlags flags;
|
||||
|
@ -66,7 +66,7 @@ auto PPU::render_screen() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::render_window(uint w) -> void {
|
||||
auto PPU::renderWindow(uint w) -> void {
|
||||
uint y = regs.vcounter;
|
||||
|
||||
uint y1 = regs.window[w].y1, y2 = regs.window[w].y2;
|
||||
|
|
|
@ -2,12 +2,12 @@ enum class Input : uint {
|
|||
A, B, Select, Start, Right, Left, Up, Down, R, L,
|
||||
};
|
||||
|
||||
struct BIOS : Memory {
|
||||
struct BIOS {
|
||||
BIOS();
|
||||
~BIOS();
|
||||
|
||||
auto read(uint mode, uint32 addr) -> uint32 override;
|
||||
auto write(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
auto read(uint mode, uint32 addr) -> uint32;
|
||||
auto write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
uint8* data = nullptr;
|
||||
uint size = 0;
|
||||
|
|
|
@ -26,21 +26,21 @@ auto ARM::power() -> void {
|
|||
}
|
||||
|
||||
auto ARM::exec() -> void {
|
||||
cpsr().t ? thumb_step() : arm_step();
|
||||
cpsr().t ? stepTHUMB() : stepARM();
|
||||
}
|
||||
|
||||
auto ARM::idle() -> void {
|
||||
pipeline.nonsequential = true;
|
||||
return busIdle();
|
||||
return _idle();
|
||||
}
|
||||
|
||||
auto ARM::read(unsigned mode, uint32 addr) -> uint32 {
|
||||
return busRead(mode, addr);
|
||||
auto ARM::read(uint mode, uint32 addr) -> uint32 {
|
||||
return _read(mode, addr);
|
||||
}
|
||||
|
||||
auto ARM::load(unsigned mode, uint32 addr) -> uint32 {
|
||||
auto ARM::load(uint mode, uint32 addr) -> uint32 {
|
||||
pipeline.nonsequential = true;
|
||||
uint32 word = busRead(Load | mode, addr);
|
||||
uint32 word = _read(Load | mode, addr);
|
||||
|
||||
if(mode & Half) {
|
||||
addr &= 1;
|
||||
|
@ -62,18 +62,18 @@ auto ARM::load(unsigned mode, uint32 addr) -> uint32 {
|
|||
return word;
|
||||
}
|
||||
|
||||
auto ARM::write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
auto ARM::write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
pipeline.nonsequential = true;
|
||||
return busWrite(mode, addr, word);
|
||||
return _write(mode, addr, word);
|
||||
}
|
||||
|
||||
auto ARM::store(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
auto ARM::store(uint mode, uint32 addr, uint32 word) -> void {
|
||||
pipeline.nonsequential = true;
|
||||
|
||||
if(mode & Half) { word &= 0xffff; word |= word << 16; }
|
||||
if(mode & Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
||||
|
||||
return busWrite(Store | mode, addr, word);
|
||||
return _write(Store | mode, addr, word);
|
||||
}
|
||||
|
||||
auto ARM::vector(uint32 addr, Processor::Mode mode) -> void {
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace Processor {
|
|||
//* ARMv4T (ARM7TDMI)
|
||||
|
||||
struct ARM {
|
||||
enum : unsigned { //mode flags for bus_read, bus_write:
|
||||
enum : uint { //mode flags for bus_read, bus_write:
|
||||
Nonsequential = 1, //N cycle
|
||||
Sequential = 2, //S cycle
|
||||
Prefetch = 4, //instruction fetch (eligible for prefetch)
|
||||
|
@ -24,19 +24,19 @@ struct ARM {
|
|||
#include "instructions-thumb.hpp"
|
||||
#include "disassembler.hpp"
|
||||
|
||||
virtual auto step(unsigned clocks) -> void = 0;
|
||||
virtual auto busIdle() -> void = 0;
|
||||
virtual auto busRead(unsigned mode, uint32 addr) -> uint32 = 0;
|
||||
virtual auto busWrite(unsigned mode, uint32 addr, uint32 word) -> void = 0;
|
||||
virtual auto step(uint clocks) -> void = 0;
|
||||
virtual auto _idle() -> void = 0;
|
||||
virtual auto _read(uint mode, uint32 addr) -> uint32 = 0;
|
||||
virtual auto _write(uint mode, uint32 addr, uint32 word) -> void = 0;
|
||||
|
||||
//arm.cpp
|
||||
auto power() -> void;
|
||||
auto exec() -> void;
|
||||
auto idle() -> void;
|
||||
auto read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto load(unsigned mode, uint32 addr) -> uint32;
|
||||
auto write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
auto store(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
auto read(uint mode, uint32 addr) -> uint32;
|
||||
auto load(uint mode, uint32 addr) -> uint32;
|
||||
auto write(uint mode, uint32 addr, uint32 word) -> void;
|
||||
auto store(uint mode, uint32 addr, uint32 word) -> void;
|
||||
auto vector(uint32 addr, Processor::Mode mode) -> void;
|
||||
|
||||
//algorithms.cpp
|
||||
|
@ -52,9 +52,9 @@ struct ARM {
|
|||
auto rrx(uint32 source) -> uint32;
|
||||
|
||||
//step.cpp
|
||||
auto pipeline_step() -> void;
|
||||
auto arm_step() -> void;
|
||||
auto thumb_step() -> void;
|
||||
auto stepPipeline() -> void;
|
||||
auto stepARM() -> void;
|
||||
auto stepTHUMB() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
||||
auto ARM::disassembleInstructionARM(uint32 pc) -> string {
|
||||
static string conditions[] = {
|
||||
"eq", "ne", "cs", "cc",
|
||||
"mi", "pl", "vs", "vc",
|
||||
|
@ -24,9 +24,9 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
"da", "ia", "db", "ib",
|
||||
};
|
||||
|
||||
static auto is_move = [](uint4 opcode) { return opcode == 13 || opcode == 15; };
|
||||
static auto is_comp = [](uint4 opcode) { return opcode >= 8 && opcode <= 11; };
|
||||
static auto is_math = [](uint4 opcode) { return opcode < 8 || opcode == 12 || opcode == 14; };
|
||||
static auto isMove = [](uint4 opcode) { return opcode == 13 || opcode == 15; };
|
||||
static auto isComp = [](uint4 opcode) { return opcode >= 8 && opcode <= 11; };
|
||||
static auto isMath = [](uint4 opcode) { return opcode < 8 || opcode == 12 || opcode == 14; };
|
||||
|
||||
string output{hex(pc, 8L), " "};
|
||||
|
||||
|
@ -272,13 +272,13 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
uint4 rm = instruction;
|
||||
|
||||
output.append(opcodes[opcode], conditions[condition]);
|
||||
if(is_move(opcode)) output.append(save ? "s " : " ", registers[rd]);
|
||||
if(is_comp(opcode)) output.append(" ", registers[rn]);
|
||||
if(is_math(opcode)) output.append(save ? "s " : " ", registers[rd], ",", registers[rn]);
|
||||
if(isMove(opcode)) output.append(save ? "s " : " ", registers[rd]);
|
||||
if(isComp(opcode)) output.append(" ", registers[rn]);
|
||||
if(isMath(opcode)) output.append(save ? "s " : " ", registers[rd], ",", registers[rn]);
|
||||
output.append(",", registers[rm]);
|
||||
if(op == 0 && shift != 0) output.append(" lsl #", shift);
|
||||
if(op == 1) output.append(" lsr #", shift == 0 ? 32u : (unsigned)shift);
|
||||
if(op == 2) output.append(" asr #", shift == 0 ? 32u : (unsigned)shift);
|
||||
if(op == 1) output.append(" lsr #", shift == 0 ? 32u : (uint)shift);
|
||||
if(op == 2) output.append(" asr #", shift == 0 ? 32u : (uint)shift);
|
||||
if(op == 3 && shift != 0) output.append(" ror #", shift);
|
||||
if(op == 3 && shift == 0) output.append(" rrx");
|
||||
|
||||
|
@ -300,9 +300,9 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
uint4 rm = instruction;
|
||||
|
||||
output.append(opcodes[opcode], conditions[condition]);
|
||||
if(is_move(opcode)) output.append(save ? "s " : " ", registers[rd], ",");
|
||||
if(is_comp(opcode)) output.append(registers[rn], ",");
|
||||
if(is_math(opcode)) output.append(save ? "s " : " ", registers[rd], ",", registers[rn], ",");
|
||||
if(isMove(opcode)) output.append(save ? "s " : " ", registers[rd], ",");
|
||||
if(isComp(opcode)) output.append(registers[rn], ",");
|
||||
if(isMath(opcode)) output.append(save ? "s " : " ", registers[rd], ",", registers[rn], ",");
|
||||
output.append(registers[rm]);
|
||||
if(mode == 0) output.append(" lsl ");
|
||||
if(mode == 1) output.append(" lsr ");
|
||||
|
@ -328,9 +328,9 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
|
||||
uint32 rm = (immediate >> (rotate << 1)) | (immediate << (32 - (rotate << 1)));
|
||||
output.append(opcodes[opcode], conditions[condition]);
|
||||
if(is_move(opcode)) output.append(save ? "s " : " ", registers[rd]);
|
||||
if(is_comp(opcode)) output.append(" ", registers[rn]);
|
||||
if(is_math(opcode)) output.append(save ? "s " : " ", registers[rd], ",", registers[rn]);
|
||||
if(isMove(opcode)) output.append(save ? "s " : " ", registers[rd]);
|
||||
if(isComp(opcode)) output.append(" ", registers[rn]);
|
||||
if(isMath(opcode)) output.append(save ? "s " : " ", registers[rd], ",", registers[rn]);
|
||||
output.append(",#0x", hex(rm, 8L));
|
||||
|
||||
return output;
|
||||
|
@ -382,8 +382,8 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
if(pre == 0) output.append("]");
|
||||
output.append(",", up ? "+" : "-", registers[rm]);
|
||||
if(mode == 0 && shift != 0) output.append(" lsl #", shift);
|
||||
if(mode == 1) output.append(" lsr #", shift == 0 ? 32u : (unsigned)shift);
|
||||
if(mode == 2) output.append(" asr #", shift == 0 ? 32u : (unsigned)shift);
|
||||
if(mode == 1) output.append(" lsr #", shift == 0 ? 32u : (uint)shift);
|
||||
if(mode == 2) output.append(" asr #", shift == 0 ? 32u : (uint)shift);
|
||||
if(mode == 3 && shift != 0) output.append(" ror #", shift);
|
||||
if(mode == 3 && shift == 0) output.append(" rrx");
|
||||
if(pre == 1) output.append("]");
|
||||
|
@ -405,7 +405,7 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
|
||||
output.append(load ? "ldm" : "stm", conditions[condition], indices[index], " ");
|
||||
output.append(registers[rn], writeback ? "!" : "", ",{");
|
||||
for(unsigned n = 0; n < 16; n++) if(list & (1 << n)) output.append(registers[n], ",");
|
||||
for(uint n : range(16)) if(list & (1 << n)) output.append(registers[n], ",");
|
||||
output.trimRight(",", 1L);
|
||||
output.append("}", s ? "^" : "");
|
||||
|
||||
|
@ -438,7 +438,7 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
return output;
|
||||
}
|
||||
|
||||
auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
||||
auto ARM::disassembleInstructionTHUMB(uint32 pc) -> string {
|
||||
static string conditions[] = {
|
||||
"eq", "ne", "cs", "cc",
|
||||
"mi", "pl", "vs", "vc",
|
||||
|
@ -567,7 +567,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
|||
uint3 rd = instruction >> 8;
|
||||
uint8 displacement = instruction;
|
||||
|
||||
unsigned rm = ((pc + 4) & ~3) + displacement * 4;
|
||||
uint rm = ((pc + 4) & ~3) + displacement * 4;
|
||||
output.append("ldr ", registers[rd], ",[pc,#0x", hex(rm, 3L), "]");
|
||||
output.append(" =0x", hex(read(Word | Nonsequential, rm), 8L));
|
||||
|
||||
|
@ -675,7 +675,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
|||
uint8 list = instruction;
|
||||
|
||||
output.append(load == 0 ? "push" : "pop", " {");
|
||||
for(unsigned l = 0; l < 8; l++) {
|
||||
for(uint l : range(8)) {
|
||||
if(list & (1 << l)) output.append(registers[l], ",");
|
||||
}
|
||||
if(branch) output.append(load == 0 ? "lr," : "pc,");
|
||||
|
@ -693,7 +693,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
|||
uint8 list = instruction;
|
||||
|
||||
output.append(load ? "ldmia " : "stmia ", registers[rn], "!,{");
|
||||
for(unsigned l = 0; l < 8; l++) {
|
||||
for(uint l : range(8)) {
|
||||
if(list & (1 << l)) output.append(registers[l], ",");
|
||||
}
|
||||
output.trimRight(",", 1L);
|
||||
|
@ -759,7 +759,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
|||
return output;
|
||||
}
|
||||
|
||||
auto ARM::disassemble_registers() -> string {
|
||||
auto ARM::disassembleRegisters() -> string {
|
||||
string output;
|
||||
output.append( "r0:", hex(r( 0), 8L), " r1:", hex(r( 1), 8L), " r2:", hex(r( 2), 8L), " r3:", hex(r( 3), 8L), " ");
|
||||
output.append( "r4:", hex(r( 4), 8L), " r5:", hex(r( 5), 8L), " r6:", hex(r( 6), 8L), " r7:", hex(r( 7), 8L), " ");
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
auto disassemble_arm_instruction(uint32 pc) -> string;
|
||||
auto disassemble_thumb_instruction(uint32 pc) -> string;
|
||||
auto disassemble_registers() -> string;
|
||||
auto disassembleInstructionARM(uint32 pc) -> string;
|
||||
auto disassembleInstructionTHUMB(uint32 pc) -> string;
|
||||
auto disassembleRegisters() -> string;
|
||||
|
|
|
@ -531,8 +531,8 @@ auto ARM::arm_op_move_multiple() {
|
|||
if(s && l == 0) usr = true;
|
||||
if(usr) processor.setMode(Processor::Mode::USR);
|
||||
|
||||
unsigned sequential = Nonsequential;
|
||||
for(unsigned m = 0; m < 16; m++) {
|
||||
uint sequential = Nonsequential;
|
||||
for(uint m : range(16)) {
|
||||
if(list & 1 << m) {
|
||||
if(l == 1) r(m) = read(Word | sequential, rn);
|
||||
if(l == 0) write(Word | sequential, rn, r(m));
|
||||
|
|
|
@ -69,8 +69,8 @@ auto ARM::thumb_op_shift_immediate() {
|
|||
|
||||
switch(opcode) {
|
||||
case 0: r(d) = bit(lsl(r(m), immediate)); break;
|
||||
case 1: r(d) = bit(lsr(r(m), immediate == 0 ? 32u : (unsigned)immediate)); break;
|
||||
case 2: r(d) = bit(asr(r(m), immediate == 0 ? 32u : (unsigned)immediate)); break;
|
||||
case 1: r(d) = bit(lsr(r(m), immediate == 0 ? 32u : (uint)immediate)); break;
|
||||
case 2: r(d) = bit(asr(r(m), immediate == 0 ? 32u : (uint)immediate)); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,7 @@ auto ARM::thumb_op_load_literal() {
|
|||
uint3 d = instruction() >> 8;
|
||||
uint8 displacement = instruction();
|
||||
|
||||
unsigned rm = (r(15) & ~3) + displacement * 4;
|
||||
uint rm = (r(15) & ~3) + displacement * 4;
|
||||
r(d) = load(Word | Nonsequential, rm);
|
||||
}
|
||||
|
||||
|
@ -273,8 +273,8 @@ auto ARM::thumb_op_stack_multiple() {
|
|||
if(l == 1) sp = r(13);
|
||||
if(l == 0) sp = r(13) - (bit::count(list) + branch) * 4;
|
||||
|
||||
unsigned sequential = Nonsequential;
|
||||
for(unsigned m = 0; m < 8; m++) {
|
||||
uint sequential = Nonsequential;
|
||||
for(uint m : range(8)) {
|
||||
if(list & 1 << m) {
|
||||
if(l == 1) r(m) = read(Word | sequential, sp); //POP
|
||||
if(l == 0) write(Word | sequential, sp, r(m)); //PUSH
|
||||
|
@ -310,7 +310,7 @@ auto ARM::thumb_op_move_multiple() {
|
|||
uint8 list = instruction();
|
||||
uint32 rn = r(n); //rn may be in register list; so we must cache it
|
||||
|
||||
for(unsigned m = 0; m < 8; m++) {
|
||||
for(uint m : range(8)) {
|
||||
if(list & 1 << m) {
|
||||
if(l == 1) r(m) = read(Word | Nonsequential, rn); //LDMIA
|
||||
if(l == 0) write(Word | Nonsequential, rn, r(m)); //STMIA
|
||||
|
|
|
@ -32,7 +32,7 @@ auto ARM::Processor::power() -> void {
|
|||
}
|
||||
|
||||
auto ARM::Processor::setMode(Mode mode) -> void {
|
||||
cpsr.m = 0x10 | (unsigned)mode;
|
||||
cpsr.m = 0x10 | (uint)mode;
|
||||
|
||||
if(mode == Mode::FIQ) {
|
||||
r[ 8] = &fiq.r8;
|
||||
|
|
|
@ -54,7 +54,7 @@ struct Pipeline {
|
|||
};
|
||||
|
||||
struct Processor {
|
||||
enum class Mode : unsigned {
|
||||
enum class Mode : uint {
|
||||
USR = 0x10, //user
|
||||
FIQ = 0x11, //fast interrupt request
|
||||
IRQ = 0x12, //interrupt request
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
auto ARM::pipeline_step() -> void {
|
||||
auto ARM::stepPipeline() -> void {
|
||||
pipeline.execute = pipeline.decode;
|
||||
pipeline.decode = pipeline.fetch;
|
||||
|
||||
unsigned sequential = Sequential;
|
||||
uint sequential = Sequential;
|
||||
if(pipeline.nonsequential) {
|
||||
pipeline.nonsequential = false;
|
||||
sequential = Nonsequential;
|
||||
|
@ -19,7 +19,7 @@ auto ARM::pipeline_step() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto ARM::arm_step() -> void {
|
||||
auto ARM::stepARM() -> void {
|
||||
if(pipeline.reload) {
|
||||
pipeline.reload = false;
|
||||
r(15).data &= ~3;
|
||||
|
@ -27,10 +27,10 @@ auto ARM::arm_step() -> void {
|
|||
pipeline.fetch.address = r(15) & ~3;
|
||||
pipeline.fetch.instruction = read(Prefetch | Word | Nonsequential, pipeline.fetch.address);
|
||||
|
||||
pipeline_step();
|
||||
stepPipeline();
|
||||
}
|
||||
|
||||
pipeline_step();
|
||||
stepPipeline();
|
||||
|
||||
if(processor.irqline && cpsr().i == 0) {
|
||||
vector(0x00000018, Processor::Mode::IRQ);
|
||||
|
@ -39,8 +39,8 @@ auto ARM::arm_step() -> void {
|
|||
|
||||
instructions++;
|
||||
if(trace) {
|
||||
print(disassemble_registers(), "\n");
|
||||
print(disassemble_arm_instruction(pipeline.execute.address), "\n");
|
||||
print(disassembleRegisters(), "\n");
|
||||
print(disassembleInstructionARM(pipeline.execute.address), "\n");
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ auto ARM::arm_step() -> void {
|
|||
crash = true;
|
||||
}
|
||||
|
||||
auto ARM::thumb_step() -> void {
|
||||
auto ARM::stepTHUMB() -> void {
|
||||
if(pipeline.reload) {
|
||||
pipeline.reload = false;
|
||||
r(15).data &= ~1;
|
||||
|
@ -84,10 +84,10 @@ auto ARM::thumb_step() -> void {
|
|||
pipeline.fetch.address = r(15) & ~1;
|
||||
pipeline.fetch.instruction = read(Prefetch | Half | Nonsequential, pipeline.fetch.address);
|
||||
|
||||
pipeline_step();
|
||||
stepPipeline();
|
||||
}
|
||||
|
||||
pipeline_step();
|
||||
stepPipeline();
|
||||
|
||||
if(processor.irqline && cpsr().i == 0) {
|
||||
vector(0x00000018, Processor::Mode::IRQ);
|
||||
|
@ -97,8 +97,8 @@ auto ARM::thumb_step() -> void {
|
|||
|
||||
instructions++;
|
||||
if(trace) {
|
||||
print(disassemble_registers(), "\n");
|
||||
print(disassemble_thumb_instruction(pipeline.execute.address), "\n");
|
||||
print(disassembleRegisters(), "\n");
|
||||
print(disassembleInstructionTHUMB(pipeline.execute.address), "\n");
|
||||
}
|
||||
|
||||
#define decode(pattern, execute) if( \
|
||||
|
|
|
@ -84,7 +84,7 @@ auto Cartridge::loadICD2(Markup::Node node) -> void {
|
|||
icd2.revision = max(1, node["revision"].natural());
|
||||
|
||||
//Game Boy core loads data through ICD2 interface
|
||||
for(auto leaf : node.find("map")) loadMap(leaf, {&ICD2::read, &icd2}, {&ICD2::write, &icd2});
|
||||
for(auto leaf : node.find("map")) loadMap(leaf, {&ICD2::readIO, &icd2}, {&ICD2::writeIO, &icd2});
|
||||
}
|
||||
|
||||
auto Cartridge::loadMCC(Markup::Node node) -> void {
|
||||
|
|
|
@ -39,13 +39,13 @@ auto ArmDSP::boot() -> void {
|
|||
|
||||
auto ArmDSP::main() -> void {
|
||||
if(crash) {
|
||||
print(disassemble_arm_instruction(pipeline.execute.address), "\n");
|
||||
print(disassemble_registers(), "\n");
|
||||
print(disassembleRegisters(), "\n");
|
||||
print(disassembleInstructionARM(pipeline.execute.address), "\n");
|
||||
print("Executed: ", instructions, "\n");
|
||||
while(true) step(frequency);
|
||||
}
|
||||
|
||||
arm_step();
|
||||
stepARM();
|
||||
}
|
||||
|
||||
auto ArmDSP::step(uint clocks) -> void {
|
||||
|
|
|
@ -11,9 +11,9 @@ struct ArmDSP : Processor::ARM, Cothread {
|
|||
auto main() -> void;
|
||||
|
||||
auto step(uint clocks) -> void override;
|
||||
auto busIdle() -> void override;
|
||||
auto busRead(uint mode, uint32 addr) -> uint32 override;
|
||||
auto busWrite(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
auto _idle() -> void override;
|
||||
auto _read(uint mode, uint32 addr) -> uint32 override;
|
||||
auto _write(uint mode, uint32 addr, uint32 word) -> void override;
|
||||
|
||||
auto read(uint24 addr, uint8 data) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//note: timings are completely unverified
|
||||
//due to the ST018 chip design (on-die ROM), testing is nearly impossible
|
||||
|
||||
auto ArmDSP::busIdle() -> void {
|
||||
auto ArmDSP::_idle() -> void {
|
||||
step(1);
|
||||
}
|
||||
|
||||
auto ArmDSP::busRead(uint mode, uint32 addr) -> uint32 {
|
||||
auto ArmDSP::_read(uint mode, uint32 addr) -> uint32 {
|
||||
step(1);
|
||||
|
||||
static auto memory = [&](const uint8* memory, uint mode, uint32 addr) -> uint32 {
|
||||
|
@ -46,7 +46,7 @@ auto ArmDSP::busRead(uint mode, uint32 addr) -> uint32 {
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto ArmDSP::busWrite(uint mode, uint32 addr, uint32 word) -> void {
|
||||
auto ArmDSP::_write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
step(1);
|
||||
|
||||
static auto memory = [](uint8* memory, uint mode, uint32 addr, uint32 word) {
|
||||
|
|
|
@ -7,7 +7,7 @@ ICD2 icd2;
|
|||
#if defined(SFC_SUPERGAMEBOY)
|
||||
|
||||
#include "interface.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto ICD2::Enter() -> void {
|
||||
|
|
|
@ -24,9 +24,9 @@ struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Cothread {
|
|||
auto audioSample(const double* samples, uint channels) -> void override;
|
||||
auto inputPoll(uint port, uint device, uint id) -> int16 override;
|
||||
|
||||
//mmio.cpp
|
||||
auto read(uint24 addr, uint8 data) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
//io.cpp
|
||||
auto readIO(uint24 addr, uint8 data) -> uint8;
|
||||
auto writeIO(uint24 addr, uint8 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto ICD2::read(uint24 addr, uint8 data) -> uint8 {
|
||||
auto ICD2::readIO(uint24 addr, uint8 data) -> uint8 {
|
||||
addr &= 0x40ffff;
|
||||
|
||||
//LY counter
|
||||
|
@ -38,7 +38,7 @@ auto ICD2::read(uint24 addr, uint8 data) -> uint8 {
|
|||
return 0x00;
|
||||
}
|
||||
|
||||
auto ICD2::write(uint24 addr, uint8 data) -> void {
|
||||
auto ICD2::writeIO(uint24 addr, uint8 data) -> void {
|
||||
addr &= 0xffff;
|
||||
|
||||
//VRAM port
|
|
@ -5,7 +5,7 @@ namespace SuperFamicom {
|
|||
#include "bus.cpp"
|
||||
#include "dma.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
SA1 sa1;
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ struct SA1 : Processor::R65816, Cothread {
|
|||
auto bitmapRead(uint addr, uint8 data) -> uint8;
|
||||
auto bitmapWrite(uint addr, uint8 data) -> void;
|
||||
|
||||
//mmio.cpp
|
||||
//io.cpp
|
||||
auto readIO(uint24 addr, uint8 data) -> uint8;
|
||||
auto writeIO(uint24 addr, uint8 data) -> void;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace SuperFamicom {
|
|||
#include "bus.cpp"
|
||||
#include "core.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "io.cpp"
|
||||
#include "timing.cpp"
|
||||
#include "serialization.cpp"
|
||||
SuperFX superfx;
|
||||
|
|
|
@ -44,7 +44,7 @@ struct SuperFX : Processor::GSU, Cothread {
|
|||
auto readCache(uint16 addr) -> uint8;
|
||||
auto writeCache(uint16 addr, uint8 data) -> void;
|
||||
|
||||
//mmio.cpp
|
||||
//io.cpp
|
||||
auto readIO(uint24 addr, uint8 data) -> uint8;
|
||||
auto writeIO(uint24 addr, uint8 data) -> void;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace SuperFamicom {
|
|||
CPU cpu;
|
||||
#include "dma.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "io.cpp"
|
||||
#include "timing.cpp"
|
||||
#include "irq.cpp"
|
||||
#include "joypad.cpp"
|
||||
|
|
|
@ -52,7 +52,7 @@ struct CPU : Processor::R65816, Thread, PPUcounter {
|
|||
alwaysinline auto speed(uint24 addr) const -> uint;
|
||||
auto readDisassembler(uint24 addr) -> uint8 override;
|
||||
|
||||
//mmio.cpp
|
||||
//io.cpp
|
||||
auto readAPU(uint24 addr, uint8 data) -> uint8;
|
||||
auto readCPU(uint24 addr, uint8 data) -> uint8;
|
||||
auto readDMA(uint24 addr, uint8 data) -> uint8;
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace SuperFamicom {
|
|||
|
||||
PPU ppu;
|
||||
|
||||
#include "mmio.cpp"
|
||||
#include "io.cpp"
|
||||
#include "background/background.cpp"
|
||||
#include "object/object.cpp"
|
||||
#include "window/window.cpp"
|
||||
|
|
|
@ -17,7 +17,7 @@ struct PPU : Thread, PPUcounter {
|
|||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//mmio.cpp
|
||||
//io.cpp
|
||||
alwaysinline auto getVramAddress() -> uint16;
|
||||
alwaysinline auto vramAccessible() const -> bool;
|
||||
alwaysinline auto oamWrite(uint addr, uint8 data) -> void;
|
||||
|
|
|
@ -17,9 +17,11 @@ Presentation::Presentation() {
|
|||
//add icarus menu options -- but only if icarus binary is present
|
||||
if(execute("icarus", "--name").output.strip() == "icarus") {
|
||||
libraryMenu.append(MenuSeparator());
|
||||
libraryMenu.append(MenuItem().setText("Import ROM File ...").onActivate([&] {
|
||||
libraryMenu.append(MenuItem().setText("Load ROM File ...").onActivate([&] {
|
||||
audio->clear();
|
||||
if(auto location = execute("icarus", "--import")) {
|
||||
program->mediumQueue.append(location.output.strip());
|
||||
program->loadMedium();
|
||||
}
|
||||
}));
|
||||
libraryMenu.append(MenuItem().setText("Import ROM Files ...").onActivate([&] {
|
||||
|
|
Loading…
Reference in New Issue