Overhaul Apu, add cycling and port handling

This commit is contained in:
scawful
2024-04-22 15:55:57 -04:00
parent ca1d0e9131
commit 1c9c1592eb
2 changed files with 180 additions and 50 deletions

View File

@@ -17,6 +17,14 @@ namespace app {
namespace emu {
namespace audio {
static const uint8_t bootRom[0x40] = {
0xcd, 0xef, 0xbd, 0xe8, 0x00, 0xc6, 0x1d, 0xd0, 0xfc, 0x8f, 0xaa,
0xf4, 0x8f, 0xbb, 0xf5, 0x78, 0xcc, 0xf4, 0xd0, 0xfb, 0x2f, 0x19,
0xeb, 0xf4, 0xd0, 0xfc, 0x7e, 0xf4, 0xd0, 0x0b, 0xe4, 0xf5, 0xcb,
0xf4, 0xd7, 0x00, 0xfc, 0xd0, 0xf3, 0xab, 0x01, 0x10, 0xef, 0x7e,
0xf4, 0x10, 0xeb, 0xba, 0xf6, 0xda, 0x00, 0xba, 0xf4, 0xc4, 0xf4,
0xdd, 0x5d, 0xd0, 0xdb, 0x1f, 0x00, 0x00, 0xc0, 0xff};
void Apu::Init() {
// Set the clock frequency
clock_.SetFrequency(kApuClockSpeed);
@@ -24,8 +32,9 @@ void Apu::Init() {
void Apu::Reset() {
clock_.ResetAccumulatedTime();
spc700_.Reset();
spc700_.Reset(true);
dsp_.Reset();
romReadable = true;
}
void Apu::Update() {
@@ -39,31 +48,147 @@ void Apu::Update() {
}
}
void Apu::Notify(uint32_t address, uint16_t data) {
if (address < 0x2140 || address > 0x2143) {
return;
}
auto offset = address - 0x2140;
spc700_.write(offset, data);
int Apu::RunCycles(uint32_t wanted_cycles) {
int run_cycles = 0;
uint32_t start_cycles = cycles_;
// HACK - This is a temporary solution to get the Apu to play audio
ports_[address - 0x2140] = data;
switch (address) {
case 0x2140:
SignalReady();
break;
case 0x2141:
// TODO: Handle data byte transfer here
break;
case 0x2142:
// TODO: Handle the setup of destination address
break;
case 0x2143:
// TODO: Handle additional communication/commands
break;
while (run_cycles < wanted_cycles) {
spc700_.RunOpcode();
run_cycles += (uint32_t)cycles_ - start_cycles;
start_cycles = cycles_;
}
return run_cycles;
}
void Apu::Cycle() {
if ((cycles_ & 0x1f) == 0) {
// every 32 cycles
dsp_.Cycle();
}
// handle timers
for (int i = 0; i < 3; i++) {
if (timer[i].cycles == 0) {
timer[i].cycles = i == 2 ? 16 : 128;
if (timer[i].enabled) {
timer[i].divider++;
if (timer[i].divider == timer[i].target) {
timer[i].divider = 0;
timer[i].counter++;
timer[i].counter &= 0xf;
}
}
}
timer[i].cycles--;
}
cycles_++;
}
uint8_t Apu::Read(uint16_t adr) {
switch (adr) {
case 0xf0:
case 0xf1:
case 0xfa:
case 0xfb:
case 0xfc: {
return 0;
}
case 0xf2: {
return dspAdr;
}
case 0xf3: {
return dsp_.Read(dspAdr & 0x7f);
}
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7:
case 0xf8:
case 0xf9: {
return inPorts[adr - 0xf4];
}
case 0xfd:
case 0xfe:
case 0xff: {
uint8_t ret = timer[adr - 0xfd].counter;
timer[adr - 0xfd].counter = 0;
return ret;
}
}
if (romReadable && adr >= 0xffc0) {
return bootRom[adr - 0xffc0];
}
return aram_.read(adr);
}
void Apu::Write(uint16_t adr, uint8_t val) {
switch (adr) {
case 0xf0: {
break; // test register
}
case 0xf1: {
for (int i = 0; i < 3; i++) {
if (!timer[i].enabled && (val & (1 << i))) {
timer[i].divider = 0;
timer[i].counter = 0;
}
timer[i].enabled = val & (1 << i);
}
if (val & 0x10) {
inPorts[0] = 0;
inPorts[1] = 0;
}
if (val & 0x20) {
inPorts[2] = 0;
inPorts[3] = 0;
}
romReadable = val & 0x80;
break;
}
case 0xf2: {
dspAdr = val;
break;
}
case 0xf3: {
if (dspAdr < 0x80) dsp_.Write(dspAdr, val);
break;
}
case 0xf4:
case 0xf5:
case 0xf6:
case 0xf7: {
outPorts[adr - 0xf4] = val;
break;
}
case 0xf8:
case 0xf9: {
inPorts[adr - 0xf4] = val;
break;
}
case 0xfa:
case 0xfb:
case 0xfc: {
timer[adr - 0xfa].target = val;
break;
}
}
aram_.write(adr, val);
}
uint8_t Apu::SpcRead(uint16_t adr) {
Cycle();
return Read(adr);
}
void Apu::SpcWrite(uint16_t adr, uint8_t val) {
Cycle();
Write(adr, val);
}
void Apu::SpcIdle(bool waiting) { Cycle(); }
} // namespace audio
} // namespace emu
} // namespace app