mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-02-20 05:04:37 +01:00
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:
parent
3a9c7c6843
commit
a816998122
@ -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/";
|
||||
|
@ -30,7 +30,6 @@ struct Interface {
|
||||
struct Input {
|
||||
uint type; //0 = digital, 1 = analog (relative), 2 = rumble
|
||||
string name;
|
||||
uintptr userData;
|
||||
};
|
||||
vector<Input> inputs;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
121
higan/fc/ppu/memory.cpp
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
250
higan/fc/ppu/render.cpp
Normal 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();
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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});
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user