From 17b5bae86a9d1297550faa06892028a81c79e893 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 9 Apr 2012 16:19:32 +1000 Subject: [PATCH] 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. --- bsnes/base/base.hpp | 2 +- bsnes/gb/apu/square1/square1.cpp | 3 +- bsnes/gb/apu/square2/square2.cpp | 3 +- bsnes/gb/apu/wave/wave.cpp | 3 +- bsnes/gba/apu/apu.cpp | 56 ++++++- bsnes/gba/apu/apu.hpp | 2 +- bsnes/gba/apu/fifo.cpp | 28 ++++ bsnes/gba/apu/mmio.cpp | 28 +++- bsnes/gba/apu/registers.hpp | 28 ++-- bsnes/gba/apu/sequencer.cpp | 27 +--- bsnes/gba/apu/square1.cpp | 3 +- bsnes/gba/apu/square2.cpp | 3 +- bsnes/gba/apu/wave.cpp | 3 +- bsnes/gba/cartridge/cartridge.cpp | 40 +++-- bsnes/gba/cartridge/cartridge.hpp | 6 + bsnes/gba/cartridge/eeprom.cpp | 65 ++++++++ bsnes/gba/cartridge/memory.hpp | 12 ++ bsnes/gba/cpu/cpu.cpp | 2 +- bsnes/gba/cpu/cpu.hpp | 1 + bsnes/gba/cpu/dma.cpp | 14 +- bsnes/gba/cpu/mmio.cpp | 70 ++++----- bsnes/gba/cpu/registers.hpp | 2 +- bsnes/gba/cpu/timer.cpp | 4 + bsnes/gba/memory/memory.cpp | 28 +--- bsnes/gba/ppu/mmio.cpp | 2 +- bsnes/gba/ppu/object.cpp | 188 +++++++++++++++++------ bsnes/gba/ppu/ppu.cpp | 16 +- bsnes/gba/ppu/ppu.hpp | 6 +- bsnes/gba/ppu/screen.cpp | 6 +- bsnes/gba/ppu/state.hpp | 7 + bsnes/gba/system/system.cpp | 1 + bsnes/processor/arm/instructions-arm.cpp | 1 - bsnes/target-ui/config/config.cpp | 2 +- bsnes/target-ui/settings/audio.cpp | 4 +- 34 files changed, 474 insertions(+), 192 deletions(-) create mode 100755 bsnes/gba/apu/fifo.cpp create mode 100755 bsnes/gba/cartridge/eeprom.cpp create mode 100755 bsnes/gba/cartridge/memory.hpp diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index 6e694547..497cd0d4 100755 --- a/bsnes/base/base.hpp +++ b/bsnes/base/base.hpp @@ -1,7 +1,7 @@ #ifndef BASE_HPP #define BASE_HPP -static const char Version[] = "087.18"; +static const char Version[] = "087.19"; #include #include diff --git a/bsnes/gb/apu/square1/square1.cpp b/bsnes/gb/apu/square1/square1.cpp index ae309b8e..24fafed6 100755 --- a/bsnes/gb/apu/square1/square1.cpp +++ b/bsnes/gb/apu/square1/square1.cpp @@ -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() { diff --git a/bsnes/gb/apu/square2/square2.cpp b/bsnes/gb/apu/square2/square2.cpp index 3636e9ed..cde68884 100755 --- a/bsnes/gb/apu/square2/square2.cpp +++ b/bsnes/gb/apu/square2/square2.cpp @@ -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() { diff --git a/bsnes/gb/apu/wave/wave.cpp b/bsnes/gb/apu/wave/wave.cpp index 4b17142d..32f101fd 100755 --- a/bsnes/gb/apu/wave/wave.cpp +++ b/bsnes/gb/apu/wave/wave.cpp @@ -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) { diff --git a/bsnes/gba/apu/apu.cpp b/bsnes/gba/apu/apu.cpp index 614a9441..2a597606 100755 --- a/bsnes/gba/apu/apu.cpp +++ b/bsnes/gba/apu/apu.cpp @@ -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) { - runsequencer(); - interface->audioSample(sequencer.lsample, sequencer.rsample); - step(4); + for(unsigned n = 0; n < 128; n++) { + runsequencer(); + } + + 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; diff --git a/bsnes/gba/apu/apu.hpp b/bsnes/gba/apu/apu.hpp index daff89a0..9074fe70 100755 --- a/bsnes/gba/apu/apu.hpp +++ b/bsnes/gba/apu/apu.hpp @@ -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); diff --git a/bsnes/gba/apu/fifo.cpp b/bsnes/gba/apu/fifo.cpp new file mode 100755 index 00000000..3459d97a --- /dev/null +++ b/bsnes/gba/apu/fifo.cpp @@ -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; +} diff --git a/bsnes/gba/apu/mmio.cpp b/bsnes/gba/apu/mmio.cpp index c5f4f42c..104f9a18 100755 --- a/bsnes/gba/apu/mmio.cpp +++ b/bsnes/gba/apu/mmio.cpp @@ -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); } } diff --git a/bsnes/gba/apu/registers.hpp b/bsnes/gba/apu/registers.hpp index 050bd74e..b2104be9 100755 --- a/bsnes/gba/apu/registers.hpp +++ b/bsnes/gba/apu/registers.hpp @@ -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]; diff --git a/bsnes/gba/apu/sequencer.cpp b/bsnes/gba/apu/sequencer.cpp index 7fc00e0f..9c9218fe 100755 --- a/bsnes/gba/apu/sequencer.cpp +++ b/bsnes/gba/apu/sequencer.cpp @@ -22,31 +22,8 @@ void APU::runsequencer() { if(r.enable[0]) square1.run(); 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; - } + if(r.enable[2]) wave.run(); + if(r.enable[3]) noise.run(); } uint8 APU::Sequencer::read(unsigned addr) const { diff --git a/bsnes/gba/apu/square1.cpp b/bsnes/gba/apu/square1.cpp index 606b77c6..6ecf71a4 100755 --- a/bsnes/gba/apu/square1.cpp +++ b/bsnes/gba/apu/square1.cpp @@ -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() { diff --git a/bsnes/gba/apu/square2.cpp b/bsnes/gba/apu/square2.cpp index 5ece02f3..facbb76f 100755 --- a/bsnes/gba/apu/square2.cpp +++ b/bsnes/gba/apu/square2.cpp @@ -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() { diff --git a/bsnes/gba/apu/wave.cpp b/bsnes/gba/apu/wave.cpp index 5fbf1546..de570de3 100755 --- a/bsnes/gba/apu/wave.cpp +++ b/bsnes/gba/apu/wave.cpp @@ -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 { diff --git a/bsnes/gba/cartridge/cartridge.cpp b/bsnes/gba/cartridge/cartridge.cpp index 959b37d0..ee693390 100755 --- a/bsnes/gba/cartridge/cartridge.cpp +++ b/bsnes/gba/cartridge/cartridge.cpp @@ -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() { diff --git a/bsnes/gba/cartridge/cartridge.hpp b/bsnes/gba/cartridge/cartridge.hpp index 1e508540..f771ba7e 100755 --- a/bsnes/gba/cartridge/cartridge.hpp +++ b/bsnes/gba/cartridge/cartridge.hpp @@ -1,12 +1,18 @@ struct Cartridge : property { StaticMemory rom; StaticMemory ram; + #include "memory.hpp" readonly loaded; readonly sha256; + readonly 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(); }; diff --git a/bsnes/gba/cartridge/eeprom.cpp b/bsnes/gba/cartridge/eeprom.cpp new file mode 100755 index 00000000..5cfefa44 --- /dev/null +++ b/bsnes/gba/cartridge/eeprom.cpp @@ -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; +} diff --git a/bsnes/gba/cartridge/memory.hpp b/bsnes/gba/cartridge/memory.hpp new file mode 100755 index 00000000..15d192ea --- /dev/null +++ b/bsnes/gba/cartridge/memory.hpp @@ -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; diff --git a/bsnes/gba/cpu/cpu.cpp b/bsnes/gba/cpu/cpu.cpp index 3040b4a6..cc54bcba 100755 --- a/bsnes/gba/cpu/cpu.cpp +++ b/bsnes/gba/cpu/cpu.cpp @@ -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(); } } diff --git a/bsnes/gba/cpu/cpu.hpp b/bsnes/gba/cpu/cpu.hpp index 8aca632c..e477a8f4 100755 --- a/bsnes/gba/cpu/cpu.hpp +++ b/bsnes/gba/cpu/cpu.hpp @@ -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); diff --git a/bsnes/gba/cpu/dma.cpp b/bsnes/gba/cpu/dma.cpp index 17e959d6..796e769c 100755 --- a/bsnes/gba/cpu/dma.cpp +++ b/bsnes/gba/cpu/dma.cpp @@ -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); diff --git a/bsnes/gba/cpu/mmio.cpp b/bsnes/gba/cpu/mmio.cpp index 9485e46b..c3616633 100755 --- a/bsnes/gba/cpu/mmio.cpp +++ b/bsnes/gba/cpu/mmio.cpp @@ -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; } diff --git a/bsnes/gba/cpu/registers.hpp b/bsnes/gba/cpu/registers.hpp index 0a34f87e..845b21e7 100755 --- a/bsnes/gba/cpu/registers.hpp +++ b/bsnes/gba/cpu/registers.hpp @@ -25,7 +25,7 @@ struct Registers { struct Run { uint32 target; uint32 source; - uint32 length; + uint16 length; } run; uint32 basetarget; } dma[4]; diff --git a/bsnes/gba/cpu/timer.cpp b/bsnes/gba/cpu/timer.cpp index 831c6f77..573ff523 100755 --- a/bsnes/gba/cpu/timer.cpp +++ b/bsnes/gba/cpu/timer.cpp @@ -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); } diff --git a/bsnes/gba/memory/memory.cpp b/bsnes/gba/memory/memory.cpp index 5dbc153a..e73ec7ad 100755 --- a/bsnes/gba/memory/memory.cpp +++ b/bsnes/gba/memory/memory.cpp @@ -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); } } diff --git a/bsnes/gba/ppu/mmio.cpp b/bsnes/gba/ppu/mmio.cpp index 426cd48e..e5c0346b 100755 --- a/bsnes/gba/ppu/mmio.cpp +++ b/bsnes/gba/ppu/mmio.cpp @@ -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 diff --git a/bsnes/gba/ppu/object.cpp b/bsnes/gba/ppu/object.cpp index 9192e027..ac86c86f 100755 --- a/bsnes/gba/ppu/object.cpp +++ b/bsnes/gba/ppu/object.cpp @@ -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]; +} diff --git a/bsnes/gba/ppu/ppu.cpp b/bsnes/gba/ppu/ppu.cpp index 23748c7c..6d067dcc 100755 --- a/bsnes/gba/ppu/ppu.cpp +++ b/bsnes/gba/ppu/ppu.cpp @@ -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; } - render_backgrounds(); - render_objects(); - render_screen(); + 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]; } diff --git a/bsnes/gba/ppu/ppu.hpp b/bsnes/gba/ppu/ppu.hpp index 0e2f8371..2af9f018 100755 --- a/bsnes/gba/ppu/ppu.hpp +++ b/bsnes/gba/ppu/ppu.hpp @@ -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(); diff --git a/bsnes/gba/ppu/screen.cpp b/bsnes/gba/ppu/screen.cpp index 335ef7b0..faf05ce1 100755 --- a/bsnes/gba/ppu/screen.cpp +++ b/bsnes/gba/ppu/screen.cpp @@ -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; diff --git a/bsnes/gba/ppu/state.hpp b/bsnes/gba/ppu/state.hpp index 9b61996d..5ef4900b 100755 --- a/bsnes/gba/ppu/state.hpp +++ b/bsnes/gba/ppu/state.hpp @@ -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; diff --git a/bsnes/gba/system/system.cpp b/bsnes/gba/system/system.cpp index 976dbaf9..09cfdceb 100755 --- a/bsnes/gba/system/system.cpp +++ b/bsnes/gba/system/system.cpp @@ -28,6 +28,7 @@ void System::power() { cpu.power(); ppu.power(); apu.power(); + cartridge.power(); scheduler.power(); } diff --git a/bsnes/processor/arm/instructions-arm.cpp b/bsnes/processor/arm/instructions-arm.cpp index 7d94ce4c..b313b5a6 100755 --- a/bsnes/processor/arm/instructions-arm.cpp +++ b/bsnes/processor/arm/instructions-arm.cpp @@ -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"); diff --git a/bsnes/target-ui/config/config.cpp b/bsnes/target-ui/config/config.cpp index c84a1128..7f16707d 100755 --- a/bsnes/target-ui/config/config.cpp +++ b/bsnes/target-ui/config/config.cpp @@ -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"); diff --git a/bsnes/target-ui/settings/audio.cpp b/bsnes/target-ui/settings/audio.cpp index 15676d71..62938c05 100755 --- a/bsnes/target-ui/settings/audio.cpp +++ b/bsnes/target-ui/settings/audio.cpp @@ -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);