mirror of https://github.com/bsnes-emu/bsnes.git
Update to v102r20 release.
byuu says: Changelog: - nall: `#undef OUT` on Windows platform - GBA: add missing CPU prefetch state to serialization (this was breaking serialization in games using ROM prefetch) - GBA: reset all PPU data in the power() function (some things were missing before, causing issues on reset) - GBA: restored horizontal mosaic emulation to the new pixel-based renderer - GBA: fixed tilemap background horizontal flipping (Legend of Spyro - warning screen) - GBA: fixed d8 bits of scroll registers (ATV - Thunder Ridge Racers - menu screen) - SFC: DRAM refresh ticks the ALU MUL/DIV registers five steps forward [reported by kevtris] - SFC: merged dmaCounter and autoJoypadCounter into new shared clockCounter - left stub for old dmaCounter so that I can do some traces to ensure the new code's 100% identical GBA save states would have been broken since whenever I emulated ROM prefetch. I guess not many people are using the GBA core ...
This commit is contained in:
parent
2461293ff0
commit
3bcf3c24c9
|
@ -12,13 +12,13 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "102.19";
|
||||
static const string Version = "102.20";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const string SerializerVersion = "102.19";
|
||||
static const string SerializerVersion = "102.20";
|
||||
|
||||
namespace Constants {
|
||||
namespace Colorburst {
|
||||
|
|
|
@ -81,14 +81,3 @@ auto Cartridge::EEPROM::power() -> void {
|
|||
offset = 0;
|
||||
address = 0;
|
||||
}
|
||||
|
||||
auto Cartridge::EEPROM::serialize(serializer& s) -> void {
|
||||
s.array(data, size);
|
||||
s.integer(size);
|
||||
s.integer(mask);
|
||||
s.integer(test);
|
||||
s.integer(bits);
|
||||
s.integer((uint&)mode);
|
||||
s.integer(offset);
|
||||
s.integer(address);
|
||||
}
|
||||
|
|
|
@ -85,16 +85,3 @@ auto Cartridge::FLASH::power() -> void {
|
|||
writeselect = false;
|
||||
bank = 0;
|
||||
}
|
||||
|
||||
auto Cartridge::FLASH::serialize(serializer& s) -> void {
|
||||
s.array(data, size);
|
||||
s.integer(size);
|
||||
s.integer(id);
|
||||
s.integer(unlockhi);
|
||||
s.integer(unlocklo);
|
||||
s.integer(idmode);
|
||||
s.integer(erasemode);
|
||||
s.integer(bankselect);
|
||||
s.integer(writeselect);
|
||||
s.integer(bank);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,3 @@ auto Cartridge::MROM::read(uint mode, uint32 addr) -> uint32 {
|
|||
|
||||
auto Cartridge::MROM::write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
}
|
||||
|
||||
auto Cartridge::MROM::serialize(serializer& s) -> void {
|
||||
}
|
||||
|
|
|
@ -1,6 +1,42 @@
|
|||
void Cartridge::serialize(serializer& s) {
|
||||
auto Cartridge::serialize(serializer& s) -> void {
|
||||
mrom.serialize(s);
|
||||
if(hasSRAM) sram.serialize(s);
|
||||
if(hasEEPROM) eeprom.serialize(s);
|
||||
if(hasFLASH) flash.serialize(s);
|
||||
}
|
||||
|
||||
auto Cartridge::MROM::serialize(serializer& s) -> void {
|
||||
s.integer(size);
|
||||
s.integer(mask);
|
||||
}
|
||||
|
||||
auto Cartridge::SRAM::serialize(serializer& s) -> void {
|
||||
s.array(data, size);
|
||||
s.integer(size);
|
||||
s.integer(mask);
|
||||
}
|
||||
|
||||
auto Cartridge::EEPROM::serialize(serializer& s) -> void {
|
||||
s.array(data, size);
|
||||
s.integer(size);
|
||||
s.integer(mask);
|
||||
s.integer(test);
|
||||
s.integer(bits);
|
||||
s.integer((uint&)mode);
|
||||
s.integer(offset);
|
||||
s.integer(address);
|
||||
s.integer(addressbits);
|
||||
}
|
||||
|
||||
auto Cartridge::FLASH::serialize(serializer& s) -> void {
|
||||
s.array(data, size);
|
||||
s.integer(size);
|
||||
s.integer(id);
|
||||
s.integer(unlockhi);
|
||||
s.integer(unlocklo);
|
||||
s.integer(idmode);
|
||||
s.integer(erasemode);
|
||||
s.integer(bankselect);
|
||||
s.integer(writeselect);
|
||||
s.integer(bank);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,3 @@ auto Cartridge::SRAM::read(uint mode, uint32 addr) -> uint32 {
|
|||
auto Cartridge::SRAM::write(uint mode, uint32 addr, uint32 word) -> void {
|
||||
data[addr & mask] = word;
|
||||
}
|
||||
|
||||
auto Cartridge::SRAM::serialize(serializer& s) -> void {
|
||||
s.array(data, size);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
s.integer(dma.control.timingmode);
|
||||
s.integer(dma.control.irq);
|
||||
s.integer(dma.control.enable);
|
||||
s.integer(dma.pending);
|
||||
s.integer(dma.run.target);
|
||||
s.integer(dma.run.source);
|
||||
s.integer(dma.run.length);
|
||||
|
@ -91,7 +92,14 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
s.integer((uint&)regs.mode);
|
||||
s.integer(regs.clock);
|
||||
|
||||
s.array(prefetch.slot);
|
||||
s.integer(prefetch.addr);
|
||||
s.integer(prefetch.load);
|
||||
s.integer(prefetch.wait);
|
||||
|
||||
s.integer(pending.dma.vblank);
|
||||
s.integer(pending.dma.hblank);
|
||||
s.integer(pending.dma.hdma);
|
||||
|
||||
s.integer(active.dma);
|
||||
}
|
||||
|
|
|
@ -1,136 +1,83 @@
|
|||
//I/O settings shared by all background layers
|
||||
uint3 PPU::Background::IO::mode;
|
||||
uint1 PPU::Background::IO::frame;
|
||||
uint5 PPU::Background::IO::mosaicWidth;
|
||||
uint5 PPU::Background::IO::mosaicHeight;
|
||||
|
||||
auto PPU::Background::scanline(uint y) -> void {
|
||||
mosaicOffset = 0;
|
||||
}
|
||||
|
||||
auto PPU::Background::run(uint x, uint y) -> void {
|
||||
output = {};
|
||||
if(ppu.blank() || !io.enable) return;
|
||||
if(ppu.blank() || !io.enable) {
|
||||
mosaic = {};
|
||||
return;
|
||||
}
|
||||
|
||||
switch(id) {
|
||||
case PPU::BG0:
|
||||
if(io.mode <= 1) return linear(x, y);
|
||||
if(io.mode <= 1) { linear(x, y); break; }
|
||||
break;
|
||||
|
||||
case PPU::BG1:
|
||||
if(io.mode <= 1) return linear(x, y);
|
||||
if(io.mode <= 1) { linear(x, y); break; }
|
||||
break;
|
||||
|
||||
case PPU::BG2:
|
||||
if(io.mode == 0) return linear(x, y);
|
||||
if(io.mode <= 2) return affine(x, y);
|
||||
if(io.mode <= 5) return bitmap(x, y);
|
||||
if(io.mode == 0) { linear(x, y); break; }
|
||||
if(io.mode <= 2) { affine(x, y); break; }
|
||||
if(io.mode <= 5) { bitmap(x, y); break; }
|
||||
break;
|
||||
|
||||
case PPU::BG3:
|
||||
if(io.mode == 0) return linear(x, y);
|
||||
if(io.mode == 2) return affine(x, y);
|
||||
if(io.mode == 0) { linear(x, y); break; }
|
||||
if(io.mode == 2) { affine(x, y); break; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
auto PPU::renderBackgroundLinear(Registers::Background& bg) -> void {
|
||||
if(regs.control.enable[bg.id] == false) return;
|
||||
auto& output = layer[bg.id];
|
||||
|
||||
if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) {
|
||||
bg.vmosaic = regs.vcounter;
|
||||
}
|
||||
|
||||
uint9 voffset = bg.vmosaic + bg.voffset;
|
||||
uint9 hoffset = bg.hoffset;
|
||||
|
||||
uint basemap = bg.control.screenbaseblock << 11;
|
||||
uint basechr = bg.control.characterbaseblock << 14;
|
||||
uint px = hoffset & 7, py = voffset & 7;
|
||||
|
||||
Tile tile;
|
||||
uint8 data[8];
|
||||
|
||||
for(auto x : range(240)) {
|
||||
if(x == 0 || px & 8) {
|
||||
px &= 7;
|
||||
|
||||
uint tx = hoffset / 8, ty = voffset / 8;
|
||||
uint offset = (ty & 31) * 32 + (tx & 31);
|
||||
if(bg.control.screensize & 1) if(tx & 32) offset += 32 * 32;
|
||||
if(bg.control.screensize & 2) if(ty & 32) offset += 32 * 32 * (1 + (bg.control.screensize & 1));
|
||||
offset = basemap + offset * 2;
|
||||
uint16 mapdata = readVRAM(Half, offset);
|
||||
|
||||
tile.character = mapdata >> 0;
|
||||
tile.hflip = mapdata >> 10;
|
||||
tile.vflip = mapdata >> 11;
|
||||
tile.palette = mapdata >> 12;
|
||||
|
||||
if(bg.control.colormode == 0) {
|
||||
offset = basechr + tile.character * 32 + (py ^ (tile.vflip ? 7 : 0)) * 4;
|
||||
uint32 word = readVRAM(Word, offset);
|
||||
for(auto n : range(8)) data[n] = (word >> (n * 4)) & 15;
|
||||
} else {
|
||||
offset = basechr + tile.character * 64 + (py ^ (tile.vflip ? 7 : 0)) * 8;
|
||||
uint32 wordlo = readVRAM(Word, offset + 0);
|
||||
uint32 wordhi = readVRAM(Word, offset + 4);
|
||||
for(auto n : range(4)) data[0 + n] = (wordlo >> (n * 8)) & 255;
|
||||
for(auto n : range(4)) data[4 + n] = (wordhi >> (n * 8)) & 255;
|
||||
}
|
||||
}
|
||||
|
||||
hoffset++;
|
||||
uint8 color = data[px++ ^ (tile.hflip ? 7 : 0)];
|
||||
|
||||
if(color) {
|
||||
if(bg.control.colormode == 0) output[x].write(true, bg.control.priority, pram[tile.palette * 16 + color]);
|
||||
if(bg.control.colormode == 1) output[x].write(true, bg.control.priority, pram[color]);
|
||||
}
|
||||
//horizontal mosaic
|
||||
if(!io.mosaic || ++mosaicOffset >= 1 + io.mosaicWidth) {
|
||||
mosaicOffset = 0;
|
||||
mosaic = output;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
auto PPU::Background::linear(uint x, uint y) -> void {
|
||||
if(x == 0) {
|
||||
if(!io.mosaic || (y % (1 + io.mosaicHeight)) == 0) {
|
||||
vmosaic = y;
|
||||
}
|
||||
|
||||
voffset = vmosaic + io.voffset;
|
||||
hoffset = io.hoffset;
|
||||
fx = io.hoffset;
|
||||
fy = vmosaic + io.voffset;
|
||||
}
|
||||
|
||||
uint px = hoffset & 7;
|
||||
uint py = voffset & 7;
|
||||
uint6 tx = fx >> 3;
|
||||
uint6 ty = fy >> 3;
|
||||
|
||||
uint tx = hoffset >> 3;
|
||||
uint ty = voffset >> 3;
|
||||
uint3 px = fx;
|
||||
uint3 py = fy;
|
||||
|
||||
uint offset = (ty & 31) * 32 + (tx & 31);
|
||||
if(io.screenSize.bit(0) && (tx & 32)) offset += 32 * 32;
|
||||
if(io.screenSize.bit(1) && (ty & 32)) offset += 32 * 32 * (1 + (io.screenSize.bit(0)));
|
||||
offset = (io.screenBase << 11) + offset * 2;
|
||||
uint offset = (ty & 31) << 5 | (tx & 31);
|
||||
if(io.screenSize.bit(0) && (tx & 32)) offset += 32 << 5;
|
||||
if(io.screenSize.bit(1) && (ty & 32)) offset += 32 << 5 + io.screenSize.bit(0);
|
||||
offset = (io.screenBase << 11) + (offset << 1);
|
||||
|
||||
uint16 tilemap = ppu.readVRAM(Half, offset);
|
||||
uint10 character = tilemap.bits( 0, 9);
|
||||
uint1 hflip = tilemap.bit (10);
|
||||
uint1 vflip = tilemap.bit (11);
|
||||
uint4 palette = tilemap.bits(12,15);
|
||||
if(tilemap.bit(10)) px ^= 7;
|
||||
if(tilemap.bit(11)) py ^= 7;
|
||||
|
||||
if(io.colorMode == 0) {
|
||||
offset = (io.characterBase << 14) + character * 32;
|
||||
offset += (py ^ (vflip ? 7 : 0)) * 4;
|
||||
offset += (px ^ (hflip ? 7 : 0)) / 2;
|
||||
offset = (io.characterBase << 14) + (character << 5) + (py << 2) + (px >> 1);
|
||||
if(uint4 color = ppu.readVRAM(Byte, offset) >> (px & 1 ? 4 : 0)) {
|
||||
output.enable = true;
|
||||
output.priority = io.priority;
|
||||
output.color = ppu.pram[palette * 16 + color];
|
||||
output.color = ppu.pram[palette << 4 | color];
|
||||
}
|
||||
} else {
|
||||
offset = (io.characterBase << 14) + character * 64;
|
||||
offset += (py ^ (vflip ? 7 : 0)) * 8;
|
||||
offset += (px ^ (hflip ? 7 : 0)) / 1;
|
||||
offset = (io.characterBase << 14) + (character << 6) + (py << 3) + (px);
|
||||
if(uint8 color = ppu.readVRAM(Byte, offset)) {
|
||||
output.enable = true;
|
||||
output.priority = io.priority;
|
||||
|
@ -138,53 +85,15 @@ auto PPU::Background::linear(uint x, uint y) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
hoffset++;
|
||||
fx++;
|
||||
}
|
||||
|
||||
/*
|
||||
auto PPU::renderBackgroundAffine(Registers::Background& bg) -> void {
|
||||
if(regs.control.enable[bg.id] == false) return;
|
||||
auto& output = layer[bg.id];
|
||||
|
||||
uint basemap = bg.control.screenbaseblock << 11;
|
||||
uint basechr = bg.control.characterbaseblock << 14;
|
||||
uint screensize = 16 << bg.control.screensize;
|
||||
uint screenwrap = (1 << (bg.control.affinewrap ? 7 + bg.control.screensize : 20)) - 1;
|
||||
|
||||
if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) {
|
||||
bg.hmosaic = bg.lx;
|
||||
bg.vmosaic = bg.ly;
|
||||
}
|
||||
|
||||
int28 fx = bg.hmosaic;
|
||||
int28 fy = bg.vmosaic;
|
||||
|
||||
for(auto x : range(240)) {
|
||||
uint cx = (fx >> 8) & screenwrap, tx = cx / 8, px = cx & 7;
|
||||
uint cy = (fy >> 8) & screenwrap, ty = cy / 8, py = cy & 7;
|
||||
|
||||
if(tx < screensize && ty < screensize) {
|
||||
uint8 character = vram[basemap + ty * screensize + tx];
|
||||
uint8 color = vram[basechr + (character * 64) + py * 8 + px];
|
||||
if(color) output[x].write(true, bg.control.priority, pram[color]);
|
||||
}
|
||||
|
||||
fx += bg.pa;
|
||||
fy += bg.pc;
|
||||
}
|
||||
|
||||
bg.lx += bg.pb;
|
||||
bg.ly += bg.pd;
|
||||
}
|
||||
*/
|
||||
|
||||
auto PPU::Background::affine(uint x, uint y) -> void {
|
||||
if(x == 0) {
|
||||
if(!io.mosaic || (y % (1 + io.mosaicHeight)) == 0) {
|
||||
hmosaic = io.lx;
|
||||
vmosaic = io.ly;
|
||||
}
|
||||
|
||||
fx = hmosaic;
|
||||
fy = vmosaic;
|
||||
}
|
||||
|
@ -192,12 +101,18 @@ auto PPU::Background::affine(uint x, uint y) -> void {
|
|||
uint screenSize = 16 << io.screenSize;
|
||||
uint screenWrap = (1 << (io.affineWrap ? 7 + io.screenSize : 20)) - 1;
|
||||
|
||||
uint cx = (fx >> 8) & screenWrap, tx = cx >> 3, px = cx & 7;
|
||||
uint cy = (fy >> 8) & screenWrap, ty = cy >> 3, py = cy & 7;
|
||||
uint cx = (fx >> 8) & screenWrap;
|
||||
uint cy = (fy >> 8) & screenWrap;
|
||||
|
||||
uint tx = cx >> 3;
|
||||
uint ty = cy >> 3;
|
||||
|
||||
uint3 px = cx;
|
||||
uint3 py = cy;
|
||||
|
||||
if(tx < screenSize && ty < screenSize) {
|
||||
uint8 character = ppu.vram[(io.screenBase << 11) + ty * screenSize + tx];
|
||||
if(uint8 color = ppu.vram[(io.characterBase << 14) + character * 64 + py * 8 + px]) {
|
||||
if(uint8 color = ppu.vram[(io.characterBase << 14) + (character << 6) + (py << 3) + px]) {
|
||||
output.enable = true;
|
||||
output.priority = io.priority;
|
||||
output.color = ppu.pram[color];
|
||||
|
@ -213,57 +128,12 @@ auto PPU::Background::affine(uint x, uint y) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
auto PPU::renderBackgroundBitmap(Registers::Background& bg) -> void {
|
||||
if(regs.control.enable[bg.id] == false) return;
|
||||
auto& output = layer[bg.id];
|
||||
|
||||
uint1 depth = regs.control.bgmode != 4; //0 = 8-bit (Mode 4), 1 = 15-bit (Mode 3, Mode 5)
|
||||
uint basemap = regs.control.bgmode == 3 ? 0 : 0xa000 * regs.control.frame;
|
||||
|
||||
uint width = regs.control.bgmode == 5 ? 160 : 240;
|
||||
uint height = regs.control.bgmode == 5 ? 128 : 160;
|
||||
uint mode = depth ? Half : Byte;
|
||||
|
||||
if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) {
|
||||
bg.hmosaic = bg.lx;
|
||||
bg.vmosaic = bg.ly;
|
||||
}
|
||||
|
||||
int28 fx = bg.hmosaic;
|
||||
int28 fy = bg.vmosaic;
|
||||
|
||||
for(auto x : range(240)) {
|
||||
uint px = fx >> 8;
|
||||
uint py = fy >> 8;
|
||||
|
||||
if(px < width && py < height) {
|
||||
uint offset = py * width + px;
|
||||
uint color = readVRAM(mode, basemap + (offset << depth));
|
||||
|
||||
if(depth || color) { //8bpp color 0 is transparent; 15bpp color is always opaque
|
||||
if(depth == 0) color = pram[color];
|
||||
if(depth == 1) color = color & 0x7fff;
|
||||
output[x].write(true, bg.control.priority, color);
|
||||
}
|
||||
}
|
||||
|
||||
fx += bg.pa;
|
||||
fy += bg.pc;
|
||||
}
|
||||
|
||||
bg.lx += bg.pb;
|
||||
bg.ly += bg.pd;
|
||||
}
|
||||
*/
|
||||
|
||||
auto PPU::Background::bitmap(uint x, uint y) -> void {
|
||||
if(x == 0) {
|
||||
if(!io.mosaic || (y % (1 + io.mosaicHeight)) == 0) {
|
||||
hmosaic = io.lx;
|
||||
vmosaic = io.ly;
|
||||
}
|
||||
|
||||
fx = hmosaic;
|
||||
fy = vmosaic;
|
||||
}
|
||||
|
@ -303,4 +173,11 @@ auto PPU::Background::power(uint id) -> void {
|
|||
this->id = id;
|
||||
|
||||
memory::fill(&io, sizeof(IO));
|
||||
output = {};
|
||||
mosaic = {};
|
||||
mosaicOffset = 0;
|
||||
hmosaic = 0;
|
||||
vmosaic = 0;
|
||||
fx = 0;
|
||||
fy = 0;
|
||||
}
|
||||
|
|
|
@ -202,35 +202,35 @@ auto PPU::writeIO(uint32 addr, uint8 data) -> void {
|
|||
|
||||
//BG0HOFS
|
||||
case 0x0400'0010: bg0.io.hoffset.bits(0,7) = data.bits(0,7); return;
|
||||
case 0x0400'0011: bg0.io.hoffset.bit (8) = data.bit (8); return;
|
||||
case 0x0400'0011: bg0.io.hoffset.bit (8) = data.bit (0); return;
|
||||
|
||||
//BG0VOFS
|
||||
case 0x0400'0012: bg0.io.voffset.bits(0,7) = data.bits(0,7); return;
|
||||
case 0x0400'0013: bg0.io.voffset.bit (8) = data.bit (8); return;
|
||||
case 0x0400'0013: bg0.io.voffset.bit (8) = data.bit (0); return;
|
||||
|
||||
//BG1HOFS
|
||||
case 0x0400'0014: bg1.io.hoffset.bits(0,7) = data.bits(0,7); return;
|
||||
case 0x0400'0015: bg1.io.hoffset.bit (8) = data.bit (8); return;
|
||||
case 0x0400'0015: bg1.io.hoffset.bit (8) = data.bit (0); return;
|
||||
|
||||
//BG1VOFS
|
||||
case 0x0400'0016: bg1.io.voffset.bits(0,7) = data.bits(0,7); return;
|
||||
case 0x0400'0017: bg1.io.voffset.bit (8) = data.bit (8); return;
|
||||
case 0x0400'0017: bg1.io.voffset.bit (8) = data.bit (0); return;
|
||||
|
||||
//BG2HOFS
|
||||
case 0x0400'0018: bg2.io.hoffset.bits(0,7) = data.bits(0,7); return;
|
||||
case 0x0400'0019: bg2.io.hoffset.bit (8) = data.bit (8); return;
|
||||
case 0x0400'0019: bg2.io.hoffset.bit (8) = data.bit (0); return;
|
||||
|
||||
//BG2VOFS
|
||||
case 0x0400'001a: bg2.io.voffset.bits(0,7) = data.bits(0,7); return;
|
||||
case 0x0400'001b: bg2.io.voffset.bit (8) = data.bit (8); return;
|
||||
case 0x0400'001b: bg2.io.voffset.bit (8) = data.bit (0); return;
|
||||
|
||||
//BG3HOFS
|
||||
case 0x0400'001c: bg3.io.hoffset.bits(0,7) = data.bits(0,7); return;
|
||||
case 0x0400'001d: bg3.io.hoffset.bit (8) = data.bit (8); return;
|
||||
case 0x0400'001d: bg3.io.hoffset.bit (8) = data.bit (0); return;
|
||||
|
||||
//BG3VOFS
|
||||
case 0x0400'001e: bg3.io.voffset.bits(0,7) = data.bits(0,7); return;
|
||||
case 0x0400'001f: bg3.io.voffset.bit (8) = data.bit (8); return;
|
||||
case 0x0400'001f: bg3.io.voffset.bit (8) = data.bit (0); return;
|
||||
|
||||
//BG2PA
|
||||
case 0x0400'0020: bg2.io.pa.byte(0) = data; return;
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
auto PPU::renderMosaicBackground(uint id) -> void {
|
||||
if(regs.mosaic.bghsize == 0) return;
|
||||
uint width = 1 + regs.mosaic.bghsize;
|
||||
auto& buffer = layer[id];
|
||||
|
||||
for(uint x = 0; x < 240;) {
|
||||
for(uint m = 1; m < width; m++) {
|
||||
if(x + m >= 240) break;
|
||||
buffer[x + m] = buffer[x];
|
||||
}
|
||||
x += width;
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::renderMosaicObject() -> void {
|
||||
if(regs.mosaic.objhsize == 0) return;
|
||||
uint width = 1 + regs.mosaic.objhsize;
|
||||
auto& buffer = layer[OBJ];
|
||||
|
||||
PixelData mosaicPixel;
|
||||
mosaicPixel.mosaic = false;
|
||||
uint counter = 0;
|
||||
|
||||
for(auto x : range(240)) {
|
||||
if(counter == width || mosaicPixel.mosaic == false) {
|
||||
mosaicPixel = buffer[x];
|
||||
if(counter == width) counter = 0;
|
||||
} else {
|
||||
if(buffer[x].mosaic) buffer[x] = mosaicPixel;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -1,75 +1,5 @@
|
|||
/*
|
||||
//px,py = pixel coordinates within sprite [0,0 - width,height)
|
||||
//fx,fy = affine pixel coordinates
|
||||
//pa,pb,pc,pd = affine pixel adjustments
|
||||
//x,y = adjusted coordinates within sprite (linear = vflip/hflip, affine = rotation/zoom)
|
||||
auto PPU::renderObject(ObjectInfo& obj) -> void {
|
||||
uint8 py = regs.vcounter - obj.y;
|
||||
if(obj.affine == 0 && obj.affinesize == 1) return; //hidden
|
||||
if(py >= obj.height << obj.affinesize) return; //offscreen
|
||||
|
||||
auto& output = layer[OBJ];
|
||||
uint rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8;
|
||||
uint baseaddr = obj.character * 32;
|
||||
|
||||
if(obj.mosaic && regs.mosaic.objvsize) {
|
||||
int mosaicy = (regs.vcounter / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize);
|
||||
py = obj.y >= 160 || mosaicy - obj.y >= 0 ? mosaicy - obj.y : 0;
|
||||
}
|
||||
|
||||
int16 pa = objectparam[obj.affineparam].pa;
|
||||
int16 pb = objectparam[obj.affineparam].pb;
|
||||
int16 pc = objectparam[obj.affineparam].pc;
|
||||
int16 pd = objectparam[obj.affineparam].pd;
|
||||
|
||||
//center-of-sprite coordinates
|
||||
int16 centerx = obj.width / 2;
|
||||
int16 centery = obj.height / 2;
|
||||
|
||||
//origin coordinates (top-left of sprite)
|
||||
int28 originx = -(centerx << obj.affinesize);
|
||||
int28 originy = -(centery << obj.affinesize) + py;
|
||||
|
||||
//fractional pixel coordinates
|
||||
int28 fx = originx * pa + originy * pb;
|
||||
int28 fy = originx * pc + originy * pd;
|
||||
|
||||
for(uint px = 0; px < (obj.width << obj.affinesize); px++) {
|
||||
uint x, y;
|
||||
if(obj.affine == 0) {
|
||||
x = px;
|
||||
y = py;
|
||||
if(obj.hflip) x ^= obj.width - 1;
|
||||
if(obj.vflip) y ^= obj.height - 1;
|
||||
} else {
|
||||
x = (fx >> 8) + centerx;
|
||||
y = (fy >> 8) + centery;
|
||||
}
|
||||
|
||||
uint9 ox = obj.x + px;
|
||||
if(ox < 240 && x < obj.width && y < obj.height) {
|
||||
uint offset = (y / 8) * rowsize + (x / 8);
|
||||
offset = offset * 64 + (y & 7) * 8 + (x & 7);
|
||||
|
||||
uint8 color = readObjectVRAM(baseaddr + (offset >> !obj.colors));
|
||||
if(obj.colors == 0) color = (x & 1) ? color >> 4 : color & 15;
|
||||
if(color) {
|
||||
if(obj.mode & 2) {
|
||||
windowmask[Obj][ox] = true;
|
||||
} else if(output[ox].enable == false || obj.priority < output[ox].priority) {
|
||||
if(obj.colors == 0) color = obj.palette * 16 + color;
|
||||
output[ox].write(true, obj.priority, pram[256 + color], obj.mode == 1, obj.mosaic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fx += pa;
|
||||
fy += pc;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
auto PPU::Objects::scanline(uint y) -> void {
|
||||
mosaicOffset = 0;
|
||||
for(auto& pixel : buffer) pixel = {};
|
||||
if(ppu.blank() || !io.enable) return;
|
||||
|
||||
|
@ -142,11 +72,24 @@ auto PPU::Objects::scanline(uint y) -> void {
|
|||
|
||||
auto PPU::Objects::run(uint x, uint y) -> void {
|
||||
output = {};
|
||||
if(ppu.blank() || !io.enable) return;
|
||||
if(ppu.blank() || !io.enable) {
|
||||
mosaic = {};
|
||||
return;
|
||||
}
|
||||
|
||||
output = buffer[x];
|
||||
|
||||
//horizontal mosaic
|
||||
if(!output.mosaic || ++mosaicOffset >= 1 + io.mosaicWidth) {
|
||||
mosaicOffset = 0;
|
||||
mosaic = output;
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::Objects::power() -> void {
|
||||
memory::fill(&io, sizeof(IO));
|
||||
for(auto& pixel : buffer) pixel = {};
|
||||
output = {};
|
||||
mosaic = {};
|
||||
mosaicOffset = 0;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ PPU ppu;
|
|||
#include "background.cpp"
|
||||
#include "object.cpp"
|
||||
#include "window.cpp"
|
||||
#include "mosaic.cpp"
|
||||
#include "screen.cpp"
|
||||
#include "io.cpp"
|
||||
#include "memory.cpp"
|
||||
|
@ -121,14 +120,19 @@ auto PPU::power() -> void {
|
|||
|
||||
for(uint n = 0; n < 240 * 160; n++) output[n] = 0;
|
||||
|
||||
for(uint n = 0; n < 96 * 1024; n++) vram[n] = 0x00;
|
||||
for(uint n = 0; n < 1024; n += 2) writePRAM(n, Half, 0x0000);
|
||||
for(uint n = 0; n < 1024; n += 2) writeOAM(n, Half, 0x0000);
|
||||
|
||||
memory::fill(&io, sizeof(IO));
|
||||
for(auto& object : this->object) object = {};
|
||||
for(auto& param : this->objectParam) param = {};
|
||||
|
||||
bg0.power(BG0);
|
||||
bg1.power(BG1);
|
||||
bg2.power(BG2);
|
||||
bg3.power(BG3);
|
||||
objects.power();
|
||||
window0.power(IN0);
|
||||
window1.power(IN1);
|
||||
window2.power(IN2);
|
||||
|
|
|
@ -32,7 +32,9 @@ struct PPU : Thread, IO {
|
|||
uint16 pram[512];
|
||||
uint32* output;
|
||||
|
||||
//private:
|
||||
private:
|
||||
//note: I/O register order is {BG0-BG3, OBJ, SFX}
|
||||
//however; layer ordering is {OBJ, BG0-BG3, SFX}
|
||||
enum : uint { OBJ = 0, BG0 = 1, BG1 = 2, BG2 = 3, BG3 = 4, SFX = 5 };
|
||||
enum : uint { IN0 = 0, IN1 = 1, IN2 = 2, OUT = 3 };
|
||||
|
||||
|
@ -109,13 +111,12 @@ struct PPU : Thread, IO {
|
|||
} io;
|
||||
|
||||
Pixel output;
|
||||
Pixel mosaic;
|
||||
uint mosaicOffset;
|
||||
|
||||
uint hmosaic;
|
||||
uint vmosaic;
|
||||
|
||||
uint9 hoffset;
|
||||
uint9 voffset;
|
||||
|
||||
int28 fx;
|
||||
int28 fy;
|
||||
} bg0, bg1, bg2, bg3;
|
||||
|
@ -138,6 +139,8 @@ struct PPU : Thread, IO {
|
|||
|
||||
Pixel buffer[240];
|
||||
Pixel output;
|
||||
Pixel mosaic;
|
||||
uint mosaicOffset;
|
||||
} objects;
|
||||
|
||||
struct Window {
|
||||
|
|
|
@ -1,68 +1,3 @@
|
|||
/*
|
||||
auto PPU::renderScreen() -> void {
|
||||
uint32* line = output + regs.vcounter * 240;
|
||||
|
||||
if(bg0.io.mosaic) renderMosaicBackground(BG0);
|
||||
if(bg1.io.mosaic) renderMosaicBackground(BG1);
|
||||
if(bg2.io.mosaic) renderMosaicBackground(BG2);
|
||||
if(bg3.io.mosaic) renderMosaicBackground(BG3);
|
||||
renderMosaicObject();
|
||||
|
||||
for(auto x : range(240)) {
|
||||
Registers::WindowFlags flags;
|
||||
flags.enable[BG0] = true; //enable all layers if no windows are enabled
|
||||
flags.enable[BG1] = true;
|
||||
flags.enable[BG2] = true;
|
||||
flags.enable[BG3] = true;
|
||||
flags.enable[OBJ] = true;
|
||||
flags.enable[SFX] = true;
|
||||
|
||||
//determine active window
|
||||
if(regs.control.enablewindow[In0] || regs.control.enablewindow[In1] || regs.control.enablewindow[Obj]) {
|
||||
flags = regs.windowflags[Out];
|
||||
if(regs.control.enablewindow[Obj] && windowmask[Obj][x]) flags = regs.windowflags[Obj];
|
||||
if(regs.control.enablewindow[In1] && windowmask[In1][x]) flags = regs.windowflags[In1];
|
||||
if(regs.control.enablewindow[In0] && windowmask[In0][x]) flags = regs.windowflags[In0];
|
||||
}
|
||||
|
||||
//priority sorting: find topmost two pixels
|
||||
uint a = 5, b = 5;
|
||||
for(int p = 3; p >= 0; p--) {
|
||||
for(int l = 5; l >= 0; l--) {
|
||||
if(layer[l][x].enable && layer[l][x].priority == p && flags.enable[l]) {
|
||||
b = a;
|
||||
a = l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& above = layer[a];
|
||||
auto& below = layer[b];
|
||||
bool blendabove = regs.blend.control.above[a];
|
||||
bool blendbelow = regs.blend.control.below[b];
|
||||
uint color = above[x].color;
|
||||
auto eva = min(16u, (uint)regs.blend.eva);
|
||||
auto evb = min(16u, (uint)regs.blend.evb);
|
||||
auto evy = min(16u, (uint)regs.blend.evy);
|
||||
|
||||
//perform blending, if needed
|
||||
if(flags.enable[SFX] == false) {
|
||||
} else if(above[x].translucent && blendbelow) {
|
||||
color = blend(above[x].color, eva, below[x].color, evb);
|
||||
} else if(regs.blend.control.mode == 1 && blendabove && blendbelow) {
|
||||
color = blend(above[x].color, eva, below[x].color, evb);
|
||||
} else if(regs.blend.control.mode == 2 && blendabove) {
|
||||
color = blend(above[x].color, 16 - evy, 0x7fff, evy);
|
||||
} else if(regs.blend.control.mode == 3 && blendabove) {
|
||||
color = blend(above[x].color, 16 - evy, 0x0000, evy);
|
||||
}
|
||||
|
||||
//output pixel
|
||||
line[x] = color;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
auto PPU::Screen::run(uint x, uint y) -> uint15 {
|
||||
if(ppu.blank()) return 0x7fff;
|
||||
|
||||
|
@ -77,11 +12,11 @@ auto PPU::Screen::run(uint x, uint y) -> uint15 {
|
|||
|
||||
//priority sorting: find topmost two pixels
|
||||
Pixel layers[6] = {
|
||||
ppu.objects.output,
|
||||
ppu.bg0.output,
|
||||
ppu.bg1.output,
|
||||
ppu.bg2.output,
|
||||
ppu.bg3.output,
|
||||
ppu.objects.mosaic,
|
||||
ppu.bg0.mosaic,
|
||||
ppu.bg1.mosaic,
|
||||
ppu.bg2.mosaic,
|
||||
ppu.bg3.mosaic,
|
||||
{true, 3, ppu.pram[0]},
|
||||
};
|
||||
|
||||
|
|
|
@ -57,10 +57,9 @@ auto PPU::Background::serialize(serializer& s) -> void {
|
|||
s.integer(io.lx);
|
||||
s.integer(io.ly);
|
||||
|
||||
s.integer(mosaicOffset);
|
||||
s.integer(hmosaic);
|
||||
s.integer(vmosaic);
|
||||
s.integer(hoffset);
|
||||
s.integer(voffset);
|
||||
s.integer(fx);
|
||||
s.integer(fy);
|
||||
}
|
||||
|
@ -71,6 +70,8 @@ auto PPU::Objects::serialize(serializer& s) -> void {
|
|||
s.integer(io.mapping);
|
||||
s.integer(io.mosaicWidth);
|
||||
s.integer(io.mosaicHeight);
|
||||
|
||||
s.integer(mosaicOffset);
|
||||
}
|
||||
|
||||
auto PPU::Window::serialize(serializer& s) -> void {
|
||||
|
|
|
@ -1,21 +1,3 @@
|
|||
/*
|
||||
auto PPU::renderWindow(uint w) -> void {
|
||||
uint y = regs.vcounter;
|
||||
|
||||
uint y1 = regs.window[w].y1, y2 = regs.window[w].y2;
|
||||
uint x1 = regs.window[w].x1, x2 = regs.window[w].x2;
|
||||
|
||||
if(y2 < y1 || y2 > 160) y2 = 160;
|
||||
if(x2 < x1 || x2 > 240) x2 = 240;
|
||||
|
||||
if(y >= y1 && y < y2) {
|
||||
for(uint x = x1; x < x2; x++) {
|
||||
windowmask[w][x] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
auto PPU::Window::run(uint x, uint y) -> void {
|
||||
auto x1 = io.x1, x2 = io.x2;
|
||||
auto y1 = io.y1, y2 = io.y2;
|
||||
|
@ -30,4 +12,5 @@ auto PPU::Window::power(uint id) -> void {
|
|||
this->id = id;
|
||||
|
||||
memory::fill(&io, sizeof(IO));
|
||||
output = 0;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ CPU cpu;
|
|||
#include "io.cpp"
|
||||
#include "timing.cpp"
|
||||
#include "irq.cpp"
|
||||
#include "joypad.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto CPU::interruptPending() const -> bool { return status.interruptPending; }
|
||||
|
@ -178,6 +177,8 @@ auto CPU::power() -> void {
|
|||
pipe.data = 0;
|
||||
|
||||
//Timing
|
||||
clockCounter = 0;
|
||||
|
||||
status.clockCount = 0;
|
||||
status.lineClocks = lineclocks();
|
||||
|
||||
|
@ -217,7 +218,6 @@ auto CPU::power() -> void {
|
|||
status.autoJoypadActive = false;
|
||||
status.autoJoypadLatch = false;
|
||||
status.autoJoypadCounter = 0;
|
||||
status.autoJoypadClock = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,7 +54,8 @@ struct CPU : Processor::R65816, Thread, PPUcounter {
|
|||
auto writeDMA(uint24 addr, uint8 data) -> void;
|
||||
|
||||
//timing.cpp
|
||||
auto dmaCounter() const -> uint;
|
||||
inline auto dmaCounter() const -> uint;
|
||||
inline auto joypadCounter() const -> uint;
|
||||
|
||||
auto step(uint clocks) -> void;
|
||||
auto scanline() -> void;
|
||||
|
@ -73,7 +74,7 @@ struct CPU : Processor::R65816, Thread, PPUcounter {
|
|||
alwaysinline auto irqTest() -> bool;
|
||||
|
||||
//joypad.cpp
|
||||
auto stepAutoJoypadPoll() -> void;
|
||||
auto joypadEdge() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
@ -84,6 +85,7 @@ struct CPU : Processor::R65816, Thread, PPUcounter {
|
|||
|
||||
private:
|
||||
uint version = 2; //allowed: 1, 2
|
||||
uint clockCounter;
|
||||
|
||||
struct Status {
|
||||
bool interruptPending;
|
||||
|
@ -130,7 +132,6 @@ private:
|
|||
bool autoJoypadActive;
|
||||
bool autoJoypadLatch;
|
||||
uint autoJoypadCounter;
|
||||
uint autoJoypadClock;
|
||||
} status;
|
||||
|
||||
struct IO {
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
//called every 256 clocks; see CPU::addClocks()
|
||||
auto CPU::stepAutoJoypadPoll() -> void {
|
||||
if(vcounter() >= ppu.vdisp()) {
|
||||
//cache enable state at first iteration
|
||||
if(status.autoJoypadCounter == 0) status.autoJoypadLatch = io.autoJoypadPoll;
|
||||
status.autoJoypadActive = status.autoJoypadCounter <= 15;
|
||||
|
||||
if(status.autoJoypadActive && status.autoJoypadLatch) {
|
||||
if(status.autoJoypadCounter == 0) {
|
||||
SuperFamicom::peripherals.controllerPort1->latch(1);
|
||||
SuperFamicom::peripherals.controllerPort2->latch(1);
|
||||
SuperFamicom::peripherals.controllerPort1->latch(0);
|
||||
SuperFamicom::peripherals.controllerPort2->latch(0);
|
||||
}
|
||||
|
||||
uint2 port0 = SuperFamicom::peripherals.controllerPort1->data();
|
||||
uint2 port1 = SuperFamicom::peripherals.controllerPort2->data();
|
||||
|
||||
io.joy1 = io.joy1 << 1 | port0.bit(0);
|
||||
io.joy2 = io.joy2 << 1 | port1.bit(0);
|
||||
io.joy3 = io.joy3 << 1 | port0.bit(1);
|
||||
io.joy4 = io.joy4 << 1 | port1.bit(1);
|
||||
}
|
||||
|
||||
status.autoJoypadCounter++;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
s.array(wram);
|
||||
|
||||
s.integer(version);
|
||||
s.integer(clockCounter);
|
||||
|
||||
s.integer(status.interruptPending);
|
||||
|
||||
|
@ -48,7 +49,6 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
s.integer(status.autoJoypadActive);
|
||||
s.integer(status.autoJoypadLatch);
|
||||
s.integer(status.autoJoypadCounter);
|
||||
s.integer(status.autoJoypadClock);
|
||||
|
||||
s.array(io.port);
|
||||
|
||||
|
@ -86,25 +86,25 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
s.integer(alu.divctr);
|
||||
s.integer(alu.shift);
|
||||
|
||||
for(uint n : range(8)) {
|
||||
s.integer(channel[n].dmaEnabled);
|
||||
s.integer(channel[n].hdmaEnabled);
|
||||
s.integer(channel[n].direction);
|
||||
s.integer(channel[n].indirect);
|
||||
s.integer(channel[n].unused);
|
||||
s.integer(channel[n].reverseTransfer);
|
||||
s.integer(channel[n].fixedTransfer);
|
||||
s.integer(channel[n].transferMode);
|
||||
s.integer(channel[n].targetAddress);
|
||||
s.integer(channel[n].sourceAddress);
|
||||
s.integer(channel[n].sourceBank);
|
||||
s.integer(channel[n].transferSize);
|
||||
s.integer(channel[n].indirectBank);
|
||||
s.integer(channel[n].hdmaAddress);
|
||||
s.integer(channel[n].lineCounter);
|
||||
s.integer(channel[n].unknown);
|
||||
s.integer(channel[n].hdmaCompleted);
|
||||
s.integer(channel[n].hdmaDoTransfer);
|
||||
for(auto& channel : this->channel) {
|
||||
s.integer(channel.dmaEnabled);
|
||||
s.integer(channel.hdmaEnabled);
|
||||
s.integer(channel.direction);
|
||||
s.integer(channel.indirect);
|
||||
s.integer(channel.unused);
|
||||
s.integer(channel.reverseTransfer);
|
||||
s.integer(channel.fixedTransfer);
|
||||
s.integer(channel.transferMode);
|
||||
s.integer(channel.targetAddress);
|
||||
s.integer(channel.sourceAddress);
|
||||
s.integer(channel.sourceBank);
|
||||
s.integer(channel.transferSize);
|
||||
s.integer(channel.indirectBank);
|
||||
s.integer(channel.hdmaAddress);
|
||||
s.integer(channel.lineCounter);
|
||||
s.integer(channel.unknown);
|
||||
s.integer(channel.hdmaCompleted);
|
||||
s.integer(channel.hdmaDoTransfer);
|
||||
}
|
||||
|
||||
s.integer(pipe.valid);
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
auto CPU::dmaCounter() const -> uint {
|
||||
return (status.dmaCounter + hcounter()) & 7;
|
||||
return clockCounter & 7;
|
||||
//return (status.dmaCounter + hcounter()) & 7;
|
||||
}
|
||||
|
||||
auto CPU::joypadCounter() const -> uint {
|
||||
return clockCounter & 255;
|
||||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
|
@ -8,20 +13,19 @@ auto CPU::step(uint clocks) -> void {
|
|||
while(ticks--) {
|
||||
tick();
|
||||
if(hcounter() & 2) pollInterrupts();
|
||||
clockCounter += 2;
|
||||
if(joypadCounter() == 0) joypadEdge();
|
||||
}
|
||||
|
||||
Thread::step(clocks);
|
||||
for(auto peripheral : peripherals) synchronize(*peripheral);
|
||||
|
||||
status.autoJoypadClock += clocks;
|
||||
if(status.autoJoypadClock >= 256) {
|
||||
status.autoJoypadClock -= 256;
|
||||
stepAutoJoypadPoll();
|
||||
}
|
||||
|
||||
if(!status.dramRefreshed && hcounter() >= status.dramRefreshPosition) {
|
||||
status.dramRefreshed = true;
|
||||
step(40);
|
||||
for(auto _ : range(5)) {
|
||||
step(8);
|
||||
aluEdge();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
|
@ -139,6 +143,34 @@ auto CPU::dmaEdge() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
//called every 256 clocks; see CPU::step()
|
||||
auto CPU::joypadEdge() -> void {
|
||||
if(vcounter() >= ppu.vdisp()) {
|
||||
//cache enable state at first iteration
|
||||
if(status.autoJoypadCounter == 0) status.autoJoypadLatch = io.autoJoypadPoll;
|
||||
status.autoJoypadActive = status.autoJoypadCounter <= 15;
|
||||
|
||||
if(status.autoJoypadActive && status.autoJoypadLatch) {
|
||||
if(status.autoJoypadCounter == 0) {
|
||||
SuperFamicom::peripherals.controllerPort1->latch(1);
|
||||
SuperFamicom::peripherals.controllerPort2->latch(1);
|
||||
SuperFamicom::peripherals.controllerPort1->latch(0);
|
||||
SuperFamicom::peripherals.controllerPort2->latch(0);
|
||||
}
|
||||
|
||||
uint2 port0 = SuperFamicom::peripherals.controllerPort1->data();
|
||||
uint2 port1 = SuperFamicom::peripherals.controllerPort2->data();
|
||||
|
||||
io.joy1 = io.joy1 << 1 | port0.bit(0);
|
||||
io.joy2 = io.joy2 << 1 | port1.bit(0);
|
||||
io.joy3 = io.joy3 << 1 | port0.bit(1);
|
||||
io.joy4 = io.joy4 << 1 | port1.bit(1);
|
||||
}
|
||||
|
||||
status.autoJoypadCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
//used to test for NMI/IRQ, which can trigger on the edge of every opcode.
|
||||
//test one cycle early to simulate two-stage pipeline of x816 CPU.
|
||||
//
|
||||
|
|
|
@ -60,6 +60,7 @@ namespace Math {
|
|||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
#undef IN
|
||||
#undef OUT
|
||||
#undef interface
|
||||
#define dllexport __declspec(dllexport)
|
||||
#define MSG_NOSIGNAL 0
|
||||
|
|
Loading…
Reference in New Issue