mirror of https://github.com/bsnes-emu/bsnes.git
Update to v087r19 release.
byuu says: Changelog: - added FIFO buffer emulation (with DMA and all that jazz) [Cydrak] - fixed timers and vcounter assign [Cydrak] - emulated EEPROM (you have to change size manually for 14-bit mode, we need a database badly now) [SMA runs now] - removed OAM array, now decoding directly to struct Object {} [128] and ObjectParam {} [32] (faster this way) - check forceblank (still doesn't remove all garble between transitions, though??) - lots of other stuff Delete your settings.cfg, or manually change frequencyGBA to 32768, or bad things will happen (this may change back to 256KHz-4MHz later.) 15 of 16 games are fully playable now, and look and sound great. The major missing detail right now is PPU blending support, and we really need to optimize the hell out of the code.
This commit is contained in:
parent
6189c93f3d
commit
17b5bae86a
|
@ -1,7 +1,7 @@
|
|||
#ifndef BASE_HPP
|
||||
#define BASE_HPP
|
||||
|
||||
static const char Version[] = "087.18";
|
||||
static const char Version[] = "087.19";
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/algorithm.hpp>
|
||||
|
|
|
@ -96,6 +96,7 @@ void APU::Square1::write(unsigned r, uint8 data) {
|
|||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
period = 4 * (2048 - frequency);
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
frequency_shadow = frequency;
|
||||
|
@ -106,8 +107,6 @@ void APU::Square1::write(unsigned r, uint8 data) {
|
|||
//if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
|
||||
period = 4 * (2048 - frequency);
|
||||
}
|
||||
|
||||
void APU::Square1::power() {
|
||||
|
|
|
@ -65,13 +65,12 @@ void APU::Square2::write(unsigned r, uint8 data) {
|
|||
|
||||
if(initialize) {
|
||||
enable = dac_enable();
|
||||
period = 4 * (2048 - frequency);
|
||||
envelope_period = envelope_frequency;
|
||||
volume = envelope_volume;
|
||||
//if(length == 0) length = 64;
|
||||
}
|
||||
}
|
||||
|
||||
period = 4 * (2048 - frequency);
|
||||
}
|
||||
|
||||
void APU::Square2::power() {
|
||||
|
|
|
@ -52,12 +52,11 @@ void APU::Wave::write(unsigned r, uint8 data) {
|
|||
|
||||
if(initialize) {
|
||||
enable = dac_enable;
|
||||
period = 2 * (2048 - frequency);
|
||||
pattern_offset = 0;
|
||||
//if(length == 0) length = 256;
|
||||
}
|
||||
}
|
||||
|
||||
period = 2 * (2048 - frequency);
|
||||
}
|
||||
|
||||
void APU::Wave::write_pattern(unsigned p, uint8 data) {
|
||||
|
|
|
@ -10,15 +10,59 @@ namespace GBA {
|
|||
#include "wave.cpp"
|
||||
#include "noise.cpp"
|
||||
#include "sequencer.cpp"
|
||||
#include "fifo.cpp"
|
||||
APU apu;
|
||||
|
||||
void APU::Enter() { apu.enter(); }
|
||||
void APU::Enter() { apu.main(); }
|
||||
|
||||
void APU::enter() {
|
||||
void APU::main() {
|
||||
while(true) {
|
||||
for(unsigned n = 0; n < 128; n++) {
|
||||
runsequencer();
|
||||
interface->audioSample(sequencer.lsample, sequencer.rsample);
|
||||
step(4);
|
||||
}
|
||||
|
||||
signed lsample = regs.bias.level - 0x0200;
|
||||
signed rsample = regs.bias.level - 0x0200;
|
||||
|
||||
//(4-bit x 4 -> 6-bit) + 3-bit volume = 9-bit output
|
||||
if(sequencer.masterenable) {
|
||||
signed lsequence = 0;
|
||||
if(sequencer.lenable[0]) lsequence += square1.output;
|
||||
if(sequencer.lenable[1]) lsequence += square2.output;
|
||||
if(sequencer.lenable[2]) lsequence += wave.output;
|
||||
if(sequencer.lenable[3]) lsequence += noise.output;
|
||||
|
||||
signed rsequence = 0;
|
||||
if(sequencer.renable[0]) rsequence += square1.output;
|
||||
if(sequencer.renable[1]) rsequence += square2.output;
|
||||
if(sequencer.renable[2]) rsequence += wave.output;
|
||||
if(sequencer.renable[3]) rsequence += noise.output;
|
||||
|
||||
if(sequencer.volume < 3) {
|
||||
lsample += lsequence * (sequencer.lvolume + 1) >> (2 - sequencer.volume);
|
||||
rsample += rsequence * (sequencer.rvolume + 1) >> (2 - sequencer.volume);
|
||||
}
|
||||
}
|
||||
|
||||
//(8-bit x 2 -> 7-bit) + 1-bit volume = 10-bit output
|
||||
signed fifo0 = fifo[0].output + (1 << fifo[0].volume);
|
||||
signed fifo1 = fifo[1].output + (1 << fifo[1].volume);
|
||||
|
||||
if(fifo[0].lenable) lsample += fifo0;
|
||||
if(fifo[1].lenable) lsample += fifo1;
|
||||
|
||||
if(fifo[0].renable) rsample += fifo0;
|
||||
if(fifo[1].renable) rsample += fifo1;
|
||||
|
||||
lsample = sclamp<10>(lsample);
|
||||
rsample = sclamp<10>(rsample);
|
||||
|
||||
if(regs.bias.amplitude == 1) lsample &= ~3, rsample &= ~3;
|
||||
if(regs.bias.amplitude == 2) lsample &= ~7, rsample &= ~7;
|
||||
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
|
||||
|
||||
interface->audioSample(lsample << 5, rsample << 5);
|
||||
step(512);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,6 +79,8 @@ void APU::power() {
|
|||
wave.power();
|
||||
noise.power();
|
||||
sequencer.power();
|
||||
fifo[0].power();
|
||||
fifo[1].power();
|
||||
|
||||
regs.bias = 0x0200;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ struct APU : Thread, MMIO {
|
|||
#include "registers.hpp"
|
||||
|
||||
static void Enter();
|
||||
void enter();
|
||||
void main();
|
||||
void step(unsigned clocks);
|
||||
|
||||
uint8 read(uint32 addr);
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
void APU::FIFO::read() {
|
||||
if(size == 0) return;
|
||||
size--;
|
||||
output = sample[rdoffset++];
|
||||
}
|
||||
|
||||
void APU::FIFO::write(int8 byte) {
|
||||
if(size == 32) return;
|
||||
size++;
|
||||
sample[wroffset++] = byte;
|
||||
}
|
||||
|
||||
void APU::FIFO::reset() {
|
||||
for(auto &byte : sample) byte = 0;
|
||||
output = 0;
|
||||
|
||||
rdoffset = 0;
|
||||
wroffset = 0;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
void APU::FIFO::power() {
|
||||
reset();
|
||||
|
||||
lenable = 0;
|
||||
renable = 0;
|
||||
timer = 0;
|
||||
}
|
|
@ -138,8 +138,21 @@ void APU::write(uint32 addr, uint8 byte) {
|
|||
case 0x04000081: return sequencer.write(1, byte);
|
||||
|
||||
//SOUND_CNT_H
|
||||
case 0x04000082: return;
|
||||
case 0x04000083: return;
|
||||
case 0x04000082:
|
||||
sequencer.volume = byte >> 0;
|
||||
fifo[0].volume = byte >> 2;
|
||||
fifo[1].volume = byte >> 3;
|
||||
return;
|
||||
case 0x04000083:
|
||||
fifo[0].renable = byte >> 0;
|
||||
fifo[0].lenable = byte >> 1;
|
||||
fifo[0].timer = byte >> 2;
|
||||
if(byte & 1 << 3) fifo[0].reset();
|
||||
fifo[1].renable = byte >> 4;
|
||||
fifo[1].lenable = byte >> 5;
|
||||
fifo[1].timer = byte >> 6;
|
||||
if(byte & 1 << 7) fifo[0].reset();
|
||||
return;
|
||||
|
||||
//NR52
|
||||
case 0x04000084: return sequencer.write(2, byte);
|
||||
|
@ -181,5 +194,16 @@ void APU::write(uint32 addr, uint8 byte) {
|
|||
case 0x0400009e: return wave.writeram(14, byte);
|
||||
case 0x0400009f: return wave.writeram(15, byte);
|
||||
|
||||
//FIFO_A_L
|
||||
//FIFO_A_H
|
||||
case 0x040000a0: case 0x040000a1:
|
||||
case 0x040000a2: case 0x040000a3:
|
||||
return fifo[0].write(byte);
|
||||
|
||||
//FIFO_B_L
|
||||
//FIFO_B_H
|
||||
case 0x040000a4: case 0x040000a5:
|
||||
case 0x040000a6: case 0x040000a7:
|
||||
return fifo[1].write(byte);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ struct Noise {
|
|||
} noise;
|
||||
|
||||
struct Sequencer {
|
||||
uint2 volume;
|
||||
uint3 lvolume;
|
||||
uint3 rvolume;
|
||||
uint1 lenable[4];
|
||||
|
@ -139,24 +140,19 @@ struct Sequencer {
|
|||
|
||||
struct FIFO {
|
||||
int8 sample[32];
|
||||
int8 output;
|
||||
|
||||
uint5 rdoffset;
|
||||
uint5 wroffset;
|
||||
uint6 size;
|
||||
|
||||
inline int8 pull() {
|
||||
size--;
|
||||
return sample[rdoffset++];
|
||||
}
|
||||
uint1 volume; //0 = 50%, 1 = 100%
|
||||
uint1 lenable;
|
||||
uint1 renable;
|
||||
uint1 timer;
|
||||
|
||||
inline void push(int8 data) {
|
||||
size++;
|
||||
sample[wroffset++] = data;
|
||||
}
|
||||
|
||||
inline void reset() {
|
||||
rdoffset = 0;
|
||||
wroffset = 0;
|
||||
size = 0;
|
||||
for(auto &byte : sample) byte = 0;
|
||||
}
|
||||
};
|
||||
void read();
|
||||
void write(int8 byte);
|
||||
void reset();
|
||||
void power();
|
||||
} fifo[2];
|
||||
|
|
|
@ -24,29 +24,6 @@ void APU::runsequencer() {
|
|||
if(r.enable[1]) square2.run();
|
||||
if(r.enable[2]) wave.run();
|
||||
if(r.enable[3]) noise.run();
|
||||
|
||||
signed lsample = 0;
|
||||
if(r.lenable[0]) lsample += square1.output;
|
||||
if(r.lenable[1]) lsample += square2.output;
|
||||
if(r.lenable[2]) lsample += wave.output;
|
||||
if(r.lenable[3]) lsample += noise.output;
|
||||
lsample = (lsample * 512) - 15360;
|
||||
lsample = (lsample * (r.lvolume + 1)) / 8;
|
||||
r.lsample = lsample;
|
||||
|
||||
signed rsample = 0;
|
||||
if(r.renable[0]) rsample += square1.output;
|
||||
if(r.renable[1]) rsample += square2.output;
|
||||
if(r.renable[2]) rsample += wave.output;
|
||||
if(r.renable[3]) rsample += noise.output;
|
||||
rsample = (rsample * 512) - 15360;
|
||||
rsample = (rsample * (r.rvolume + 1)) / 8;
|
||||
r.rsample = rsample;
|
||||
|
||||
if(r.masterenable == false) {
|
||||
r.lsample = 0;
|
||||
r.rsample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 APU::Sequencer::read(unsigned addr) const {
|
||||
|
|
|
@ -64,6 +64,7 @@ void APU::Square1::write(unsigned addr, uint8 byte) {
|
|||
|
||||
if(initialize) {
|
||||
enable = envelope.dacenable();
|
||||
period = 4 * (2048 - frequency);
|
||||
envelope.period = envelope.frequency;
|
||||
volume = envelope.volume;
|
||||
shadowfrequency = frequency;
|
||||
|
@ -75,8 +76,6 @@ void APU::Square1::write(unsigned addr, uint8 byte) {
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
period = 4 * (2048 - frequency);
|
||||
}
|
||||
|
||||
void APU::Square1::power() {
|
||||
|
|
|
@ -32,14 +32,13 @@ void APU::Square2::write(unsigned addr, uint8 byte) {
|
|||
|
||||
if(initialize) {
|
||||
enable = envelope.dacenable();
|
||||
period = 4 * (2048 - frequency);
|
||||
envelope.period = envelope.frequency;
|
||||
volume = envelope.volume;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
period = 4 * (2048 - frequency);
|
||||
}
|
||||
|
||||
void APU::Square2::power() {
|
||||
|
|
|
@ -55,14 +55,13 @@ void APU::Wave::write(unsigned addr, uint8 byte) {
|
|||
|
||||
if(initialize) {
|
||||
enable = dacenable;
|
||||
period = 2 * (2048 - frequency);
|
||||
patternaddr = 0;
|
||||
patternbank = mode ? (uint1)0 : bank;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
period = 2 * (2048 - frequency);
|
||||
}
|
||||
|
||||
uint8 APU::Wave::readram(unsigned addr) const {
|
||||
|
|
|
@ -2,19 +2,20 @@
|
|||
|
||||
namespace GBA {
|
||||
|
||||
#include "eeprom.cpp"
|
||||
Cartridge cartridge;
|
||||
|
||||
bool Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
|
||||
if(cartridge.rom.data) delete[] cartridge.rom.data;
|
||||
cartridge.rom.data = new uint8[cartridge.rom.size = 32 * 1024 * 1024];
|
||||
if(rom.data) delete[] rom.data;
|
||||
rom.data = new uint8[rom.size = 32 * 1024 * 1024];
|
||||
for(unsigned addr = 0; addr < 32 * 1024 * 1024; addr++) {
|
||||
cartridge.rom.data[addr] = data[Bus::mirror(addr, size)];
|
||||
rom.data[addr] = data[Bus::mirror(addr, size)];
|
||||
}
|
||||
|
||||
if(cartridge.ram.data) delete[] cartridge.ram.data;
|
||||
cartridge.ram.data = new uint8[cartridge.ram.size = 64 * 1024]();
|
||||
if(ram.data) delete[] ram.data;
|
||||
ram.data = new uint8[ram.size = 64 * 1024]();
|
||||
|
||||
sha256 = nall::sha256(cartridge.rom.data, cartridge.rom.size);
|
||||
sha256 = nall::sha256(rom.data, rom.size);
|
||||
|
||||
return loaded = true;
|
||||
}
|
||||
|
@ -23,9 +24,30 @@ void Cartridge::unload() {
|
|||
if(loaded) return;
|
||||
loaded = false;
|
||||
|
||||
delete[] cartridge.rom.data;
|
||||
cartridge.rom.data = nullptr;
|
||||
cartridge.rom.size = 0u;
|
||||
delete[] rom.data;
|
||||
rom.data = nullptr;
|
||||
rom.size = 0u;
|
||||
}
|
||||
|
||||
void Cartridge::power() {
|
||||
eeprom.power();
|
||||
|
||||
has_eeprom = false;
|
||||
}
|
||||
|
||||
uint32 Cartridge::read(uint32 addr, uint32 size) {
|
||||
if(has_eeprom && (addr & 0x0f000000) == 0x0d000000) return eeprom.read();
|
||||
|
||||
if((addr & 0x0e000000) == 0x08000000) return rom.read(addr & 0x01ffffff, size);
|
||||
if((addr & 0x0e000000) == 0x0a000000) return rom.read(addr & 0x01ffffff, size);
|
||||
if((addr & 0x0e000000) == 0x0c000000) return rom.read(addr & 0x01ffffff, size);
|
||||
if((addr & 0x0e000000) == 0x0e000000) return ram.read(addr & 0x0000ffff, size);
|
||||
}
|
||||
|
||||
void Cartridge::write(uint32 addr, uint32 size, uint32 word) {
|
||||
if((addr & 0x0f000000) == 0x0d000000) { has_eeprom = true; return eeprom.write(word & 1); }
|
||||
|
||||
if((addr & 0x0e000000) == 0x0e000000) return ram.write(addr & 0x0000ffff, size, word);
|
||||
}
|
||||
|
||||
Cartridge::Cartridge() {
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
struct Cartridge : property<Cartridge> {
|
||||
StaticMemory rom;
|
||||
StaticMemory ram;
|
||||
#include "memory.hpp"
|
||||
|
||||
readonly<bool> loaded;
|
||||
readonly<string> sha256;
|
||||
readonly<bool> has_eeprom;
|
||||
|
||||
bool load(const string &markup, const uint8_t *data, unsigned size);
|
||||
void unload();
|
||||
void power();
|
||||
|
||||
uint32 read(uint32 addr, uint32 size);
|
||||
void write(uint32 addr, uint32 size, uint32 word);
|
||||
|
||||
Cartridge();
|
||||
};
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
bool Cartridge::EEPROM::read() {
|
||||
bool bit = 1;
|
||||
|
||||
if(mode == Mode::ReadData) {
|
||||
if(offset >= 4) bit = data[address * 64 + (offset - 4)];
|
||||
if(++offset == 68) mode = Mode::Wait;
|
||||
}
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
void Cartridge::EEPROM::write(bool bit) {
|
||||
if(mode == Mode::Wait) {
|
||||
if(bit == 1) mode = Mode::Command;
|
||||
}
|
||||
|
||||
else if(mode == Mode::Command) {
|
||||
if(bit == 0) mode = Mode::WriteAddress;
|
||||
if(bit == 1) mode = Mode::ReadAddress;
|
||||
offset = 0;
|
||||
address = 0;
|
||||
}
|
||||
|
||||
else if(mode == Mode::ReadAddress) {
|
||||
address = (address << 1) | bit;
|
||||
if(++offset == size) {
|
||||
mode = Mode::ReadValidate;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
else if(mode == Mode::ReadValidate) {
|
||||
if(bit == 1); //invalid
|
||||
mode = Mode::ReadData;
|
||||
}
|
||||
|
||||
else if(mode == Mode::WriteAddress) {
|
||||
address = (address << 1) | bit;
|
||||
if(++offset == size) {
|
||||
mode = Mode::WriteData;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
else if(mode == Mode::WriteData) {
|
||||
data[address * 64 + offset] = bit;
|
||||
if(++offset == 64) {
|
||||
mode = Mode::WriteValidate;
|
||||
}
|
||||
}
|
||||
|
||||
else if(mode == Mode::WriteValidate) {
|
||||
if(bit == 1); //invalid
|
||||
mode = Mode::Wait;
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::EEPROM::power() {
|
||||
for(auto &bit : data) bit = 0;
|
||||
size = 6;
|
||||
|
||||
mode = Mode::Wait;
|
||||
offset = 0;
|
||||
address = 0;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
struct EEPROM {
|
||||
bool data[64 * 1024];
|
||||
unsigned size;
|
||||
|
||||
enum class Mode : unsigned { Wait, Command, ReadAddress, ReadValidate, ReadData, WriteAddress, WriteData, WriteValidate } mode;
|
||||
unsigned offset;
|
||||
unsigned address;
|
||||
|
||||
bool read();
|
||||
void write(bool bit);
|
||||
void power();
|
||||
} eeprom;
|
|
@ -21,6 +21,7 @@ void CPU::enter() {
|
|||
}
|
||||
|
||||
processor.irqline = regs.ime && (regs.irq.enable & regs.irq.flag);
|
||||
dma_run();
|
||||
|
||||
if(regs.mode == Registers::Mode::Halt) {
|
||||
if((regs.irq.enable & regs.irq.flag) == 0) {
|
||||
|
@ -30,7 +31,6 @@ void CPU::enter() {
|
|||
regs.mode = Registers::Mode::Normal;
|
||||
}
|
||||
|
||||
dma_run();
|
||||
exec();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ struct CPU : Processor::ARM, Thread, MMIO {
|
|||
static void Enter();
|
||||
void enter();
|
||||
void step(unsigned clocks);
|
||||
|
||||
uint32 bus_read(uint32 addr, uint32 size);
|
||||
void bus_write(uint32 addr, uint32 size, uint32 word);
|
||||
|
||||
|
|
|
@ -19,7 +19,19 @@ void CPU::dma_run() {
|
|||
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;
|
||||
case 3:
|
||||
if(n == 0) {
|
||||
continue;
|
||||
}
|
||||
if(n == 1 || n == 2) {
|
||||
if(apu.fifo[n - 1].size > 16) continue;
|
||||
dma.control.targetmode = 2;
|
||||
dma.control.size = 1;
|
||||
dma.run.length = 4;
|
||||
}
|
||||
if(n == 3) {
|
||||
if(pending.dma.hdma == false) continue;
|
||||
}
|
||||
}
|
||||
|
||||
dma_transfer(dma);
|
||||
|
|
|
@ -4,52 +4,40 @@ 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;
|
||||
case 0x040000ba: case 0x040000bb:
|
||||
case 0x040000c6: case 0x040000c7:
|
||||
case 0x040000d2: case 0x040000d3:
|
||||
case 0x040000de: case 0x040000df: {
|
||||
auto &dma = regs.dma[(addr - 0x040000ba) / 12];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return dma.control >> shift;
|
||||
}
|
||||
|
||||
//TM0CNT_L
|
||||
case 0x04000100: return regs.timer[0].counter >> 0;
|
||||
case 0x04000101: return regs.timer[0].counter >> 8;
|
||||
//TM1CNT_L
|
||||
//TM2CNT_L
|
||||
//TM3CNT_L
|
||||
case 0x04000100: case 0x04000101:
|
||||
case 0x04000104: case 0x04000105:
|
||||
case 0x04000108: case 0x04000109:
|
||||
case 0x0400010c: case 0x0400010d: {
|
||||
auto &timer = regs.timer[(addr >> 2) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return timer.counter >> shift;
|
||||
}
|
||||
|
||||
//TIM0CNT_H
|
||||
case 0x04000102: return regs.timer[0].control >> 0;
|
||||
case 0x04000103: return regs.timer[0].control >> 8;
|
||||
|
||||
//TM1CNT_L
|
||||
case 0x04000104: return regs.timer[1].reload >> 0;
|
||||
case 0x04000105: return regs.timer[1].reload >> 8;
|
||||
|
||||
//TM1CNT_H
|
||||
case 0x04000106: return regs.timer[1].control >> 0;
|
||||
case 0x04000107: return regs.timer[1].control >> 8;
|
||||
|
||||
//TM2CNT_L
|
||||
case 0x04000108: return regs.timer[2].reload >> 0;
|
||||
case 0x04000109: return regs.timer[2].reload >> 8;
|
||||
|
||||
//TM2CNT_H
|
||||
case 0x0400010a: return regs.timer[2].control >> 0;
|
||||
case 0x0400010b: return regs.timer[2].control >> 8;
|
||||
|
||||
//TM3CNT_L
|
||||
case 0x0400010c: return regs.timer[3].reload >> 0;
|
||||
case 0x0400010d: return regs.timer[3].reload >> 8;
|
||||
|
||||
//TM3CNT_H
|
||||
case 0x0400010e: return regs.timer[3].control >> 0;
|
||||
case 0x0400010f: return regs.timer[3].control >> 8;
|
||||
case 0x04000102: case 0x04000103:
|
||||
case 0x04000106: case 0x04000107:
|
||||
case 0x0400010a: case 0x0400010b:
|
||||
case 0x0400010e: case 0x0400010f: {
|
||||
auto &timer = regs.timer[(addr >> 2) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
return timer.control >> shift;
|
||||
}
|
||||
|
||||
//KEYINPUT
|
||||
case 0x04000130:
|
||||
|
@ -208,10 +196,10 @@ void CPU::write(uint32 addr, uint8 byte) {
|
|||
case 0x0400010e: {
|
||||
auto &timer = regs.timer[(addr >> 2) & 3];
|
||||
bool enable = timer.control.enable;
|
||||
if(timer.control.enable == 0 && enable == 1) {
|
||||
timer.control = byte;
|
||||
if(enable == 0 && timer.control.enable == 1) {
|
||||
timer.counter = timer.reload;
|
||||
}
|
||||
timer.control = byte;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ struct Registers {
|
|||
struct Run {
|
||||
uint32 target;
|
||||
uint32 source;
|
||||
uint32 length;
|
||||
uint16 length;
|
||||
} run;
|
||||
uint32 basetarget;
|
||||
} dma[4];
|
||||
|
|
|
@ -17,7 +17,11 @@ void CPU::timer_increment(unsigned n) {
|
|||
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();
|
||||
|
||||
regs.timer[n].counter = regs.timer[n].reload;
|
||||
|
||||
if(n < 3 && regs.timer[n + 1].control.cascade) {
|
||||
timer_increment(n + 1);
|
||||
}
|
||||
|
|
|
@ -74,7 +74,9 @@ uint32 Bus::mirror(uint32 addr, uint32 size) {
|
|||
}
|
||||
|
||||
uint32 Bus::read(uint32 addr, uint32 size) {
|
||||
switch(addr & 0x0f000000) {
|
||||
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 0x02000000: return cpu.ewram.read(addr & 0x3ffff, size);
|
||||
|
@ -85,20 +87,14 @@ uint32 Bus::read(uint32 addr, uint32 size) {
|
|||
return 0u;
|
||||
case 0x05000000: return ppu.pram.read(addr & 0x3ff, size);
|
||||
case 0x06000000: return ppu.vram.read(addr & 0x10000 ? (0x10000 + (addr & 0x7fff)) : (addr & 0xffff), size);
|
||||
case 0x07000000: return ppu.oam.read(addr & 0x3ff, size);
|
||||
case 0x08000000: return cartridge.rom.read(addr & 0x1ffffff, size);
|
||||
case 0x09000000: return cartridge.rom.read(addr & 0x1ffffff, size);
|
||||
case 0x0a000000: return cartridge.rom.read(addr & 0x1ffffff, size);
|
||||
case 0x0b000000: return cartridge.rom.read(addr & 0x1ffffff, size);
|
||||
case 0x0c000000: return cartridge.rom.read(addr & 0x1ffffff, size);
|
||||
case 0x0d000000: return cartridge.rom.read(addr & 0x1ffffff, size);
|
||||
case 0x0e000000: return cartridge.ram.read(addr & 0xffff, size);
|
||||
case 0x0f000000: return cartridge.ram.read(addr & 0xffff, size);
|
||||
case 0x07000000: return ppu.oam_read(addr & 0x3ff, size);
|
||||
}
|
||||
}
|
||||
|
||||
void Bus::write(uint32 addr, uint32 size, uint32 word) {
|
||||
switch(addr & 0x0f000000) {
|
||||
if(addr & 0x08000000) return cartridge.write(addr, size, word);
|
||||
|
||||
switch(addr & 0x07000000) {
|
||||
case 0x00000000: return;
|
||||
case 0x01000000: return;
|
||||
case 0x02000000: return cpu.ewram.write(addr & 0x3ffff, size, word);
|
||||
|
@ -109,15 +105,7 @@ void Bus::write(uint32 addr, uint32 size, uint32 word) {
|
|||
return;
|
||||
case 0x05000000: return ppu.pram.write(addr & 0x3ff, size, word);
|
||||
case 0x06000000: return ppu.vram.write(addr & 0x10000 ? (0x10000 + (addr & 0x7fff)) : (addr & 0xffff), size, word);
|
||||
case 0x07000000: return ppu.oam.write(addr & 0x3ff, size, word);
|
||||
case 0x08000000: return;
|
||||
case 0x09000000: return;
|
||||
case 0x0a000000: return;
|
||||
case 0x0b000000: return;
|
||||
case 0x0c000000: return;
|
||||
case 0x0d000000: return;
|
||||
case 0x0e000000: return cartridge.ram.write(addr & 0xffff, size, word);
|
||||
case 0x0f000000: return cartridge.ram.write(addr & 0xffff, size, word);
|
||||
case 0x07000000: return ppu.oam_write(addr & 0x3ff, size, word);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ void PPU::write(uint32 addr, uint8 byte) {
|
|||
regs.status.irqvcoincidence = byte & (1 << 5);
|
||||
return;
|
||||
case 0x04000005:
|
||||
regs.status.vcoincidence = byte;
|
||||
regs.status.vcompare = byte;
|
||||
return;
|
||||
|
||||
//BG0CNT
|
||||
|
|
|
@ -3,47 +3,8 @@ void PPU::render_objects() {
|
|||
|
||||
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);
|
||||
uint16 attr2 = oam.read(n * 8 + 4, Half);
|
||||
|
||||
obj.y = attr0 >> 0;
|
||||
obj.affine = attr0 >> 8;
|
||||
obj.affinesize = attr0 >> 9;
|
||||
obj.mode = attr0 >> 10;
|
||||
obj.mosaic = attr0 >> 12;
|
||||
obj.colors = attr0 >> 13;
|
||||
obj.shape = attr0 >> 14;
|
||||
|
||||
obj.x = attr1 >> 0;
|
||||
obj.affineparam = attr1 >> 9;
|
||||
obj.hflip = attr1 >> 12;
|
||||
obj.vflip = attr1 >> 13;
|
||||
obj.size = attr1 >> 14;
|
||||
|
||||
obj.character = attr2 >> 0;
|
||||
obj.priority = attr2 >> 10;
|
||||
obj.palette = attr2 >> 12;
|
||||
|
||||
static unsigned widths[] = {
|
||||
8, 16, 32, 64,
|
||||
16, 32, 32, 64,
|
||||
8, 8, 16, 32,
|
||||
0, 0, 0, 0, //8?
|
||||
};
|
||||
|
||||
static unsigned heights[] = {
|
||||
8, 16, 32, 64,
|
||||
8, 8, 16, 32,
|
||||
16, 32, 32, 64,
|
||||
0, 0, 0, 0, //8?
|
||||
};
|
||||
|
||||
obj.width = widths [obj.shape * 4 + obj.size];
|
||||
obj.height = heights[obj.shape * 4 + obj.size];
|
||||
|
||||
uint8 py = regs.vcounter - obj.y;
|
||||
if(py >= obj.height << obj.affinesize) continue;
|
||||
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);
|
||||
|
@ -85,10 +46,10 @@ void PPU::render_object_affine(Object &obj) {
|
|||
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);
|
||||
int16 pa = objectparam[obj.affineparam].pa;
|
||||
int16 pb = objectparam[obj.affineparam].pb;
|
||||
int16 pc = objectparam[obj.affineparam].pc;
|
||||
int16 pd = objectparam[obj.affineparam].pd;
|
||||
|
||||
//center-of-sprite coordinates
|
||||
int16 centerx = obj.width / 2;
|
||||
|
@ -123,3 +84,142 @@ void PPU::render_object_affine(Object &obj) {
|
|||
fy += pc;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 PPU::oam_read(uint32 addr, uint32 size) {
|
||||
uint32 word = 0;
|
||||
|
||||
switch(size) {
|
||||
case Word:
|
||||
addr &= ~3;
|
||||
word |= oam_read(addr + 0) << 0;
|
||||
word |= oam_read(addr + 1) << 8;
|
||||
word |= oam_read(addr + 2) << 16;
|
||||
word |= oam_read(addr + 3) << 24;
|
||||
break;
|
||||
case Half:
|
||||
word |= oam_read(addr + 0) << 0;
|
||||
word |= oam_read(addr + 1) << 8;
|
||||
break;
|
||||
case Byte:
|
||||
word |= oam_read(addr + 0) << 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return word;
|
||||
}
|
||||
|
||||
//16-bit bus (8-bit writes are ignored)
|
||||
void PPU::oam_write(uint32 addr, uint32 size, uint32 word) {
|
||||
switch(size) {
|
||||
case Word:
|
||||
addr &= ~3;
|
||||
oam_write(addr + 0, word >> 0);
|
||||
oam_write(addr + 1, word >> 8);
|
||||
oam_write(addr + 2, word >> 16);
|
||||
oam_write(addr + 3, word >> 24);
|
||||
break;
|
||||
case Half:
|
||||
addr &= ~1;
|
||||
oam_write(addr + 0, word >> 0);
|
||||
oam_write(addr + 1, word >> 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 PPU::oam_read(uint32 addr) {
|
||||
auto &obj = object[(addr >> 3) & 127];
|
||||
auto &par = objectparam[(addr >> 5) & 31];
|
||||
|
||||
switch(addr & 7) {
|
||||
case 0: return (obj.y);
|
||||
case 1: return (obj.affine << 0) + (obj.affinesize << 1) + (obj.mode << 2) + (obj.mosaic << 4) + (obj.colors << 5) + (obj.shape << 6);
|
||||
case 2: return (obj.x >> 0);
|
||||
case 3: return (obj.x >> 8) + (obj.affineparam << 1) + (obj.hflip << 4) + (obj.vflip << 5) + (obj.size << 6);
|
||||
case 4: return (obj.character >> 0);
|
||||
case 5: return (obj.character >> 8) + (obj.priority << 2) + (obj.palette << 4);
|
||||
case 6:
|
||||
switch((addr >> 3) & 3) {
|
||||
case 0: return par.pa >> 0;
|
||||
case 1: return par.pb >> 0;
|
||||
case 2: return par.pc >> 0;
|
||||
case 3: return par.pd >> 0;
|
||||
}
|
||||
case 7:
|
||||
switch((addr >> 3) & 3) {
|
||||
case 0: return par.pa >> 8;
|
||||
case 1: return par.pb >> 8;
|
||||
case 2: return par.pc >> 8;
|
||||
case 3: return par.pd >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::oam_write(uint32 addr, uint8 byte) {
|
||||
auto &obj = object[(addr >> 3) & 127];
|
||||
auto &par = objectparam[(addr >> 5) & 31];
|
||||
|
||||
switch(addr & 7) {
|
||||
case 0:
|
||||
obj.y = byte;
|
||||
break;
|
||||
case 1:
|
||||
obj.affine = byte >> 0;
|
||||
obj.affinesize = byte >> 1;
|
||||
obj.mode = byte >> 2;
|
||||
obj.mosaic = byte >> 4;
|
||||
obj.colors = byte >> 5;
|
||||
obj.shape = byte >> 6;
|
||||
break;
|
||||
case 2:
|
||||
obj.x = (obj.x & 0xff00) | (byte << 0);
|
||||
break;
|
||||
case 3:
|
||||
obj.x = (obj.x & 0x00ff) | (byte << 8);
|
||||
obj.affineparam = byte >> 1;
|
||||
obj.hflip = byte >> 4;
|
||||
obj.vflip = byte >> 5;
|
||||
obj.size = byte >> 6;
|
||||
break;
|
||||
case 4:
|
||||
obj.character = (obj.character & 0xff00) | (byte << 0);
|
||||
break;
|
||||
case 5:
|
||||
obj.character = (obj.character & 0x00ff) | (byte << 8);
|
||||
obj.priority = byte >> 2;
|
||||
obj.palette = byte >> 4;
|
||||
break;
|
||||
case 6:
|
||||
switch((addr >> 3) & 3) {
|
||||
case 0: par.pa = (par.pa & 0xff00) | (byte << 0); break;
|
||||
case 1: par.pb = (par.pb & 0xff00) | (byte << 0); break;
|
||||
case 2: par.pc = (par.pc & 0xff00) | (byte << 0); break;
|
||||
case 3: par.pd = (par.pd & 0xff00) | (byte << 0); break;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
switch((addr >> 3) & 3) {
|
||||
case 0: par.pa = (par.pa & 0x00ff) | (byte << 8); break;
|
||||
case 1: par.pb = (par.pb & 0x00ff) | (byte << 8); break;
|
||||
case 2: par.pc = (par.pc & 0x00ff) | (byte << 8); break;
|
||||
case 3: par.pd = (par.pd & 0x00ff) | (byte << 8); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
static unsigned widths[] = {
|
||||
8, 16, 32, 64,
|
||||
16, 32, 32, 64,
|
||||
8, 8, 16, 32,
|
||||
0, 0, 0, 0, //8?
|
||||
};
|
||||
|
||||
static unsigned heights[] = {
|
||||
8, 16, 32, 64,
|
||||
8, 8, 16, 32,
|
||||
16, 32, 32, 64,
|
||||
0, 0, 0, 0, //8?
|
||||
};
|
||||
|
||||
obj.width = widths [obj.shape * 4 + obj.size];
|
||||
obj.height = heights[obj.shape * 4 + obj.size];
|
||||
}
|
||||
|
|
|
@ -36,10 +36,11 @@ void PPU::power() {
|
|||
create(PPU::Enter, 16777216);
|
||||
|
||||
for(unsigned n = 0; n < vram.size; n++) vram.data[n] = 0;
|
||||
for(unsigned n = 0; n < oam.size; n++) oam.data[n] = 0;
|
||||
for(unsigned n = 0; n < pram.size; n++) pram.data[n] = 0;
|
||||
for(unsigned n = 0; n < 240 * 160; n++) output[n] = 0;
|
||||
|
||||
for(unsigned n = 0; n < 1024; n++) oam_write(n, 0);
|
||||
|
||||
regs.control = 0;
|
||||
regs.greenswap = 0;
|
||||
regs.status = 0;
|
||||
|
@ -109,15 +110,19 @@ void PPU::scanline() {
|
|||
layer[3][x].exists = false;
|
||||
}
|
||||
|
||||
if(regs.control.forceblank) {
|
||||
render_forceblank();
|
||||
} else {
|
||||
render_backgrounds();
|
||||
render_objects();
|
||||
render_screen();
|
||||
}
|
||||
}
|
||||
|
||||
step(960);
|
||||
regs.status.hblank = 1;
|
||||
if(regs.status.irqhblank) cpu.regs.irq.flag.hblank = 1;
|
||||
cpu.pending.dma.hblank = true;
|
||||
if(regs.vcounter < 160) cpu.pending.dma.hblank = true;
|
||||
|
||||
step(240);
|
||||
regs.status.hblank = 0;
|
||||
|
@ -134,7 +139,6 @@ void PPU::frame() {
|
|||
|
||||
PPU::PPU() {
|
||||
vram.data = new uint8[vram.size = 96 * 1024];
|
||||
oam.data = new uint8[oam.size = 1024];
|
||||
pram.data = new uint8[pram.size = 1024];
|
||||
output = new uint16[240 * 160];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
struct PPU : Thread, MMIO {
|
||||
StaticMemory vram;
|
||||
StaticMemory oam;
|
||||
StaticMemory pram;
|
||||
#include "registers.hpp"
|
||||
#include "state.hpp"
|
||||
|
@ -25,8 +24,13 @@ struct PPU : Thread, MMIO {
|
|||
void render_objects();
|
||||
void render_object_linear(Object&);
|
||||
void render_object_affine(Object&);
|
||||
uint32 oam_read(uint32 addr, uint32 size);
|
||||
void oam_write(uint32 addr, uint32 size, uint32 word);
|
||||
uint8 oam_read(uint32 addr);
|
||||
void oam_write(uint32 addr, uint8 byte);
|
||||
|
||||
uint15 palette(uint9 index);
|
||||
void render_forceblank();
|
||||
void render_screen();
|
||||
|
||||
PPU();
|
||||
|
|
|
@ -5,9 +5,13 @@ uint15 PPU::palette(uint9 index) {
|
|||
return result;
|
||||
}
|
||||
|
||||
void PPU::render_forceblank() {
|
||||
uint16 *line = output + regs.vcounter * 240;
|
||||
for(unsigned x = 0; x < 240; x++) line[x] = 0x7fff;
|
||||
}
|
||||
|
||||
void PPU::render_screen() {
|
||||
uint16 *line = output + regs.vcounter * 240;
|
||||
|
||||
for(unsigned x = 0; x < 240; x++) {
|
||||
uint15 color = palette(0) & 0x7fff;
|
||||
if(layer[3][x].exists) color = layer[3][x].color;
|
||||
|
|
|
@ -27,6 +27,13 @@ struct Object {
|
|||
unsigned height;
|
||||
} object[128];
|
||||
|
||||
struct ObjectParam {
|
||||
int16 pa;
|
||||
int16 pb;
|
||||
int16 pc;
|
||||
int16 pd;
|
||||
} objectparam[32];
|
||||
|
||||
struct Tile {
|
||||
uint10 character;
|
||||
uint1 hflip;
|
||||
|
|
|
@ -28,6 +28,7 @@ void System::power() {
|
|||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
cartridge.power();
|
||||
scheduler.power();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ void ARM::arm_step() {
|
|||
}
|
||||
|
||||
instructions++;
|
||||
if(pipeline.execute.address == 0x08000000) print("Entry Point\n");
|
||||
if(trace) {
|
||||
print(disassemble_registers(), "\n");
|
||||
print(disassemble_arm_instruction(pipeline.execute.address), "\n");
|
||||
|
|
|
@ -32,7 +32,7 @@ Config::Config() {
|
|||
append(audio.frequencyNES = 1789772, "Audio::Frequency::NES");
|
||||
append(audio.frequencySNES = 32000, "Audio::Frequency::SNES");
|
||||
append(audio.frequencyGB = 4194304, "Audio::Frequency::GB");
|
||||
append(audio.frequencyGBA = 4194304, "Audio::Frequency::GBA");
|
||||
append(audio.frequencyGBA = 32768, "Audio::Frequency::GBA");
|
||||
|
||||
append(input.driver = "", "Input::Driver");
|
||||
append(input.focusPolicy = 1, "Input::FocusPolicy");
|
||||
|
|
|
@ -70,8 +70,8 @@ AudioSettings::AudioSettings() {
|
|||
|
||||
gba.name.setText("GBA:");
|
||||
gba.slider.setLength(2001);
|
||||
gba.base = 4194304;
|
||||
gba.step = 131;
|
||||
gba.base = 32768;
|
||||
gba.step = 1;
|
||||
|
||||
append(title, { ~0, 0 }, 5);
|
||||
append(outputLabel, { ~0, 0 }, 0);
|
||||
|
|
Loading…
Reference in New Issue