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