mirror of https://github.com/bsnes-emu/bsnes.git
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.
This commit is contained in:
parent
01b4cb9919
commit
303a0a67d0
|
@ -1,7 +1,7 @@
|
|||
#ifndef BASE_HPP
|
||||
#define BASE_HPP
|
||||
|
||||
static const char Version[] = "087.20";
|
||||
static const char Version[] = "087.21";
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/algorithm.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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -3,25 +3,25 @@
|
|||
|
||||
namespace nall {
|
||||
template<unsigned bits>
|
||||
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<unsigned bits>
|
||||
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<unsigned bits>
|
||||
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<unsigned bits>
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue