cleanup SNES
This commit is contained in:
@@ -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
|
||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user