223 lines
5.6 KiB
C++
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);
|
|
}
|
|
}
|