Update to v087r18 release.

byuu says:

Merged Cydrak's r17c changes:
- BG affine mode added
- BG bitmap mode added
- OBJ affine mode added
- fixed IRQ bug in THUMB mode (fixed almost every game)
- timers added (broke almost every game, whee.)

Cydrak is absolutely amazingly awesome and patient. This really wouldn't
be happening without him.

Also fixed some things from my end, including greatly improved sprite
priorities, and a much better priority sorter. Mr. Driller looks a lot
better now.
This commit is contained in:
Tim Allen 2012-04-07 18:17:49 +10:00
parent 1de484262c
commit 6189c93f3d
25 changed files with 404 additions and 199 deletions

View File

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

View File

@ -20,36 +20,18 @@ void APU::Master::run() {
if(channel2_left_enable) sample += apu.square2.output;
if(channel3_left_enable) sample += apu.wave.output;
if(channel4_left_enable) sample += apu.noise.output;
left = (sample * 512) - 16384;
switch(left_volume) {
case 0: left >>= 3; break; // 12.5%
case 1: left >>= 2; break; // 25.0%
case 2: left = (left >> 2) + (left >> 3); break; // 37.5%
case 3: left >>= 1; break; // 50.0%
case 4: left = (left >> 1) + (left >> 3); break; // 62.5%
case 5: left -= (left >> 2); break; // 75.0%
case 6: left -= (left >> 3); break; // 87.5%
//case 7: break; //100.0%
}
sample = (sample * 512) - 16384;
sample = (sample * (left_volume + 1)) / 8;
left = sample;
sample = 0;
if(channel1_right_enable) sample += apu.square1.output;
if(channel2_right_enable) sample += apu.square2.output;
if(channel3_right_enable) sample += apu.wave.output;
if(channel4_right_enable) sample += apu.noise.output;
right = (sample * 512) - 16384;
switch(right_volume) {
case 0: right >>= 3; break; // 12.5%
case 1: right >>= 2; break; // 25.0%
case 2: right = (right >> 2) + (right >> 3); break; // 37.5%
case 3: right >>= 1; break; // 50.0%
case 4: right = (right >> 1) + (right >> 3); break; // 62.5%
case 5: right -= (right >> 2); break; // 75.0%
case 6: right -= (right >> 3); break; // 87.5%
//case 7: break; //100.0%
}
sample = (sample * 512) - 16384;
sample = (sample * (right_volume + 1)) / 8;
right = sample;
}
void APU::Master::write(unsigned r, uint8 data) {

View File

@ -145,6 +145,10 @@ void APU::write(uint32 addr, uint8 byte) {
case 0x04000084: return sequencer.write(2, byte);
case 0x04000085: return;
//SOUNDBIAS
case 0x04000088: regs.bias = (regs.bias & 0xff00) | (byte << 0); return;
case 0x04000089: regs.bias = (regs.bias & 0x00ff) | (byte << 8); return;
//WAVE_RAM0_L
case 0x04000090: return wave.writeram( 0, byte);
case 0x04000091: return wave.writeram( 1, byte);
@ -177,9 +181,5 @@ void APU::write(uint32 addr, uint8 byte) {
case 0x0400009e: return wave.writeram(14, byte);
case 0x0400009f: return wave.writeram(15, byte);
//SOUNDBIAS
case 0x04000088: regs.bias = (regs.bias & 0xff00) | (byte << 0); return;
case 0x04000089: regs.bias = (regs.bias & 0x00ff) | (byte << 8); return;
}
}

View File

@ -7,6 +7,8 @@ struct Registers {
uint16 operator=(uint16 source);
SoundBias& operator=(const SoundBias&) = delete;
} bias;
unsigned clock;
} regs;
struct Sweep {
@ -130,8 +132,31 @@ struct Sequencer {
int16 lsample;
int16 rsample;
signed volumeadjust(signed sample, uint3 volume);
uint8 read(unsigned addr) const;
void write(unsigned addr, uint8 byte);
void power();
} sequencer;
struct FIFO {
int8 sample[32];
uint5 rdoffset;
uint5 wroffset;
uint6 size;
inline int8 pull() {
size--;
return sample[rdoffset++];
}
inline void push(int8 data) {
size++;
sample[wroffset++] = data;
}
inline void reset() {
rdoffset = 0;
wroffset = 0;
size = 0;
for(auto &byte : sample) byte = 0;
}
};

View File

@ -31,7 +31,7 @@ void APU::runsequencer() {
if(r.lenable[2]) lsample += wave.output;
if(r.lenable[3]) lsample += noise.output;
lsample = (lsample * 512) - 15360;
lsample = r.volumeadjust(lsample, r.lvolume);
lsample = (lsample * (r.lvolume + 1)) / 8;
r.lsample = lsample;
signed rsample = 0;
@ -40,7 +40,7 @@ void APU::runsequencer() {
if(r.renable[2]) rsample += wave.output;
if(r.renable[3]) rsample += noise.output;
rsample = (rsample * 512) - 15360;
rsample = r.volumeadjust(rsample, r.rvolume);
rsample = (rsample * (r.rvolume + 1)) / 8;
r.rsample = rsample;
if(r.masterenable == false) {
@ -49,19 +49,6 @@ void APU::runsequencer() {
}
}
signed APU::Sequencer::volumeadjust(signed sample, uint3 volume) {
switch(volume) {
case 0: return (sample >> 3); // 12.5%
case 1: return (sample >> 2); // 25.0%
case 2: return (sample >> 2) + (sample >> 3); // 37.5%
case 3: return (sample >> 1); // 50.0%
case 4: return (sample >> 1) + (sample >> 3); // 62.5%
case 5: return (sample >> 0) - (sample >> 2); // 75.0%
case 6: return (sample >> 0) - (sample >> 3); // 87.5%
case 7: return (sample >> 0); //100.0%
}
}
uint8 APU::Sequencer::read(unsigned addr) const {
switch(addr) {
case 0: return (rvolume << 0) | (lvolume << 4);

View File

@ -6,16 +6,8 @@ void APU::Wave::run() {
}
output = patternsample;
switch(volume) {
case 0: output = 0; // 0%
case 1: break; //100%
case 2: output >>= 1; // 50%
case 3: output >>= 2; // 25%
case 4: output -= output >> 2; // 75%
case 5: output -= output >> 2; // 75%
case 6: output -= output >> 2; // 75%
case 7: output -= output >> 2; // 75%
}
static unsigned multiplier[] = { 0, 4, 2, 1, 3, 3, 3, 3};
output = (output * multiplier[volume]) / 4;
if(enable == false) output = 0;
}

View File

@ -5,6 +5,7 @@ namespace GBA {
#include "registers.cpp"
#include "mmio.cpp"
#include "dma.cpp"
#include "timer.cpp"
CPU cpu;
void CPU::Enter() { cpu.enter(); }
@ -35,6 +36,8 @@ void CPU::enter() {
}
void CPU::step(unsigned clocks) {
for(unsigned n = 0; n < clocks; n++) timer_tick();
ppu.clock -= clocks;
if(ppu.clock < 0) co_switch(ppu.thread);
@ -59,12 +62,18 @@ void CPU::power() {
for(unsigned n = 0; n < iwram.size; n++) iwram.data[n] = 0;
for(unsigned n = 0; n < ewram.size; n++) ewram.data[n] = 0;
regs.dma[0].source = regs.dma[1].source = regs.dma[2].source = regs.dma[3].source = 0;
regs.dma[0].target = regs.dma[1].target = regs.dma[2].target = regs.dma[3].target = 0;
regs.dma[0].length = regs.dma[1].length = regs.dma[2].length = regs.dma[3].length = 0;
regs.dma[0].control = regs.dma[1].control = regs.dma[2].control = regs.dma[3].control = 0;
regs.timer[0].reload = regs.timer[1].reload = regs.timer[2].reload = regs.timer[3].reload = 0;
regs.timer[0].control = regs.timer[1].control = regs.timer[2].control = regs.timer[3].control = 0;
for(auto &dma : regs.dma) {
dma.source = 0;
dma.target = 0;
dma.length = 0;
dma.control = 0;
dma.active = 0;
}
for(auto &timer : regs.timer) {
timer.counter = 0;
timer.reload = 0;
timer.control = 0;
}
regs.keypad.control = 0;
regs.ime = 0;
regs.irq.enable = 0;
@ -72,6 +81,7 @@ void CPU::power() {
regs.wait.control = 0;
regs.postboot = 0;
regs.mode = Registers::Mode::Normal;
regs.clock = 0;
regs.memory.control = 0x0d000020;
pending.dma.vblank = 0;

View File

@ -16,7 +16,10 @@ struct CPU : Processor::ARM, Thread, MMIO {
void write(uint32 addr, uint8 byte);
void dma_run();
void dma_transfer(uint2 channel);
void dma_transfer(Registers::DMA &dma);
void timer_tick();
void timer_increment(unsigned n);
CPU();
};

View File

@ -1,13 +1,29 @@
void CPU::dma_run() {
for(unsigned n = 0; n < 4; n++) {
if(regs.dma[n].control.enable == false) continue;
switch(regs.dma[n].control.timingmode) {
auto &dma = regs.dma[n];
if(dma.control.enable == false) {
dma.active = false;
continue;
}
if(dma.active == false) {
dma.active = true;
dma.run.target = dma.target;
dma.run.source = dma.source;
dma.run.length = dma.length;
step(2);
}
switch(dma.control.timingmode) {
case 0: break;
case 1: if(pending.dma.vblank == false) continue; break;
case 2: if(pending.dma.hblank == false) continue; break;
case 3: if(pending.dma.hdma == false || n != 3) continue; break;
}
dma_transfer(n);
dma_transfer(dma);
if(dma.control.irq) regs.irq.flag.dma[n] = 1;
}
pending.dma.vblank = false;
@ -15,31 +31,28 @@ void CPU::dma_run() {
pending.dma.hdma = false;
}
void CPU::dma_transfer(uint2 n) {
auto &channel = regs.dma[n];
void CPU::dma_transfer(Registers::DMA &dma) {
unsigned size = dma.control.size ? Word : Half;
unsigned seek = dma.control.size ? 4 : 2;
unsigned size = channel.control.size ? Word : Half;
unsigned seek = channel.control.size ? 4 : 2;
uint16 length = channel.length;
channel.basetarget = channel.target;
do {
uint32 word = bus.read(channel.source, size);
bus.write(channel.target, size, word);
uint32 word = bus.read(dma.run.source, size);
bus.write(dma.run.target, size, word);
step(2);
switch(channel.control.sourcemode) {
case 0: channel.source += seek; break;
case 1: channel.source -= seek; break;
switch(dma.control.sourcemode) {
case 0: dma.run.source += seek; break;
case 1: dma.run.source -= seek; break;
}
switch(channel.control.targetmode) {
case 0: channel.target += seek; break;
case 1: channel.target -= seek; break;
case 3: channel.target += seek; break;
switch(dma.control.targetmode) {
case 0: dma.run.target += seek; break;
case 1: dma.run.target -= seek; break;
case 3: dma.run.target += seek; break;
}
} while(--length);
if(channel.control.targetmode == 3) channel.target = channel.basetarget;
} while(--dma.run.length);
channel.control.enable = false;
if(channel.control.irq) regs.irq.flag.dma[n] = 1;
if(dma.control.targetmode == 3) dma.run.target = dma.target;
if(dma.control.repeat == 1) dma.run.length = dma.length;
if(dma.control.repeat == 0) dma.active = false, dma.control.enable = false;
}

View File

@ -3,9 +3,25 @@ uint8 CPU::read(uint32 addr) {
switch(addr) {
//DMA0CNT_H
case 0x040000ba: return regs.dma[0].control >> 0;
case 0x040000bb: return regs.dma[0].control >> 8;
//DMA1CNT_H
case 0x040000c6: return regs.dma[1].control >> 0;
case 0x040000c7: return regs.dma[1].control >> 8;
//DMA2CNT_H
case 0x040000d2: return regs.dma[2].control >> 0;
case 0x040000d3: return regs.dma[2].control >> 8;
//DMA3CNT_H
case 0x040000de: return regs.dma[3].control >> 0;
case 0x040000df: return regs.dma[3].control >> 8;
//TM0CNT_L
case 0x04000100: return regs.timer[0].reload >> 0;
case 0x04000101: return regs.timer[0].reload >> 8;
case 0x04000100: return regs.timer[0].counter >> 0;
case 0x04000101: return regs.timer[0].counter >> 8;
//TIM0CNT_H
case 0x04000102: return regs.timer[0].control >> 0;
@ -170,33 +186,34 @@ void CPU::write(uint32 addr, uint8 byte) {
case 0x04000100: regs.timer[0].reload = (regs.timer[0].reload & 0xff00) | (byte << 0); return;
case 0x04000101: regs.timer[0].reload = (regs.timer[0].reload & 0x00ff) | (byte << 8); return;
//TM0CNT_H
case 0x04000102: regs.timer[0].control = (regs.timer[0].control & 0xff00) | (byte << 0); return;
case 0x04000103: regs.timer[0].control = (regs.timer[0].control & 0x00ff) | (byte << 8); return;
//TM1CNT_L
case 0x04000104: regs.timer[1].reload = (regs.timer[1].reload & 0xff00) | (byte << 0); return;
case 0x04000105: regs.timer[1].reload = (regs.timer[1].reload & 0x00ff) | (byte << 8); return;
//TM1CNT_H
case 0x04000106: regs.timer[1].control = (regs.timer[1].control & 0xff00) | (byte << 0); return;
case 0x04000107: regs.timer[1].control = (regs.timer[1].control & 0x00ff) | (byte << 8); return;
//TM2CNT_L
case 0x04000108: regs.timer[2].reload = (regs.timer[2].reload & 0xff00) | (byte << 0); return;
case 0x04000109: regs.timer[2].reload = (regs.timer[2].reload & 0x00ff) | (byte << 8); return;
//TM2CNT_H
case 0x0400010a: regs.timer[2].control = (regs.timer[2].control & 0xff00) | (byte << 0); return;
case 0x0400010b: regs.timer[2].control = (regs.timer[2].control & 0x00ff) | (byte << 8); return;
//TM3CNT_L
case 0x0400010c: regs.timer[3].reload = (regs.timer[3].reload & 0xff00) | (byte << 0); return;
case 0x0400010d: regs.timer[3].reload = (regs.timer[3].reload & 0x00ff) | (byte << 8); return;
//TM0CNT_H
//TM1CNT_H
//TM2CNT_H
//TM3CNT_H
case 0x0400010e: regs.timer[3].control = (regs.timer[3].control & 0xff00) | (byte << 0); return;
case 0x0400010f: regs.timer[3].control = (regs.timer[3].control & 0x00ff) | (byte << 8); return;
case 0x04000102:
case 0x04000106:
case 0x0400010a:
case 0x0400010e: {
auto &timer = regs.timer[(addr >> 2) & 3];
bool enable = timer.control.enable;
if(timer.control.enable == 0 && enable == 1) {
timer.counter = timer.reload;
}
timer.control = byte;
return;
}
//KEYCNT
case 0x04000132: regs.keypad.control = (regs.keypad.control & 0xff00) | (byte << 0); return;

View File

@ -1,4 +1,4 @@
CPU::Registers::DMA::Control::operator uint16() const {
CPU::Registers::DMAControl::operator uint16() const {
return (
(targetmode << 5)
| (sourcemode << 7)
@ -11,7 +11,7 @@ CPU::Registers::DMA::Control::operator uint16() const {
);
}
uint16 CPU::Registers::DMA::Control::operator=(uint16 source) {
uint16 CPU::Registers::DMAControl::operator=(uint16 source) {
targetmode = source >> 5;
sourcemode = source >> 7;
repeat = source >> 9;
@ -23,21 +23,21 @@ uint16 CPU::Registers::DMA::Control::operator=(uint16 source) {
return operator uint16();
}
CPU::Registers::TimerControl::operator uint16() const {
CPU::Registers::TimerControl::operator uint8() const {
return (
(prescalar << 0)
| (countup << 2)
(frequency << 0)
| (cascade << 2)
| (irq << 6)
| (enable << 7)
);
}
uint16 CPU::Registers::TimerControl::operator=(uint16 source) {
prescalar = (source >> 0) & 3;
countup = (source >> 2) & 1;
irq = (source >> 6) & 1;
enable = (source >> 7) & 1;
return operator uint16();
uint8 CPU::Registers::TimerControl::operator=(uint8 source) {
frequency = source >> 0;
cascade = source >> 2;
irq = source >> 6;
enable = source >> 7;
return operator uint8();
}
CPU::Registers::KeypadControl::operator uint16() const {

View File

@ -1,41 +1,53 @@
struct Registers {
struct DMAControl {
uint2 targetmode;
uint2 sourcemode;
uint1 repeat;
uint1 size;
uint1 drq;
uint2 timingmode;
uint1 irq;
uint1 enable;
operator uint16() const;
uint16 operator=(uint16 source);
DMAControl& operator=(const DMAControl&) = delete;
};
struct DMA {
uint32 source;
uint32 target;
uint16 length;
struct Control {
uint2 targetmode;
uint2 sourcemode;
uint1 repeat;
uint1 size;
uint1 drq;
uint2 timingmode;
uint1 irq;
uint1 enable;
operator uint16() const;
uint16 operator=(uint16 source);
DMA& operator=(const DMA&) = delete;
} control;
DMAControl control;
//internal
uint1 active;
struct Run {
uint32 target;
uint32 source;
uint32 length;
} run;
uint32 basetarget;
} dma[4];
struct TimerControl {
uint2 prescalar;
bool countup;
bool irq;
bool enable;
uint2 frequency;
uint1 cascade;
uint1 irq;
uint1 enable;
operator uint16() const;
uint16 operator=(uint16 source);
operator uint8() const;
uint8 operator=(uint8 source);
TimerControl& operator=(const TimerControl&) = delete;
};
struct Timer {
uint16 counter;
uint16 reload;
TimerControl control;
//internal
uint1 active;
} timer[4];
struct KeypadControl {
@ -122,4 +134,5 @@ struct Registers {
bool postboot;
enum class Mode : unsigned { Normal, Halt, Stop } mode;
unsigned clock;
} regs;

25
bsnes/gba/cpu/timer.cpp Executable file
View File

@ -0,0 +1,25 @@
void CPU::timer_tick() {
for(unsigned n = 0; n < 4; n++) {
if(regs.timer[n].control.cascade) continue;
static unsigned mask[] = { 0, 63, 255, 1023 };
if((regs.clock & mask[regs.timer[n].control.frequency]) == 0) {
timer_increment(n);
}
}
regs.clock++;
}
void CPU::timer_increment(unsigned n) {
if(regs.timer[n].control.enable == false) return;
if(++regs.timer[n].counter == 0) {
if(regs.timer[n].control.irq) regs.irq.flag.timer[n] = 1;
regs.timer[n].counter = regs.timer[n].reload;
if(n < 3 && regs.timer[n + 1].control.cascade) {
timer_increment(n + 1);
}
}
}

View File

@ -1,28 +1,30 @@
void PPU::render_backgrounds() {
if(regs.control.bgmode == 0) {
render_background_linear(0);
render_background_linear(1);
render_background_linear(2);
switch(regs.control.bgmode) {
case 0:
render_background_linear(3);
}
if(regs.control.bgmode == 1) {
render_background_linear(0);
render_background_linear(2);
render_background_linear(1);
//render_background_affine(2);
}
if(regs.control.bgmode == 2) {
//render_background_affine(2);
//render_background_affine(3);
render_background_linear(0);
break;
case 1:
render_background_affine(2);
render_background_linear(1);
render_background_linear(0);
break;
case 2:
render_background_affine(3);
render_background_affine(2);
break;
case 3: case 4: case 5:
render_background_bitmap(2);
break;
}
}
void PPU::render_background_linear(unsigned bgnumber) {
for(unsigned n = 0; n < 240; n++) pixel[bgnumber][n].exists = false;
if(regs.control.enablebg[bgnumber] == false) return;
auto &bg = regs.bg[bgnumber];
uint9 voffset = regs.vcounter + bg.voffset;
uint9 hoffset = bg.hoffset;
@ -64,9 +66,77 @@ void PPU::render_background_linear(unsigned bgnumber) {
hoffset++;
uint8 color = data[px++ ^ (tile.hflip ? 7 : 0)];
if(color == 0) continue; //transparent
if(bg.control.colormode == 0) pixel[bgnumber][x] = { true, palette(tile.palette * 16 + color), bg.control.priority };
if(bg.control.colormode == 1) pixel[bgnumber][x] = { true, palette(color), bg.control.priority };
if(color) {
if(bg.control.colormode == 0) layer[bg.control.priority][x] = { true, palette(tile.palette * 16 + color) };
if(bg.control.colormode == 1) layer[bg.control.priority][x] = { true, palette(color) };
}
}
}
void PPU::render_background_affine(unsigned bgnumber) {
if(regs.control.enablebg[bgnumber] == false) return;
auto &bg = regs.bg[bgnumber];
unsigned basemap = bg.control.screenbaseblock << 11;
unsigned basechr = bg.control.characterbaseblock << 14;
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;
for(unsigned x = 0; x < 240; x++) {
unsigned cx = (fx >> 8) & screenwrap, tx = cx / 8, px = cx & 7;
unsigned cy = (fy >> 8) & screenwrap, ty = cy / 8, py = cy & 7;
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, palette(color) };
}
fx += bg.pa;
fy += bg.pc;
}
bg.lx += bg.pb;
bg.ly += bg.pd;
}
void PPU::render_background_bitmap(unsigned bgnumber) {
if(regs.control.enablebg[bgnumber] == false) return;
auto &bg = regs.bg[bgnumber];
uint1 depth = regs.control.bgmode != 4; //0 = 8-bit (Mode 4), 1 = 15-bit (Mode 3, Mode 5)
unsigned basemap = regs.control.bgmode == 3 ? 0 : 0xa000 * regs.control.frame;
unsigned width = regs.control.bgmode == 5 ? 160 : 240;
unsigned height = regs.control.bgmode == 5 ? 128 : 160;
unsigned size = depth ? Half : Byte;
int28 fx = bg.lx;
int28 fy = bg.ly;
for(unsigned x = 0; x < 240; x++) {
unsigned px = fx >> 8;
unsigned py = fy >> 8;
if(px < width && py < height) {
unsigned offset = py * width + px;
unsigned color = vram.read(basemap + (offset << depth), size);
if(depth || color) { //8bpp color 0 is transparent; 15bpp color is always opaque
if(depth == 0) color = palette(color);
if(depth == 1) color = color & 0x7fff;
layer[bg.control.priority][x] = { true, color };
}
}
fx += bg.pa;
fy += bg.pc;
}
bg.lx += bg.pb;
bg.ly += bg.pd;
}

View File

@ -127,27 +127,27 @@ void PPU::write(uint32 addr, uint8 byte) {
//BG2PC
case 0x04000024: regs.bg[2].pc = (regs.bg[2].pc & 0xff00) | (byte << 0); return;
case 0x04000025: regs.bg[2].pc = (regs.bg[2].pc & 0x00ff) | (byte << 0); return;
case 0x04000025: regs.bg[2].pc = (regs.bg[2].pc & 0x00ff) | (byte << 8); return;
//BG2PD
case 0x04000026: regs.bg[2].pd = (regs.bg[2].pd & 0xff00) | (byte << 0); return;
case 0x04000027: regs.bg[2].pd = (regs.bg[2].pd & 0x00ff) | (byte << 8); return;
//BG2X_L
case 0x04000028: regs.bg[2].x = (regs.bg[2].x & 0xffffff00) | (byte << 0); return;
case 0x04000029: regs.bg[2].x = (regs.bg[2].x & 0xffff00ff) | (byte << 8); return;
case 0x04000028: regs.bg[2].lx = regs.bg[2].x = (regs.bg[2].x & 0xffffff00) | (byte << 0); return;
case 0x04000029: regs.bg[2].lx = regs.bg[2].x = (regs.bg[2].x & 0xffff00ff) | (byte << 8); return;
//BG2X_H
case 0x0400002a: regs.bg[2].x = (regs.bg[2].x & 0xff00ffff) | (byte << 16); return;
case 0x0400002b: regs.bg[2].x = (regs.bg[2].x & 0x00ffffff) | (byte << 24); return;
case 0x0400002a: regs.bg[2].lx = regs.bg[2].x = (regs.bg[2].x & 0xff00ffff) | (byte << 16); return;
case 0x0400002b: regs.bg[2].lx = regs.bg[2].x = (regs.bg[2].x & 0x00ffffff) | (byte << 24); return;
//BG2Y_L
case 0x0400002c: regs.bg[2].y = (regs.bg[2].y & 0xffffff00) | (byte << 0); return;
case 0x0400002d: regs.bg[2].y = (regs.bg[2].y & 0xffff00ff) | (byte << 8); return;
case 0x0400002c: regs.bg[2].ly = regs.bg[2].y = (regs.bg[2].y & 0xffffff00) | (byte << 0); return;
case 0x0400002d: regs.bg[2].ly = regs.bg[2].y = (regs.bg[2].y & 0xffff00ff) | (byte << 8); return;
//BG2Y_H
case 0x0400002e: regs.bg[2].y = (regs.bg[2].y & 0xff00ffff) | (byte << 16); return;
case 0x0400002f: regs.bg[2].y = (regs.bg[2].y & 0x00ffffff) | (byte << 24); return;
case 0x0400002e: regs.bg[2].ly = regs.bg[2].y = (regs.bg[2].y & 0xff00ffff) | (byte << 16); return;
case 0x0400002f: regs.bg[2].ly = regs.bg[2].y = (regs.bg[2].y & 0x00ffffff) | (byte << 24); return;
//BG3PA
case 0x04000030: regs.bg[3].pa = (regs.bg[3].pa & 0xff00) | (byte << 0); return;
@ -166,20 +166,20 @@ void PPU::write(uint32 addr, uint8 byte) {
case 0x04000037: regs.bg[3].pd = (regs.bg[3].pd & 0x00ff) | (byte << 8); return;
//BG3X_L
case 0x04000038: regs.bg[3].x = (regs.bg[3].x & 0xffffff00) | (byte << 0); return;
case 0x04000039: regs.bg[3].x = (regs.bg[3].x & 0xffff00ff) | (byte << 8); return;
case 0x04000038: regs.bg[3].lx = regs.bg[3].x = (regs.bg[3].x & 0xffffff00) | (byte << 0); return;
case 0x04000039: regs.bg[3].lx = regs.bg[3].x = (regs.bg[3].x & 0xffff00ff) | (byte << 8); return;
//BG3X_H
case 0x0400003a: regs.bg[3].x = (regs.bg[3].x & 0xff00ffff) | (byte << 16); return;
case 0x0400003b: regs.bg[3].x = (regs.bg[3].x & 0x00ffffff) | (byte << 24); return;
case 0x0400003a: regs.bg[3].lx = regs.bg[3].x = (regs.bg[3].x & 0xff00ffff) | (byte << 16); return;
case 0x0400003b: regs.bg[3].lx = regs.bg[3].x = (regs.bg[3].x & 0x00ffffff) | (byte << 24); return;
//BG3Y_L
case 0x0400003c: regs.bg[3].y = (regs.bg[3].y & 0xffffff00) | (byte << 0); return;
case 0x0400003d: regs.bg[3].y = (regs.bg[3].y & 0xffff00ff) | (byte << 8); return;
case 0x0400003c: regs.bg[3].ly = regs.bg[3].y = (regs.bg[3].y & 0xffffff00) | (byte << 0); return;
case 0x0400003d: regs.bg[3].ly = regs.bg[3].y = (regs.bg[3].y & 0xffff00ff) | (byte << 8); return;
//BG3Y_H
case 0x0400003e: regs.bg[3].y = (regs.bg[3].y & 0xff00ffff) | (byte << 16); return;
case 0x0400003f: regs.bg[3].y = (regs.bg[3].y & 0x00ffffff) | (byte << 24); return;
case 0x0400003e: regs.bg[3].ly = regs.bg[3].y = (regs.bg[3].y & 0xff00ffff) | (byte << 16); return;
case 0x0400003f: regs.bg[3].ly = regs.bg[3].y = (regs.bg[3].y & 0x00ffffff) | (byte << 24); return;
//WIN0H
case 0x04000040: regs.window[0].x2 = byte; return;

View File

@ -1,8 +1,7 @@
void PPU::render_objects() {
for(unsigned n = 0; n < 240; n++) pixel[4][n].exists = false;
if(regs.control.enableobj == false) return;
for(unsigned n = 0; n < 128; n++) {
for(signed n = 127; n >= 0; n--) {
auto &obj = object[n];
uint16 attr0 = oam.read(n * 8 + 0, Half);
uint16 attr1 = oam.read(n * 8 + 2, Half);
@ -43,25 +42,25 @@ void PPU::render_objects() {
obj.width = widths [obj.shape * 4 + obj.size];
obj.height = heights[obj.shape * 4 + obj.size];
if(regs.vcounter < obj.y) continue;
if(regs.vcounter >= (obj.y + obj.height) & 255) continue;
uint8 py = regs.vcounter - obj.y;
if(py >= obj.height << obj.affinesize) continue;
if(obj.affine == 0 && obj.affinesize == 1) continue; //hidden
if(obj.affine == 0 && obj.affinesize == 1) continue; //invalid mode
if(obj.affine == 0) render_object_linear(obj);
if(obj.affine == 1) render_object_affine(obj);
}
}
void PPU::render_object_linear(Object &obj) {
unsigned py = regs.vcounter - obj.y;
uint8 py = regs.vcounter - obj.y;
if(obj.vflip) py ^= obj.height - 1;
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++) {
if(sx++ >= 240) continue;
for(unsigned x = 0; x < obj.width; x++, sx++) {
if(sx >= 240) continue;
unsigned px = x;
if(obj.hflip) px ^= obj.width - 1;
@ -71,12 +70,56 @@ void PPU::render_object_linear(Object &obj) {
uint8 color = vram[offset];
if(obj.colors == 0) color = (px & 1) ? color >> 4 : color & 15;
if(color == 0) continue; //transparent
if(obj.colors == 0) pixel[4][sx] = { true, palette(256 + obj.palette * 16 + color), obj.priority };
if(obj.colors == 1) pixel[4][sx] = { true, palette(256 + color), obj.priority };
if(color) {
if(obj.colors == 0) layer[obj.priority][sx] = { true, palette(256 + obj.palette * 16 + color) };
if(obj.colors == 1) layer[obj.priority][sx] = { true, palette(256 + color) };
}
}
}
void PPU::render_object_affine(Object &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;
int16 pa = oam.read(obj.affineparam * 32 + 0x06, Half);
int16 pb = oam.read(obj.affineparam * 32 + 0x0e, Half);
int16 pc = oam.read(obj.affineparam * 32 + 0x16, Half);
int16 pd = oam.read(obj.affineparam * 32 + 0x1e, Half);
//center-of-sprite coordinates
int16 centerx = obj.width / 2;
int16 centery = obj.height / 2;
//origin coordinates (top-left of sprite)
int28 originx = -(centerx << obj.affinesize);
int28 originy = -(centery << obj.affinesize) + py;
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;
if(sx < 240 && px < obj.width && py < obj.height) {
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) {
if(obj.colors == 0) layer[obj.priority][sx] = { true, palette(256 + obj.palette * 16 + color) };
if(obj.colors == 1) layer[obj.priority][sx] = { true, palette(256 + color) };
}
}
fx += pa;
fy += pc;
}
}

View File

@ -54,6 +54,8 @@ void PPU::power() {
bg.pd = 0;
bg.x = 0;
bg.y = 0;
bg.lx = 0;
bg.ly = 0;
}
for(auto &w : regs.window) {
w.x1 = 0;
@ -82,6 +84,12 @@ void PPU::scanline() {
if(regs.vcounter == 0) {
frame();
regs.bg[2].lx = regs.bg[2].x;
regs.bg[2].ly = regs.bg[2].y;
regs.bg[3].lx = regs.bg[3].x;
regs.bg[3].ly = regs.bg[3].y;
}
if(regs.vcounter == 160) {
@ -94,21 +102,28 @@ 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;
}
render_backgrounds();
render_objects();
render_screen();
}
step(1024);
step(960);
regs.status.hblank = 1;
if(regs.status.irqhblank) cpu.regs.irq.flag.hblank = 1;
cpu.pending.dma.hblank = true;
step(200);
step(240);
regs.status.hblank = 0;
cpu.pending.dma.hdma = true;
if(regs.vcounter < 160) cpu.pending.dma.hdma = true;
step(8);
step(32);
if(++regs.vcounter == 228) regs.vcounter = 0;
}

View File

@ -19,6 +19,8 @@ struct PPU : Thread, MMIO {
void render_backgrounds();
void render_background_linear(unsigned bgnumber);
void render_background_affine(unsigned bgnumber);
void render_background_bitmap(unsigned bgnumber);
void render_objects();
void render_object_linear(Object&);

View File

@ -65,6 +65,7 @@ PPU::Registers::BackgroundControl::operator uint16() const {
| (mosaic << 6)
| (colormode << 7)
| (screenbaseblock << 8)
| (affinewrap << 13)
| (screensize << 14)
);
}
@ -75,6 +76,7 @@ uint16 PPU::Registers::BackgroundControl::operator=(uint16 source) {
mosaic = source >> 6;
colormode = source >> 7;
screenbaseblock = source >> 8;
affinewrap = source >> 13;
screensize = source >> 14;
return operator uint16();
}

View File

@ -40,6 +40,7 @@ struct Registers {
uint1 mosaic;
uint1 colormode;
uint5 screenbaseblock;
uint1 affinewrap; //BG2,3 only
uint2 screensize;
operator uint16() const;
@ -53,8 +54,11 @@ struct Registers {
uint9 voffset;
//BG2,3 only
uint16 pa, pb, pc, pd;
uint28 x, y;
int16 pa, pb, pc, pd;
int28 x, y;
//internal
int28 lx, ly;
} bg[4];
struct WindowFlags {

View File

@ -10,13 +10,10 @@ void PPU::render_screen() {
for(unsigned x = 0; x < 240; x++) {
uint15 color = palette(0) & 0x7fff;
for(signed p = 3; p >= 0; p--) {
if(pixel[3][x].exists && pixel[3][x].priority == p) color = pixel[3][x].color;
if(pixel[2][x].exists && pixel[2][x].priority == p) color = pixel[2][x].color;
if(pixel[1][x].exists && pixel[1][x].priority == p) color = pixel[1][x].color;
if(pixel[0][x].exists && pixel[0][x].priority == p) color = pixel[0][x].color;
if(pixel[4][x].exists && pixel[4][x].priority == p) color = pixel[4][x].color;
}
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;
}
}

View File

@ -1,8 +1,7 @@
struct Pixel {
bool exists;
uint15 color;
uint2 priority;
} pixel[5][256];
} layer[4][240];
struct Object {
uint8 y;

View File

@ -23,11 +23,6 @@ void ARM::power() {
}
void ARM::exec() {
if(processor.irqline && cpsr().i == 0) {
vector(0x00000018, Processor::Mode::IRQ);
r(14) += 4;
}
cpsr().t ? thumb_step() : arm_step();
}

View File

@ -15,6 +15,11 @@ void ARM::arm_step() {
pipeline_step();
step(2);
if(processor.irqline && cpsr().i == 0) {
vector(0x00000018, Processor::Mode::IRQ);
return;
}
instructions++;
if(pipeline.execute.address == 0x08000000) print("Entry Point\n");
if(trace) {

View File

@ -15,6 +15,12 @@ void ARM::thumb_step() {
pipeline_step();
step(1);
if(processor.irqline && cpsr().i == 0) {
vector(0x00000018, Processor::Mode::IRQ);
r(14) += 2;
return;
}
instructions++;
if(trace) {
print(disassemble_registers(), "\n");