BizHawk/libmeteor/source/interpreter.cpp

223 lines
5.6 KiB
C++

// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/interpreter.hpp"
#include "ameteor/cpu.hpp"
#include "ameteor/memory.hpp"
#include "ameteor/bios.hpp"
#include "ameteor/disassembler/instruction.hpp"
#include "globals.hpp"
#include "cpu_globals.hpp"
#include "ameteor.hpp"
#include "debug.hpp"
#include <sstream>
namespace AMeteor
{
Interpreter::Interpreter() :
m_run(false),
m_interrupt_(false),
m_haltcnt(IO.GetRef8(Io::HALTCNT)),
m_if(IO.GetRef16(Io::IF)),
m_ie(IO.GetRef16(Io::IE))
{
}
void Interpreter::SendInterrupt (uint16_t interrupt)
{
IO.GetRef16(Io::IF) |= interrupt;
if ((interrupt & IO.DRead16(Io::IE)) &&
(IO.DRead16(Io::IME) & 0x1) &&
!m_st.icpsr.irq_d)
// irq are enabled and theses irq are enabled...
m_interrupt = true;
}
void Interpreter::CheckInterrupt ()
{
m_interrupt =
(IO.DRead16(Io::IF) & IO.DRead16(Io::IE)) &&
(IO.DRead16(Io::IME) & 0x1) &&
!m_st.icpsr.irq_d;
}
void Interpreter::Run (unsigned int cycles)
{
m_run = true;
CLOCK.ResetCounter();
while(m_run && CLOCK.GetCounter() < cycles)
{
switch (m_haltcnt)
{
case 255: // normal mode
PrintRegs();
if (FLAG_T)
{
if (R(15) & 0x1)
met_abort("PC not 16 bit aligned : " << IOS_ADD << R(15));
code = MEM.Read16(R(15)-2);
if (traceenabled)
{
std::stringstream ss;
ss << IOS_TRACE << R(15) - 2 << ':' << std::setw(4) << code << " ";
ss.setf(std::ios::left, std::ios::adjustfield);
ss << std::setw(32) << std::setfill(' ') << Disassembler::Instruction(R(15), (uint16_t)code).ToString();
ss.setf(std::ios::right, std::ios::adjustfield);
ss << IOS_TRACE;
for (int i = 0; i < 16; i++)
ss << std::setw(8) << R(i) << ' ';
UpdateCpsr();
ss << std::setw(8) << m_st.cpsr.dw << ' ';
ss << std::setw(8) << m_st.spsr.dw << ' ';
trace_bizhawk(ss.str());
}
R(15) += 2;
t_Code();
}
else
{
if (R(15) & 0x3)
met_abort("PC not 32 bit aligned : " << IOS_ADD << R(15));
if (R(15) < 0x01000000 && !MEM.HasBios())
{
switch (R(15))
{
case 0x004:
Bios::Bios000h();
break;
case 0x00C:
Bios::Bios008h();
break;
case 0x01C:
Bios::Bios018h();
break;
case 0x134:
Bios::Bios130h();
break;
case 0x33C:
Bios::Bios338h();
break;
case 0x16C:
Bios::Bios168h();
break;
default:
met_abort("Jump to " << IOS_ADD << R(15));
}
}
else
{
code = MEM.Read32(R(15)-4);
if (traceenabled)
{
std::stringstream ss;
ss << IOS_TRACE << R(15) - 4 << ':' << std::setw(8) << code << ' ';
ss.setf(std::ios::left, std::ios::adjustfield);
ss << std::setw(32) << std::setfill(' ') << Disassembler::Instruction(R(15), (uint32_t)code).ToString();
ss.setf(std::ios::right, std::ios::adjustfield);
ss << IOS_TRACE;
for (int i = 0; i < 16; i++)
ss << std::setw(8) << R(i) << ' ';
UpdateCpsr();
ss << std::setw(8) << m_st.cpsr.dw << ' ';
ss << std::setw(8) << m_st.spsr.dw << ' ';
trace_bizhawk(ss.str());
}
R(15) += 4;
a_Code();
}
}
if (R(15) < 0x01000000 && FLAG_T && !MEM.HasBios())
met_abort("Jump to " << IOS_ADD << R(15));
CLOCK.Commit();
if (m_interrupt)
// irq are enabled and there are irq waiting...
{
// FIXME : do we really need this ??
// if not, we can get rid of save and load state and reset
if (m_interrupt_)
{
m_interrupt_ = false;
// XXX we must be sure the cpu isn't halted when an interrupt
// occurs
// should be removed after since it takes no time to make a new
// iteration of the loop
m_haltcnt = 255;
CPU.Interrupt();
}
else
{
// XXX
if (m_haltcnt != 255)
{
m_haltcnt = 255;
CPU.Interrupt();
}
else
m_interrupt_ = true;
}
}
break;
case 0: // halt mode
if (m_if & m_ie) // interrupt occured
{
m_haltcnt = 255; // return to normal mode
CPU.Interrupt();
// XXX use an else
break;
}
CLOCK.WaitForNext();
// XXX remove this block
if (m_if & m_ie) // interrupt occured
{
m_haltcnt = 255; // return to normal mode
CPU.Interrupt();
}
break;
case 1: // stop mode
met_abort("Stop mode not implemented");
break;
default:
met_abort("Unknown HALTCNT value : " << (int)m_haltcnt);
break;
}
}
m_run = false;
}
bool Interpreter::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_interrupt_);
return Cpu::SaveState(stream);
}
bool Interpreter::LoadState (std::istream& stream)
{
SS_READ_VAR(m_interrupt_);
return Cpu::LoadState(stream);
}
}