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 emu {
void SNES::Init(Rom& rom) {
// Initialize CPU
namespace {
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();
// Initialize PPU
ppu_.Init();
// Initialize APU
apu_.Init();
// Load the ROM into memory and set up the memory mapping
rom_data = rom.vector();
memory_.Initialize(rom_data);
// Read the ROM header
// rom_info_ = memory_.ReadRomHeader();
Reset(true);
running_ = true;
@@ -45,11 +52,10 @@ void SNES::Reset(bool hard) {
apu_.Reset();
ppu_.Reset();
memory::dma::Reset(&memory_);
input1.latchLine = false;
input2.latchLine = false;
input1.latchedState = 0;
input2.latchedState = 0;
// cart_reset();
input1.latch_line_ = false;
input2.latch_line_ = false;
input1.latched_state_ = 0;
input2.latched_state_ = 0;
if (hard) memset(ram, 0, sizeof(ram));
ram_adr_ = 0;
memory_.set_h_pos(0);
@@ -70,15 +76,15 @@ void SNES::Reset(bool hard) {
memset(port_auto_read_, 0, sizeof(port_auto_read_));
auto_joy_read_ = false;
auto_joy_timer_ = 0;
ppuLatch = false;
ppu_latch_ = false;
multiply_a_ = 0xff;
multiply_result_ = 0xFE01;
divide_a_ = 0xffFF;
divide_result_ = 0x101;
fast_mem_ = false;
memory_.set_open_bus(0);
nextHoriEvent = 16;
build_accesstime(false);
next_horiz_event = 16;
InitAccessTime(false);
}
void SNES::RunFrame() {
@@ -91,25 +97,7 @@ void SNES::RunFrame() {
}
}
void SNES::CatchUpApu() {
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::CatchUpApu() { apu_.RunCycles(cycles_); }
void SNES::HandleInput() {
memset(port_auto_read_, 0, sizeof(port_auto_read_));
@@ -130,95 +118,109 @@ void SNES::HandleInput() {
void SNES::RunCycle() {
cycles_ += 2;
// check for h/v timer irq's
bool condition = (
(v_irq_enabled_ || h_irq_enabled_) &&
(memory_.v_pos() == v_timer_ || !v_irq_enabled_) &&
(memory_.h_pos() == h_timer_ || !h_irq_enabled_)
);
if(!irq_condition_ && condition) {
bool condition = ((v_irq_enabled_ || h_irq_enabled_) &&
(memory_.v_pos() == v_timer_ || !v_irq_enabled_) &&
(memory_.h_pos() == h_timer_ || !h_irq_enabled_));
if (!irq_condition_ && condition) {
in_irq_ = true;
cpu_.SetIrq(true);
}
irq_condition_ = condition;
// increment position; must come after irq checks! (hagane, cybernator)
memory_.set_h_pos(memory_.h_pos() + 2);
// handle positional stuff
if (memory_.h_pos() == nextHoriEvent) {
if (memory_.h_pos() == next_horiz_event) {
switch (memory_.h_pos()) {
case 16: {
nextHoriEvent = 512;
if(memory_.v_pos() == 0) memory_.init_hdma_request();
next_horiz_event = 512;
if (memory_.v_pos() == 0) memory_.init_hdma_request();
} break;
case 512: {
nextHoriEvent = 1104;
next_horiz_event = 1104;
// 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;
case 1104: {
if(!in_vblank_) memory_.run_hdma_request();
if(!memory_.pal_timing()) {
if (!in_vblank_) memory_.run_hdma_request();
if (!memory_.pal_timing()) {
// 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) {
nextHoriEvent = (memory_.v_pos() == 240 && !ppu_.even_frame && !ppu_.frame_interlace) ? 1360 : 1364;
next_horiz_event = (memory_.v_pos() == 240 && !ppu_.even_frame &&
!ppu_.frame_interlace)
? 1360
: 1364;
} else {
// 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)
nextHoriEvent = (memory_.v_pos() != 311 || ppu_.even_frame || !ppu_.frame_interlace) ? 1364 : 1368;
next_horiz_event = (memory_.v_pos() != 311 || ppu_.even_frame ||
!ppu_.frame_interlace)
? 1364
: 1368;
}
} break;
case 1360:
case 1364:
case 1368: { // this is the end (of the h-line)
nextHoriEvent = 16;
case 1368: { // this is the end (of the h-line)
next_horiz_event = 16;
memory_.set_h_pos(0);
memory_.set_v_pos(memory_.v_pos() + 1);
if(!memory_.pal_timing()) {
if (!memory_.pal_timing()) {
// 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);
frames_++;
}
} else {
} else {
// 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);
frames_++;
}
}
// end of hblank, do most memory_.v_pos()-tests
bool startingVblank = false;
if(memory_.v_pos() == 0) {
bool starting_vblank = false;
if (memory_.v_pos() == 0) {
// end of vblank
in_vblank_ = false;
in_nmi_ = false;
ppu_.HandleFrameStart();
} else if(memory_.v_pos() == 225) {
// ask the ppu if we start vblank now or at memory_.v_pos() 240 (overscan)
startingVblank = !ppu_.CheckOverscan();
} else if(memory_.v_pos() == 240){
// if we are not yet in vblank, we had an overscan frame, set startingVblank
if(!in_vblank_) startingVblank = true;
} else if (memory_.v_pos() == 225) {
// ask the ppu if we start vblank now or at memory_.v_pos() 240
// (overscan)
starting_vblank = !ppu_.CheckOverscan();
} else if (memory_.v_pos() == 240) {
// if we are not yet in vblank, we had an overscan frame, set
// starting_vblank
if (!in_vblank_) starting_vblank = true;
}
if(startingVblank) {
// catch up the apu at end of emulated frame (we end frame @ start of vblank)
if (starting_vblank) {
// catch up the apu at end of emulated frame (we end frame @ start of
// vblank)
CatchUpApu();
// notify dsp of frame-end, because sometimes dma will extend much further past vblank (or even into the next frame)
// Megaman X2 (titlescreen animation), Tales of Phantasia (game demo), Actraiser 2 (fade-in @ bootup)
// dsp_.newFrame();
// notify dsp of frame-end, because sometimes dma will extend much
// further past vblank (or even into the next frame) Megaman X2
// (titlescreen animation), Tales of Phantasia (game demo), Actraiser
// 2 (fade-in @ bootup)
apu_.dsp().NewFrame();
// we are starting vblank
ppu_.HandleVblank();
in_vblank_ = true;
in_nmi_ = true;
if(auto_joy_read_) {
if (auto_joy_read_) {
// TODO: this starts a little after start of vblank
auto_joy_timer_ = 4224;
HandleInput();
}
if(nmi_enabled_) {
if (nmi_enabled_) {
cpu_.Nmi();
}
}
@@ -226,7 +228,7 @@ void SNES::RunCycle() {
}
}
// 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) {
@@ -252,7 +254,7 @@ void SNES::SyncCycles(bool start, int sync_cycles) {
uint8_t SNES::ReadBBus(uint8_t adr) {
if (adr < 0x40) {
return ppu_.Read(adr);
return ppu_.Read(adr, ppu_latch_);
}
if (adr < 0x80) {
CatchUpApu(); // catch up the apu before reading
@@ -287,7 +289,7 @@ uint8_t SNES::ReadReg(uint16_t adr) {
return val | (memory_.open_bus() & 0x3e);
}
case 0x4213: {
return ppuLatch << 7; // IO-port
return ppu_latch_ << 7; // IO-port
}
case 0x4214: {
return divide_result_ & 0xff;
@@ -405,11 +407,11 @@ void SNES::WriteReg(uint16_t adr, uint8_t val) {
break;
}
case 0x4201: {
if (!(val & 0x80) && ppuLatch) {
if (!(val & 0x80) && ppu_latch_) {
// latch the ppu
ppu_.Read(0x37);
ppu_.LatchHV();
}
ppuLatch = val & 0x80;
ppu_latch_ = val & 0x80;
break;
}
case 0x4202: {
@@ -529,7 +531,7 @@ uint8_t SNES::CpuRead(uint32_t adr) {
void SNES::CpuWrite(uint32_t adr, uint8_t val) {
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_);
RunCycles(cycles_);
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::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 app
} // namespace yaze

View File

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