From 303a0a67d01fe9843eaa96f3a78f7b7185349021 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Tue, 10 Apr 2012 21:41:35 +1000 Subject: [PATCH] Update to v087r21 release. byuu says: Timer speedup added. Boosts Mr. Driller 2 title from 170fps to 400fps. Other games still benefit, but not as amazingly. I don't dip below 160fps ever here. Reverted the memory speed to 2 for everything for now, to fix Castlevania slowdown. We obviously need to add the N/S stuff before we do that. Added linear BG and linear OBJ mosaic-Y. Did not add mosaic-X, or any mosaic to the affine/bitmap modes, because I'm not sure when to apply the compensation. Rewrote layer stuff. It now has two layers (above and below), and it performs the four blending modes as needed. Didn't add semi-transparent sprites because the docs are too confusing. Added a blur filter directly into the PPU for now. This obviously violates my interface, but F-Zero needed it for HUD display. We can remove it when we have an official release with a blur filter available. The filter still doesn't warp colors like a real GBA, because I don't know the formula. --- bsnes/base/base.hpp | 2 +- bsnes/gb/gb.hpp | 4 +-- bsnes/gba/cpu/cpu.cpp | 4 +-- bsnes/gba/cpu/cpu.hpp | 2 +- bsnes/gba/cpu/mmio.cpp | 4 ++- bsnes/gba/cpu/registers.cpp | 10 ++++++ bsnes/gba/cpu/registers.hpp | 5 +-- bsnes/gba/cpu/timer.cpp | 28 +++++++--------- bsnes/gba/memory/memory.cpp | 2 ++ bsnes/gba/ppu/background.cpp | 11 ++++--- bsnes/gba/ppu/object.cpp | 9 +++--- bsnes/gba/ppu/ppu.cpp | 11 +++---- bsnes/gba/ppu/ppu.hpp | 3 ++ bsnes/gba/ppu/registers.cpp | 52 ++++++++++++++--------------- bsnes/gba/ppu/registers.hpp | 10 ++---- bsnes/gba/ppu/screen.cpp | 63 ++++++++++++++++++++++++++++++++---- bsnes/gba/ppu/state.hpp | 9 ++++-- bsnes/nall/array.hpp | 2 +- bsnes/nall/bit.hpp | 8 ++--- 19 files changed, 151 insertions(+), 88 deletions(-) diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index 4fd13ed2..5058a6b2 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.20"; +static const char Version[] = "087.21"; #include #include diff --git a/bsnes/gb/gb.hpp b/bsnes/gb/gb.hpp index d37d6cc7..6046a5ce 100755 --- a/bsnes/gb/gb.hpp +++ b/bsnes/gb/gb.hpp @@ -5,13 +5,13 @@ namespace GB { namespace Info { - static const char Name[] = "bsgbc"; + static const char Name[] = "bgbc"; static const unsigned SerializerVersion = 3; } } /* - bsgbc - Game Boy, Super Game Boy, and Game Boy Color emulator + bgbc - Game Boy, Super Game Boy, and Game Boy Color emulator author: byuu license: GPLv3 project started: 2010-12-27 diff --git a/bsnes/gba/cpu/cpu.cpp b/bsnes/gba/cpu/cpu.cpp index 71814228..a0dfa87f 100755 --- a/bsnes/gba/cpu/cpu.cpp +++ b/bsnes/gba/cpu/cpu.cpp @@ -36,7 +36,7 @@ void CPU::enter() { } void CPU::step(unsigned clocks) { - for(unsigned n = 0; n < clocks; n++) timer_tick(); + timer_step(clocks); ppu.clock -= clocks; if(ppu.clock < 0) co_switch(ppu.thread); @@ -69,9 +69,9 @@ void CPU::power() { dma.control = 0; } for(auto &timer : regs.timer) { - timer.counter = 0; timer.reload = 0; timer.control = 0; + timer.counter = 0; } regs.keypad.control = 0; regs.ime = 0; diff --git a/bsnes/gba/cpu/cpu.hpp b/bsnes/gba/cpu/cpu.hpp index e477a8f4..7e5ea11a 100755 --- a/bsnes/gba/cpu/cpu.hpp +++ b/bsnes/gba/cpu/cpu.hpp @@ -19,7 +19,7 @@ struct CPU : Processor::ARM, Thread, MMIO { void dma_run(); void dma_transfer(Registers::DMA &dma); - void timer_tick(); + void timer_step(unsigned clocks); void timer_increment(unsigned n); CPU(); diff --git a/bsnes/gba/cpu/mmio.cpp b/bsnes/gba/cpu/mmio.cpp index f56fa950..665f31a6 100755 --- a/bsnes/gba/cpu/mmio.cpp +++ b/bsnes/gba/cpu/mmio.cpp @@ -178,7 +178,9 @@ 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.reload; + timer.counter = timer.period(); + } else if(timer.control.enable == 0) { + timer.counter = 0; } return; } diff --git a/bsnes/gba/cpu/registers.cpp b/bsnes/gba/cpu/registers.cpp index 600588a5..e9515da6 100755 --- a/bsnes/gba/cpu/registers.cpp +++ b/bsnes/gba/cpu/registers.cpp @@ -23,6 +23,11 @@ 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) @@ -40,6 +45,11 @@ 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 68581ba4..e2f0ca15 100755 --- a/bsnes/gba/cpu/registers.hpp +++ b/bsnes/gba/cpu/registers.hpp @@ -34,18 +34,19 @@ struct Registers { uint1 irq; uint1 enable; + unsigned multiplier() const; operator uint8() const; uint8 operator=(uint8 source); TimerControl& operator=(const TimerControl&) = delete; }; struct Timer { - uint16 counter; uint16 reload; TimerControl control; //internal - uint1 active; + signed period() const; + signed counter; } timer[4]; struct KeypadControl { diff --git a/bsnes/gba/cpu/timer.cpp b/bsnes/gba/cpu/timer.cpp index 573ff523..e74543a7 100755 --- a/bsnes/gba/cpu/timer.cpp +++ b/bsnes/gba/cpu/timer.cpp @@ -1,29 +1,23 @@ -void CPU::timer_tick() { +void CPU::timer_step(unsigned clocks) { for(unsigned n = 0; n < 4; n++) { - if(regs.timer[n].control.cascade) continue; + auto &timer = regs.timer[n]; + if(timer.control.enable == false || timer.control.cascade == true) continue; - static unsigned mask[] = { 0, 63, 255, 1023 }; - if((regs.clock & mask[regs.timer[n].control.frequency]) == 0) { + timer.counter -= clocks; + while(timer.counter <= 0) { timer_increment(n); + timer.counter = timer.period(); } } - - regs.clock++; } void CPU::timer_increment(unsigned n) { - if(regs.timer[n].control.enable == false) return; + if(regs.timer[n].control.irq) regs.irq.flag.timer[n] = 1; - if(++regs.timer[n].counter == 0) { - if(regs.timer[n].control.irq) regs.irq.flag.timer[n] = 1; + if(apu.fifo[0].timer == n) apu.fifo[0].read(); + if(apu.fifo[1].timer == n) apu.fifo[1].read(); - if(apu.fifo[0].timer == n) apu.fifo[0].read(); - if(apu.fifo[1].timer == n) apu.fifo[1].read(); - - regs.timer[n].counter = regs.timer[n].reload; - - if(n < 3 && regs.timer[n + 1].control.cascade) { - timer_increment(n + 1); - } + 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 3e21eb50..3ecec664 100755 --- a/bsnes/gba/memory/memory.cpp +++ b/bsnes/gba/memory/memory.cpp @@ -74,6 +74,8 @@ uint32 Bus::mirror(uint32 addr, uint32 size) { } uint32 Bus::speed(uint32 addr, uint32 size) { + return 2; + //B B E I M P V O R R R R R R S S //I I R R M R R A O O O O O O R R //O O A A I A A M M M M M M M A A diff --git a/bsnes/gba/ppu/background.cpp b/bsnes/gba/ppu/background.cpp index ecedfc87..8f2c8e6e 100755 --- a/bsnes/gba/ppu/background.cpp +++ b/bsnes/gba/ppu/background.cpp @@ -24,9 +24,11 @@ void PPU::render_backgrounds() { void PPU::render_background_linear(unsigned bgnumber) { if(regs.control.enablebg[bgnumber] == false) return; auto &bg = regs.bg[bgnumber]; + bgnumber = 1 + bgnumber; uint9 voffset = regs.vcounter + bg.voffset; uint9 hoffset = bg.hoffset; + voffset = (voffset / (1 + regs.mosaic.bgvsize)) * (1 + regs.mosaic.bgvsize); unsigned basemap = bg.control.screenbaseblock << 11; unsigned basechr = bg.control.characterbaseblock << 14; @@ -68,8 +70,8 @@ void PPU::render_background_linear(unsigned bgnumber) { uint8 color = data[px++ ^ (tile.hflip ? 7 : 0)]; if(color) { - if(bg.control.colormode == 0) layer[bg.control.priority][x] = { true, pram[tile.palette * 16 + color] }; - if(bg.control.colormode == 1) layer[bg.control.priority][x] = { true, pram[color] }; + if(bg.control.colormode == 0) draw(x, bgnumber, bg.control.priority, pram[tile.palette * 16 + color]); + if(bg.control.colormode == 1) draw(x, bgnumber, bg.control.priority, pram[color]); } } } @@ -77,6 +79,7 @@ void PPU::render_background_linear(unsigned bgnumber) { void PPU::render_background_affine(unsigned bgnumber) { if(regs.control.enablebg[bgnumber] == false) return; auto &bg = regs.bg[bgnumber]; + bgnumber = 1 + bgnumber; unsigned basemap = bg.control.screenbaseblock << 11; unsigned basechr = bg.control.characterbaseblock << 14; @@ -93,7 +96,7 @@ void PPU::render_background_affine(unsigned bgnumber) { if(tx < screensize && ty < screensize) { uint8 character = vram[basemap + ty * screensize + tx]; uint8 color = vram[basechr + (character * 64) + py * 8 + px]; - if(color) layer[bg.control.priority][x] = { true, pram[color] }; + if(color) draw(x, bgnumber, bg.control.priority, pram[color]); } fx += bg.pa; @@ -129,7 +132,7 @@ void PPU::render_background_bitmap(unsigned bgnumber) { 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; - layer[bg.control.priority][x] = { true, color }; + draw(x, bgnumber, bg.control.priority, color); } } diff --git a/bsnes/gba/ppu/object.cpp b/bsnes/gba/ppu/object.cpp index 5c28baa1..7cfbe1af 100755 --- a/bsnes/gba/ppu/object.cpp +++ b/bsnes/gba/ppu/object.cpp @@ -15,6 +15,7 @@ void PPU::render_objects() { void PPU::render_object_linear(Object &obj) { uint8 py = regs.vcounter - obj.y; if(obj.vflip) py ^= obj.height - 1; + py = (py / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize); unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8; unsigned baseaddr = 0x10000 + obj.character * 32; @@ -33,8 +34,8 @@ void PPU::render_object_linear(Object &obj) { if(obj.colors == 0) color = (px & 1) ? color >> 4 : color & 15; if(color) { - if(obj.colors == 0) layer[obj.priority][sx] = { true, pram[256 + obj.palette * 16 + color] }; - if(obj.colors == 1) layer[obj.priority][sx] = { true, pram[256 + color] }; + if(obj.colors == 0) draw(sx, 0, obj.priority, pram[256 + obj.palette * 16 + color]); + if(obj.colors == 1) draw(sx, 0, obj.priority, pram[256 + color]); } } } @@ -75,8 +76,8 @@ void PPU::render_object_affine(Object &obj) { if(obj.colors == 0) color = (px & 1) ? color >> 4 : color & 15; if(color) { - if(obj.colors == 0) layer[obj.priority][sx] = { true, pram[256 + obj.palette * 16 + color] }; - if(obj.colors == 1) layer[obj.priority][sx] = { true, pram[256 + color] }; + if(obj.colors == 0) draw(sx, 0, obj.priority, pram[256 + obj.palette * 16 + color]); + if(obj.colors == 1) draw(sx, 0, obj.priority, pram[256 + color]); } } diff --git a/bsnes/gba/ppu/ppu.cpp b/bsnes/gba/ppu/ppu.cpp index 6c0709f1..5c9ef9a1 100755 --- a/bsnes/gba/ppu/ppu.cpp +++ b/bsnes/gba/ppu/ppu.cpp @@ -36,8 +36,7 @@ void PPU::step(unsigned clocks) { void PPU::power() { create(PPU::Enter, 16777216); -//for(unsigned n = 0; n < vram.size; n++) vram.data[n] = 0; - for(unsigned n = 0; n < 240 * 160; n++) output[n] = 0; + for(unsigned n = 0; n < 240 * 160; n++) output[n] = 0, blur[n] = 0; for(unsigned n = 0; n < 1024; n += 2) pram_write(n, Half, 0x0000); for(unsigned n = 0; n < 1024; n += 2) oam_write(n, Half, 0x0000); @@ -105,10 +104,8 @@ void PPU::scanline() { if(regs.vcounter < 160) { for(unsigned x = 0; x < 240; x++) { - layer[0][x].exists = false; - layer[1][x].exists = false; - layer[2][x].exists = false; - layer[3][x].exists = false; + above[x] = { (3 << 3) | 5, pram[0] }; + below[x] = { (3 << 3) | 5, pram[0] }; } if(regs.control.forceblank) { @@ -140,10 +137,12 @@ void PPU::frame() { PPU::PPU() { output = new uint16[240 * 160]; + blur = new uint16[240 * 160]; } PPU::~PPU() { delete[] output; + delete[] blur; } } diff --git a/bsnes/gba/ppu/ppu.hpp b/bsnes/gba/ppu/ppu.hpp index dfe7f6e4..494e04b0 100755 --- a/bsnes/gba/ppu/ppu.hpp +++ b/bsnes/gba/ppu/ppu.hpp @@ -4,6 +4,7 @@ struct PPU : Thread, MMIO { #include "registers.hpp" #include "state.hpp" uint16 *output; + uint16 *blur; static void Enter(); void enter(); @@ -36,6 +37,8 @@ struct PPU : Thread, MMIO { void render_forceblank(); void render_screen(); + void draw(unsigned x, unsigned layer, unsigned priority, unsigned color); + unsigned blend(unsigned above, unsigned eva, unsigned below, unsigned evb); PPU(); ~PPU(); diff --git a/bsnes/gba/ppu/registers.cpp b/bsnes/gba/ppu/registers.cpp index 129dbe4e..13057f9f 100755 --- a/bsnes/gba/ppu/registers.cpp +++ b/bsnes/gba/ppu/registers.cpp @@ -104,35 +104,35 @@ uint8 PPU::Registers::WindowFlags::operator=(uint8 source) { PPU::Registers::BlendControl::operator uint16() const { return ( - (firstbg[0] << 0) - | (firstbg[1] << 1) - | (firstbg[2] << 2) - | (firstbg[3] << 3) - | (firstobj << 4) - | (firstbd << 5) - | (effect << 6) - | (secondbg[0] << 8) - | (secondbg[1] << 9) - | (secondbg[2] << 10) - | (secondbg[3] << 11) - | (secondobj << 12) - | (secondbd << 13) + (above[1] << 0) + | (above[2] << 1) + | (above[3] << 2) + | (above[4] << 3) + | (above[0] << 4) + | (above[5] << 5) + | (mode << 6) + | (below[1] << 8) + | (below[2] << 9) + | (below[3] << 10) + | (below[4] << 11) + | (below[0] << 12) + | (below[5] << 13) ); } uint16 PPU::Registers::BlendControl::operator=(uint16 source) { - firstbg[0] = source & (1 << 0); - firstbg[1] = source & (1 << 1); - firstbg[2] = source & (1 << 2); - firstbg[3] = source & (1 << 3); - firstobj = source & (1 << 4); - firstbd = source & (1 << 5); - effect = source >> 6; - secondbg[0] = source & (1 << 8); - secondbg[1] = source & (1 << 9); - secondbg[2] = source & (1 << 10); - secondbg[3] = source & (1 << 11); - secondobj = source & (1 << 12); - secondbd = source & (1 << 13); + above[1] = source & (1 << 0); + above[2] = source & (1 << 1); + above[3] = source & (1 << 2); + above[4] = source & (1 << 3); + above[0] = source & (1 << 4); + above[5] = source & (1 << 5); + mode = source >> 6; + below[1] = source & (1 << 8); + below[2] = source & (1 << 9); + below[3] = source & (1 << 10); + below[4] = source & (1 << 11); + below[0] = source & (1 << 12); + below[5] = source & (1 << 13); return operator uint16(); } diff --git a/bsnes/gba/ppu/registers.hpp b/bsnes/gba/ppu/registers.hpp index cbc0b943..03cc08fd 100755 --- a/bsnes/gba/ppu/registers.hpp +++ b/bsnes/gba/ppu/registers.hpp @@ -89,13 +89,9 @@ struct Registers { } mosaic; struct BlendControl { - bool firstbg[4]; - bool firstobj; - bool firstbd; - uint2 effect; - bool secondbg[4]; - bool secondobj; - bool secondbd; + bool above[6]; + bool below[6]; + uint2 mode; operator uint16() const; uint16 operator=(uint16 source); diff --git a/bsnes/gba/ppu/screen.cpp b/bsnes/gba/ppu/screen.cpp index 0dcc6e50..296d4da0 100755 --- a/bsnes/gba/ppu/screen.cpp +++ b/bsnes/gba/ppu/screen.cpp @@ -1,16 +1,65 @@ void PPU::render_forceblank() { uint16 *line = output + regs.vcounter * 240; - for(unsigned x = 0; x < 240; x++) line[x] = 0x7fff; + uint16 *last = blur + regs.vcounter * 240; + for(unsigned x = 0; x < 240; x++) { + line[x] = ((last[x] >> 1) & 0x3def) + ((0x7fff >> 1) & 0x3def); + last[x] = 0x7fff; + } } void PPU::render_screen() { uint16 *line = output + regs.vcounter * 240; + uint16 *last = blur + regs.vcounter * 240; + for(unsigned x = 0; x < 240; x++) { - auto color = pram[0]; - if(layer[3][x].exists) color = layer[3][x].color; - if(layer[2][x].exists) color = layer[2][x].color; - if(layer[1][x].exists) color = layer[1][x].color; - if(layer[0][x].exists) color = layer[0][x].color; - line[x] = color; + uint16 color = above[x].color; + + switch(regs.blend.control.mode) { default: + case 0: //none + break; + case 1: //blend + if(regs.blend.control.above[above[x].priority & 7] && regs.blend.control.below[below[x].priority & 7]) { + color = blend(above[x].color, regs.blend.eva, below[x].color, regs.blend.evb); + } + break; + case 2: //brighten + if(regs.blend.control.above[above[x].priority & 7]) { + color = blend(above[x].color, 16 - regs.blend.evy, 0x7fff, regs.blend.evy); + } + break; + case 3: //darken + if(regs.blend.control.above[above[x].priority & 7]) { + color = blend(above[x].color, 16 - regs.blend.evy, 0x0000, regs.blend.evy); + } + } + + line[x] = ((last[x] >> 1) & 0x3def) + ((color >> 1) & 0x3def); + last[x] = color; } } + +void PPU::draw(unsigned x, unsigned layer, unsigned priority, unsigned color) { + priority = (priority << 3) | layer; + + if(priority <= above[x].priority) { + below[x] = above[x]; + above[x] = { priority, color }; + return; + } + + if(priority <= below[x].priority) { + below[x] = { priority, color }; + return; + } +} + +unsigned PPU::blend(unsigned above, unsigned eva, unsigned below, unsigned evb) { + uint5 ar = above >> 0, ag = above >> 5, ab = above >> 10; + uint5 br = below >> 0, bg = below >> 6, bb = below >> 10; + + unsigned r = ((ar * eva) + (br * evb)) >> 4; + unsigned g = ((ag * eva) + (bg * evb)) >> 4; + unsigned b = ((ab * eva) + (bb * evb)) >> 4; + + return min(31, r) << 0 | min(31, g) << 5 | min(31, b) << 10; +} diff --git a/bsnes/gba/ppu/state.hpp b/bsnes/gba/ppu/state.hpp index 5ef4900b..5b22467b 100755 --- a/bsnes/gba/ppu/state.hpp +++ b/bsnes/gba/ppu/state.hpp @@ -1,7 +1,10 @@ struct Pixel { - bool exists; - uint15 color; -} layer[4][240]; + unsigned priority; + unsigned color; +}; + +Pixel above[240]; +Pixel below[240]; struct Object { uint8 y; diff --git a/bsnes/nall/array.hpp b/bsnes/nall/array.hpp index cf376e54..e05c646c 100755 --- a/bsnes/nall/array.hpp +++ b/bsnes/nall/array.hpp @@ -63,7 +63,7 @@ public: } void remove() { - if(size > 0) resize(size - 1); //remove last element only + if(size() > 0) resize(size - 1); //remove last element only } void remove(unsigned index, unsigned count = 1) { diff --git a/bsnes/nall/bit.hpp b/bsnes/nall/bit.hpp index 66ab7b33..78b47c1e 100755 --- a/bsnes/nall/bit.hpp +++ b/bsnes/nall/bit.hpp @@ -3,25 +3,25 @@ namespace nall { template - constexpr inline uintmax_t uclamp(const uintmax_t x) { + inline uintmax_t uclamp(const uintmax_t x) { enum : uintmax_t { b = 1ull << (bits - 1), y = b * 2 - 1 }; return y + ((x - y) & -(x < y)); //min(x, y); } template - constexpr inline uintmax_t uclip(const uintmax_t x) { + inline uintmax_t uclip(const uintmax_t x) { enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 }; return (x & m); } template - constexpr inline intmax_t sclamp(const intmax_t x) { + inline intmax_t sclamp(const intmax_t x) { enum : intmax_t { b = 1ull << (bits - 1), m = b - 1 }; return (x > m) ? m : (x < -b) ? -b : x; } template - constexpr inline intmax_t sclip(const intmax_t x) { + inline intmax_t sclip(const intmax_t x) { enum : uintmax_t { b = 1ull << (bits - 1), m = b * 2 - 1 }; return ((x & m) ^ b) - b; }