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