From 28885db586b46a4624f5d97d16a53ae810a1b8af Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 14 Apr 2012 17:26:45 +1000 Subject: [PATCH] Update to v087r23 release. byuu says: Changelog: - fixed cascading timers and readouts (speed hit from 320fps to 240fps; would be 155fps with r20 timers) (fixes Spyro) - OBJ mode 3 acts like OBJ mode 2 now (may not be correct, but nobody has info on it) - added background + object vertical+horizontal mosaic in all modes (linear+affine+bitmap) - object mosaic uses sprite (0,0) for start coordinates, not screen (0,0) (again, nobody seems to have info on it) - BIOS cannot be read by r(15)>=0x02000000; returns last BIOS read instead (I can't believe games rely on this to work ... fixes SMA Mario Bros.) Mosaic is what concerns me the most, I've no idea if I'm doing it correctly. But anything is probably better than nothing, so there's that. I don't really notice the effect in Metroid Fusion. So either it's broken, or it's really subtle. --- bsnes/base/base.hpp | 2 +- bsnes/gba/cpu/cpu.cpp | 2 +- bsnes/gba/cpu/mmio.cpp | 6 +- bsnes/gba/cpu/registers.cpp | 10 --- bsnes/gba/cpu/registers.hpp | 6 +- bsnes/gba/cpu/timer.cpp | 32 +++++--- bsnes/gba/memory/memory.cpp | 4 +- bsnes/gba/ppu/background.cpp | 24 ++++-- bsnes/gba/ppu/object.cpp | 101 +++++++++++--------------- bsnes/gba/ppu/ppu.hpp | 5 +- bsnes/gba/ppu/registers.hpp | 2 + bsnes/gba/ppu/screen.cpp | 18 +++++ bsnes/gba/ppu/state.hpp | 2 + bsnes/gba/system/bios.cpp | 29 ++++++++ bsnes/gba/system/system.cpp | 15 +--- bsnes/gba/system/system.hpp | 19 +++-- bsnes/target-ui/interface/gba/gba.cpp | 2 +- 17 files changed, 160 insertions(+), 119 deletions(-) create mode 100755 bsnes/gba/system/bios.cpp diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index dadc3f5a..06f92f31 100755 --- a/bsnes/base/base.hpp +++ b/bsnes/base/base.hpp @@ -1,7 +1,7 @@ #ifndef BASE_HPP #define BASE_HPP -static const char Version[] = "087.22"; +static const char Version[] = "087.23"; #include #include diff --git a/bsnes/gba/cpu/cpu.cpp b/bsnes/gba/cpu/cpu.cpp index a0dfa87f..47294b90 100755 --- a/bsnes/gba/cpu/cpu.cpp +++ b/bsnes/gba/cpu/cpu.cpp @@ -69,9 +69,9 @@ void CPU::power() { dma.control = 0; } for(auto &timer : regs.timer) { + timer.period = 0; timer.reload = 0; timer.control = 0; - timer.counter = 0; } regs.keypad.control = 0; regs.ime = 0; diff --git a/bsnes/gba/cpu/mmio.cpp b/bsnes/gba/cpu/mmio.cpp index 665f31a6..d4bcbc1b 100755 --- a/bsnes/gba/cpu/mmio.cpp +++ b/bsnes/gba/cpu/mmio.cpp @@ -26,7 +26,7 @@ uint8 CPU::read(uint32 addr) { case 0x0400010c: case 0x0400010d: { auto &timer = regs.timer[(addr >> 2) & 3]; unsigned shift = (addr & 1) * 8; - return timer.counter >> shift; + return timer.period >> shift; } //TIM0CNT_H @@ -178,9 +178,7 @@ void CPU::write(uint32 addr, uint8 byte) { bool enable = timer.control.enable; timer.control = byte; if(enable == 0 && timer.control.enable == 1) { - timer.counter = timer.period(); - } else if(timer.control.enable == 0) { - timer.counter = 0; + timer.period = timer.reload; } return; } diff --git a/bsnes/gba/cpu/registers.cpp b/bsnes/gba/cpu/registers.cpp index e9515da6..600588a5 100755 --- a/bsnes/gba/cpu/registers.cpp +++ b/bsnes/gba/cpu/registers.cpp @@ -23,11 +23,6 @@ uint16 CPU::Registers::DMAControl::operator=(uint16 source) { return operator uint16(); } -unsigned CPU::Registers::TimerControl::multiplier() const { - static unsigned multiplier[] = { 1, 64, 256, 1024 }; - return multiplier[frequency]; -} - CPU::Registers::TimerControl::operator uint8() const { return ( (frequency << 0) @@ -45,11 +40,6 @@ uint8 CPU::Registers::TimerControl::operator=(uint8 source) { return operator uint8(); } -//return number of clocks before counter overflow -signed CPU::Registers::Timer::period() const { - return (65536 - reload) * control.multiplier() + counter; -} - CPU::Registers::KeypadControl::operator uint16() const { return ( (a << 0) diff --git a/bsnes/gba/cpu/registers.hpp b/bsnes/gba/cpu/registers.hpp index e2f0ca15..bf439a82 100755 --- a/bsnes/gba/cpu/registers.hpp +++ b/bsnes/gba/cpu/registers.hpp @@ -34,19 +34,15 @@ struct Registers { uint1 irq; uint1 enable; - unsigned multiplier() const; operator uint8() const; uint8 operator=(uint8 source); TimerControl& operator=(const TimerControl&) = delete; }; struct Timer { + uint16 period; uint16 reload; TimerControl control; - - //internal - signed period() const; - signed counter; } timer[4]; struct KeypadControl { diff --git a/bsnes/gba/cpu/timer.cpp b/bsnes/gba/cpu/timer.cpp index e74543a7..dbb78ed6 100755 --- a/bsnes/gba/cpu/timer.cpp +++ b/bsnes/gba/cpu/timer.cpp @@ -1,23 +1,31 @@ void CPU::timer_step(unsigned clocks) { - for(unsigned n = 0; n < 4; n++) { - auto &timer = regs.timer[n]; - if(timer.control.enable == false || timer.control.cascade == true) continue; + for(unsigned c = 0; c < clocks; c++) { + for(unsigned n = 0; n < 4; n++) { + auto &timer = regs.timer[n]; + if(timer.control.enable == false || timer.control.cascade == true) continue; - timer.counter -= clocks; - while(timer.counter <= 0) { - timer_increment(n); - timer.counter = timer.period(); + static unsigned mask[] = { 0, 63, 255, 1023 }; + if((regs.clock & mask[timer.control.frequency]) == 0) { + timer_increment(n); + } } + + regs.clock++; } } void CPU::timer_increment(unsigned n) { - if(regs.timer[n].control.irq) regs.irq.flag.timer[n] = 1; + auto &timer = regs.timer[n]; + if(++timer.period == 0) { + timer.period = timer.reload; - if(apu.fifo[0].timer == n) apu.fifo[0].read(); - if(apu.fifo[1].timer == n) apu.fifo[1].read(); + if(timer.control.irq) regs.irq.flag.timer[n] = 1; - if(n < 3 && regs.timer[n + 1].control.enable && regs.timer[n + 1].control.cascade) { - timer_increment(n + 1); + if(apu.fifo[0].timer == n) apu.fifo[0].read(); + if(apu.fifo[1].timer == n) apu.fifo[1].read(); + + if(n < 3 && regs.timer[n + 1].control.enable && regs.timer[n + 1].control.cascade) { + timer_increment(n + 1); + } } } diff --git a/bsnes/gba/memory/memory.cpp b/bsnes/gba/memory/memory.cpp index 3ecec664..9e4a3c8a 100755 --- a/bsnes/gba/memory/memory.cpp +++ b/bsnes/gba/memory/memory.cpp @@ -96,8 +96,8 @@ uint32 Bus::read(uint32 addr, uint32 size) { if(addr & 0x08000000) return cartridge.read(addr, size); switch(addr & 0x07000000) { - case 0x00000000: return system.bios.read(addr & 0x3fff, size); - case 0x01000000: return system.bios.read(addr & 0x3fff, size); + case 0x00000000: return bios.read(addr, size); + case 0x01000000: return bios.read(addr, size); case 0x02000000: return cpu.ewram.read(addr & 0x3ffff, size); case 0x03000000: return cpu.iwram.read(addr & 0x7fff, size); case 0x04000000: diff --git a/bsnes/gba/ppu/background.cpp b/bsnes/gba/ppu/background.cpp index 878cf0f5..44912342 100755 --- a/bsnes/gba/ppu/background.cpp +++ b/bsnes/gba/ppu/background.cpp @@ -25,7 +25,11 @@ void PPU::render_background_linear(Registers::Background &bg) { if(regs.control.enable[bg.id] == false) return; auto &output = layer[bg.id]; - uint9 voffset = regs.vcounter + bg.voffset; + 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; unsigned basemap = bg.control.screenbaseblock << 11; @@ -83,8 +87,13 @@ void PPU::render_background_affine(Registers::Background &bg) { unsigned screensize = 16 << bg.control.screensize; unsigned screenwrap = (1 << (bg.control.affinewrap ? 7 + bg.control.screensize : 20)) - 1; - int28 fx = bg.lx; - int28 fy = bg.ly; + 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(unsigned x = 0; x < 240; x++) { unsigned cx = (fx >> 8) & screenwrap, tx = cx / 8, px = cx & 7; @@ -115,8 +124,13 @@ void PPU::render_background_bitmap(Registers::Background &bg) { unsigned height = regs.control.bgmode == 5 ? 128 : 160; unsigned size = depth ? Half : Byte; - int28 fx = bg.lx; - int28 fy = bg.ly; + 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(unsigned x = 0; x < 240; x++) { unsigned px = fx >> 8; diff --git a/bsnes/gba/ppu/object.cpp b/bsnes/gba/ppu/object.cpp index e029ffc7..82e5b376 100755 --- a/bsnes/gba/ppu/object.cpp +++ b/bsnes/gba/ppu/object.cpp @@ -1,43 +1,28 @@ void PPU::render_objects() { if(regs.control.enable[OBJ] == false) return; - - for(unsigned n = 0; n < 128; n++) { - auto &obj = object[n]; - uint8 py = regs.vcounter - obj.y; - if(py >= obj.height << obj.affinesize) continue; //offscreen - if(obj.affine == 0 && obj.affinesize == 1) continue; //hidden - - if(obj.affine == 0) render_object_linear(obj); - if(obj.affine == 1) render_object_affine(obj); - } + for(unsigned n = 0; n < 128; n++) render_object(object[n]); } -void PPU::render_object_linear(Object &obj) { - auto &output = layer[OBJ]; +//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) +void PPU::render_object(Object &obj) { uint8 py = regs.vcounter - obj.y; - if(obj.vflip) py ^= obj.height - 1; + if(obj.affine == 0 && obj.affinesize == 1) return; //hidden + if(py >= obj.height << obj.affinesize) return; //offscreen + auto &output = layer[OBJ]; unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8; unsigned baseaddr = 0x10000 + obj.character * 32; - uint9 sx = obj.x; - for(unsigned x = 0; x < obj.width; x++, sx++) { - unsigned px = x; - if(obj.hflip) px ^= obj.width - 1; - - if(sx < 240) { - render_object_pixel(obj, sx, px, py, rowsize, baseaddr); - } + if(obj.vflip && obj.affine == 0) { + py ^= obj.height - 1; } -} -void PPU::render_object_affine(Object &obj) { - auto &output = layer[OBJ]; - uint8 py = regs.vcounter - obj.y; - - unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8; - unsigned baseaddr = 0x10000 + obj.character * 32; - uint9 sx = obj.x; + if(obj.mosaic && regs.mosaic.objvsize) { + py = (py / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize); + } int16 pa = objectparam[obj.affineparam].pa; int16 pb = objectparam[obj.affineparam].pb; @@ -52,41 +37,43 @@ void PPU::render_object_affine(Object &obj) { 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(unsigned x = 0; x < (obj.width << obj.affinesize); x++, sx++) { - unsigned px = (fx >> 8) + centerx; - unsigned py = (fy >> 8) + centery; + for(unsigned px = 0; px < (obj.width << obj.affinesize); px++) { + unsigned x, y; + if(obj.affine == 0) { + x = px; + y = py; + if(obj.hflip) x ^= obj.width - 1; + } else { + x = (fx >> 8) + centerx; + y = (fy >> 8) + centery; + } - if(sx < 240 && px < obj.width && py < obj.height) { - render_object_pixel(obj, sx, px, py, rowsize, baseaddr); + if(obj.mosaic && regs.mosaic.objhsize) { + x = (x / (1 + regs.mosaic.objhsize)) * (1 + regs.mosaic.objhsize); + } + + unsigned ox = obj.x + px; + if(ox < 240 && x < obj.width && y < obj.height) { + unsigned offset = (y / 8) * rowsize + (x / 8); + offset = offset * 64 + (y & 7) * 8 + (x & 7); + + uint8 color = vram[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] = { true, obj.mode == 1, obj.priority, pram[256 + color] }; + } + } } fx += pa; fy += pc; } } - -void PPU::render_object_pixel(Object &obj, unsigned x, unsigned px, unsigned py, unsigned rowsize, unsigned baseaddr) { - auto &output = layer[OBJ]; - - unsigned offset = (py / 8) * rowsize + (px / 8); - if(obj.colors == 0) offset = baseaddr + offset * 32 + (py & 7) * 4 + (px & 7) / 2; - if(obj.colors == 1) offset = baseaddr + offset * 64 + (py & 7) * 8 + (px & 7); - - uint8 color = vram[offset]; - if(obj.colors == 0) color = (px & 1) ? color >> 4 : color & 15; - - if(color == 0) return; //transparent - - if(obj.mode == 2) { - windowmask[Obj][x] = true; - return; - } - - if(output[x].enable == false || obj.priority < output[x].priority) { - if(obj.colors == 0) output[x] = { true, obj.mode == 1, obj.priority, pram[256 + obj.palette * 16 + color] }; - if(obj.colors == 1) output[x] = { true, obj.mode == 1, obj.priority, pram[256 + color] }; - } -} diff --git a/bsnes/gba/ppu/ppu.hpp b/bsnes/gba/ppu/ppu.hpp index 650d988a..f9597cef 100755 --- a/bsnes/gba/ppu/ppu.hpp +++ b/bsnes/gba/ppu/ppu.hpp @@ -32,12 +32,11 @@ struct PPU : Thread, MMIO { void render_background_bitmap(Registers::Background&); void render_objects(); - void render_object_linear(Object&); - void render_object_affine(Object&); - void render_object_pixel(Object&, unsigned x, unsigned px, unsigned py, unsigned rowsize, unsigned baseaddr); + void render_object(Object&); void render_forceblank(); void render_screen(); + void render_mosaic(unsigned id, unsigned width); void render_window(unsigned window); unsigned blend(unsigned above, unsigned eva, unsigned below, unsigned evb); diff --git a/bsnes/gba/ppu/registers.hpp b/bsnes/gba/ppu/registers.hpp index 7ce05b5e..c09c999e 100755 --- a/bsnes/gba/ppu/registers.hpp +++ b/bsnes/gba/ppu/registers.hpp @@ -60,6 +60,8 @@ struct Registers { //internal int28 lx, ly; + unsigned vmosaic; + unsigned hmosaic; unsigned id; } bg[4]; diff --git a/bsnes/gba/ppu/screen.cpp b/bsnes/gba/ppu/screen.cpp index 7867066f..af250b04 100755 --- a/bsnes/gba/ppu/screen.cpp +++ b/bsnes/gba/ppu/screen.cpp @@ -11,6 +11,11 @@ void PPU::render_screen() { uint16 *line = output + regs.vcounter * 240; uint16 *last = blur + regs.vcounter * 240; + if(regs.bg[0].control.mosaic) render_mosaic(BG0, regs.mosaic.bghsize); + if(regs.bg[1].control.mosaic) render_mosaic(BG1, regs.mosaic.bghsize); + if(regs.bg[2].control.mosaic) render_mosaic(BG2, regs.mosaic.bghsize); + if(regs.bg[3].control.mosaic) render_mosaic(BG3, regs.mosaic.bghsize); + for(unsigned x = 0; x < 240; x++) { Registers::WindowFlags flags; flags = ~0; //enable all layers if no windows are enabled @@ -58,6 +63,19 @@ void PPU::render_screen() { } } +void PPU::render_mosaic(unsigned id, unsigned width) { + if(++width == 1) return; + auto &buffer = layer[id]; + + for(unsigned x = 0; x < 240;) { + for(unsigned m = 1; m < width; m++) { + if(x + m >= 240) break; + buffer[x + m] = buffer[x]; + } + x += width; + } +} + void PPU::render_window(unsigned w) { unsigned y = regs.vcounter; diff --git a/bsnes/gba/ppu/state.hpp b/bsnes/gba/ppu/state.hpp index cbbb6350..9cfc9c0a 100755 --- a/bsnes/gba/ppu/state.hpp +++ b/bsnes/gba/ppu/state.hpp @@ -6,6 +6,8 @@ struct Pixel { } layer[6][240]; bool windowmask[3][240]; +unsigned vmosaic[5]; +unsigned hmosaic[5]; struct Object { uint8 y; diff --git a/bsnes/gba/system/bios.cpp b/bsnes/gba/system/bios.cpp new file mode 100755 index 00000000..b6b56781 --- /dev/null +++ b/bsnes/gba/system/bios.cpp @@ -0,0 +1,29 @@ +void BIOS::load(const uint8 *biosdata, unsigned biossize) { + memcpy(data, biosdata, min(size, biossize)); + + string sha256 = nall::sha256(data, size); + if(sha256 != "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570") { + interface->message("Warning: Game Boy Advance BIOS SHA256 sum is incorrect."); + } +} + +uint32 BIOS::read(uint32 addr, uint32 size) { + //GBA BIOS is read-protected; only the BIOS itself can read its own memory + //when accessed elsewhere; this returns the last value read by the BIOS program + if(cpu.r(15) >= 0x02000000) return mdr; + + if(size == Word) return mdr = read(addr &~ 2, Half) << 0 | read(addr | 2, Half) << 16; + if(size == Half) return mdr = read(addr &~ 1, Byte) << 0 | read(addr | 1, Byte) << 8; + return mdr = data[addr & 0x3fff]; +} + +void BIOS::write(uint32 addr, uint32 size, uint32 word) { +} + +BIOS::BIOS() { + data = new uint8[size = 16384](); +} + +BIOS::~BIOS() { + delete[] data; +} diff --git a/bsnes/gba/system/system.cpp b/bsnes/gba/system/system.cpp index 09cfdceb..05bcda01 100755 --- a/bsnes/gba/system/system.cpp +++ b/bsnes/gba/system/system.cpp @@ -2,21 +2,10 @@ namespace GBA { +#include "bios.cpp" +BIOS bios; System system; -void System::BIOS::load(const uint8_t *biosdata, unsigned biossize) { - memcpy(data, biosdata, min(size, biossize)); - - string sha256 = nall::sha256(data, size); - if(sha256 != "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570") { - interface->message("Warning: Game Boy Advance BIOS SHA256 sum is incorrect."); - } -} - -System::BIOS::BIOS() { - data = new uint8[size = 16384](); -} - void System::init() { } diff --git a/bsnes/gba/system/system.hpp b/bsnes/gba/system/system.hpp index 4315ccf7..6f89488c 100755 --- a/bsnes/gba/system/system.hpp +++ b/bsnes/gba/system/system.hpp @@ -2,16 +2,25 @@ enum class Input : unsigned { A, B, Select, Start, Right, Left, Up, Down, R, L, }; -struct System { - struct BIOS : StaticMemory { - void load(const uint8_t *data, unsigned size); - BIOS(); - } bios; +struct BIOS : Memory { + uint8 *data; + unsigned size; + uint32 mdr; + void load(const uint8 *data, unsigned size); + uint32 read(uint32 addr, uint32 size); + void write(uint32 addr, uint32 size, uint32 word); + + BIOS(); + ~BIOS(); +}; + +struct System { void init(); void term(); void power(); void run(); }; +extern BIOS bios; extern System system; diff --git a/bsnes/target-ui/interface/gba/gba.cpp b/bsnes/target-ui/interface/gba/gba.cpp index f8633f74..11ae4c98 100755 --- a/bsnes/target-ui/interface/gba/gba.cpp +++ b/bsnes/target-ui/interface/gba/gba.cpp @@ -41,7 +41,7 @@ bool InterfaceGBA::loadCartridge(const string &filename) { string markup; markup.readfile(interface->base.filename("manifest.xml", ".xml")); - GBA::system.bios.load(biosdata, biossize); + GBA::bios.load(biosdata, biossize); GBA::cartridge.load(markup, cartdata, cartsize); GBA::system.power(); delete[] biosdata;