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:
Tim Allen 2012-04-10 21:41:35 +10:00
parent 01b4cb9919
commit 303a0a67d0
19 changed files with 151 additions and 88 deletions

View File

@ -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>

View File

@ -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

View File

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

View File

@ -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();

View File

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

View File

@ -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)

View File

@ -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 {

View File

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

View File

@ -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

View File

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

View File

@ -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]);
}
}

View File

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

View File

@ -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();

View File

@ -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();
}

View File

@ -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);

View File

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

View File

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

View File

@ -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) {

View File

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