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