Update to v099r10 release.

byuu says:

Changelog:
- higan/profile/ => higan/systems/ [temporary; unless we can't think of
  a better base folder name]
- god-damn-better-have fixed the input polling bug
- re-added command-line and drag-and-drop loading
  - command-line loading can now load multiple folders at once (SGB+GB
    game; Sufami Turbo+Slot A+Slot B; etc)
  - if you load just the base cart, it'll present you with a dialog to
    optionally load slotted cart(s)
- MSU1 now goes through nall/vfs instead of directly accessing the
  filesystem
- Famicom Cartridge, PPU cores updated to newer programming style
  - there's countless opportunity for BitField and .bits() in the PPU
    ... but I'm worried about breaking things

If anyone has a working MSU1 game and can test the changes out, that'd
be appreciated. I still don't have a test ROM on my dev box.

I wouldn't worry too much about extensively testing the Famicom PPU
changes just yet ... I'm still struggling with what to name the structs
inside the classes between all of my emulators, and the BitField/.bits()
changes will be much more important to test at a later date.

The only use case left for Emulator::Interface::path(uint id) is for
21fx emulation. This peripheral loads a DLL/SO via LoadLibrary/dlopen,
which do not have any official ways to open a file in RAM. I'm
very hesitant to use the portable trick of writing the memory to a
temporary file, loading it, and deleting the temporary file once done
... it's a real waste of disk activity. I might make something like
vfs::file::isVirtual->bool,path()->string to get around this. But even
once I do, the underlying LoadLibrary/dlopen call is still going to be
direct disk access.
This commit is contained in:
Tim Allen 2016-06-26 18:54:12 +10:00
parent 3a9c7c6843
commit a816998122
52 changed files with 705 additions and 702 deletions

View File

@ -11,7 +11,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "099.09";
static const string Version = "099.10";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -30,7 +30,6 @@ struct Interface {
struct Input {
uint type; //0 = digital, 1 = analog (relative), 2 = rumble
string name;
uintptr userData;
};
vector<Input> inputs;
};

View File

@ -65,13 +65,13 @@ struct BandaiFCG : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(ciram_addr(addr));
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
return Board::chr_read(addr);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(ciram_addr(addr), data);
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
return Board::chr_write(addr, data);
}

View File

@ -12,12 +12,12 @@ struct KonamiVRC1 : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc1.ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(vrc1.ciram_addr(addr));
return Board::chr_read(vrc1.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc1.ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(vrc1.ciram_addr(addr), data);
return Board::chr_write(vrc1.chr_addr(addr), data);
}

View File

@ -22,12 +22,12 @@ struct KonamiVRC2 : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc2.ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(vrc2.ciram_addr(addr));
return Board::chr_read(vrc2.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc2.ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(vrc2.ciram_addr(addr), data);
return Board::chr_write(vrc2.chr_addr(addr), data);
}

View File

@ -21,7 +21,7 @@ struct KonamiVRC3 : Board {
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
return ppu.readCIRAM(addr & 0x07ff);
}
return chrram.read(addr);
}
@ -29,7 +29,7 @@ struct KonamiVRC3 : Board {
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
return ppu.writeCIRAM(addr & 0x07ff, data);
}
return chrram.write(addr, data);
}

View File

@ -26,12 +26,12 @@ struct KonamiVRC4 : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc4.ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(vrc4.ciram_addr(addr));
return Board::chr_read(vrc4.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc4.ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(vrc4.ciram_addr(addr), data);
return Board::chr_write(vrc4.chr_addr(addr), data);
}

View File

@ -18,12 +18,12 @@ struct KonamiVRC6 : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc6.ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(vrc6.ciram_addr(addr));
return Board::chr_read(vrc6.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc6.ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(vrc6.ciram_addr(addr), data);
return Board::chr_write(vrc6.chr_addr(addr), data);
}

View File

@ -19,12 +19,12 @@ struct KonamiVRC7 : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(vrc7.ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(vrc7.ciram_addr(addr));
return chrram.read(vrc7.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(vrc7.ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(vrc7.ciram_addr(addr), data);
return chrram.write(vrc7.chr_addr(addr), data);
}

View File

@ -20,12 +20,12 @@ struct NES_AxROM : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff));
if(addr & 0x2000) return ppu.readCIRAM((mirror_select << 10) | (addr & 0x03ff));
return Board::chr_read(addr);
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data);
if(addr & 0x2000) return ppu.writeCIRAM((mirror_select << 10) | (addr & 0x03ff), data);
return Board::chr_write(addr, data);
}

View File

@ -17,7 +17,7 @@ struct NES_BNROM : Board {
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr);
return ppu.readCIRAM(addr);
}
return Board::chr_read(addr);
}
@ -25,7 +25,7 @@ struct NES_BNROM : Board {
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr, data);
return ppu.writeCIRAM(addr, data);
}
return Board::chr_write(addr, data);
}

View File

