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