cleanup spc700 and add todos

This commit is contained in:
scawful
2023-12-06 01:32:59 -05:00
parent d0c9229093
commit 87db938963
17 changed files with 559 additions and 633 deletions

View File

@@ -58,8 +58,9 @@ set(
app/emu/audio/spc700.cc
app/emu/audio/dsp.cc
app/emu/audio/internal/addressing.cc
app/emu/cpu/internal/instructions.cc
app/emu/audio/internal/instructions.cc
app/emu/cpu/internal/addressing.cc
app/emu/cpu/internal/instructions.cc
app/emu/cpu/cpu.cc
app/emu/video/ppu.cc
app/emu/memory/dma.cc

View File

@@ -41,6 +41,7 @@ absl::Status OverworldEditor::Update() {
gfx_group_editor_.InitBlockset(tile16_blockset_bmp_);
all_gfx_loaded_ = true;
} else if (!rom()->isLoaded() && all_gfx_loaded_) {
// TODO: Destroy the overworld graphics canvas.
// Reset the editor if the ROM is unloaded
Shutdown();
all_gfx_loaded_ = false;
@@ -226,8 +227,9 @@ void OverworldEditor::DrawOverworldMapSettings() {
ImGui::SetNextItemWidth(100.f);
ImGui::Combo("##World", &game_state_, kGamePartComboString, 3);
// TODO: Make enable grid bool change the current canvas.
TableNextColumn();
ImGui::Checkbox("Show grid", &opt_enable_grid); // TODO
ImGui::Checkbox("Show grid", &opt_enable_grid);
ImGui::EndTable();
}
}
@@ -521,14 +523,13 @@ void OverworldEditor::DrawTileSelector() {
ImGuiTabBarFlags_FittingPolicyScroll)) {
if (ImGui::BeginTabItem("Tile16")) {
gui::BitmapCanvasPipeline(blockset_canvas_, tile16_blockset_bmp_, 0x100,
(8192 * 2), 0x20, map_blockset_loaded_, true,
1);
(8192 * 2), 0x20, map_blockset_loaded_, true,
1);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Tile8")) {
if (ImGui::BeginChild("##tile8viewer",
ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
if (ImGui::BeginChild("##tile8viewer", ImGui::GetContentRegionAvail(),
true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
DrawTile8Selector();
}
ImGui::EndChild();
@@ -536,8 +537,8 @@ void OverworldEditor::DrawTileSelector() {
}
if (ImGui::BeginTabItem("Area Graphics")) {
gui::BitmapCanvasPipeline(current_gfx_canvas_, current_gfx_bmp_, 256,
0x10 * 0x40, 0x20, overworld_.isLoaded(), true,
3);
0x10 * 0x40, 0x20, overworld_.isLoaded(), true,
3);
ImGui::EndTabItem();
}
ImGui::EndTabBar();
@@ -557,13 +558,13 @@ absl::Status OverworldEditor::LoadGraphics() {
// Create the area graphics image
gui::BuildAndRenderBitmapPipeline(0x80, 0x200, 0x40,
overworld_.AreaGraphics(), *rom(),
current_gfx_bmp_, palette_);
overworld_.AreaGraphics(), *rom(),
current_gfx_bmp_, palette_);
// Create the tile16 blockset image
gui::BuildAndRenderBitmapPipeline(0x80, 0x2000, 0x80,
overworld_.Tile16Blockset(), *rom(),
tile16_blockset_bmp_, palette_);
overworld_.Tile16Blockset(), *rom(),
tile16_blockset_bmp_, palette_);
map_blockset_loaded_ = true;
// Copy the tile16 data into individual tiles.
@@ -653,10 +654,10 @@ absl::Status OverworldEditor::DrawExperimentalModal() {
&tile32_configuration_filename_);
ImGui::SameLine();
gui::FileDialogPipeline("ImportTile32Key", ".DAT,.dat\0", "Tile32 Hex File",
[this]() {
tile32_configuration_filename_ =
ImGuiFileDialog::Instance()->GetFilePathName();
});
[this]() {
tile32_configuration_filename_ =
ImGuiFileDialog::Instance()->GetFilePathName();
});
if (ImGui::Button("Load Prototype Overworld with ROM graphics")) {
RETURN_IF_ERROR(LoadGraphics())

View File

@@ -28,11 +28,9 @@ void APU::Init() {
}
void APU::Reset() {
// Reset the clock
clock_.ResetAccumulatedTime();
// Reset the SPC700
// ...
spc700_.Reset();
dsp_.Reset();
}
void APU::Update() {
@@ -51,6 +49,33 @@ void APU::Update() {
ProcessSamples();
}
void APU::Notify(uint32_t address, uint8_t data) {
if (address < 0x2140 || address > 0x2143) {
return;
}
auto offset = address - 0x2140;
spc700_.write(offset, data);
// HACK - This is a temporary solution to get the APU to play audio
ports_[address - 0x2140] = data;
switch (address) {
case 0x2140:
if (data == BEGIN_SIGNAL) {
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;
}
}
void APU::ProcessSamples() {
// Fetch sample data from AudioRam
// Iterate over all voices
@@ -62,63 +87,51 @@ void APU::ProcessSamples() {
int16_t processed_sample = dsp_.ProcessSample(voice_num, sample);
// Add the processed sample to the audio buffer
audioSamples_.push_back(processed_sample);
audio_samples_.push_back(processed_sample);
}
}
uint8_t APU::FetchSampleForVoice(uint8_t voice_num) {
// Define how you determine the address based on the voice_num
uint16_t address = CalculateAddressForVoice(voice_num);
return aram_.read(address);
}
uint16_t APU::CalculateAddressForVoice(uint8_t voice_num) {
// Placeholder logic to calculate the address in the AudioRam
// based on the voice number.
return voice_num; // Assuming each voice has a fixed size
// TODO: Calculate the address for the specified voice
return voice_num;
}
int16_t APU::GetNextSample() {
// This method fetches the next sample. If there's no sample available, it can
// return 0 or the last sample.
if (!audioSamples_.empty()) {
int16_t sample = audioSamples_.front();
audioSamples_.erase(audioSamples_.begin());
if (!audio_samples_.empty()) {
int16_t sample = audio_samples_.front();
audio_samples_.erase(audio_samples_.begin());
return sample;
}
return 0; // or return the last sample
}
uint8_t APU::ReadRegister(uint16_t address) {
// ...
}
void APU::WriteRegister(uint16_t address, uint8_t value) {
// ...
return 0; // TODO: Return the last sample instead of 0.
}
const std::vector<int16_t>& APU::GetAudioSamples() const {
// ...
return audio_samples_;
}
void APU::UpdateChannelSettings() {
// ...
// TODO: Implement this method to update the channel settings.
}
int16_t APU::GenerateSample(int channel) {
// ...
// TODO: Implement this method to generate a sample for the specified channel.
}
void APU::ApplyEnvelope(int channel) {
// ...
// TODO: Implement this method to apply an envelope to the specified channel.
}
uint8_t APU::ReadDSPMemory(uint16_t address) {
// ...
uint8_t APU::ReadDspMemory(uint16_t address) {
return dsp_.ReadGlobalReg(address);
}
void APU::WriteDSPMemory(uint16_t address, uint8_t value) {
// ...
void APU::WriteDspMemory(uint16_t address, uint8_t value) {
dsp_.WriteGlobalReg(address, value);
}
} // namespace emu

View File

@@ -39,134 +39,32 @@ const int apuClocksPerSample = 64; // 64 clocks per sample
class APU : public Observer {
public:
// Initializes the APU with the necessary resources and dependencies
APU(MemoryImpl &memory, AudioRam &aram, Clock &clock)
: aram_(aram), clock_(clock), memory_(memory) {}
void Init();
// Resets the APU to its initial state
void Reset();
// Runs the APU for one frame
void Update();
void Notify(uint32_t address, uint8_t data) override;
void ProcessSamples();
uint8_t FetchSampleForVoice(uint8_t voice_num);
uint16_t CalculateAddressForVoice(uint8_t voice_num);
int16_t GetNextSample();
void Notify(uint32_t address, uint8_t data) override {
ports_[address - 0x2140] = data;
switch (address) {
case 0x2140:
if (data == BEGIN_SIGNAL) {
BeginTransfer();
} else {
AcknowledgeSignal();
}
break;
case 0x2141:
// Handle data byte transfer here, if needed
break;
case 0x2142:
// Handle the setup of destination address, if needed
break;
case 0x2143:
// Handle additional communication or commands
break;
}
}
// Called upon a reset
void Initialize() {
spc700_.Reset();
dsp_.Reset();
// Set stack pointer, zero-page values, etc. for the SPC700
SignalReady();
}
// Set Port 0 = $AA and Port 1 = $BB
void SignalReady() {
// Set Port 0 = $AA and Port 1 = $BB
ports_[0] = READY_SIGNAL_0;
ports_[1] = READY_SIGNAL_1;
memory_.WriteByte(0x2140, READY_SIGNAL_0);
memory_.WriteByte(0x2141, READY_SIGNAL_1);
}
bool IsReadySignalReceived() const {
return ports_[0] == READY_SIGNAL_0 && ports_[1] == READY_SIGNAL_1;
}
void WaitForSignal() const {
// This might be an active wait or a passive state where APU does nothing
// until it's externally triggered by the main CPU writing to its ports.
while (ports_[0] != BEGIN_SIGNAL)
;
}
uint16_t ReadAddressFromPorts() const {
// Read 2 byte address from port 2 (low) and 3 (high)
return static_cast<uint16_t>(ports_[2]) |
(static_cast<uint16_t>(ports_[3]) << 8);
}
void AcknowledgeSignal() {
// Read value from Port 0 and write it back to Port 0
ports_[0] = ports_[0];
}
void BeginTransfer() {
const uint16_t startAddress = 0x0200;
// Write the starting address to ports 0x2142 and 0x2143
WriteToPort(2, static_cast<uint8_t>(startAddress & 0xFF)); // Lower byte
WriteToPort(3, static_cast<uint8_t>(startAddress >> 8)); // Upper byte
// Trigger the actual data transfer process
TriggerDataTransfer(startAddress);
}
void TriggerDataTransfer(uint16_t startAddress) {
const int DATA_SIZE = 0x1000; // Size of the data to be transferred
uint8_t audioData[DATA_SIZE]; // Buffer containing the audio data
// Load audioData as needed...
for (int i = 0; i < DATA_SIZE; ++i) {
WriteToPort(1, audioData[i]); // Write data byte
WriteToPort(0, i & 0xFF); // Write index and wait for acknowledgment
WaitForAcknowledgment(i & 0xFF);
}
// After transferring all data, trigger the execution of the program
StartSpcProgram(startAddress);
}
void WaitForAcknowledgment(uint8_t expectedIndex) {
while (ports_[0] != expectedIndex) {
// Active wait - consider implementing a more efficient mechanism
}
}
void StartSpcProgram(uint16_t startAddress) {
// Send the start address for execution
WriteToPort(2, static_cast<uint8_t>(startAddress & 0xFF)); // Lower byte
WriteToPort(3, static_cast<uint8_t>(startAddress >> 8)); // Upper byte
WriteToPort(1, 0x00); // Zero value indicates execution command
WriteToPort(0, 0xCE); // Send a unique signal to start execution
// Wait for acknowledgment
WaitForAcknowledgment(0xCE);
}
void ExecuteProgram() { spc700_.ExecuteInstructions(ReadAddressFromPorts()); }
void WriteToPort(uint8_t portNum, uint8_t value) {
ports_[portNum] = value;
switch (portNum) {
@@ -185,10 +83,6 @@ class APU : public Observer {
}
}
void SetReadyCallback(std::function<void()> callback) {
ready_callback_ = callback;
}
void UpdateClock(int delta_time) { clock_.UpdateClock(delta_time); }
// Method to fetch a sample from AudioRam
@@ -197,13 +91,7 @@ class APU : public Observer {
}
// Method to push a processed sample to the audio buffer
void PushToAudioBuffer(int16_t sample) { audioSamples_.push_back(sample); }
// Reads a byte from the specified APU register
uint8_t ReadRegister(uint16_t address);
// Writes a byte to the specified APU register
void WriteRegister(uint16_t address, uint8_t value);
void PushToAudioBuffer(int16_t sample) { audio_samples_.push_back(sample); }
// Returns the audio samples for the current frame
const std::vector<int16_t> &GetAudioSamples() const;
@@ -227,8 +115,8 @@ class APU : public Observer {
void ApplyEnvelope(int channel);
// Handles DSP (Digital Signal Processor) memory reads and writes
uint8_t ReadDSPMemory(uint16_t address);
void WriteDSPMemory(uint16_t address, uint8_t value);
uint8_t ReadDspMemory(uint16_t address);
void WriteDspMemory(uint16_t address, uint8_t value);
// Member variables to store internal APU state and resources
AudioRam &aram_;
@@ -237,7 +125,7 @@ class APU : public Observer {
DigitalSignalProcessor dsp_;
Spc700 spc700_{aram_};
std::vector<int16_t> audioSamples_;
std::vector<int16_t> audio_samples_;
std::function<void()> ready_callback_;
};

View File

@@ -4,358 +4,89 @@ namespace yaze {
namespace app {
namespace emu {
void Spc700::MOV(uint8_t& dest, uint8_t operand) {
dest = operand;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
// Immediate
uint8_t Spc700::imm() {
PC++;
return read(PC);
}
void Spc700::MOV_ADDR(uint16_t address, uint8_t operand) {
write(address, operand);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
// Direct page
uint8_t Spc700::dp() {
PC++;
uint8_t offset = read(PC);
return read((PSW.P << 8) + offset);
}
void Spc700::ADC(uint8_t& dest, uint8_t operand) {
uint16_t result = dest + operand + PSW.C;
PSW.V = ((A ^ result) & (operand ^ result) & 0x80);
PSW.C = (result > 0xFF);
PSW.Z = ((result & 0xFF) == 0);
PSW.N = (result & 0x80);
PSW.H = ((A ^ operand ^ result) & 0x10);
dest = result & 0xFF;
uint8_t Spc700::get_dp_addr() {
PC++;
uint8_t offset = read(PC);
return (PSW.P << 8) + offset;
}
void Spc700::SBC(uint8_t& dest, uint8_t operand) {
uint16_t result = dest - operand - (1 - PSW.C);
PSW.V = ((dest ^ result) & (dest ^ operand) & 0x80);
PSW.C = (result < 0x100);
PSW.Z = ((result & 0xFF) == 0);
PSW.N = (result & 0x80);
PSW.H = ((dest ^ operand ^ result) & 0x10);
dest = result & 0xFF;
// Direct page indexed by X
uint8_t Spc700::dp_plus_x() {
PC++;
uint8_t offset = read(PC);
return read((PSW.P << 8) + offset + X);
}
void Spc700::CMP(uint8_t& dest, uint8_t operand) {
uint16_t result = dest - operand;
PSW.C = (result < 0x100);
PSW.Z = ((result & 0xFF) == 0);
PSW.N = (result & 0x80);
// Direct page indexed by Y
uint8_t Spc700::dp_plus_y() {
PC++;
uint8_t offset = read(PC);
return read((PSW.P << 8) + offset + Y);
}
void Spc700::AND(uint8_t& dest, uint8_t operand) {
dest &= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
// Indexed indirect (add index before 16-bit lookup).
uint16_t Spc700::dp_plus_x_indirect() {
PC++;
uint8_t offset = read(PC);
uint16_t addr = read((PSW.P << 8) + offset + X) |
(read((PSW.P << 8) + offset + X + 1) << 8);
return addr;
}
void Spc700::OR(uint8_t& dest, uint8_t operand) {
dest |= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
// Indirect indexed (add index after 16-bit lookup).
uint16_t Spc700::dp_indirect_plus_y() {
PC++;
uint8_t offset = read(PC);
uint16_t baseAddr =
read((PSW.P << 8) + offset) | (read((PSW.P << 8) + offset + 1) << 8);
return baseAddr + Y;
}
void Spc700::EOR(uint8_t& dest, uint8_t operand) {
dest ^= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
uint16_t Spc700::abs() {
PC++;
uint16_t addr = read(PC) | (read(PC) << 8);
return addr;
}
void Spc700::ASL(uint8_t operand) {
PSW.C = (operand & 0x80);
operand <<= 1;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
// A = value;
int8_t Spc700::rel() {
PC++;
return static_cast<int8_t>(read(PC));
}
void Spc700::LSR(uint8_t& operand) {
PSW.C = (operand & 0x01);
operand >>= 1;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
uint8_t Spc700::i() { return read((PSW.P << 8) + X); }
uint8_t Spc700::i_postinc() {
uint8_t value = read((PSW.P << 8) + X);
X++;
return value;
}
void Spc700::ROL(uint8_t operand, bool isImmediate) {
uint8_t value = isImmediate ? imm() : operand;
uint8_t carry = PSW.C;
PSW.C = (value & 0x80);
value <<= 1;
value |= carry;
PSW.Z = (value == 0);
PSW.N = (value & 0x80);
// operand = value;
uint16_t Spc700::addr_plus_i() {
PC++;
uint16_t addr = read(PC) | (read(PC) << 8);
return read(addr) + X;
}
void Spc700::XCN(uint8_t operand, bool isImmediate) {
uint8_t value = isImmediate ? imm() : operand;
value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4);
PSW.Z = (value == 0);
PSW.N = (value & 0x80);
// operand = value;
uint16_t Spc700::addr_plus_i_indexed() {
PC++;
uint16_t addr = read(PC) | (read(PC) << 8);
addr += X;
return read(addr) | (read(addr + 1) << 8);
}
void Spc700::INC(uint8_t& operand) {
operand++;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::DEC(uint8_t& operand) {
operand--;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::MOVW(uint16_t& dest, uint16_t operand) {
dest = operand;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x8000);
}
void Spc700::INCW(uint16_t& operand) {
operand++;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x8000);
}
void Spc700::DECW(uint16_t& operand) {
operand--;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x8000);
}
void Spc700::ADDW(uint16_t& dest, uint16_t operand) {
uint32_t result = dest + operand;
PSW.C = (result > 0xFFFF);
PSW.Z = ((result & 0xFFFF) == 0);
PSW.N = (result & 0x8000);
PSW.V = ((dest ^ result) & (operand ^ result) & 0x8000);
dest = result & 0xFFFF;
}
void Spc700::SUBW(uint16_t& dest, uint16_t operand) {
uint32_t result = dest - operand;
PSW.C = (result < 0x10000);
PSW.Z = ((result & 0xFFFF) == 0);
PSW.N = (result & 0x8000);
PSW.V = ((dest ^ result) & (dest ^ operand) & 0x8000);
dest = result & 0xFFFF;
}
void Spc700::CMPW(uint16_t operand) {
uint32_t result = YA - operand;
PSW.C = (result < 0x10000);
PSW.Z = ((result & 0xFFFF) == 0);
PSW.N = (result & 0x8000);
}
void Spc700::MUL(uint8_t operand) {
uint16_t result = A * operand;
YA = result;
PSW.Z = (result == 0);
PSW.N = (result & 0x8000);
}
void Spc700::DIV(uint8_t operand) {
if (operand == 0) {
// Handle divide by zero error
return;
}
uint8_t quotient = A / operand;
uint8_t remainder = A % operand;
A = quotient;
Y = remainder;
PSW.Z = (quotient == 0);
PSW.N = (quotient & 0x80);
}
void Spc700::BRA(int8_t offset) { PC += offset; }
void Spc700::BEQ(int8_t offset) {
if (PSW.Z) {
PC += offset;
}
}
void Spc700::BNE(int8_t offset) {
if (!PSW.Z) {
PC += offset;
}
}
void Spc700::BCS(int8_t offset) {
if (PSW.C) {
PC += offset;
}
}
void Spc700::BCC(int8_t offset) {
if (!PSW.C) {
PC += offset;
}
}
void Spc700::BVS(int8_t offset) {
if (PSW.V) {
PC += offset;
}
}
void Spc700::BVC(int8_t offset) {
if (!PSW.V) {
PC += offset;
}
}
void Spc700::BMI(int8_t offset) {
if (PSW.N) {
PC += offset;
}
}
void Spc700::BPL(int8_t offset) {
if (!PSW.N) {
PC += offset;
}
}
void Spc700::BBS(uint8_t bit, uint8_t operand) {
if (operand & (1 << bit)) {
PC += rel();
}
}
void Spc700::BBC(uint8_t bit, uint8_t operand) {
if (!(operand & (1 << bit))) {
PC += rel();
}
}
// CBNE DBNZ
// JMP
void Spc700::JMP(uint16_t address) { PC = address; }
void Spc700::CALL(uint16_t address) {
uint16_t return_address = PC + 2;
write(SP, return_address & 0xFF);
write(SP - 1, (return_address >> 8) & 0xFF);
SP -= 2;
PC = address;
}
void Spc700::PCALL(uint8_t offset) {
uint16_t return_address = PC + 2;
write(SP, return_address & 0xFF);
write(SP - 1, (return_address >> 8) & 0xFF);
SP -= 2;
PC += offset;
}
void Spc700::TCALL(uint8_t offset) {
uint16_t return_address = PC + 2;
write(SP, return_address & 0xFF);
write(SP - 1, (return_address >> 8) & 0xFF);
SP -= 2;
PC = 0xFFDE + offset;
}
void Spc700::BRK() {
uint16_t return_address = PC + 2;
write(SP, return_address & 0xFF);
write(SP - 1, (return_address >> 8) & 0xFF);
SP -= 2;
PC = 0xFFDE;
}
void Spc700::RET() {
uint16_t return_address = read(SP) | (read(SP + 1) << 8);
SP += 2;
PC = return_address;
}
void Spc700::RETI() {
uint16_t return_address = read(SP) | (read(SP + 1) << 8);
SP += 2;
PC = return_address;
PSW.I = 1;
}
void Spc700::PUSH(uint8_t operand) {
write(SP, operand);
SP--;
}
void Spc700::POP(uint8_t& operand) {
SP++;
operand = read(SP);
}
void Spc700::SET1(uint8_t bit, uint8_t& operand) { operand |= (1 << bit); }
void Spc700::CLR1(uint8_t bit, uint8_t& operand) { operand &= ~(1 << bit); }
void Spc700::TSET1(uint8_t bit, uint8_t& operand) {
PSW.C = (operand & (1 << bit));
operand |= (1 << bit);
}
void Spc700::TCLR1(uint8_t bit, uint8_t& operand) {
PSW.C = (operand & (1 << bit));
operand &= ~(1 << bit);
}
void Spc700::AND1(uint8_t bit, uint8_t& operand) {
operand &= (1 << bit);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::OR1(uint8_t bit, uint8_t& operand) {
operand |= (1 << bit);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::EOR1(uint8_t bit, uint8_t& operand) {
operand ^= (1 << bit);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::NOT1(uint8_t bit, uint8_t& operand) {
operand ^= (1 << bit);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::MOV1(uint8_t bit, uint8_t& operand) {
PSW.C = (operand & (1 << bit));
operand |= (1 << bit);
}
void Spc700::CLRC() { PSW.C = 0; }
void Spc700::SETC() { PSW.C = 1; }
void Spc700::NOTC() { PSW.C = !PSW.C; }
void Spc700::CLRV() { PSW.V = 0; }
void Spc700::CLRP() { PSW.P = 0; }
void Spc700::SETP() { PSW.P = 1; }
void Spc700::EI() { PSW.I = 1; }
void Spc700::DI() { PSW.I = 0; }
void Spc700::NOP() { PC++; }
void Spc700::SLEEP() {}
void Spc700::STOP() {}
} // namespace emu
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,361 @@
#include "app/emu/audio/spc700.h"
namespace yaze {
namespace app {
namespace emu {
void Spc700::MOV(uint8_t& dest, uint8_t operand) {
dest = operand;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::MOV_ADDR(uint16_t address, uint8_t operand) {
write(address, operand);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::ADC(uint8_t& dest, uint8_t operand) {
uint16_t result = dest + operand + PSW.C;
PSW.V = ((A ^ result) & (operand ^ result) & 0x80);
PSW.C = (result > 0xFF);
PSW.Z = ((result & 0xFF) == 0);
PSW.N = (result & 0x80);
PSW.H = ((A ^ operand ^ result) & 0x10);
dest = result & 0xFF;
}
void Spc700::SBC(uint8_t& dest, uint8_t operand) {
uint16_t result = dest - operand - (1 - PSW.C);
PSW.V = ((dest ^ result) & (dest ^ operand) & 0x80);
PSW.C = (result < 0x100);
PSW.Z = ((result & 0xFF) == 0);
PSW.N = (result & 0x80);
PSW.H = ((dest ^ operand ^ result) & 0x10);
dest = result & 0xFF;
}
void Spc700::CMP(uint8_t& dest, uint8_t operand) {
uint16_t result = dest - operand;
PSW.C = (result < 0x100);
PSW.Z = ((result & 0xFF) == 0);
PSW.N = (result & 0x80);
}
void Spc700::AND(uint8_t& dest, uint8_t operand) {
dest &= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
}
void Spc700::OR(uint8_t& dest, uint8_t operand) {
dest |= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
}
void Spc700::EOR(uint8_t& dest, uint8_t operand) {
dest ^= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
}
void Spc700::ASL(uint8_t operand) {
PSW.C = (operand & 0x80);
operand <<= 1;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
// A = value;
}
void Spc700::LSR(uint8_t& operand) {
PSW.C = (operand & 0x01);
operand >>= 1;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::ROL(uint8_t operand, bool isImmediate) {
uint8_t value = isImmediate ? imm() : operand;
uint8_t carry = PSW.C;
PSW.C = (value & 0x80);
value <<= 1;
value |= carry;
PSW.Z = (value == 0);
PSW.N = (value & 0x80);
// operand = value;
}
void Spc700::XCN(uint8_t operand, bool isImmediate) {
uint8_t value = isImmediate ? imm() : operand;
value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4);
PSW.Z = (value == 0);
PSW.N = (value & 0x80);
// operand = value;
}
void Spc700::INC(uint8_t& operand) {
operand++;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::DEC(uint8_t& operand) {
operand--;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::MOVW(uint16_t& dest, uint16_t operand) {
dest = operand;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x8000);
}
void Spc700::INCW(uint16_t& operand) {
operand++;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x8000);
}
void Spc700::DECW(uint16_t& operand) {
operand--;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x8000);
}
void Spc700::ADDW(uint16_t& dest, uint16_t operand) {
uint32_t result = dest + operand;
PSW.C = (result > 0xFFFF);
PSW.Z = ((result & 0xFFFF) == 0);
PSW.N = (result & 0x8000);
PSW.V = ((dest ^ result) & (operand ^ result) & 0x8000);
dest = result & 0xFFFF;
}
void Spc700::SUBW(uint16_t& dest, uint16_t operand) {
uint32_t result = dest - operand;
PSW.C = (result < 0x10000);
PSW.Z = ((result & 0xFFFF) == 0);
PSW.N = (result & 0x8000);
PSW.V = ((dest ^ result) & (dest ^ operand) & 0x8000);
dest = result & 0xFFFF;
}
void Spc700::CMPW(uint16_t operand) {
uint32_t result = YA - operand;
PSW.C = (result < 0x10000);
PSW.Z = ((result & 0xFFFF) == 0);
PSW.N = (result & 0x8000);
}
void Spc700::MUL(uint8_t operand) {
uint16_t result = A * operand;
YA = result;
PSW.Z = (result == 0);
PSW.N = (result & 0x8000);
}
void Spc700::DIV(uint8_t operand) {
if (operand == 0) {
// Handle divide by zero error
return;
}
uint8_t quotient = A / operand;
uint8_t remainder = A % operand;
A = quotient;
Y = remainder;
PSW.Z = (quotient == 0);
PSW.N = (quotient & 0x80);
}
void Spc700::BRA(int8_t offset) { PC += offset; }
void Spc700::BEQ(int8_t offset) {
if (PSW.Z) {
PC += offset;
}
}
void Spc700::BNE(int8_t offset) {
if (!PSW.Z) {
PC += offset;
}
}
void Spc700::BCS(int8_t offset) {
if (PSW.C) {
PC += offset;
}
}
void Spc700::BCC(int8_t offset) {
if (!PSW.C) {
PC += offset;
}
}
void Spc700::BVS(int8_t offset) {
if (PSW.V) {
PC += offset;
}
}
void Spc700::BVC(int8_t offset) {
if (!PSW.V) {
PC += offset;
}
}
void Spc700::BMI(int8_t offset) {
if (PSW.N) {
PC += offset;
}
}
void Spc700::BPL(int8_t offset) {
if (!PSW.N) {
PC += offset;
}
}
void Spc700::BBS(uint8_t bit, uint8_t operand) {
if (operand & (1 << bit)) {
PC += rel();
}
}
void Spc700::BBC(uint8_t bit, uint8_t operand) {
if (!(operand & (1 << bit))) {
PC += rel();
}
}
// CBNE DBNZ
// JMP
void Spc700::JMP(uint16_t address) { PC = address; }
void Spc700::CALL(uint16_t address) {
uint16_t return_address = PC + 2;
write(SP, return_address & 0xFF);
write(SP - 1, (return_address >> 8) & 0xFF);
SP -= 2;
PC = address;
}
void Spc700::PCALL(uint8_t offset) {
uint16_t return_address = PC + 2;
write(SP, return_address & 0xFF);
write(SP - 1, (return_address >> 8) & 0xFF);
SP -= 2;
PC += offset;
}
void Spc700::TCALL(uint8_t offset) {
uint16_t return_address = PC + 2;
write(SP, return_address & 0xFF);
write(SP - 1, (return_address >> 8) & 0xFF);
SP -= 2;
PC = 0xFFDE + offset;
}
void Spc700::BRK() {
uint16_t return_address = PC + 2;
write(SP, return_address & 0xFF);
write(SP - 1, (return_address >> 8) & 0xFF);
SP -= 2;
PC = 0xFFDE;
}
void Spc700::RET() {
uint16_t return_address = read(SP) | (read(SP + 1) << 8);
SP += 2;
PC = return_address;
}
void Spc700::RETI() {
uint16_t return_address = read(SP) | (read(SP + 1) << 8);
SP += 2;
PC = return_address;
PSW.I = 1;
}
void Spc700::PUSH(uint8_t operand) {
write(SP, operand);
SP--;
}
void Spc700::POP(uint8_t& operand) {
SP++;
operand = read(SP);
}
void Spc700::SET1(uint8_t bit, uint8_t& operand) { operand |= (1 << bit); }
void Spc700::CLR1(uint8_t bit, uint8_t& operand) { operand &= ~(1 << bit); }
void Spc700::TSET1(uint8_t bit, uint8_t& operand) {
PSW.C = (operand & (1 << bit));
operand |= (1 << bit);
}
void Spc700::TCLR1(uint8_t bit, uint8_t& operand) {
PSW.C = (operand & (1 << bit));
operand &= ~(1 << bit);
}
void Spc700::AND1(uint8_t bit, uint8_t& operand) {
operand &= (1 << bit);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::OR1(uint8_t bit, uint8_t& operand) {
operand |= (1 << bit);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::EOR1(uint8_t bit, uint8_t& operand) {
operand ^= (1 << bit);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::NOT1(uint8_t bit, uint8_t& operand) {
operand ^= (1 << bit);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::MOV1(uint8_t bit, uint8_t& operand) {
PSW.C = (operand & (1 << bit));
operand |= (1 << bit);
}
void Spc700::CLRC() { PSW.C = 0; }
void Spc700::SETC() { PSW.C = 1; }
void Spc700::NOTC() { PSW.C = !PSW.C; }
void Spc700::CLRV() { PSW.V = 0; }
void Spc700::CLRP() { PSW.P = 0; }
void Spc700::SETP() { PSW.P = 1; }
void Spc700::EI() { PSW.I = 1; }
void Spc700::DI() { PSW.I = 0; }
void Spc700::NOP() { PC++; }
void Spc700::SLEEP() {}
void Spc700::STOP() {}
} // namespace emu
} // namespace app
} // namespace yaze

View File

@@ -12,20 +12,21 @@ namespace yaze {
namespace app {
namespace emu {
void Spc700::Reset() {}
void Spc700::Reset() {
PC = 0;
A = 0;
X = 0;
Y = 0;
SP = 0xFF;
PSW = ByteToFlags(0x00);
aram_.reset();
}
void Spc700::BootIplRom() {
PC = 0xFFC0;
A = 0;
X = 0;
Y = 0;
// for (int i = 0; i < 0x40; ++i) {
// uint8_t opcode = read(PC);
// ExecuteInstructions(opcode);
// PC++;
// }
int i = 0;
while (PC != 0xFFC0 + 0x3F) {
uint8_t opcode = read(PC);
@@ -615,16 +616,27 @@ void Spc700::ExecuteInstructions(uint8_t opcode) {
case 0x1F: // JMP [!abs+X]
break;
// . subroutines
// . subroutines
case 0x3F: // CALL !abs
{
CALL(abs());
break;
}
case 0x4F: // PCALL up
{
PCALL(imm());
break;
}
case 0x6F: // RET
{
RET();
break;
}
case 0x7F: // RETI
{
RETI();
break;
}
// . stack
case 0x2D: // PUSH A

View File

@@ -13,27 +13,23 @@ namespace emu {
class AudioRam {
public:
virtual ~AudioRam() = default;
// Read a byte from ARAM at the given address
virtual void reset() = 0;
virtual uint8_t read(uint16_t address) const = 0;
// Write a byte to ARAM at the given address
virtual void write(uint16_t address, uint8_t value) = 0;
};
class AudioRamImpl : public AudioRam {
static const size_t ARAM_SIZE = 64 * 1024; // 64 KB
static const int ARAM_SIZE = 0x10000;
std::vector<uint8_t> ram = std::vector<uint8_t>(ARAM_SIZE, 0);
public:
AudioRamImpl() = default;
void reset() override { ram = std::vector<uint8_t>(ARAM_SIZE, 0); }
// Read a byte from ARAM at the given address
uint8_t read(uint16_t address) const override {
return ram[address % ARAM_SIZE];
}
// Write a byte to ARAM at the given address
void write(uint16_t address, uint8_t value) override {
ram[address % ARAM_SIZE] = value;
}
@@ -54,17 +50,14 @@ class Spc700 {
public:
explicit Spc700(AudioRam& aram) : aram_(aram) {}
uint8_t test_register_;
uint8_t control_register_;
uint8_t dsp_address_register_;
// Registers
uint8_t A = 0; // 8-bit accumulator
uint8_t X = 0; // 8-bit index
uint8_t Y = 0; // 8-bit index
uint16_t YA = 0; // 16-bit pair of A (lsb) and Y (msb)
uint16_t PC = 0; // program counter
uint8_t SP = 0; // stack pointer
uint8_t A = 0x00; // 8-bit accumulator
uint8_t X = 0x00; // 8-bit index
uint8_t Y = 0x00; // 8-bit index
uint16_t YA = 0x00; // 16-bit pair of A (lsb) and Y (msb)
uint16_t PC = 0xFFC0; // program counter
uint8_t SP = 0x00; // stack pointer
struct Flags {
uint8_t N : 1; // Negative flag
@@ -105,42 +98,26 @@ class Spc700 {
// Read a byte from the memory-mapped registers
uint8_t read(uint16_t address) {
switch (address) {
case 0xF0:
return test_register_;
case 0xF1:
return control_register_;
case 0xF2:
return dsp_address_register_;
default:
if (address < 0xFFC0) {
return aram_.read(address);
} else {
// Handle IPL ROM or RAM reads here
return ipl_rom_[address - 0xFFC0];
}
if (address < 0xFFC0) {
return aram_.read(address);
} else {
// Check if register is set to unmap the IPL ROM
if (read(0xF1) & 0x80) {
return aram_.read(address);
}
return ipl_rom_[address - 0xFFC0];
}
return 0;
}
// Write a byte to the memory-mapped registers
void write(uint16_t address, uint8_t value) {
switch (address) {
case 0xF0:
test_register_ = value;
break;
case 0xF1:
control_register_ = value;
break;
case 0xF2:
dsp_address_register_ = value;
break;
default:
if (address < 0xFFC0) {
aram_.write(address, value);
} else {
// Handle IPL ROM or RAM writes here
}
if (address < 0xFFC0) {
aram_.write(address, value);
} else {
// Check if register is set to unmap the IPL ROM
if (read(0xF1) & 0x80) {
aram_.write(address, value);
}
}
}
@@ -148,87 +125,36 @@ class Spc700 {
// Addressing modes
// Immediate
uint8_t imm() {
PC++;
return read(PC);
}
uint8_t imm();
// Direct page
uint8_t dp() {
PC++;
uint8_t offset = read(PC);
return read((PSW.P << 8) + offset);
}
uint8_t dp();
uint8_t get_dp_addr() {
PC++;
uint8_t offset = read(PC);
return (PSW.P << 8) + offset;
}
uint8_t get_dp_addr();
// Direct page indexed by X
uint8_t dp_plus_x() {
PC++;
uint8_t offset = read(PC);
return read((PSW.P << 8) + offset + X);
}
uint8_t dp_plus_x();
// Direct page indexed by Y
uint8_t dp_plus_y() {
PC++;
uint8_t offset = read(PC);
return read((PSW.P << 8) + offset + Y);
}
uint8_t dp_plus_y();
// Indexed indirect (add index before 16-bit lookup).
uint16_t dp_plus_x_indirect() {
PC++;
uint8_t offset = read(PC);
uint16_t addr = read((PSW.P << 8) + offset + X) |
(read((PSW.P << 8) + offset + X + 1) << 8);
return addr;
}
uint16_t dp_plus_x_indirect();
// Indirect indexed (add index after 16-bit lookup).
uint16_t dp_indirect_plus_y() {
PC++;
uint8_t offset = read(PC);
uint16_t baseAddr =
read((PSW.P << 8) + offset) | (read((PSW.P << 8) + offset + 1) << 8);
return baseAddr + Y;
}
uint16_t dp_indirect_plus_y();
uint16_t abs() {
PC++;
uint16_t addr = read(PC) | (read(PC) << 8);
return addr;
}
uint16_t abs();
int8_t rel() {
PC++;
return static_cast<int8_t>(read(PC));
}
int8_t rel();
uint8_t i() { return read((PSW.P << 8) + X); }
uint8_t i();
uint8_t i_postinc() {
uint8_t value = read((PSW.P << 8) + X);
X++;
return value;
}
uint8_t i_postinc();
uint16_t addr_plus_i() {
PC++;
uint16_t addr = read(PC) | (read(PC) << 8);
return read(addr) + X;
}
uint16_t addr_plus_i();
uint16_t addr_plus_i_indexed() {
PC++;
uint16_t addr = read(PC) | (read(PC) << 8);
addr += X;
return read(addr) | (read(addr + 1) << 8);
}
uint16_t addr_plus_i_indexed();
// ==========================================================================
// Instructions

View File

@@ -1876,6 +1876,7 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) {
}
}
// TODO: Implement 65816 interrupts.
void CPU::HandleInterrupts() {
if (GetInterruptFlag()) {
return;

View File

@@ -178,7 +178,6 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// LDA addr, Y
uint32_t AbsoluteIndexedY();
// Test Me :)
// Effective Address:
// Bank: Program Bank Register (PBR)
// High/low: The Indirect Address

View File

@@ -329,17 +329,6 @@ void SNES::VBlankRoutine() {
// ...
}
void SNES::BootApuWithIPL() {
// 1. Check if the SPC700 is ready, else set a callback for when it becomes
// ready
if (!apu_.IsReadySignalReceived()) {
apu_.SetReadyCallback([this]() { this->StartApuDataTransfer(); });
return; // Exit and wait for callback to be called
}
StartApuDataTransfer();
}
void SNES::StartApuDataTransfer() {
// 2. Setting the starting address
const uint16_t startAddress = 0x0200;

View File

@@ -389,6 +389,7 @@ void Canvas::DrawGrid(float grid_step) {
draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
if (enable_grid_) {
if (custom_step_ != 0.f) grid_step = custom_step_;
grid_step *= global_scale_; // Apply global scale to grid step
for (float x = fmodf(scrolling_.x, grid_step); x < canvas_sz_.x;
x += grid_step)

View File

@@ -96,7 +96,7 @@ class Canvas {
bool custom_canvas_size_ = false;
bool is_hovered_ = false;
float custom_step_ = 8.0f;
float custom_step_ = 0.0f;
float global_scale_ = 1.0f;
ImDrawList* draw_list_;

View File

@@ -202,6 +202,7 @@ absl::StatusOr<Bytes> ROM::Load2BppGraphics() {
return sheet;
}
// TODO: Load Links graphics from the ROM
absl::Status ROM::LoadLinkGraphics() {
const auto link_gfx_offset = 81920; // $10:8000
const auto link_gfx_length = 0x800;

View File

@@ -7,7 +7,7 @@
#include <string>
#include <vector>
#include "app/emu/cpu.h"
#include "app/emu/cpu/cpu.h"
#include "app/emu/memory/memory.h"
#include "app/emu/video/ppu.h"
#include "app/gfx/snes_palette.h"
@@ -147,7 +147,7 @@ class DungeonObjectRenderer : public SharedROM {
int i = 0;
while (true) {
uint8_t opcode = cpu.FetchByte();
uint8_t opcode = cpu.ReadByte(cpu.PC);
cpu.ExecuteInstruction(opcode);
cpu.HandleInterrupts();
@@ -156,7 +156,7 @@ class DungeonObjectRenderer : public SharedROM {
}
i++;
UpdateObjectBitmap();
// UpdateObjectBitmap();
}
}

View File

@@ -26,6 +26,7 @@ add_executable(
../src/app/emu/cpu/internal/instructions.cc
../src/app/emu/cpu/internal/addressing.cc
../src/app/emu/audio/internal/addressing.cc
../src/app/emu/audio/internal/instructions.cc
../src/app/emu/audio/apu.cc
../src/app/emu/video/ppu.cc
../src/app/emu/audio/dsp.cc

View File

@@ -11,8 +11,9 @@ namespace emu {
using testing::_;
using testing::Return;
class MockAudioRAM : public AudioRam {
class MockAudioRam : public AudioRam {
public:
MOCK_METHOD(void, reset, (), (override));
MOCK_METHOD(uint8_t, read, (uint16_t address), (const, override));
MOCK_METHOD(void, write, (uint16_t address, uint8_t value), (override));
};
@@ -21,7 +22,7 @@ class Spc700Test : public ::testing::Test {
public:
Spc700Test() = default;
testing::NiceMock<MockAudioRAM> audioRAM;
testing::NiceMock<MockAudioRam> audioRAM;
Spc700 spc700{audioRAM};
};