@ -17,7 +17,7 @@ struct NES_CNROM : Board {
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
return ppu.readCIRAM(addr & 0x07ff);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
return Board::chr_read(addr);
@ -26,7 +26,7 @@ struct NES_CNROM : Board {
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
return ppu.writeCIRAM(addr & 0x07ff, data);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
Board::chr_write(addr, data);

View File

@ -34,7 +34,7 @@ struct NES_FxROM : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(ciram_addr(addr));
bool region = addr & 0x1000;
uint bank = chr_bank[region][latch[region]];
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
@ -43,7 +43,7 @@ struct NES_FxROM : Board {
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(ciram_addr(addr), data);
bool region = addr & 0x1000;
uint bank = chr_bank[region][latch[region]];
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;

View File

@ -21,7 +21,7 @@ struct NES_GxROM : Board {
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
return ppu.readCIRAM(addr & 0x07ff);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
return Board::chr_read(addr);
@ -30,7 +30,7 @@ struct NES_GxROM : Board {
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
return ppu.writeCIRAM(addr & 0x07ff, data);
}
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
Board::chr_write(addr, data);

View File

@ -19,13 +19,13 @@ struct NES_HKROM : Board {
auto chr_read(uint addr) -> uint8 {
mmc6.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_read(mmc6.ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(mmc6.ciram_addr(addr));
return Board::chr_read(mmc6.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
mmc6.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_write(mmc6.ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(mmc6.ciram_addr(addr), data);
return Board::chr_write(mmc6.chr_addr(addr), data);
}

View File

@ -17,7 +17,7 @@ struct NES_NROM : Board {
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr & 0x07ff);
return ppu.readCIRAM(addr & 0x07ff);
}
if(chrram.size) return chrram.read(addr);
return chrrom.read(addr);
@ -26,7 +26,7 @@ struct NES_NROM : Board {
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr & 0x07ff, data);
return ppu.writeCIRAM(addr & 0x07ff, data);
}
if(chrram.size) return chrram.write(addr, data);
}

View File

@ -40,7 +40,7 @@ struct NES_PxROM : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(ciram_addr(addr));
bool region = addr & 0x1000;
uint bank = chr_bank[region][latch[region]];
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;
@ -49,7 +49,7 @@ struct NES_PxROM : Board {
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(ciram_addr(addr), data);
bool region = addr & 0x1000;
uint bank = chr_bank[region][latch[region]];
if((addr & 0x0ff8) == 0x0fd8) latch[region] = 0;

View File

@ -48,12 +48,12 @@ struct NES_SxROM : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(mmc1.ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(mmc1.ciram_addr(addr));
return Board::chr_read(mmc1.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(mmc1.ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(mmc1.ciram_addr(addr), data);
return Board::chr_write(mmc1.chr_addr(addr), data);
}

View File

@ -20,13 +20,13 @@ struct NES_TxROM : Board {
auto chr_read(uint addr) -> uint8 {
mmc3.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_read(mmc3.ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(mmc3.ciram_addr(addr));
return Board::chr_read(mmc3.chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
mmc3.irq_test(addr);
if(addr & 0x2000) return ppu.ciram_write(mmc3.ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(mmc3.ciram_addr(addr), data);
return Board::chr_write(mmc3.chr_addr(addr), data);
}

View File

@ -19,7 +19,7 @@ struct NES_UxROM : Board {
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_read(addr);
return ppu.readCIRAM(addr);
}
return Board::chr_read(addr);
}
@ -27,7 +27,7 @@ struct NES_UxROM : Board {
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
if(settings.mirror == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
return ppu.ciram_write(addr, data);
return ppu.writeCIRAM(addr, data);
}
return Board::chr_write(addr, data);
}

View File

@ -154,12 +154,12 @@ struct Sunsoft5B : Board {
}
auto chr_read(uint addr) -> uint8 {
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
if(addr & 0x2000) return ppu.readCIRAM(ciram_addr(addr));
return Board::chr_read(chr_addr(addr));
}
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
if(addr & 0x2000) return ppu.writeCIRAM(ciram_addr(addr), data);
return Board::chr_write(chr_addr(addr), data);
}

View File

@ -53,19 +53,19 @@ auto Cartridge::reset() -> void {
board->reset();
}
auto Cartridge::prg_read(uint addr) -> uint8 {
auto Cartridge::readPRG(uint addr) -> uint8 {
return board->prg_read(addr);
}
auto Cartridge::prg_write(uint addr, uint8 data) -> void {
auto Cartridge::writePRG(uint addr, uint8 data) -> void {
return board->prg_write(addr, data);
}
auto Cartridge::chr_read(uint addr) -> uint8 {
auto Cartridge::readCHR(uint addr) -> uint8 {
return board->chr_read(addr);
}
auto Cartridge::chr_write(uint addr, uint8 data) -> void {
auto Cartridge::writeCHR(uint addr, uint8 data) -> void {
return board->chr_write(addr, data);
}

View File

@ -29,11 +29,11 @@ struct Cartridge : Thread {
//privileged:
Board* board = nullptr;
auto prg_read(uint addr) -> uint8;
auto prg_write(uint addr, uint8 data) -> void;
auto readPRG(uint addr) -> uint8;
auto writePRG(uint addr, uint8 data) -> void;
auto chr_read(uint addr) -> uint8;
auto chr_write(uint addr, uint8 data) -> void;
auto readCHR(uint addr) -> uint8;
auto writeCHR(uint addr, uint8 data) -> void;
//scanline() is for debugging purposes only:
//boards must detect scanline edges on their own

View File

@ -269,8 +269,8 @@ struct MMC5 : Chip {
if(vs_fetch && (hcounter & 2) != 0) return exram[vs_vpos / 32 * 8 + vs_hpos / 32 + 0x03c0];
switch(nametable_mode[(addr >> 10) & 3]) {
case 0: return ppu.ciram_read(0x0000 | (addr & 0x03ff));
case 1: return ppu.ciram_read(0x0400 | (addr & 0x03ff));
case 0: return ppu.readCIRAM(0x0000 | (addr & 0x03ff));
case 1: return ppu.readCIRAM(0x0400 | (addr & 0x03ff));
case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : (uint8)0x00;
case 3: return (hcounter & 2) == 0 ? fillmode_tile : fillmode_color;
}
@ -327,8 +327,8 @@ struct MMC5 : Chip {
auto chr_write(uint addr, uint8 data) -> void {
if(addr & 0x2000) {
switch(nametable_mode[(addr >> 10) & 3]) {
case 0: return ppu.ciram_write(0x0000 | (addr & 0x03ff), data);
case 1: return ppu.ciram_write(0x0400 | (addr & 0x03ff), data);
case 0: return ppu.writeCIRAM(0x0000 | (addr & 0x03ff), data);
case 1: return ppu.writeCIRAM(0x0400 | (addr & 0x03ff), data);
case 2: exram[addr & 0x03ff] = data; break;
}
}

View File

@ -12,7 +12,7 @@ Bus bus;
//$4018-ffff = Cartridge
auto Bus::read(uint16 addr) -> uint8 {
uint8 data = cartridge.prg_read(addr);
uint8 data = cartridge.readPRG(addr);
if(addr <= 0x1fff) data = cpu.readRAM(addr);
else if(addr <= 0x3fff) data = ppu.readIO(addr);
else if(addr <= 0x4017) data = cpu.readIO(addr);
@ -25,7 +25,7 @@ auto Bus::read(uint16 addr) -> uint8 {
}
auto Bus::write(uint16 addr, uint8 data) -> void {
cartridge.prg_write(addr, data);
cartridge.writePRG(addr, data);
if(addr <= 0x1fff) return cpu.writeRAM(addr, data);
if(addr <= 0x3fff) return ppu.writeIO(addr, data);
if(addr <= 0x4017) return cpu.writeIO(addr, data);

121
higan/fc/ppu/memory.cpp Normal file
View File

@ -0,0 +1,121 @@
auto PPU::readCIRAM(uint11 addr) -> uint8 {
return ciram[addr];
}
auto PPU::writeCIRAM(uint11 addr, uint8 data) -> void {
ciram[addr] = data;
}
auto PPU::readCGRAM(uint5 addr) -> uint8 {
if((addr & 0x13) == 0x10) addr &= ~0x10;
uint8 data = cgram[addr];
if(r.grayscale) data &= 0x30;
return data;
}
auto PPU::writeCGRAM(uint5 addr, uint8 data) -> void {
if((addr & 0x13) == 0x10) addr &= ~0x10;
cgram[addr] = data;
}
auto PPU::readIO(uint16 addr) -> uint8 {
uint8 result = 0x00;
switch(addr & 7) {
case 2: //PPUSTATUS
result |= r.nmiFlag << 7;
result |= r.spriteZeroHit << 6;
result |= r.spriteOverflow << 5;
result |= r.mdr & 0x1f;
r.addressLatch = 0;
r.nmiHold = 0;
cpu.nmiLine(r.nmiFlag = 0);
break;
case 4: //OAMDATA
result = oam[r.oamAddress];
break;
case 7: //PPUDATA
if(enable() && (r.ly <= 240 || r.ly == 261)) return 0x00;
addr = r.vaddr & 0x3fff;
if(addr <= 0x1fff) {
result = r.busData;
r.busData = cartridge.readCHR(addr);
} else if(addr <= 0x3eff) {
result = r.busData;
r.busData = cartridge.readCHR(addr);
} else if(addr <= 0x3fff) {
result = readCGRAM(addr);
r.busData = cartridge.readCHR(addr);
}
r.vaddr += r.vramIncrement;
break;
}
return result;
}
auto PPU::writeIO(uint16 addr, uint8 data) -> void {
r.mdr = data;
switch(addr & 7) {
case 0: //PPUCTRL
r.nmiEnable = data & 0x80;
r.masterSelect = data & 0x40;
r.spriteHeight = data & 0x20 ? 16 : 8;
r.bgAddress = (data & 0x10) ? 0x1000 : 0x0000;
r.spriteAddress = (data & 0x08) ? 0x1000 : 0x0000;
r.vramIncrement = (data & 0x04) ? 32 : 1;
r.taddr = (r.taddr & 0x73ff) | ((data & 0x03) << 10);
cpu.nmiLine(r.nmiEnable && r.nmiHold && r.nmiFlag);
return;
case 1: //PPUMASK
r.emphasis = data >> 5;
r.spriteEnable = data & 0x10;
r.bgEnable = data & 0x08;
r.spriteEdgeEnable = data & 0x04;
r.bgEdgeEnable = data & 0x02;
r.grayscale = data & 0x01;
return;
case 2: //PPUSTATUS
return;
case 3: //OAMADDR
r.oamAddress = data;
return;
case 4: //OAMDATA
if(r.oamAddress.bits(0,1) == 2) data.bits(2,4) = 0; //clear non-existent bits (always read back as 0)
oam[r.oamAddress++] = data;
return;
case 5: //PPUSCROLL
if(r.addressLatch == 0) {
r.xaddr = data & 0x07;
r.taddr = (r.taddr & 0x7fe0) | (data >> 3);
} else {
r.taddr = (r.taddr & 0x0c1f) | ((data & 0x07) << 12) | ((data >> 3) << 5);
}
r.addressLatch ^= 1;
return;
case 6: //PPUADDR
if(r.addressLatch == 0) {
r.taddr = (r.taddr & 0x00ff) | ((data & 0x3f) << 8);
} else {
r.taddr = (r.taddr & 0x7f00) | data;
r.vaddr = r.taddr;
}
r.addressLatch ^= 1;
return;
case 7: //PPUDATA
if(enable() && (r.ly <= 240 || r.ly == 261)) return;
addr = r.vaddr & 0x3fff;
if(addr <= 0x1fff) {
cartridge.writeCHR(addr, data);
} else if(addr <= 0x3eff) {
cartridge.writeCHR(addr, data);
} else if(addr <= 0x3fff) {
writeCGRAM(addr, data);
}
r.vaddr += r.vramIncrement;
return;
}
}

View File

@ -3,7 +3,8 @@
namespace Famicom {
PPU ppu;
#include "memory.cpp"
#include "render.cpp"
#include "serialization.cpp"
auto PPU::Enter() -> void {
@ -11,37 +12,37 @@ auto PPU::Enter() -> void {
}
auto PPU::main() -> void {
raster_scanline();
renderScanline();
}
auto PPU::tick() -> void {
if(status.ly == 240 && status.lx == 340) status.nmi_hold = 1;
if(status.ly == 241 && status.lx == 0) status.nmi_flag = status.nmi_hold;
if(status.ly == 241 && status.lx == 2) cpu.nmiLine(status.nmi_enable && status.nmi_flag);
if(r.ly == 240 && r.lx == 340) r.nmiHold = 1;
if(r.ly == 241 && r.lx == 0) r.nmiFlag = r.nmiHold;
if(r.ly == 241 && r.lx == 2) cpu.nmiLine(r.nmiEnable && r.nmiFlag);
if(status.ly == 260 && status.lx == 340) status.sprite_zero_hit = 0, status.sprite_overflow = 0;
if(r.ly == 260 && r.lx == 340) r.spriteZeroHit = 0, r.spriteOverflow = 0;
if(status.ly == 260 && status.lx == 340) status.nmi_hold = 0;
if(status.ly == 261 && status.lx == 0) status.nmi_flag = status.nmi_hold;
if(status.ly == 261 && status.lx == 2) cpu.nmiLine(status.nmi_enable && status.nmi_flag);
if(r.ly == 260 && r.lx == 340) r.nmiHold = 0;
if(r.ly == 261 && r.lx == 0) r.nmiFlag = r.nmiHold;
if(r.ly == 261 && r.lx == 2) cpu.nmiLine(r.nmiEnable && r.nmiFlag);
clock += 4;
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
status.lx++;
r.lx++;
}
auto PPU::scanline() -> void {
status.lx = 0;
if(++status.ly == 262) {
status.ly = 0;
r.lx = 0;
if(++r.ly == 262) {
r.ly = 0;
frame();
}
cartridge.scanline(status.ly);
cartridge.scanline(r.ly);
}
auto PPU::frame() -> void {
status.field ^= 1;
r.field ^= 1;
scheduler.exit(Scheduler::Event::Frame);
}
@ -55,432 +56,15 @@ auto PPU::power() -> void {
auto PPU::reset() -> void {
create(PPU::Enter, 21'477'272);
status.mdr = 0x00;
status.field = 0;
status.ly = 0;
status.bus_data = 0x00;
status.address_latch = 0;
memory::fill(&r, sizeof(Registers));
memory::fill(&l, sizeof(Latches));
r.vramIncrement = 1;
status.vaddr = 0x0000;
status.taddr = 0x0000;
status.xaddr = 0x00;
status.nmi_hold = 0;
status.nmi_flag = 0;
//$2000
status.nmi_enable = false;
status.master_select = 0;
status.sprite_size = 0;
status.bg_addr = 0x0000;
status.sprite_addr = 0x0000;
status.vram_increment = 1;
//$2001
status.emphasis = 0;
status.sprite_enable = false;
status.bg_enable = false;
status.sprite_edge_enable = false;
status.bg_edge_enable = false;
status.grayscale = false;
//$2002
status.sprite_zero_hit = false;
status.sprite_overflow = false;
//$2003
status.oam_addr = 0x00;
for(auto& n : buffer) n = 0;
for(auto& n : ciram ) n = 0;
for(auto& n : cgram ) n = 0;
for(auto& n : oam ) n = 0;
}
auto PPU::readIO(uint16 addr) -> uint8 {
uint8 result = 0x00;
switch(addr & 7) {
case 2: //PPUSTATUS
result |= status.nmi_flag << 7;
result |= status.sprite_zero_hit << 6;
result |= status.sprite_overflow << 5;
result |= status.mdr & 0x1f;
status.address_latch = 0;
status.nmi_hold = 0;
cpu.nmiLine(status.nmi_flag = 0);
break;
case 4: //OAMDATA
result = oam[status.oam_addr];
break;
case 7: //PPUDATA
if(raster_enable() && (status.ly <= 240 || status.ly == 261)) return 0x00;
addr = status.vaddr & 0x3fff;
if(addr <= 0x1fff) {
result = status.bus_data;
status.bus_data = cartridge.chr_read(addr);
} else if(addr <= 0x3eff) {
result = status.bus_data;
status.bus_data = cartridge.chr_read(addr);
} else if(addr <= 0x3fff) {
result = cgram_read(addr);
status.bus_data = cartridge.chr_read(addr);
}
status.vaddr += status.vram_increment;
break;
}
return result;
}
auto PPU::writeIO(uint16 addr, uint8 data) -> void {
status.mdr = data;
switch(addr & 7) {
case 0: //PPUCTRL
status.nmi_enable = data & 0x80;
status.master_select = data & 0x40;
status.sprite_size = data & 0x20;
status.bg_addr = (data & 0x10) ? 0x1000 : 0x0000;
status.sprite_addr = (data & 0x08) ? 0x1000 : 0x0000;
status.vram_increment = (data & 0x04) ? 32 : 1;
status.taddr = (status.taddr & 0x73ff) | ((data & 0x03) << 10);
cpu.nmiLine(status.nmi_enable && status.nmi_hold && status.nmi_flag);
return;
case 1: //PPUMASK
status.emphasis = data >> 5;
status.sprite_enable = data & 0x10;
status.bg_enable = data & 0x08;
status.sprite_edge_enable = data & 0x04;
status.bg_edge_enable = data & 0x02;
status.grayscale = data & 0x01;
return;
case 2: //PPUSTATUS
return;
case 3: //OAMADDR
status.oam_addr = data;
return;
case 4: //OAMDATA
if(status.oam_addr.bits(0,1) == 2) data.bits(2,4) = 0; //clear non-existent bits (always read back as 0)
oam[status.oam_addr++] = data;
return;
case 5: //PPUSCROLL
if(status.address_latch == 0) {
status.xaddr = data & 0x07;
status.taddr = (status.taddr & 0x7fe0) | (data >> 3);
} else {
status.taddr = (status.taddr & 0x0c1f) | ((data & 0x07) << 12) | ((data >> 3) << 5);
}
status.address_latch ^= 1;
return;
case 6: //PPUADDR
if(status.address_latch == 0) {
status.taddr = (status.taddr & 0x00ff) | ((data & 0x3f) << 8);
} else {
status.taddr = (status.taddr & 0x7f00) | data;
status.vaddr = status.taddr;
}
status.address_latch ^= 1;
return;
case 7: //PPUDATA
if(raster_enable() && (status.ly <= 240 || status.ly == 261)) return;
addr = status.vaddr & 0x3fff;
if(addr <= 0x1fff) {
cartridge.chr_write(addr, data);
} else if(addr <= 0x3eff) {
cartridge.chr_write(addr, data);
} else if(addr <= 0x3fff) {
cgram_write(addr, data);
}
status.vaddr += status.vram_increment;
return;
}
}
auto PPU::ciram_read(uint16 addr) -> uint8 {
return ciram[addr & 0x07ff];
}
auto PPU::ciram_write(uint16 addr, uint8 data) -> void {
ciram[addr & 0x07ff] = data;
}
auto PPU::cgram_read(uint16 addr) -> uint8 {
if((addr & 0x13) == 0x10) addr &= ~0x10;
uint8 data = cgram[addr & 0x1f];
if(status.grayscale) data &= 0x30;
return data;
}
auto PPU::cgram_write(uint16 addr, uint8 data) -> void {
if((addr & 0x13) == 0x10) addr &= ~0x10;
cgram[addr & 0x1f] = data;
}
//
//vaddr = 0yyy VHYY YYYX XXXX
//yyy = fine Yscroll (y:d0-d2)
//V = V nametable (y:d8)
//H = H nametable (x:d8)
//YYYYY = Y nametable (y:d3-d7)
//XXXXX = X nametable (x:d3-d7)
auto PPU::raster_enable() const -> bool {
return (status.bg_enable || status.sprite_enable);
}
auto PPU::nametable_addr() const -> uint {
return 0x2000 + (status.vaddr & 0x0c00);
}
auto PPU::scrollx() const -> uint {
return ((status.vaddr & 0x1f) << 3) | status.xaddr;
}
auto PPU::scrolly() const -> uint {
return (((status.vaddr >> 5) & 0x1f) << 3) | ((status.vaddr >> 12) & 7);
}
auto PPU::sprite_height() const -> uint {
return status.sprite_size == 0 ? 8 : 16;
}
//
auto PPU::chr_load(uint16 addr) -> uint8 {
if(raster_enable() == false) return 0x00;
return cartridge.chr_read(addr);
}
//
auto PPU::scrollx_increment() -> void {
if(raster_enable() == false) return;
status.vaddr = (status.vaddr & 0x7fe0) | ((status.vaddr + 0x0001) & 0x001f);
if((status.vaddr & 0x001f) == 0x0000) {
status.vaddr ^= 0x0400;
}
}
auto PPU::scrolly_increment() -> void {
if(raster_enable() == false) return;
status.vaddr = (status.vaddr & 0x0fff) | ((status.vaddr + 0x1000) & 0x7000);
if((status.vaddr & 0x7000) == 0x0000) {
status.vaddr = (status.vaddr & 0x7c1f) | ((status.vaddr + 0x0020) & 0x03e0);
if((status.vaddr & 0x03e0) == 0x03c0) { //0x03c0 == 30 << 5; 30 * 8 = 240
status.vaddr &= 0x7c1f;
status.vaddr ^= 0x0800;
}
}
}
//
auto PPU::raster_pixel() -> void {
uint32* output = buffer + status.ly * 256;
uint mask = 0x8000 >> (status.xaddr + (status.lx & 7));
uint palette = 0, object_palette = 0;
bool object_priority = 0;
palette |= (raster.tiledatalo & mask) ? 1 : 0;
palette |= (raster.tiledatahi & mask) ? 2 : 0;
if(palette) {
uint attr = raster.attribute;
if(mask >= 256) attr >>= 2;
palette |= (attr & 3) << 2;
}
if(status.bg_enable == false) palette = 0;
if(status.bg_edge_enable == false && status.lx < 8) palette = 0;
if(status.sprite_enable == true)
for(int sprite = 7; sprite >= 0; sprite--) {
if(status.sprite_edge_enable == false && status.lx < 8) continue;
if(raster.oam[sprite].id == 64) continue;
uint spritex = status.lx - raster.oam[sprite].x;
if(spritex >= 8) continue;
if(raster.oam[sprite].attr & 0x40) spritex ^= 7;
uint mask = 0x80 >> spritex;
uint sprite_palette = 0;
sprite_palette |= (raster.oam[sprite].tiledatalo & mask) ? 1 : 0;
sprite_palette |= (raster.oam[sprite].tiledatahi & mask) ? 2 : 0;
if(sprite_palette == 0) continue;
if(raster.oam[sprite].id == 0 && palette && status.lx != 255) status.sprite_zero_hit = 1;
sprite_palette |= (raster.oam[sprite].attr & 3) << 2;
object_priority = raster.oam[sprite].attr & 0x20;
object_palette = 16 + sprite_palette;
}
if(object_palette) {
if(palette == 0 || object_priority == 0) palette = object_palette;
}
if(raster_enable() == false) palette = 0;
output[status.lx] = (status.emphasis << 6) | cgram_read(palette);
}
auto PPU::raster_sprite() -> void {
if(raster_enable() == false) return;
uint n = raster.oam_iterator++;
int ly = (status.ly == 261 ? -1 : status.ly);
uint y = ly - oam[(n * 4) + 0];
if(y >= sprite_height()) return;
if(raster.oam_counter == 8) {
status.sprite_overflow = 1;
return;
}
raster.soam[raster.oam_counter].id = n;
raster.soam[raster.oam_counter].y = oam[(n * 4) + 0];
raster.soam[raster.oam_counter].tile = oam[(n * 4) + 1];
raster.soam[raster.oam_counter].attr = oam[(n * 4) + 2];
raster.soam[raster.oam_counter].x = oam[(n * 4) + 3];
raster.oam_counter++;
}
auto PPU::raster_scanline() -> void {
if((status.ly >= 240 && status.ly <= 260)) {
for(auto x : range(341)) tick();
return scanline();
}
raster.oam_iterator = 0;
raster.oam_counter = 0;
for(auto n : range(8)) {
raster.soam[n].id = 64;
raster.soam[n].y = 0xff;
raster.soam[n].tile = 0xff;
raster.soam[n].attr = 0xff;
raster.soam[n].x = 0xff;
raster.soam[n].tiledatalo = 0;
raster.soam[n].tiledatahi = 0;
}
for(uint tile : range(32)) { // 0-255
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
uint tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
raster_pixel();
tick();
raster_pixel();
tick();
uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
if(scrolly() & 16) attribute >>= 4;
if(scrollx() & 16) attribute >>= 2;
raster_pixel();
tick();
scrollx_increment();
if(tile == 31) scrolly_increment();
raster_pixel();
raster_sprite();
tick();
uint tiledatalo = chr_load(tileaddr + 0);
raster_pixel();
tick();
raster_pixel();
tick();
uint tiledatahi = chr_load(tileaddr + 8);
raster_pixel();
tick();
raster_pixel();
raster_sprite();
tick();
raster.nametable = (raster.nametable << 8) | nametable;
raster.attribute = (raster.attribute << 2) | (attribute & 3);
raster.tiledatalo = (raster.tiledatalo << 8) | tiledatalo;
raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi;
}
for(auto n : range(8)) raster.oam[n] = raster.soam[n];
for(uint sprite : range(8)) { //256-319
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
tick();
if(raster_enable() && sprite == 0) status.vaddr = (status.vaddr & 0x7be0) | (status.taddr & 0x041f); //257
tick();
uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
uint tileaddr = (sprite_height() == 8)
? status.sprite_addr + raster.oam[sprite].tile * 16
: ((raster.oam[sprite].tile & ~1) * 16) + ((raster.oam[sprite].tile & 1) * 0x1000);
tick();
tick();
uint spritey = (status.ly - raster.oam[sprite].y) & (sprite_height() - 1);
if(raster.oam[sprite].attr & 0x80) spritey ^= (sprite_height() - 1);
tileaddr += spritey + (spritey & 8);
raster.oam[sprite].tiledatalo = chr_load(tileaddr + 0);
tick();
tick();
raster.oam[sprite].tiledatahi = chr_load(tileaddr + 8);
tick();
tick();
if(raster_enable() && sprite == 6 && status.ly == 261) status.vaddr = status.taddr; //304
}
for(uint tile : range(2)) { //320-335
uint nametable = chr_load(0x2000 | (status.vaddr & 0x0fff));
uint tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
tick();
tick();
uint attribute = chr_load(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
if(scrolly() & 16) attribute >>= 4;
if(scrollx() & 16) attribute >>= 2;
tick();
scrollx_increment();
tick();
uint tiledatalo = chr_load(tileaddr + 0);
tick();
tick();
uint tiledatahi = chr_load(tileaddr + 8);
tick();
tick();
raster.nametable = (raster.nametable << 8) | nametable;
raster.attribute = (raster.attribute << 2) | (attribute & 3);
raster.tiledatalo = (raster.tiledatalo << 8) | tiledatalo;
raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi;
}
//336-339
chr_load(0x2000 | (status.vaddr & 0x0fff));
tick();
bool skip = (raster_enable() && status.field == 1 && status.ly == 261);
tick();
chr_load(0x2000 | (status.vaddr & 0x0fff));
tick();
tick();
//340
if(skip == false) tick();
return scanline();
for(auto& n : buffer) n = 0;
}
}

View File

@ -10,99 +10,109 @@ struct PPU : Thread {
auto power() -> void;
auto reset() -> void;
//memory.cpp
auto readCIRAM(uint11 addr) -> uint8;
auto writeCIRAM(uint11 addr, uint8 data) -> void;
auto readCGRAM(uint5 addr) -> uint8;
auto writeCGRAM(uint5 addr, uint8 data) -> void;
auto readIO(uint16 addr) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void;
auto ciram_read(uint16 addr) -> uint8;
auto ciram_write(uint16 addr, uint8 data) -> void;
//render.cpp
auto enable() const -> bool;
auto nametableAddress() const -> uint;
auto scrollX() const -> uint;
auto scrollY() const -> uint;
auto cgram_read(uint16 addr) -> uint8;
auto cgram_write(uint16 addr, uint8 data) -> void;
auto loadCHR(uint16 addr) -> uint8;
auto raster_enable() const -> bool;
auto nametable_addr() const -> uint;
auto scrollx() const -> uint;
auto scrolly() const -> uint;
auto sprite_height() const -> uint;
auto scrollX_increment() -> void;
auto scrollY_increment() -> void;
auto chr_load(uint16 addr) -> uint8;
auto scrollx_increment() -> void;
auto scrolly_increment() -> void;
auto raster_pixel() -> void;
auto raster_sprite() -> void;
auto raster_scanline() -> void;
auto renderPixel() -> void;
auto renderSprite() -> void;
auto renderScanline() -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
struct Status {
struct Registers {
//internal
uint8 mdr;
bool field;
uint lx;
uint ly;
uint8 bus_data;
uint8 busData;
bool address_latch;
bool addressLatch;
uint15 vaddr;
uint15 taddr;
uint8 xaddr;
bool nmi_hold;
bool nmi_flag;
bool nmiHold;
bool nmiFlag;
//$2000
bool nmi_enable;
bool master_select;
bool sprite_size;
uint bg_addr;
uint sprite_addr;
uint vram_increment;
bool nmiEnable;
bool masterSelect;
uint spriteHeight;
uint bgAddress;
uint spriteAddress;
uint vramIncrement;
//$2001
uint3 emphasis;
bool sprite_enable;
bool bg_enable;
bool sprite_edge_enable;
bool bg_edge_enable;
bool spriteEnable;
bool bgEnable;
bool spriteEdgeEnable;
bool bgEdgeEnable;
bool grayscale;
//$2002
bool sprite_zero_hit;
bool sprite_overflow;
bool spriteZeroHit;
bool spriteOverflow;
//$2003
uint8 oam_addr;
} status;
uint8 oamAddress;
} r;
struct Raster {
struct OAM {
//serialization.cpp
auto serialize(serializer&) -> void;
uint8 id = 64;
uint8 y = 0xff;
uint8 tile = 0xff;
uint8 attr = 0xff;
uint8 x = 0xff;
uint8 tiledataLo = 0;
uint8 tiledataHi = 0;
};
struct Latches {
uint16 nametable;
uint16 attribute;
uint16 tiledatalo;
uint16 tiledatahi;
uint16 tiledataLo;
uint16 tiledataHi;
uint oam_iterator;
uint oam_counter;
uint oamIterator;
uint oamCounter;
struct OAM {
uint8 id;
uint8 y;
uint8 tile;
uint8 attr;
uint8 x;
OAM oam[8]; //primary
OAM soam[8]; //secondary
} l;
uint8 tiledatalo;
uint8 tiledatahi;
} oam[8], soam[8];
} raster;
uint32 buffer[256 * 262];
uint8 ciram[2048];
uint8 cgram[32];
uint8 oam[256];
uint32 buffer[256 * 262];
};
extern PPU ppu;

250
higan/fc/ppu/render.cpp Normal file
View File

@ -0,0 +1,250 @@
//vaddr = 0yyy VHYY YYYX XXXX
//yyy = fine Yscroll (y:d0-d2)
//V = V nametable (y:d8)
//H = H nametable (x:d8)
//YYYYY = Y nametable (y:d3-d7)
//XXXXX = X nametable (x:d3-d7)
auto PPU::enable() const -> bool {
return r.bgEnable || r.spriteEnable;
}
auto PPU::nametableAddress() const -> uint {
return 0x2000 + (r.vaddr & 0x0c00);
}
auto PPU::scrollX() const -> uint {
return ((r.vaddr & 0x1f) << 3) | r.xaddr;
}
auto PPU::scrollY() const -> uint {
return (((r.vaddr >> 5) & 0x1f) << 3) | ((r.vaddr >> 12) & 7);
}
//
auto PPU::loadCHR(uint16 addr) -> uint8 {
if(!enable()) return 0x00;
return cartridge.readCHR(addr);
}
//
auto PPU::scrollX_increment() -> void {
if(!enable()) return;
r.vaddr = (r.vaddr & 0x7fe0) | ((r.vaddr + 0x0001) & 0x001f);
if((r.vaddr & 0x001f) == 0x0000) {
r.vaddr ^= 0x0400;
}
}
auto PPU::scrollY_increment() -> void {
if(!enable()) return;
r.vaddr = (r.vaddr & 0x0fff) | ((r.vaddr + 0x1000) & 0x7000);
if((r.vaddr & 0x7000) == 0x0000) {
r.vaddr = (r.vaddr & 0x7c1f) | ((r.vaddr + 0x0020) & 0x03e0);
if((r.vaddr & 0x03e0) == 0x03c0) { //0x03c0 == 30 << 5; 30 * 8 = 240
r.vaddr &= 0x7c1f;
r.vaddr ^= 0x0800;
}
}
}
//
auto PPU::renderPixel() -> void {
uint32* output = buffer + r.ly * 256;
uint mask = 0x8000 >> (r.xaddr + (r.lx & 7));
uint palette = 0;
uint objectPalette = 0;
bool objectPriority = 0;
palette |= (l.tiledataLo & mask) ? 1 : 0;
palette |= (l.tiledataHi & mask) ? 2 : 0;
if(palette) {
uint attr = l.attribute;
if(mask >= 256) attr >>= 2;
palette |= (attr & 3) << 2;
}
if(!r.bgEnable) palette = 0;
if(!r.bgEdgeEnable && r.lx < 8) palette = 0;
if(r.spriteEnable)
for(int sprite = 7; sprite >= 0; sprite--) {
if(!r.spriteEdgeEnable && r.lx < 8) continue;
if(l.oam[sprite].id == 64) continue;
uint spriteX = r.lx - l.oam[sprite].x;
if(spriteX >= 8) continue;
if(l.oam[sprite].attr & 0x40) spriteX ^= 7;
uint mask = 0x80 >> spriteX;
uint spritePalette = 0;
spritePalette |= (l.oam[sprite].tiledataLo & mask) ? 1 : 0;
spritePalette |= (l.oam[sprite].tiledataHi & mask) ? 2 : 0;
if(spritePalette == 0) continue;
if(l.oam[sprite].id == 0 && palette && r.lx != 255) r.spriteZeroHit = 1;
spritePalette |= (l.oam[sprite].attr & 3) << 2;
objectPriority = l.oam[sprite].attr & 0x20;
objectPalette = 16 + spritePalette;
}
if(objectPalette) {
if(palette == 0 || objectPriority == 0) palette = objectPalette;
}
if(!enable()) palette = 0;
output[r.lx] = r.emphasis << 6 | readCGRAM(palette);
}
auto PPU::renderSprite() -> void {
if(!enable()) return;
uint n = l.oamIterator++;
int ly = (r.ly == 261 ? -1 : r.ly);
uint y = ly - oam[(n * 4) + 0];
if(y >= r.spriteHeight) return;
if(l.oamCounter == 8) {
r.spriteOverflow = 1;
return;
}
auto& o = l.soam[l.oamCounter++];
o.id = n;
o.y = oam[(n * 4) + 0];
o.tile = oam[(n * 4) + 1];
o.attr = oam[(n * 4) + 2];
o.x = oam[(n * 4) + 3];
}
auto PPU::renderScanline() -> void {
if(r.ly >= 240 && r.ly <= 260) {
for(auto x : range(341)) tick();
return scanline();
}
l.oamIterator = 0;
l.oamCounter = 0;
for(auto n : range(8)) l.soam[n] = {};
for(uint tile : range(32)) { // 0-255
uint nametable = loadCHR(0x2000 | (r.vaddr & 0x0fff));
uint tileaddr = r.bgAddress + (nametable << 4) + (scrollY() & 7);
renderPixel();
tick();
renderPixel();
tick();
uint attribute = loadCHR(0x23c0 | (r.vaddr & 0x0fc0) | ((scrollY() >> 5) << 3) | (scrollX() >> 5));
if(scrollY() & 16) attribute >>= 4;
if(scrollX() & 16) attribute >>= 2;
renderPixel();
tick();
scrollX_increment();
if(tile == 31) scrollY_increment();
renderPixel();
renderSprite();
tick();
uint tiledataLo = loadCHR(tileaddr + 0);
renderPixel();
tick();
renderPixel();
tick();
uint tiledataHi = loadCHR(tileaddr + 8);
renderPixel();
tick();
renderPixel();
renderSprite();
tick();
l.nametable = l.nametable << 8 | nametable;
l.attribute = l.attribute << 2 | (attribute & 3);
l.tiledataLo = l.tiledataLo << 8 | tiledataLo;
l.tiledataHi = l.tiledataHi << 8 | tiledataHi;
}
for(auto n : range(8)) l.oam[n] = l.soam[n];
for(uint sprite : range(8)) { //256-319
uint nametable = loadCHR(0x2000 | (r.vaddr & 0x0fff));
tick();
if(enable() && sprite == 0) r.vaddr = (r.vaddr & 0x7be0) | (r.taddr & 0x041f); //257
tick();
uint attribute = loadCHR(0x23c0 | (r.vaddr & 0x0fc0) | ((scrollY() >> 5) << 3) | (scrollX() >> 5));
uint tileaddr = (r.spriteHeight == 8)
? r.spriteAddress + l.oam[sprite].tile * 16
: ((l.oam[sprite].tile & ~1) * 16) + ((l.oam[sprite].tile & 1) * 0x1000);
tick();
tick();
uint spriteY = (r.ly - l.oam[sprite].y) & (r.spriteHeight - 1);
if(l.oam[sprite].attr & 0x80) spriteY ^= (r.spriteHeight - 1);
tileaddr += spriteY + (spriteY & 8);
l.oam[sprite].tiledataLo = loadCHR(tileaddr + 0);
tick();
tick();
l.oam[sprite].tiledataHi = loadCHR(tileaddr + 8);
tick();
tick();
if(enable() && sprite == 6 && r.ly == 261) r.vaddr = r.taddr; //304
}
for(uint tile : range(2)) { //320-335
uint nametable = loadCHR(0x2000 | (r.vaddr & 0x0fff));
uint tileaddr = r.bgAddress + (nametable << 4) + (scrollY() & 7);
tick();
tick();
uint attribute = loadCHR(0x23c0 | (r.vaddr & 0x0fc0) | ((scrollY() >> 5) << 3) | (scrollX() >> 5));
if(scrollY() & 16) attribute >>= 4;
if(scrollX() & 16) attribute >>= 2;
tick();
scrollX_increment();
tick();
uint tiledataLo = loadCHR(tileaddr + 0);
tick();
tick();
uint tiledataHi = loadCHR(tileaddr + 8);
tick();
tick();
l.nametable = l.nametable << 8 | nametable;
l.attribute = l.attribute << 2 | (attribute & 3);
l.tiledataLo = l.tiledataLo << 8 | tiledataLo;
l.tiledataHi = l.tiledataHi << 8 | tiledataHi;
}
//336-339
loadCHR(0x2000 | (r.vaddr & 0x0fff));
tick();
bool skip = enable() && r.field == 1 && r.ly == 261;
tick();
loadCHR(0x2000 | (r.vaddr & 0x0fff));
tick();
tick();
//340
if(!skip) tick();
return scanline();
}

View File

@ -1,74 +1,67 @@
auto PPU::serialize(serializer& s) -> void {
Thread::serialize(s);
s.integer(status.mdr);
s.integer(r.mdr);
s.integer(status.field);
s.integer(status.lx);
s.integer(status.ly);
s.integer(r.field);
s.integer(r.lx);
s.integer(r.ly);
s.integer(status.bus_data);
s.integer(r.busData);
s.integer(status.address_latch);
s.integer(r.addressLatch);
s.integer(status.vaddr);
s.integer(status.taddr);
s.integer(status.xaddr);
s.integer(r.vaddr);
s.integer(r.taddr);
s.integer(r.xaddr);
s.integer(status.nmi_hold);
s.integer(status.nmi_flag);
s.integer(r.nmiHold);
s.integer(r.nmiFlag);
s.integer(status.nmi_enable);
s.integer(status.master_select);
s.integer(status.sprite_size);
s.integer(status.bg_addr);
s.integer(status.sprite_addr);
s.integer(status.vram_increment);
s.integer(r.nmiEnable);
s.integer(r.masterSelect);
s.integer(r.spriteHeight);
s.integer(r.bgAddress);
s.integer(r.spriteAddress);
s.integer(r.vramIncrement);
s.integer(status.emphasis);
s.integer(status.sprite_enable);
s.integer(status.bg_enable);
s.integer(status.sprite_edge_enable);
s.integer(status.bg_edge_enable);
s.integer(status.grayscale);
s.integer(r.emphasis);
s.integer(r.spriteEnable);
s.integer(r.bgEnable);
s.integer(r.spriteEdgeEnable);
s.integer(r.bgEdgeEnable);
s.integer(r.grayscale);
s.integer(status.sprite_zero_hit);
s.integer(status.sprite_overflow);
s.integer(r.spriteZeroHit);
s.integer(r.spriteOverflow);
s.integer(status.oam_addr);
s.integer(r.oamAddress);
s.integer(raster.nametable);
s.integer(raster.attribute);
s.integer(raster.tiledatalo);
s.integer(raster.tiledatahi);
s.integer(l.nametable);
s.integer(l.attribute);
s.integer(l.tiledataLo);
s.integer(l.tiledataHi);
s.integer(raster.oam_iterator);
s.integer(raster.oam_counter);
s.integer(l.oamIterator);
s.integer(l.oamCounter);
for(auto n : range(8)) {
s.integer(raster.oam[n].id);
s.integer(raster.oam[n].y);
s.integer(raster.oam[n].tile);
s.integer(raster.oam[n].attr);
s.integer(raster.oam[n].x);
for(auto& o : l.oam) o.serialize(s);
for(auto& o : l.soam) o.serialize(s);
s.integer(raster.oam[n].tiledatalo);
s.integer(raster.oam[n].tiledatahi);
}
for(auto n : range(8)) {
s.integer(raster.soam[n].id);
s.integer(raster.soam[n].y);
s.integer(raster.soam[n].tile);
s.integer(raster.soam[n].attr);
s.integer(raster.soam[n].x);
s.integer(raster.soam[n].tiledatalo);
s.integer(raster.soam[n].tiledatahi);
}
s.array(buffer);
s.array(ciram);
s.array(cgram);
s.array(oam);
s.array(buffer);
}
auto PPU::OAM::serialize(serializer& s) -> void {
s.integer(id);
s.integer(y);
s.integer(tile);
s.integer(attr);
s.integer(x);
s.integer(tiledataLo);
s.integer(tiledataHi);
}

View File

@ -11,34 +11,30 @@ auto MSU1::Enter() -> void {
}
auto MSU1::main() -> void {
int16 left = 0, right = 0;
double left = 0.0;
double right = 0.0;
if(mmio.audioPlay) {
if(audioFile.open()) {
if(audioFile.end()) {
if(audioFile) {
if(audioFile->end()) {
if(!mmio.audioRepeat) {
mmio.audioPlay = false;
audioFile.seek(mmio.audioPlayOffset = 8);
audioFile->seek(mmio.audioPlayOffset = 8);
} else {
audioFile.seek(mmio.audioPlayOffset = mmio.audioLoopOffset);
audioFile->seek(mmio.audioPlayOffset = mmio.audioLoopOffset);
}
} else {
mmio.audioPlayOffset += 4;
left = audioFile.readl(2);
right = audioFile.readl(2);
left = (double)audioFile->readl(2) / 32768.0 * (double)mmio.audioVolume / 255.0;
right = (double)audioFile->readl(2) / 32768.0 * (double)mmio.audioVolume / 255.0;
if(dsp.mute()) left = 0, right = 0;
}
} else {
mmio.audioPlay = false;
}
}
int lchannel = (double)left * (double)mmio.audioVolume / 255.0;
int rchannel = (double)right * (double)mmio.audioVolume / 255.0;
left = sclamp<16>(lchannel);
right = sclamp<16>(rchannel);
if(dsp.mute()) left = 0, right = 0;
stream->sample(left / 32768.0, right / 32768.0);
stream->sample(left, right);
step(1);
synchronizeCPU();
}
@ -50,8 +46,8 @@ auto MSU1::load() -> void {
}
auto MSU1::unload() -> void {
if(dataFile.open()) dataFile.close();
if(audioFile.open()) audioFile.close();
dataFile.reset();
audioFile.reset();
}
auto MSU1::power() -> void {
@ -73,28 +69,28 @@ auto MSU1::reset() -> void {
mmio.audioResumeTrack = ~0; //no resume
mmio.audioResumeOffset = 0;
mmio.dataBusy = false;
mmio.audioBusy = false;
mmio.audioRepeat = false;
mmio.audioPlay = false;
mmio.audioError = false;
mmio.audioPlay = false;
mmio.audioRepeat = false;
mmio.audioBusy = false;
mmio.dataBusy = false;
dataOpen();
audioOpen();
}
auto MSU1::dataOpen() -> void {
if(dataFile.open()) dataFile.close();
dataFile.reset();
auto document = BML::unserialize(cartridge.information.manifest.cartridge);
string name = document["board/msu1/rom/name"].text();
if(!name) name = "msu1.rom";
if(dataFile.open({interface->path(ID::SuperFamicom), name}, file::mode::read)) {
dataFile.seek(mmio.dataReadOffset);
if(dataFile = interface->open(ID::SuperFamicom, name, File::Read)) {
dataFile->seek(mmio.dataReadOffset);
}
}
auto MSU1::audioOpen() -> void {
if(audioFile.open()) audioFile.close();
audioFile.reset();
auto document = BML::unserialize(cartridge.information.manifest.cartridge);
string name = {"track-", mmio.audioTrack, ".pcm"};
for(auto track : document.find("board/msu1/track")) {
@ -102,18 +98,18 @@ auto MSU1::audioOpen() -> void {
name = track["name"].text();
break;
}
if(audioFile.open({interface->path(ID::SuperFamicom), name}, file::mode::read)) {
if(audioFile.size() >= 8) {
uint32 header = audioFile.readm(4);
if(audioFile = interface->open(ID::SuperFamicom, name, File::Read)) {
if(audioFile->size() >= 8) {
uint32 header = audioFile->readm(4);
if(header == 0x4d535531) { //"MSU1"
mmio.audioLoopOffset = 8 + audioFile.readl(4) * 4;
if(mmio.audioLoopOffset > audioFile.size()) mmio.audioLoopOffset = 8;
mmio.audioLoopOffset = 8 + audioFile->readl(4) * 4;
if(mmio.audioLoopOffset > audioFile->size()) mmio.audioLoopOffset = 8;
mmio.audioError = false;
audioFile.seek(mmio.audioPlayOffset);
audioFile->seek(mmio.audioPlayOffset);
return;
}
}
audioFile.close();
audioFile.reset();
}
mmio.audioError = true;
}
@ -125,18 +121,19 @@ auto MSU1::read(uint24 addr, uint8) -> uint8 {
switch(addr) {
case 0x2000:
return (
mmio.dataBusy << 7
| mmio.audioBusy << 6
| mmio.audioRepeat << 5
| mmio.audioPlay << 4
Revision << 0
| mmio.audioError << 3
| Revision << 0
| mmio.audioPlay << 4
| mmio.audioRepeat << 5
| mmio.audioBusy << 6
| mmio.dataBusy << 7
);
case 0x2001:
if(mmio.dataBusy) return 0x00;
if(dataFile.end()) return 0x00;
if(!dataFile) return 0x00;
if(dataFile->end()) return 0x00;
mmio.dataReadOffset++;
return dataFile.read();
return dataFile->read();
case 0x2002: return 'S';
case 0x2003: return '-';
case 0x2004: return 'M';
@ -156,7 +153,7 @@ auto MSU1::write(uint24 addr, uint8 data) -> void {
case 0x2002: mmio.dataSeekOffset.byte(2) = data; break;
case 0x2003: mmio.dataSeekOffset.byte(3) = data;
mmio.dataReadOffset = mmio.dataSeekOffset;
dataOpen();
if(dataFile) dataFile->seek(mmio.dataReadOffset);
break;
case 0x2004: mmio.audioTrack.byte(0) = data; break;
case 0x2005: mmio.audioTrack.byte(1) = data;
@ -174,9 +171,9 @@ auto MSU1::write(uint24 addr, uint8 data) -> void {
case 0x2007:
if(mmio.audioBusy) break;
if(mmio.audioError) break;
bool audioResume = data.bit(2);
mmio.audioRepeat = data.bit(1);
mmio.audioPlay = data.bit(0);
mmio.audioRepeat = data.bit(1);
bool audioResume = data.bit(2);
if(!mmio.audioPlay && audioResume) {
mmio.audioResumeTrack = mmio.audioTrack;
mmio.audioResumeOffset = mmio.audioPlayOffset;

View File

@ -18,16 +18,16 @@ struct MSU1 : Cothread {
auto serialize(serializer&) -> void;
private:
file dataFile;
file audioFile;
vfs::shared::file dataFile;
vfs::shared::file audioFile;
enum Flag : uint {
DataBusy = 0x80,
AudioBusy = 0x40,
AudioRepeating = 0x20,
AudioPlaying = 0x10,
Revision = 0x02, //max: 0x07
AudioError = 0x08,
Revision = 0x02,
AudioPlaying = 0x10,
AudioRepeating = 0x20,
AudioBusy = 0x40,
DataBusy = 0x80,
};
struct MMIO {
@ -43,11 +43,11 @@ private:
uint32 audioResumeTrack;
uint32 audioResumeOffset;
bool dataBusy;
bool audioBusy;
bool audioRepeat;
bool audioPlay;
bool audioError;
bool audioPlay;
bool audioRepeat;
bool audioBusy;
bool dataBusy;
} mmio;
};

View File

@ -13,11 +13,11 @@ auto MSU1::serialize(serializer& s) -> void {
s.integer(mmio.audioResumeTrack);
s.integer(mmio.audioResumeOffset);
s.integer(mmio.dataBusy);
s.integer(mmio.audioBusy);
s.integer(mmio.audioRepeat);
s.integer(mmio.audioPlay);
s.integer(mmio.audioError);
s.integer(mmio.audioPlay);
s.integer(mmio.audioRepeat);
s.integer(mmio.audioBusy);
s.integer(mmio.dataBusy);
dataOpen();
audioOpen();

View File

@ -83,7 +83,7 @@ else ifeq ($(platform),macosx)
mkdir -p ~/Emulation/System/
cp -R out/$(name).app /Applications/$(name).app
cp data/cheats.bml ~/Library/Application\ Support/$(name)/
cp -R profile/* ~/Library/Application\ Support/$(name)/
cp -R systems/* ~/Library/Application\ Support/$(name)/
else ifneq ($(filter $(platform),linux bsd),)
mkdir -p $(prefix)/bin/
mkdir -p $(prefix)/share/icons/
@ -92,7 +92,7 @@ else ifneq ($(filter $(platform),linux bsd),)
cp data/$(name).desktop $(prefix)/share/applications/$(name).desktop
cp data/$(name).png $(prefix)/share/icons/$(name).png
cp data/cheats.bml $(prefix)/share/$(name)/cheats.bml
cp -R profile/* $(prefix)/share/$(name)/
cp -R systems/* $(prefix)/share/$(name)/
endif
uninstall:

View File

@ -146,8 +146,8 @@ InputManager::InputManager() {
inputManager = this;
for(auto& emulator : program->emulators) {
emulators.append(InputEmulator());
auto& inputEmulator = emulators.right();
auto& inputEmulator = emulators(emulators.size());
inputEmulator.interface = emulator;
inputEmulator.name = emulator->information.name;
for(auto& port : emulator->ports) {
@ -157,16 +157,13 @@ InputManager::InputManager() {
auto& inputDevice = inputPort.devices(device.id);
inputDevice.name = device.name;
for(auto& input : device.inputs) {
auto inputMapping = new InputMapping;
inputDevice.mappings.append(inputMapping);
auto& inputMapping = inputDevice.mappings(inputDevice.mappings.size());
inputMapping.name = input.name;
inputMapping.type = input.type;
inputMapping->name = input.name;
inputMapping->link = &input;
input.userData = (uintptr)inputMapping;
inputMapping->path = string{inputEmulator.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping->name}.replace(" ", "");
inputMapping->assignment = settings(inputMapping->path).text();
inputMapping->bind();
inputMapping.path = string{inputEmulator.name, "/", inputPort.name, "/", inputDevice.name, "/", inputMapping.name}.replace(" ", "");
inputMapping.assignment = settings(inputMapping.path).text();
inputMapping.bind();
}
}
}
@ -175,12 +172,24 @@ InputManager::InputManager() {
appendHotkeys();
}
//Emulator::Interface::inputPoll() needs to call into InputManager::InputEmulator
//this function is calling during Program::loadMedium() to link the two together
auto InputManager::bind(Emulator::Interface* interface) -> void {
this->emulator = nullptr;
for(auto& emulator : emulators) {
if(emulator.interface == interface) {
this->emulator = &emulator;
}
}
assert(this->emulator != nullptr);
}
auto InputManager::bind() -> void {
for(auto& emulator : emulators) {
for(auto& port : emulator.ports) {
for(auto& device : port.devices) {
for(auto& mapping : device.mappings) {
mapping->bind();
mapping.bind();
}
}
}
@ -194,13 +203,13 @@ auto InputManager::bind() -> void {
auto InputManager::poll() -> void {
auto devices = input->poll();
bool changed = devices.size() != this->devices.size();
if(changed == false) {
if(!changed) {
for(auto n : range(devices)) {
changed = devices[n] != this->devices[n];
if(changed) break;
}
}
if(changed == true) {
if(changed) {
this->devices = devices;
bind();
}

View File

@ -5,17 +5,17 @@ struct InputMapping {
auto rumble(bool enable) -> void;
auto unbind() -> void;
auto isDigital() const -> bool { return !link || link->type == 0; }
auto isAnalog() const -> bool { return link && link->type == 1; }
auto isRumble() const -> bool { return link && link->type == 2; }
auto isDigital() const -> bool { return type == 0; }
auto isAnalog() const -> bool { return type == 1; }
auto isRumble() const -> bool { return type == 2; }
auto assignmentName() -> string;
auto deviceName() -> string;
string path; //configuration file key path
string name; //input name (human readable)
uint type = 0;
string assignment = "None";
Emulator::Interface::Device::Input* link = nullptr;
shared_pointer<HID::Device> device;
uint group = 0;
uint input = 0;
@ -31,7 +31,7 @@ struct InputHotkey : InputMapping {
struct InputDevice {
string name;
vector<InputMapping*> mappings; //pointers used so that addresses do not change when arrays are resized
vector<InputMapping> mappings;
};
struct InputPort {
@ -40,12 +40,14 @@ struct InputPort {
};
struct InputEmulator {
Emulator::Interface* interface = nullptr;
string name;
vector<InputPort> ports;
};
struct InputManager {
InputManager();
auto bind(Emulator::Interface*) -> void;
auto bind() -> void;
auto poll() -> void;
auto onChange(shared_pointer<HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void;
@ -60,6 +62,8 @@ struct InputManager {
vector<shared_pointer<HID::Device>> devices;
vector<InputEmulator> emulators;
vector<InputHotkey*> hotkeys;
InputEmulator* emulator = nullptr; //points to InputEmulator that represents the currently active emulator
};
extern unique_pointer<InputManager> inputManager;

View File

@ -131,6 +131,12 @@ Presentation::Presentation() {
statusBar.setFont(Font().setBold());
statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean());
viewport.setDroppable().onDrop([&](auto locations) {
if(!directory::exists(locations(0))) return;
program->mediumQueue.append(locations(0));
program->loadMedium();
});
onClose([&] { program->quit(); });
setTitle({"higan v", Emulator::Version});

View File

@ -3,28 +3,39 @@ auto Program::path(uint id) -> string {
}
auto Program::open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file {
if(auto result = vfs::fs::file::open({path(id), name}, mode)) return result;
if(name == "manifest.bml") {
if(auto manifest = execute("icarus", "--manifest", path(id))) {
return vfs::memory::file::open(manifest.output.data<uint8_t>(), manifest.output.size());
if(name == "manifest.bml" && !path(id).endsWith(".sys/")) {
if(!file::exists({path(id), name}) || settings["Library/IgnoreManifests"].boolean()) {
if(auto manifest = execute("icarus", "--manifest", path(id))) {
return vfs::memory::file::open(manifest.output.data<uint8_t>(), manifest.output.size());
}
}
}
if(auto result = vfs::fs::file::open({path(id), name}, mode)) return result;
if(required) {
MessageDialog()
.setTitle({"Error"})
.setText({"Error: missing required file:\n\n", path(id), name})
.error();
}
return {};
}
auto Program::load(uint id, string name, string type) -> maybe<uint> {
string location = BrowserDialog()
.setTitle({"Load ", name})
.setPath({settings["Library/Location"].text(), name})
.setFilters({string{name, "|*.", type}, "All|*.*"})
.openFolder();
if(!directory::exists(location)) return nothing;
string location;
if(mediumQueue) {
location = mediumQueue.takeLeft().transform("\\", "/");
if(!location.endsWith("/")) location.append("/");
} else {
location = BrowserDialog()
.setTitle({"Load ", name})
.setPath({settings["Library/Location"].text(), name})
.setFilters({string{name, "|*.", type}, "All|*.*"})
.openFolder();
}
if(!directory::exists(location)) return mediumQueue.reset(), nothing;
uint pathID = mediumPaths.size();
mediumPaths.append(location);
@ -81,18 +92,16 @@ auto Program::audioSample(const double* samples, uint channels) -> void {
auto Program::inputPoll(uint port, uint device, uint input) -> int16 {
if(presentation->focused() || settings["Input/FocusLoss/AllowInput"].boolean()) {
auto userData = emulator->ports[port].devices[device].inputs[input].userData;
auto mapping = (InputMapping*)userData;
if(mapping) return mapping->poll();
auto& mapping = inputManager->emulator->ports[port].devices[device].mappings[input];
return mapping.poll();
}
return 0;
}
auto Program::inputRumble(uint port, uint device, uint input, bool enable) -> void {
if(presentation->focused() || settings["Input/FocusLoss/AllowInput"].boolean() || !enable) {
auto userData = emulator->ports[port].devices[device].inputs[input].userData;
auto mapping = (InputMapping*)userData;
if(mapping) return mapping->rumble(enable);
auto& mapping = inputManager->emulator->ports[port].devices[device].mappings[input];
return mapping.rumble(enable);
}
}

View File

@ -1,10 +1,26 @@
auto Program::loadMedium() -> void {
if(!mediumQueue) return;
string location = mediumQueue.left();
string type = suffixname(location).trimLeft(".", 1L);
for(auto& emulator : emulators) {
for(auto& medium : emulator->media) {
if(medium.type != type) continue;
return loadMedium(*emulator, medium);
}
}
mediumQueue.reset();
}
auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interface::Medium& medium) -> void {
unloadMedium();
mediumPaths.append(locate({medium.name, ".sys/"}));
Emulator::audio.reset(2, audio->get(Audio::Frequency).get<uint>(44100));
emulator = &interface;
inputManager->bind(emulator = &interface);
connectDevices();
if(!emulator->load(medium.id)) {
emulator = nullptr;

View File

@ -56,8 +56,11 @@ Program::Program(lstring args) {
for(auto& argument : args) {
if(argument == "--fullscreen") {
presentation->toggleFullScreen();
} else if(directory::exists(argument)) {
mediumQueue.append(argument);
}
}
loadMedium();
}
auto Program::main() -> void {

View File

@ -16,6 +16,7 @@ struct Program : Emulator::Interface::Bind {
auto notify(string text) -> void override;
//medium.cpp
auto loadMedium() -> void;
auto loadMedium(Emulator::Interface& interface, const Emulator::Interface::Medium& medium) -> void;
auto unloadMedium() -> void;
@ -39,7 +40,8 @@ struct Program : Emulator::Interface::Bind {
vector<Emulator::Interface*> emulators;
vector<string> mediumPaths;
vector<string> mediumQueue; //for command-line and drag-and-drop loading
vector<string> mediumPaths; //for keeping track of loaded folder locations
string statusText;
string statusMessage;

View File

@ -24,13 +24,13 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) {
assignMouse3.setVisible(false).onActivate([&] { assignMouseInput(2); });
resetButton.setText("Reset").onActivate([&] {
if(MessageDialog("Are you sure you want to erase all mappings for this device?").setParent(*settingsManager).question() == "Yes") {
for(auto& mapping : activeDevice().mappings) mapping->unbind();
for(auto& mapping : activeDevice().mappings) mapping.unbind();
refreshMappings();
}
});
eraseButton.setText("Erase").onActivate([&] {
if(auto mapping = mappingList.selected()) {
activeDevice().mappings[mapping.offset()]->unbind();
activeDevice().mappings[mapping.offset()].unbind();
refreshMappings();
}
});
@ -45,13 +45,13 @@ auto InputSettings::updateControls() -> void {
assignMouse3.setVisible(false);
if(auto mapping = mappingList.selected()) {
auto input = activeDevice().mappings[mapping.offset()];
auto& input = activeDevice().mappings[mapping.offset()];
if(input->isDigital()) {
if(input.isDigital()) {
assignMouse1.setVisible().setText("Mouse Left");
assignMouse2.setVisible().setText("Mouse Middle");
assignMouse3.setVisible().setText("Mouse Right");
} else if(input->isAnalog()) {
} else if(input.isAnalog()) {
assignMouse1.setVisible().setText("Mouse X-axis");
assignMouse2.setVisible().setText("Mouse Y-axis");
}
@ -99,7 +99,7 @@ auto InputSettings::reloadMappings() -> void {
);
for(auto& mapping : activeDevice().mappings) {
mappingList.append(TableViewItem()
.append(TableViewCell().setText(mapping->name))
.append(TableViewCell().setText(mapping.name))
.append(TableViewCell())
.append(TableViewCell())
);
@ -110,8 +110,8 @@ auto InputSettings::reloadMappings() -> void {
auto InputSettings::refreshMappings() -> void {
uint position = 0;
for(auto& mapping : activeDevice().mappings) {
mappingList.item(position).cell(1).setText(mapping->assignmentName());
mappingList.item(position).cell(2).setText(mapping->deviceName());
mappingList.item(position).cell(1).setText(mapping.assignmentName());
mappingList.item(position).cell(2).setText(mapping.deviceName());
position++;
}
mappingList.resizeColumns();
@ -121,7 +121,7 @@ auto InputSettings::assignMapping() -> void {
inputManager->poll(); //clear any pending events first
if(auto mapping = mappingList.selected()) {
activeMapping = activeDevice().mappings[mapping->offset()];
activeMapping = &activeDevice().mappings[mapping.offset()];
settingsManager->layout.setEnabled(false);
settingsManager->statusBar.setText({"Press a key or button to map [", activeMapping->name, "] ..."});
}
@ -130,7 +130,7 @@ auto InputSettings::assignMapping() -> void {
auto InputSettings::assignMouseInput(uint id) -> void {
if(auto mouse = inputManager->findMouse()) {
if(auto mapping = mappingList.selected()) {
activeMapping = activeDevice().mappings[mapping->offset()];
activeMapping = &activeDevice().mappings[mapping.offset()];
if(activeMapping->isDigital()) {
return inputEvent(mouse, HID::Mouse::GroupID::Button, id, 0, 1, true);