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:
Tim Allen 2017-06-06 11:39:27 +10:00
parent 2461293ff0
commit 3bcf3c24c9
22 changed files with 208 additions and 477 deletions

View File

@ -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 {

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 {
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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++;
}
}
*/

View File

@ -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;
}

View File

@ -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);

View File

@ -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 {

View File

@ -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]},
};

View File

@ -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 {

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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++;
}
}

View File

@ -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);

View File

@ -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.
//

View File

@ -60,6 +60,7 @@ namespace Math {
#if defined(PLATFORM_WINDOWS)
#undef IN
#undef OUT
#undef interface
#define dllexport __declspec(dllexport)
#define MSG_NOSIGNAL 0