SPC700 to get notified when ports are written
This commit is contained in:
@@ -61,7 +61,7 @@ class APU : public Observer {
|
|||||||
|
|
||||||
void Notify(uint32_t address, uint8_t data) override {
|
void Notify(uint32_t address, uint8_t data) override {
|
||||||
if (address >= 0x2140 && address <= 0x2143) {
|
if (address >= 0x2140 && address <= 0x2143) {
|
||||||
// Handle communication with the APU
|
spc700_.Notify(address, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,12 +176,12 @@ class APU : public Observer {
|
|||||||
void WriteDSPMemory(uint16_t address, uint8_t value);
|
void WriteDSPMemory(uint16_t address, uint8_t value);
|
||||||
|
|
||||||
// Member variables to store internal APU state and resources
|
// Member variables to store internal APU state and resources
|
||||||
Memory &memory_;
|
|
||||||
AudioRam &aram_;
|
AudioRam &aram_;
|
||||||
Clock &clock_;
|
Clock &clock_;
|
||||||
|
Memory &memory_;
|
||||||
|
|
||||||
|
DigitalSignalProcessor dsp_;
|
||||||
SPC700 spc700_{aram_};
|
SPC700 spc700_{aram_};
|
||||||
Dsp dsp_;
|
|
||||||
std::vector<int16_t> audioSamples_;
|
std::vector<int16_t> audioSamples_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
|
||||||
void Dsp::Reset() {}
|
void DigitalSignalProcessor::Reset() {}
|
||||||
|
|
||||||
uint8_t Dsp::ReadVoiceReg(uint8_t voice, uint8_t reg) const {
|
uint8_t DigitalSignalProcessor::ReadVoiceReg(uint8_t voice, uint8_t reg) const {
|
||||||
voice %= kNumVoices;
|
voice %= kNumVoices;
|
||||||
switch (reg % kNumVoiceRegs) {
|
switch (reg % kNumVoiceRegs) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -37,7 +37,7 @@ uint8_t Dsp::ReadVoiceReg(uint8_t voice, uint8_t reg) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dsp::WriteVoiceReg(uint8_t voice, uint8_t reg, uint8_t value) {
|
void DigitalSignalProcessor::WriteVoiceReg(uint8_t voice, uint8_t reg, uint8_t value) {
|
||||||
voice %= kNumVoices;
|
voice %= kNumVoices;
|
||||||
switch (reg % kNumVoiceRegs) {
|
switch (reg % kNumVoiceRegs) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -69,11 +69,11 @@ void Dsp::WriteVoiceReg(uint8_t voice, uint8_t reg, uint8_t value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the callbacks
|
// Set the callbacks
|
||||||
void Dsp::SetSampleFetcher(SampleFetcher fetcher) { sample_fetcher_ = fetcher; }
|
void DigitalSignalProcessor::SetSampleFetcher(SampleFetcher fetcher) { sample_fetcher_ = fetcher; }
|
||||||
|
|
||||||
void Dsp::SetSamplePusher(SamplePusher pusher) { sample_pusher_ = pusher; }
|
void DigitalSignalProcessor::SetSamplePusher(SamplePusher pusher) { sample_pusher_ = pusher; }
|
||||||
|
|
||||||
int16_t Dsp::DecodeSample(uint8_t voice_num) {
|
int16_t DigitalSignalProcessor::DecodeSample(uint8_t voice_num) {
|
||||||
Voice const& voice = voices_[voice_num];
|
Voice const& voice = voices_[voice_num];
|
||||||
uint16_t sample_address = voice.source_number;
|
uint16_t sample_address = voice.source_number;
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ int16_t Dsp::DecodeSample(uint8_t voice_num) {
|
|||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t Dsp::ProcessSample(uint8_t voice_num, int16_t sample) {
|
int16_t DigitalSignalProcessor::ProcessSample(uint8_t voice_num, int16_t sample) {
|
||||||
Voice const& voice = voices_[voice_num];
|
Voice const& voice = voices_[voice_num];
|
||||||
|
|
||||||
// Adjust the pitch (for simplicity, we're just adjusting the sample value)
|
// Adjust the pitch (for simplicity, we're just adjusting the sample value)
|
||||||
@@ -96,7 +96,7 @@ int16_t Dsp::ProcessSample(uint8_t voice_num, int16_t sample) {
|
|||||||
return (left_sample + right_sample) / 2;
|
return (left_sample + right_sample) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dsp::MixSamples() {
|
void DigitalSignalProcessor::MixSamples() {
|
||||||
int16_t mixed_sample = 0;
|
int16_t mixed_sample = 0;
|
||||||
|
|
||||||
for (uint8_t i = 0; i < kNumVoices; i++) {
|
for (uint8_t i = 0; i < kNumVoices; i++) {
|
||||||
@@ -116,7 +116,7 @@ void Dsp::MixSamples() {
|
|||||||
sample_pusher_(mixed_sample);
|
sample_pusher_(mixed_sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dsp::UpdateEnvelope(uint8_t voice) {
|
void DigitalSignalProcessor::UpdateEnvelope(uint8_t voice) {
|
||||||
uint8_t adsr1 = ReadVoiceReg(voice, 0x05);
|
uint8_t adsr1 = ReadVoiceReg(voice, 0x05);
|
||||||
uint8_t adsr2 = ReadVoiceReg(voice, 0x06);
|
uint8_t adsr2 = ReadVoiceReg(voice, 0x06);
|
||||||
uint8_t gain = ReadVoiceReg(voice, 0x07);
|
uint8_t gain = ReadVoiceReg(voice, 0x07);
|
||||||
@@ -216,7 +216,7 @@ void Dsp::UpdateEnvelope(uint8_t voice) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dsp::update_voice_state(uint8_t voice_num) {
|
void DigitalSignalProcessor::update_voice_state(uint8_t voice_num) {
|
||||||
if (voice_num >= kNumVoices) return;
|
if (voice_num >= kNumVoices) return;
|
||||||
|
|
||||||
Voice& voice = voices_[voice_num];
|
Voice& voice = voices_[voice_num];
|
||||||
@@ -261,7 +261,7 @@ void Dsp::update_voice_state(uint8_t voice_num) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dsp::process_envelope(uint8_t voice_num) {
|
void DigitalSignalProcessor::process_envelope(uint8_t voice_num) {
|
||||||
if (voice_num >= kNumVoices) return;
|
if (voice_num >= kNumVoices) return;
|
||||||
|
|
||||||
Voice& voice = voices_[voice_num];
|
Voice& voice = voices_[voice_num];
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ using SamplePusher = std::function<void(int16_t)>;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Dsp {
|
class DigitalSignalProcessor {
|
||||||
private:
|
private:
|
||||||
static const size_t kNumVoices = 8;
|
static const size_t kNumVoices = 8;
|
||||||
static const size_t kNumVoiceRegs = 10;
|
static const size_t kNumVoiceRegs = 10;
|
||||||
@@ -130,12 +130,12 @@ class Dsp {
|
|||||||
180, 150, 110, 92, 55, 37, 18}};
|
180, 150, 110, 92, 55, 37, 18}};
|
||||||
|
|
||||||
// DSP Period Table
|
// DSP Period Table
|
||||||
const std::vector<std::vector<uint16_t>> DspPeriodTable = {
|
const std::vector<std::vector<uint16_t>> DigitalSignalProcessorPeriodTable = {
|
||||||
// ... Your DSP period table here ...
|
// ... Your DSP period table here ...
|
||||||
};
|
};
|
||||||
|
|
||||||
// DSP Period Offset
|
// DSP Period Offset
|
||||||
const std::vector<uint16_t> DspPeriodOffset = {
|
const std::vector<uint16_t> DigitalSignalProcessorPeriodOffset = {
|
||||||
// ... Your DSP period offsets here ...
|
// ... Your DSP period offsets here ...
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ class Dsp {
|
|||||||
SamplePusher sample_pusher_;
|
SamplePusher sample_pusher_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Dsp() = default;
|
DigitalSignalProcessor() = default;
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,24 @@ namespace emu {
|
|||||||
|
|
||||||
void SPC700::Reset() {}
|
void SPC700::Reset() {}
|
||||||
|
|
||||||
|
void SPC700::Notify(uint32_t address, uint8_t data) {
|
||||||
|
// Check if the address corresponds to the APU's I/O ports
|
||||||
|
if (address >= 0x2140 && address <= 0x2143) {
|
||||||
|
// Handle the IPL process based on the address and data
|
||||||
|
if (address == 0x2140) {
|
||||||
|
// ... Handle data sent to APUIO0 ...
|
||||||
|
// For instance, checking for the $CC signal to start the APU program
|
||||||
|
} else if (address == 0x2141) {
|
||||||
|
// ... Handle data sent to APUIO1 ...
|
||||||
|
// This might involve storing data for the APU or signaling the DSP, etc.
|
||||||
|
} else if (address == 0x2142) {
|
||||||
|
// ... Handle data sent to APUIO2 ...
|
||||||
|
} else if (address == 0x2143) {
|
||||||
|
// ... Handle data sent to APUIO3 ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SPC700::ExecuteInstructions(uint8_t opcode) {
|
void SPC700::ExecuteInstructions(uint8_t opcode) {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
// 8-bit Move Memory to Register
|
// 8-bit Move Memory to Register
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ class SPC700 {
|
|||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
void Notify(uint32_t address, uint8_t data);
|
||||||
|
|
||||||
void ExecuteInstructions(uint8_t opcode);
|
void ExecuteInstructions(uint8_t opcode);
|
||||||
|
|
||||||
// Read a byte from the memory-mapped registers
|
// Read a byte from the memory-mapped registers
|
||||||
|
|||||||
@@ -379,6 +379,41 @@ void SNES::VBlankRoutine() {
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SNES::BootAPUWithIPL() {
|
||||||
|
// 1. Waiting for the SPC700 to be ready
|
||||||
|
while (!apu.IsReadySignalReceived()) {
|
||||||
|
// Active waiting (this can be optimized)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Setting the starting address
|
||||||
|
const uint16_t startAddress = 0x0200;
|
||||||
|
memory_.WriteByte(0x2142, startAddress & 0xFF); // Lower byte
|
||||||
|
memory_.WriteByte(0x2143, startAddress >> 8); // Upper byte
|
||||||
|
memory_.WriteByte(0x2141, 0xCC); // Any non-zero value
|
||||||
|
memory_.WriteByte(0x2140, 0xCC); // Signal to start
|
||||||
|
|
||||||
|
const int DATA_SIZE = 0x1000; // 4 KiB
|
||||||
|
|
||||||
|
// 3. Sending data (simplified)
|
||||||
|
// Assuming a buffer `audioData` containing the audio program/data
|
||||||
|
uint8_t audioData[DATA_SIZE]; // Define DATA_SIZE and populate audioData as
|
||||||
|
// needed
|
||||||
|
for (int i = 0; i < DATA_SIZE; ++i) {
|
||||||
|
memory_.WriteByte(0x2141, audioData[i]);
|
||||||
|
memory_.WriteByte(0x2140, i & 0xFF);
|
||||||
|
while (memory_.ReadByte(0x2140) != (i & 0xFF))
|
||||||
|
; // Wait for acknowledgment
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Running the SPC700 program
|
||||||
|
memory_.WriteByte(0x2142, startAddress & 0xFF); // Lower byte
|
||||||
|
memory_.WriteByte(0x2143, startAddress >> 8); // Upper byte
|
||||||
|
memory_.WriteByte(0x2141, 0x00); // Zero to start the program
|
||||||
|
memory_.WriteByte(0x2140, 0xCE); // Increment by 2
|
||||||
|
while (memory_.ReadByte(0x2140) != 0xCE)
|
||||||
|
; // Wait for acknowledgment
|
||||||
|
}
|
||||||
|
|
||||||
void SNES::HandleInput() {
|
void SNES::HandleInput() {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,9 @@ class SNES : public DMA {
|
|||||||
// VBlank routine
|
// VBlank routine
|
||||||
void VBlankRoutine();
|
void VBlankRoutine();
|
||||||
|
|
||||||
|
// Boot the APU with the IPL ROM
|
||||||
|
void BootAPUWithIPL();
|
||||||
|
|
||||||
// Controller input handling
|
// Controller input handling
|
||||||
void HandleInput();
|
void HandleInput();
|
||||||
|
|
||||||
|
|||||||
@@ -150,24 +150,6 @@ class CPUTest : public ::testing::Test {
|
|||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
using ::testing::Return;
|
using ::testing::Return;
|
||||||
|
|
||||||
TEST_F(CPUTest, CPURunTest) {
|
|
||||||
// Arrange
|
|
||||||
EXPECT_CALL(mock_clock, UpdateClock(_)).Times(1);
|
|
||||||
EXPECT_CALL(mock_clock, GetCycleCount()).WillOnce(Return(1));
|
|
||||||
EXPECT_CALL(mock_clock, ResetAccumulatedTime()).Times(1);
|
|
||||||
|
|
||||||
mock_memory.InsertMemory(0x00, {0x69, 0x01, 0xEA, 0xEA});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
mock_clock.UpdateClock(1.0);
|
|
||||||
cpu.Update();
|
|
||||||
mock_clock.ResetAccumulatedTime();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
// Google Mock will automatically check the expectations
|
|
||||||
ASSERT_THAT(cpu.A, ::testing::Eq(0x01));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Infrastructure
|
// Infrastructure
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user