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.
This commit is contained in:
Tim Allen 2012-04-14 17:26:45 +10:00
parent d423ae0a29
commit 28885db586
17 changed files with 160 additions and 119 deletions

View File

@ -1,7 +1,7 @@
#ifndef BASE_HPP
#define BASE_HPP
static const char Version[] = "087.22";
static const char Version[] = "087.23";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,6 +60,8 @@ struct Registers {
//internal
int28 lx, ly;
unsigned vmosaic;
unsigned hmosaic;
unsigned id;
} bg[4];

View File

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

View File

@ -6,6 +6,8 @@ struct Pixel {
} layer[6][240];
bool windowmask[3][240];
unsigned vmosaic[5];
unsigned hmosaic[5];
struct Object {
uint8 y;

29
bsnes/gba/system/bios.cpp Executable file
View File

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

View File

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

View File

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

View File

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