diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 865a9f37..7e71519f 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -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 { diff --git a/higan/gba/cartridge/eeprom.cpp b/higan/gba/cartridge/eeprom.cpp index 0c280781..9ec2de0a 100644 --- a/higan/gba/cartridge/eeprom.cpp +++ b/higan/gba/cartridge/eeprom.cpp @@ -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); -} diff --git a/higan/gba/cartridge/flash.cpp b/higan/gba/cartridge/flash.cpp index f07ed7e9..a8f7d4c6 100644 --- a/higan/gba/cartridge/flash.cpp +++ b/higan/gba/cartridge/flash.cpp @@ -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); -} diff --git a/higan/gba/cartridge/mrom.cpp b/higan/gba/cartridge/mrom.cpp index c5c3293a..70e2f9d4 100644 --- a/higan/gba/cartridge/mrom.cpp +++ b/higan/gba/cartridge/mrom.cpp @@ -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 { -} diff --git a/higan/gba/cartridge/serialization.cpp b/higan/gba/cartridge/serialization.cpp index a5be5a14..2d5e3a8c 100644 --- a/higan/gba/cartridge/serialization.cpp +++ b/higan/gba/cartridge/serialization.cpp @@ -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); +} diff --git a/higan/gba/cartridge/sram.cpp b/higan/gba/cartridge/sram.cpp index b714a2b6..501d5fb9 100644 --- a/higan/gba/cartridge/sram.cpp +++ b/higan/gba/cartridge/sram.cpp @@ -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); -} diff --git a/higan/gba/cpu/serialization.cpp b/higan/gba/cpu/serialization.cpp index 2b292bdc..e9c90917 100644 --- a/higan/gba/cpu/serialization.cpp +++ b/higan/gba/cpu/serialization.cpp @@ -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); } diff --git a/higan/gba/ppu/background.cpp b/higan/gba/ppu/background.cpp index b01c51da..22d8cfdc 100644 --- a/higan/gba/ppu/background.cpp +++ b/higan/gba/ppu/background.cpp @@ -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; } diff --git a/higan/gba/ppu/io.cpp b/higan/gba/ppu/io.cpp index ce7e9dca..e3173e9f 100644 --- a/higan/gba/ppu/io.cpp +++ b/higan/gba/ppu/io.cpp @@ -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; diff --git a/higan/gba/ppu/mosaic.cpp b/higan/gba/ppu/mosaic.cpp deleted file mode 100644 index e6a01302..00000000 --- a/higan/gba/ppu/mosaic.cpp +++ /dev/null @@ -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++; - } -} -*/ diff --git a/higan/gba/ppu/object.cpp b/higan/gba/ppu/object.cpp index e7fee8bb..5e8d674d 100644 --- a/higan/gba/ppu/object.cpp +++ b/higan/gba/ppu/object.cpp @@ -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; } diff --git a/higan/gba/ppu/ppu.cpp b/higan/gba/ppu/ppu.cpp index 9b6a8b43..e94345ba 100644 --- a/higan/gba/ppu/ppu.cpp +++ b/higan/gba/ppu/ppu.cpp @@ -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" @@ -49,7 +48,7 @@ auto PPU::main() -> void { io.vblank = io.vcounter >= 160 && io.vcounter <= 226; io.vcoincidence = io.vcounter == io.vcompare; - if(io.vcounter == 0) { + if(io.vcounter == 0) { frame(); bg2.io.lx = bg2.io.x; @@ -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); diff --git a/higan/gba/ppu/ppu.hpp b/higan/gba/ppu/ppu.hpp index 115bcaff..e2d54442 100644 --- a/higan/gba/ppu/ppu.hpp +++ b/higan/gba/ppu/ppu.hpp @@ -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 { diff --git a/higan/gba/ppu/screen.cpp b/higan/gba/ppu/screen.cpp index 217da669..3271fa38 100644 --- a/higan/gba/ppu/screen.cpp +++ b/higan/gba/ppu/screen.cpp @@ -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]}, }; diff --git a/higan/gba/ppu/serialization.cpp b/higan/gba/ppu/serialization.cpp index 955e6c72..3b4dec7e 100644 --- a/higan/gba/ppu/serialization.cpp +++ b/higan/gba/ppu/serialization.cpp @@ -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 { diff --git a/higan/gba/ppu/window.cpp b/higan/gba/ppu/window.cpp index b4a00071..1760cde1 100644 --- a/higan/gba/ppu/window.cpp +++ b/higan/gba/ppu/window.cpp @@ -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; } diff --git a/higan/sfc/cpu/cpu.cpp b/higan/sfc/cpu/cpu.cpp index 0aef901b..60bdd169 100644 --- a/higan/sfc/cpu/cpu.cpp +++ b/higan/sfc/cpu/cpu.cpp @@ -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; } } diff --git a/higan/sfc/cpu/cpu.hpp b/higan/sfc/cpu/cpu.hpp index f252104c..49859488 100644 --- a/higan/sfc/cpu/cpu.hpp +++ b/higan/sfc/cpu/cpu.hpp @@ -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 { diff --git a/higan/sfc/cpu/joypad.cpp b/higan/sfc/cpu/joypad.cpp deleted file mode 100644 index cd14c400..00000000 --- a/higan/sfc/cpu/joypad.cpp +++ /dev/null @@ -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++; - } -} diff --git a/higan/sfc/cpu/serialization.cpp b/higan/sfc/cpu/serialization.cpp index 6439ca60..27e84ea4 100644 --- a/higan/sfc/cpu/serialization.cpp +++ b/higan/sfc/cpu/serialization.cpp @@ -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); diff --git a/higan/sfc/cpu/timing.cpp b/higan/sfc/cpu/timing.cpp index 72fe6142..6449d8a2 100644 --- a/higan/sfc/cpu/timing.cpp +++ b/higan/sfc/cpu/timing.cpp @@ -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. // diff --git a/nall/platform.hpp b/nall/platform.hpp index 9a02175b..7c41d1c1 100644 --- a/nall/platform.hpp +++ b/nall/platform.hpp @@ -60,6 +60,7 @@ namespace Math { #if defined(PLATFORM_WINDOWS) #undef IN + #undef OUT #undef interface #define dllexport __declspec(dllexport) #define MSG_NOSIGNAL 0