nestopia/source/core/NstCpu.hpp
2020-12-12 10:08:52 -05:00

600 lines
15 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia 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 2 of the License, or
// (at your option) any later version.
//
// Nestopia 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 Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
////////////////////////////////////////////////////////////////////////////////////////
#ifndef NST_CPU_H
#define NST_CPU_H
#include "NstAssert.hpp"
#include "NstIoMap.hpp"
#include "NstApu.hpp"
#ifdef NST_PRAGMA_ONCE
#pragma once
#endif
namespace Nes
{
namespace Core
{
class Hook;
class Cpu
{
public:
Cpu();
enum
{
CYCLE_MAX = Cycle(~0UL),
RAM_SIZE = SIZE_2K
};
enum IrqLine
{
IRQ_EXT = 0x01,
IRQ_FRAME = 0x40,
IRQ_DMC = 0x80
};
enum Level
{
LEVEL_LOW = 1,
LEVEL_HIGH = 9,
LEVEL_HIGHEST = 10
};
void Reset(bool);
void SetRamPowerState(uint);
void Boot(bool);
void ExecuteFrame(Sound::Output*);
void EndFrame();
void PowerOff();
void DoNMI(Cycle);
void DoIRQ(IrqLine,Cycle);
uint Peek(uint) const;
void Poke(uint,uint) const;
bool IsOddCycle() const;
bool IsWriteCycle(Cycle) const;
Cycle GetClockBase() const;
uint GetClockDivider() const;
dword GetTime(Cycle) const;
dword GetFps() const;
void SetModel(CpuModel);
void AddHook(const Hook&);
void RemoveHook(const Hook&);
void SaveState(State::Saver&,dword,dword) const;
void LoadState(State::Loader&,dword,dword,dword);
private:
static void NotifyOp(const char (&)[4],dword);
enum
{
NMI_VECTOR = 0xFFFA,
RESET_VECTOR = 0xFFFC,
IRQ_VECTOR = 0xFFFE,
RESET_CYCLES = 7,
INT_CYCLES = 7,
BRK_CYCLES = 7,
RTI_CYCLES = 6,
RTS_CYCLES = 6,
PHA_CYCLES = 3,
PHP_CYCLES = 3,
PLA_CYCLES = 4,
PLP_CYCLES = 4,
JSR_CYCLES = 6,
JMP_ABS_CYCLES = 3,
JMP_IND_CYCLES = 5
};
void Reset(bool,bool);
NES_DECL_POKE( Nop );
NES_DECL_PEEK( Nop );
NES_DECL_POKE( Overflow );
NES_DECL_PEEK( Overflow );
NES_DECL_PEEK( Jam_1 );
NES_DECL_PEEK( Jam_2 );
void DoISR(uint);
uint FetchIRQISRVector();
void Clock();
void Run0();
void Run1();
void Run2();
inline void ExecuteOp();
inline uint FetchPc8();
inline uint FetchPc16();
inline uint FetchZpg16(uint) const;
inline void Push8(uint);
NST_FORCE_INLINE void Push16(uint);
inline uint Pull8();
inline uint Pull16();
inline uint ZpgReg_R (uint);
inline uint ZpgReg_RW (uint&,uint);
inline uint ZpgReg_W (uint);
uint AbsReg_R (uint);
uint AbsReg_RW (uint&,uint);
NST_FORCE_INLINE uint AbsReg_W (uint);
template<bool STATE>
NST_FORCE_INLINE void Branch(uint);
inline uint Imm_R ();
inline uint Zpg_R ();
inline uint ZpgX_R ();
inline uint ZpgY_R ();
uint Abs_R ();
inline uint AbsX_R ();
inline uint AbsY_R ();
uint IndX_R ();
uint IndY_R ();
inline uint Zpg_RW (uint&);
inline uint ZpgX_RW (uint&);
inline uint ZpgY_RW (uint&);
uint Abs_RW (uint&);
inline uint AbsY_RW (uint&);
inline uint AbsX_RW (uint&);
inline uint IndX_RW (uint&);
inline uint IndY_RW (uint&);
inline uint Zpg_W ();
inline uint ZpgX_W ();
inline uint ZpgY_W ();
inline uint Abs_W ();
NST_FORCE_INLINE uint AbsX_W ();
NST_FORCE_INLINE uint AbsY_W ();
inline uint IndX_W ();
NST_FORCE_INLINE uint IndY_W ();
inline void StoreMem (uint,uint);
inline void StoreZpg (uint,uint);
inline void Lda (uint);
inline void Ldx (uint);
inline void Ldy (uint);
inline uint Sta() const;
inline uint Stx() const;
inline uint Sty() const;
NST_SINGLE_CALL void Tax ();
NST_SINGLE_CALL void Tay ();
NST_SINGLE_CALL void Txa ();
NST_SINGLE_CALL void Tya ();
NST_SINGLE_CALL void JmpAbs ();
NST_SINGLE_CALL void JmpInd ();
NST_SINGLE_CALL void Jsr ();
NST_SINGLE_CALL void Rts ();
NST_SINGLE_CALL void Rti ();
NST_SINGLE_CALL void Bcc ();
NST_SINGLE_CALL void Bcs ();
NST_SINGLE_CALL void Beq ();
NST_SINGLE_CALL void Bmi ();
NST_SINGLE_CALL void Bne ();
NST_SINGLE_CALL void Bpl ();
NST_SINGLE_CALL void Bvc ();
NST_SINGLE_CALL void Bvs ();
inline void Adc (uint);
inline void Sbc (uint);
inline void And (uint);
inline void Ora (uint);
inline void Eor (uint);
inline void Bit (uint);
inline void Cmp (uint);
inline void Cpx (uint);
inline void Cpy (uint);
inline uint Asl (uint);
inline uint Lsr (uint);
inline uint Rol (uint);
inline uint Ror (uint);
inline uint Dec (uint);
inline uint Inc (uint);
NST_SINGLE_CALL void Dex ();
NST_SINGLE_CALL void Dey ();
NST_SINGLE_CALL void Inx ();
NST_SINGLE_CALL void Iny ();
NST_SINGLE_CALL void Clc ();
NST_SINGLE_CALL void Cld ();
NST_SINGLE_CALL void Clv ();
NST_SINGLE_CALL void Sec ();
NST_SINGLE_CALL void Sed ();
NST_SINGLE_CALL void Sei ();
NST_SINGLE_CALL void Cli ();
NST_SINGLE_CALL void Php ();
NST_SINGLE_CALL void Plp ();
NST_SINGLE_CALL void Pha ();
NST_SINGLE_CALL void Pla ();
NST_SINGLE_CALL void Tsx ();
NST_SINGLE_CALL void Txs ();
NST_SINGLE_CALL void Ane (uint);
NST_SINGLE_CALL void Arr (uint);
NST_SINGLE_CALL void Asr (uint);
NST_SINGLE_CALL void Las (uint);
NST_SINGLE_CALL void Lxa (uint);
NST_SINGLE_CALL void Sbx (uint);
NST_SINGLE_CALL uint Shs (uint);
NST_SINGLE_CALL void Shx (uint);
NST_SINGLE_CALL void Shy (uint);
NST_NO_INLINE void Anc (uint);
NST_NO_INLINE uint Dcp (uint);
NST_NO_INLINE uint Isb (uint);
NST_NO_INLINE void Lax (uint);
NST_NO_INLINE uint Rla (uint);
NST_NO_INLINE uint Rra (uint);
NST_NO_INLINE uint Sax ();
NST_NO_INLINE uint Sha (uint);
NST_NO_INLINE uint Slo (uint);
NST_NO_INLINE uint Sre (uint);
void Dop ();
void Top (uint);
NST_SINGLE_CALL void Brk ();
NST_NO_INLINE void Jam ();
void op0x00(); void op0x01(); void op0x02(); void op0x03();
void op0x04(); void op0x05(); void op0x06(); void op0x07();
void op0x08(); void op0x09(); void op0x0A(); void op0x0B();
void op0x0C(); void op0x0D(); void op0x0E(); void op0x0F();
void op0x10(); void op0x11(); void op0x12(); void op0x13();
void op0x14(); void op0x15(); void op0x16(); void op0x17();
void op0x18(); void op0x19(); void op0x1A(); void op0x1B();
void op0x1C(); void op0x1D(); void op0x1E(); void op0x1F();
void op0x20(); void op0x21(); void op0x22(); void op0x23();
void op0x24(); void op0x25(); void op0x26(); void op0x27();
void op0x28(); void op0x29(); void op0x2A(); void op0x2B();
void op0x2C(); void op0x2D(); void op0x2E(); void op0x2F();
void op0x30(); void op0x31(); void op0x32(); void op0x33();
void op0x34(); void op0x35(); void op0x36(); void op0x37();
void op0x38(); void op0x39(); void op0x3A(); void op0x3B();
void op0x3C(); void op0x3D(); void op0x3E(); void op0x3F();
void op0x40(); void op0x41(); void op0x42(); void op0x43();
void op0x44(); void op0x45(); void op0x46(); void op0x47();
void op0x48(); void op0x49(); void op0x4A(); void op0x4B();
void op0x4C(); void op0x4D(); void op0x4E(); void op0x4F();
void op0x50(); void op0x51(); void op0x52(); void op0x53();
void op0x54(); void op0x55(); void op0x56(); void op0x57();
void op0x58(); void op0x59(); void op0x5A(); void op0x5B();
void op0x5C(); void op0x5D(); void op0x5E(); void op0x5F();
void op0x60(); void op0x61(); void op0x62(); void op0x63();
void op0x64(); void op0x65(); void op0x66(); void op0x67();
void op0x68(); void op0x69(); void op0x6A(); void op0x6B();
void op0x6C(); void op0x6D(); void op0x6E(); void op0x6F();
void op0x70(); void op0x71(); void op0x72(); void op0x73();
void op0x74(); void op0x75(); void op0x76(); void op0x77();
void op0x78(); void op0x79(); void op0x7A(); void op0x7B();
void op0x7C(); void op0x7D(); void op0x7E(); void op0x7F();
void op0x80(); void op0x81(); void op0x82(); void op0x83();
void op0x84(); void op0x85(); void op0x86(); void op0x87();
void op0x88(); void op0x89(); void op0x8A(); void op0x8B();
void op0x8C(); void op0x8D(); void op0x8E(); void op0x8F();
void op0x90(); void op0x91(); void op0x92(); void op0x93();
void op0x94(); void op0x95(); void op0x96(); void op0x97();
void op0x98(); void op0x99(); void op0x9A(); void op0x9B();
void op0x9C(); void op0x9D(); void op0x9E(); void op0x9F();
void op0xA0(); void op0xA1(); void op0xA2(); void op0xA3();
void op0xA4(); void op0xA5(); void op0xA6(); void op0xA7();
void op0xA8(); void op0xA9(); void op0xAA(); void op0xAB();
void op0xAC(); void op0xAD(); void op0xAE(); void op0xAF();
void op0xB0(); void op0xB1(); void op0xB2(); void op0xB3();
void op0xB4(); void op0xB5(); void op0xB6(); void op0xB7();
void op0xB8(); void op0xB9(); void op0xBA(); void op0xBB();
void op0xBC(); void op0xBD(); void op0xBE(); void op0xBF();
void op0xC0(); void op0xC1(); void op0xC2(); void op0xC3();
void op0xC4(); void op0xC5(); void op0xC6(); void op0xC7();
void op0xC8(); void op0xC9(); void op0xCA(); void op0xCB();
void op0xCC(); void op0xCD(); void op0xCE(); void op0xCF();
void op0xD0(); void op0xD1(); void op0xD2(); void op0xD3();
void op0xD4(); void op0xD5(); void op0xD6(); void op0xD7();
void op0xD8(); void op0xD9(); void op0xDA(); void op0xDB();
void op0xDC(); void op0xDD(); void op0xDE(); void op0xDF();
void op0xE0(); void op0xE1(); void op0xE2(); void op0xE3();
void op0xE4(); void op0xE5(); void op0xE6(); void op0xE7();
void op0xE8(); void op0xE9(); void op0xEA(); void op0xEB();
void op0xEC(); void op0xED(); void op0xEE(); void op0xEF();
void op0xF0(); void op0xF1(); void op0xF2(); void op0xF3();
void op0xF4(); void op0xF5(); void op0xF6(); void op0xF7();
void op0xF8(); void op0xF9(); void op0xFA(); void op0xFB();
void op0xFC(); void op0xFD(); void op0xFE(); void op0xFF();
struct Cycles
{
void UpdateTable(CpuModel);
inline uint InterruptEdge() const;
Cycle count;
byte clock[8];
Cycle offset;
Cycle round;
Cycle frame;
void NextRound(Cycle next)
{
if (round > next)
round = next;
}
};
struct Flags
{
uint Pack() const;
void Unpack(uint);
enum
{
C = 0x01, // carry
Z = 0x02, // zero
I = 0x04, // interrupt enable/disable
D = 0x08, // decimal mode (not supported on the N2A03)
B = 0x10, // software interrupt
R = 0x20, // unused but always set
V = 0x40, // overflow
N = 0x80 // negative
};
uint nz;
uint c;
uint v;
uint i;
uint d;
};
struct Interrupt
{
void Reset();
Cycle nmiClock;
Cycle irqClock;
uint low;
};
class Hooks
{
public:
Hooks();
~Hooks();
void Add(const Hook&);
void Remove(const Hook&);
void Clear();
inline uint Size() const;
inline const Hook* Ptr() const;
private:
Hook* hooks;
word size;
word capacity;
};
struct Ram
{
typedef byte (&Ref)[RAM_SIZE];
typedef const byte (&ConstRef)[RAM_SIZE];
void Reset(CpuModel);
NES_DECL_PEEK( Ram_0 );
NES_DECL_POKE( Ram_0 );
NES_DECL_PEEK( Ram_1 );
NES_DECL_POKE( Ram_1 );
NES_DECL_PEEK( Ram_2 );
NES_DECL_POKE( Ram_2 );
NES_DECL_PEEK( Ram_3 );
NES_DECL_POKE( Ram_3 );
byte mem[RAM_SIZE];
byte powerstate;
};
struct IoMap : Io::Map<SIZE_64K>
{
template<typename T,typename U>
IoMap(Cpu*,T,U);
inline uint Peek8(uint) const;
inline uint Peek16(uint) const;
inline void Poke8(uint,uint) const;
};
class Linker
{
struct Chain : Io::Port
{
Chain(const Port&,uint,uint=0);
uint address;
uint level;
Chain* next;
};
Chain* chain;
public:
Linker();
~Linker();
void Clear();
const Io::Port* Add(Address,uint,const Io::Port&,IoMap&);
void Remove(Address,const Io::Port&,IoMap&);
};
uint pc;
Cycles cycles;
uint a;
uint x;
uint y;
uint sp;
Flags flags;
Interrupt interrupt;
Hooks hooks;
uint opcode;
word jammed;
word model;
Linker linker;
qaword ticks;
Ram ram;
Apu apu;
IoMap map;
static dword logged;
static void (Cpu::*const opcodes[0x100])();
static const byte writeClocks[0x100];
public:
Apu& GetApu()
{
return apu;
}
Cycle Update(uint readAddress=0)
{
apu.ClockDMA( readAddress );
return cycles.count;
}
void DoIRQ(IrqLine line=IRQ_EXT)
{
DoIRQ( line, cycles.count );
}
void ClearIRQ(IrqLine line=IRQ_EXT)
{
NST_VERIFY( interrupt.irqClock == CYCLE_MAX );
interrupt.low &= line ^ uint(IRQ_EXT|IRQ_FRAME|IRQ_DMC);
if (!interrupt.low)
interrupt.irqClock = CYCLE_MAX;
}
uint GetIRQ() const
{
return interrupt.low;
}
CpuModel GetModel() const
{
return static_cast<CpuModel>(model);
}
Region GetRegion() const
{
return model == CPU_RP2A03 ? REGION_NTSC : REGION_PAL;
}
Cycle GetClock(uint count=1) const
{
NST_ASSERT( count >= 1 && count <= 8 );
return cycles.clock[count-1];
}
Cycle GetCycles() const
{
return cycles.count;
}
void StealCycles(Cycle count)
{
cycles.count += count;
}
Cycle GetFrameCycles() const
{
return cycles.frame;
}
void SetFrameCycles(Cycle count)
{
cycles.frame = count;
cycles.NextRound( count );
}
Ram::Ref GetRam()
{
return ram.mem;
}
Ram::ConstRef GetRam() const
{
return ram.mem;
}
Io::Port& Map(Address address)
{
return map( address );
}
IoMap::Section Map(Address first,Address last)
{
return map( first, last );
}
template<typename T,typename U,typename V>
const Io::Port* Link(Address address,Level level,T t,U u,V v)
{
return linker.Add( address, level, Io::Port(t,u,v), map );
}
template<typename T,typename U,typename V>
void Unlink(Address address,T t,U u,V v)
{
linker.Remove( address, Io::Port(t,u,v), map );
}
};
}
}
#endif