mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
600 lines
15 KiB
C++
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
|