cleanup SNES

This commit is contained in:
scawful
2024-04-24 23:34:43 -04:00
parent 4c466d5ab8
commit c3379d8adc
2 changed files with 118 additions and 99 deletions

View File

@@ -19,22 +19,29 @@ namespace yaze {
namespace app { namespace app {
namespace emu { namespace emu {
void SNES::Init(Rom& rom) { namespace {
// Initialize CPU void input_latch(Input* input, bool value) {
input->latch_line_ = value;
if (input->latch_line_) input->latched_state_ = input->current_state_;
}
uint8_t input_read(Input* input) {
if (input->latch_line_) input->latched_state_ = input->current_state_;
uint8_t ret = input->latched_state_ & 1;
input->latched_state_ >>= 1;
input->latched_state_ |= 0x8000;
return ret;
}
} // namespace
void SNES::Init(std::vector<uint8_t>& rom_data) {
// Initialize the CPU, PPU, and APU
cpu_.Init(); cpu_.Init();
// Initialize PPU
ppu_.Init(); ppu_.Init();
// Initialize APU
apu_.Init(); apu_.Init();
// Load the ROM into memory and set up the memory mapping // Load the ROM into memory and set up the memory mapping
rom_data = rom.vector();
memory_.Initialize(rom_data); memory_.Initialize(rom_data);
// Read the ROM header
// rom_info_ = memory_.ReadRomHeader();
Reset(true); Reset(true);
running_ = true; running_ = true;
@@ -45,11 +52,10 @@ void SNES::Reset(bool hard) {
apu_.Reset(); apu_.Reset();
ppu_.Reset(); ppu_.Reset();
memory::dma::Reset(&memory_); memory::dma::Reset(&memory_);
input1.latchLine = false; input1.latch_line_ = false;
input2.latchLine = false; input2.latch_line_ = false;
input1.latchedState = 0; input1.latched_state_ = 0;
input2.latchedState = 0; input2.latched_state_ = 0;
// cart_reset();
if (hard) memset(ram, 0, sizeof(ram)); if (hard) memset(ram, 0, sizeof(ram));
ram_adr_ = 0; ram_adr_ = 0;
memory_.set_h_pos(0); memory_.set_h_pos(0);
@@ -70,15 +76,15 @@ void SNES::Reset(bool hard) {
memset(port_auto_read_, 0, sizeof(port_auto_read_)); memset(port_auto_read_, 0, sizeof(port_auto_read_));
auto_joy_read_ = false; auto_joy_read_ = false;
auto_joy_timer_ = 0; auto_joy_timer_ = 0;
ppuLatch = false; ppu_latch_ = false;
multiply_a_ = 0xff; multiply_a_ = 0xff;
multiply_result_ = 0xFE01; multiply_result_ = 0xFE01;
divide_a_ = 0xffFF; divide_a_ = 0xffFF;
divide_result_ = 0x101; divide_result_ = 0x101;
fast_mem_ = false; fast_mem_ = false;
memory_.set_open_bus(0); memory_.set_open_bus(0);
nextHoriEvent = 16; next_horiz_event = 16;
build_accesstime(false); InitAccessTime(false);
} }
void SNES::RunFrame() { void SNES::RunFrame() {
@@ -91,25 +97,7 @@ void SNES::RunFrame() {
} }
} }
void SNES::CatchUpApu() { void SNES::CatchUpApu() { apu_.RunCycles(cycles_); }
apu_.RunCycles(cycles_);
}
namespace {
void input_latch(Input* input, bool value) {
input->latchLine = value;
if (input->latchLine) input->latchedState = input->currentState;
}
uint8_t input_read(Input* input) {
if (input->latchLine) input->latchedState = input->currentState;
uint8_t ret = input->latchedState & 1;
input->latchedState >>= 1;
input->latchedState |= 0x8000;
return ret;
}
} // namespace
void SNES::HandleInput() { void SNES::HandleInput() {
memset(port_auto_read_, 0, sizeof(port_auto_read_)); memset(port_auto_read_, 0, sizeof(port_auto_read_));
@@ -130,95 +118,109 @@ void SNES::HandleInput() {
void SNES::RunCycle() { void SNES::RunCycle() {
cycles_ += 2; cycles_ += 2;
// check for h/v timer irq's // check for h/v timer irq's
bool condition = ( bool condition = ((v_irq_enabled_ || h_irq_enabled_) &&
(v_irq_enabled_ || h_irq_enabled_) && (memory_.v_pos() == v_timer_ || !v_irq_enabled_) &&
(memory_.v_pos() == v_timer_ || !v_irq_enabled_) && (memory_.h_pos() == h_timer_ || !h_irq_enabled_));
(memory_.h_pos() == h_timer_ || !h_irq_enabled_)
); if (!irq_condition_ && condition) {
if(!irq_condition_ && condition) {
in_irq_ = true; in_irq_ = true;
cpu_.SetIrq(true); cpu_.SetIrq(true);
} }
irq_condition_ = condition; irq_condition_ = condition;
// increment position; must come after irq checks! (hagane, cybernator) // increment position; must come after irq checks! (hagane, cybernator)
memory_.set_h_pos(memory_.h_pos() + 2); memory_.set_h_pos(memory_.h_pos() + 2);
// handle positional stuff // handle positional stuff
if (memory_.h_pos() == nextHoriEvent) { if (memory_.h_pos() == next_horiz_event) {
switch (memory_.h_pos()) { switch (memory_.h_pos()) {
case 16: { case 16: {
nextHoriEvent = 512; next_horiz_event = 512;
if(memory_.v_pos() == 0) memory_.init_hdma_request(); if (memory_.v_pos() == 0) memory_.init_hdma_request();
} break; } break;
case 512: { case 512: {
nextHoriEvent = 1104; next_horiz_event = 1104;
// render the line halfway of the screen for better compatibility // render the line halfway of the screen for better compatibility
if(!in_vblank_ && memory_.v_pos() > 0) ppu_.RunLine(memory_.v_pos()); if (!in_vblank_ && memory_.v_pos() > 0) ppu_.RunLine(memory_.v_pos());
} break; } break;
case 1104: { case 1104: {
if(!in_vblank_) memory_.run_hdma_request(); if (!in_vblank_) memory_.run_hdma_request();
if(!memory_.pal_timing()) { if (!memory_.pal_timing()) {
// line 240 of odd frame with no interlace is 4 cycles shorter // line 240 of odd frame with no interlace is 4 cycles shorter
// if((memory_.h_pos() == 1360 && memory_.v_pos() == 240 && !ppu_evenFrame() && !ppu_frameInterlace()) || memory_.h_pos() == 1364) { next_horiz_event = (memory_.v_pos() == 240 && !ppu_.even_frame &&
nextHoriEvent = (memory_.v_pos() == 240 && !ppu_.even_frame && !ppu_.frame_interlace) ? 1360 : 1364; !ppu_.frame_interlace)
? 1360
: 1364;
} else { } else {
// line 311 of odd frame with interlace is 4 cycles longer // line 311 of odd frame with interlace is 4 cycles longer
// if((memory_.h_pos() == 1364 && (memory_.v_pos() != 311 || ppu_evenFrame() || !ppu_frameInterlace())) || memory_.h_pos() == 1368) next_horiz_event = (memory_.v_pos() != 311 || ppu_.even_frame ||
nextHoriEvent = (memory_.v_pos() != 311 || ppu_.even_frame || !ppu_.frame_interlace) ? 1364 : 1368; !ppu_.frame_interlace)
? 1364
: 1368;
} }
} break; } break;
case 1360: case 1360:
case 1364: case 1364:
case 1368: { // this is the end (of the h-line) case 1368: { // this is the end (of the h-line)
nextHoriEvent = 16; next_horiz_event = 16;
memory_.set_h_pos(0); memory_.set_h_pos(0);
memory_.set_v_pos(memory_.v_pos() + 1); memory_.set_v_pos(memory_.v_pos() + 1);
if(!memory_.pal_timing()) { if (!memory_.pal_timing()) {
// even interlace frame is 263 lines // even interlace frame is 263 lines
if((memory_.v_pos() == 262 && (!ppu_.frame_interlace || !ppu_.even_frame)) || memory_.v_pos() == 263) { if ((memory_.v_pos() == 262 &&
(!ppu_.frame_interlace || !ppu_.even_frame)) ||
memory_.v_pos() == 263) {
memory_.set_v_pos(0); memory_.set_v_pos(0);
frames_++; frames_++;
} }
} else { } else {
// even interlace frame is 313 lines // even interlace frame is 313 lines
if((memory_.v_pos() == 312 && (!ppu_.frame_interlace || !ppu_.even_frame)) || memory_.v_pos() == 313) { if ((memory_.v_pos() == 312 &&
(!ppu_.frame_interlace || !ppu_.even_frame)) ||
memory_.v_pos() == 313) {
memory_.set_v_pos(0); memory_.set_v_pos(0);
frames_++; frames_++;
} }
} }
// end of hblank, do most memory_.v_pos()-tests // end of hblank, do most memory_.v_pos()-tests
bool startingVblank = false; bool starting_vblank = false;
if(memory_.v_pos() == 0) { if (memory_.v_pos() == 0) {
// end of vblank // end of vblank
in_vblank_ = false; in_vblank_ = false;
in_nmi_ = false; in_nmi_ = false;
ppu_.HandleFrameStart(); ppu_.HandleFrameStart();
} else if(memory_.v_pos() == 225) { } else if (memory_.v_pos() == 225) {
// ask the ppu if we start vblank now or at memory_.v_pos() 240 (overscan) // ask the ppu if we start vblank now or at memory_.v_pos() 240
startingVblank = !ppu_.CheckOverscan(); // (overscan)
} else if(memory_.v_pos() == 240){ starting_vblank = !ppu_.CheckOverscan();
// if we are not yet in vblank, we had an overscan frame, set startingVblank } else if (memory_.v_pos() == 240) {
if(!in_vblank_) startingVblank = true; // if we are not yet in vblank, we had an overscan frame, set
// starting_vblank
if (!in_vblank_) starting_vblank = true;
} }
if(startingVblank) { if (starting_vblank) {
// catch up the apu at end of emulated frame (we end frame @ start of vblank) // catch up the apu at end of emulated frame (we end frame @ start of
// vblank)
CatchUpApu(); CatchUpApu();
// notify dsp of frame-end, because sometimes dma will extend much further past vblank (or even into the next frame) // notify dsp of frame-end, because sometimes dma will extend much
// Megaman X2 (titlescreen animation), Tales of Phantasia (game demo), Actraiser 2 (fade-in @ bootup) // further past vblank (or even into the next frame) Megaman X2
// dsp_.newFrame(); // (titlescreen animation), Tales of Phantasia (game demo), Actraiser
// 2 (fade-in @ bootup)
apu_.dsp().NewFrame();
// we are starting vblank // we are starting vblank
ppu_.HandleVblank(); ppu_.HandleVblank();
in_vblank_ = true; in_vblank_ = true;
in_nmi_ = true; in_nmi_ = true;
if(auto_joy_read_) { if (auto_joy_read_) {
// TODO: this starts a little after start of vblank // TODO: this starts a little after start of vblank
auto_joy_timer_ = 4224; auto_joy_timer_ = 4224;
HandleInput(); HandleInput();
} }
if(nmi_enabled_) { if (nmi_enabled_) {
cpu_.Nmi(); cpu_.Nmi();
} }
} }
@@ -226,7 +228,7 @@ void SNES::RunCycle() {
} }
} }
// handle auto_joy_read_-timer // handle auto_joy_read_-timer
if(auto_joy_timer_ > 0) auto_joy_timer_ -= 2; if (auto_joy_timer_ > 0) auto_joy_timer_ -= 2;
} }
void SNES::RunCycles(int cycles) { void SNES::RunCycles(int cycles) {
@@ -252,7 +254,7 @@ void SNES::SyncCycles(bool start, int sync_cycles) {
uint8_t SNES::ReadBBus(uint8_t adr) { uint8_t SNES::ReadBBus(uint8_t adr) {
if (adr < 0x40) { if (adr < 0x40) {
return ppu_.Read(adr); return ppu_.Read(adr, ppu_latch_);
} }
if (adr < 0x80) { if (adr < 0x80) {
CatchUpApu(); // catch up the apu before reading CatchUpApu(); // catch up the apu before reading
@@ -287,7 +289,7 @@ uint8_t SNES::ReadReg(uint16_t adr) {
return val | (memory_.open_bus() & 0x3e); return val | (memory_.open_bus() & 0x3e);
} }
case 0x4213: { case 0x4213: {
return ppuLatch << 7; // IO-port return ppu_latch_ << 7; // IO-port
} }
case 0x4214: { case 0x4214: {
return divide_result_ & 0xff; return divide_result_ & 0xff;
@@ -405,11 +407,11 @@ void SNES::WriteReg(uint16_t adr, uint8_t val) {
break; break;
} }
case 0x4201: { case 0x4201: {
if (!(val & 0x80) && ppuLatch) { if (!(val & 0x80) && ppu_latch_) {
// latch the ppu // latch the ppu
ppu_.Read(0x37); ppu_.LatchHV();
} }
ppuLatch = val & 0x80; ppu_latch_ = val & 0x80;
break; break;
} }
case 0x4202: { case 0x4202: {
@@ -529,7 +531,7 @@ uint8_t SNES::CpuRead(uint32_t adr) {
void SNES::CpuWrite(uint32_t adr, uint8_t val) { void SNES::CpuWrite(uint32_t adr, uint8_t val) {
cpu_.set_int_delay(false); cpu_.set_int_delay(false);
const int cycles = access_time[adr]; // GetAccessTime(adr); const int cycles = access_time[adr];
memory::dma::HandleDma(this, &memory_, cycles_); memory::dma::HandleDma(this, &memory_, cycles_);
RunCycles(cycles_); RunCycles(cycles_);
Write(adr, val); Write(adr, val);
@@ -547,6 +549,31 @@ void SNES::SetSamples(int16_t* sample_data, int wanted_samples) {
void SNES::SetPixels(uint8_t* pixel_data) { ppu_.PutPixels(pixel_data); } void SNES::SetPixels(uint8_t* pixel_data) { ppu_.PutPixels(pixel_data); }
void SNES::SetButtonState(int player, int button, bool pressed) {
// set key in controller
if (player == 1) {
if (pressed) {
input1.current_state_ |= 1 << button;
} else {
input1.current_state_ &= ~(1 << button);
}
} else {
if (pressed) {
input2.current_state_ |= 1 << button;
} else {
input2.current_state_ &= ~(1 << button);
}
}
}
void SNES::InitAccessTime(bool recalc) {
int start = (recalc) ? 0x800000 : 0; // recalc only updates fast rom
access_time.resize(0x1000000);
for (int i = start; i < 0x1000000; i++) {
access_time[i] = GetAccessTime(i);
}
}
} // namespace emu } // namespace emu
} // namespace app } // namespace app
} // namespace yaze } // namespace yaze

View File

@@ -22,10 +22,10 @@ namespace emu {
struct Input { struct Input {
uint8_t type; uint8_t type;
// latchline // latchline
bool latchLine; bool latch_line_;
// for controller // for controller
uint16_t currentState; // actual state uint16_t current_state_; // actual state
uint16_t latchedState; uint16_t latched_state_;
}; };
class SNES { class SNES {
@@ -34,7 +34,7 @@ class SNES {
~SNES() = default; ~SNES() = default;
// Initialization // Initialization
void Init(Rom& rom); void Init(std::vector<uint8_t>& rom_data);
void Reset(bool hard = false); void Reset(bool hard = false);
// Emulation // Emulation
@@ -65,26 +65,18 @@ class SNES {
void SetSamples(int16_t* sample_data, int wanted_samples); void SetSamples(int16_t* sample_data, int wanted_samples);
void SetPixels(uint8_t* pixel_data); void SetPixels(uint8_t* pixel_data);
void SetButtonState(int player, int button, bool pressed);
bool running() const { return running_; } bool running() const { return running_; }
auto cpu() -> Cpu& { return cpu_; } auto cpu() -> Cpu& { return cpu_; }
auto ppu() -> video::Ppu& { return ppu_; } auto ppu() -> video::Ppu& { return ppu_; }
auto apu() -> audio::Apu& { return apu_; } auto apu() -> audio::Apu& { return apu_; }
auto Memory() -> memory::MemoryImpl& { return memory_; } auto Memory() -> memory::MemoryImpl& { return memory_; }
auto get_ram() -> uint8_t* { return ram; } auto get_ram() -> uint8_t* { return ram; }
auto mutable_cycles() -> uint64_t& { return cycles_; } auto mutable_cycles() -> uint64_t& { return cycles_; }
void InitAccessTime(bool recalc);
std::vector<uint8_t> access_time; std::vector<uint8_t> access_time;
void build_accesstime( bool recalc) {
int start = (recalc) ? 0x800000 : 0; // recalc only updates "fastMem" area
access_time.resize(0x1000000);
for (int i = start; i < 0x1000000; i++) {
access_time[i] = GetAccessTime(i);
}
}
private: private:
// Components of the SNES // Components of the SNES
ClockImpl clock_; ClockImpl clock_;
@@ -116,7 +108,7 @@ class SNES {
uint64_t cycles_ = 0; uint64_t cycles_ = 0;
uint64_t sync_cycle_ = 0; uint64_t sync_cycle_ = 0;
double apu_catchup_cycles_; double apu_catchup_cycles_;
uint32_t nextHoriEvent; uint32_t next_horiz_event;
// Nmi / Irq // Nmi / Irq
bool h_irq_enabled_ = false; bool h_irq_enabled_ = false;
@@ -141,7 +133,7 @@ class SNES {
uint16_t port_auto_read_[4]; // as read by auto-joypad read uint16_t port_auto_read_[4]; // as read by auto-joypad read
bool auto_joy_read_ = false; bool auto_joy_read_ = false;
uint16_t auto_joy_timer_ = 0; uint16_t auto_joy_timer_ = 0;
bool ppuLatch; bool ppu_latch_;
bool fast_mem_ = false; bool fast_mem_ = false;
}; };