From edc66c26ddf53a31874181c5bef4b87c3219ce1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrea=20Orr=C3=B9?= Date: Fri, 8 Nov 2013 18:48:17 +0100 Subject: [PATCH] Initial commit. Cycle-accurate 6502 emulator. --- .gitignore | 1 + SConstruct | 5 ++ src/cpu.cpp | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/cpu.hpp | 30 ++++++++ src/types.hpp | 13 ++++ 5 files changed, 247 insertions(+) create mode 100644 .gitignore create mode 100644 SConstruct create mode 100644 src/cpu.cpp create mode 100644 src/cpu.hpp create mode 100644 src/types.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b2d462 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.sconsign.dblite diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..c189a66 --- /dev/null +++ b/SConstruct @@ -0,0 +1,5 @@ +env = Environment(CXX = 'clang++', + CXXFLAGS = '-std=c++1y -stdlib=libc++', + LIBS = 'c++abi') + +env.Program('laines', Glob('src/*.cpp')) diff --git a/src/cpu.cpp b/src/cpu.cpp new file mode 100644 index 0000000..5a40c38 --- /dev/null +++ b/src/cpu.cpp @@ -0,0 +1,198 @@ +#include +#include +#include "cpu.hpp" + + +u8 mem[0x10000]; +u8 A, X, Y, S; +u16 PC; +Flags P; + +u64 clock; +#define T clock++ + +inline bool cross(u16 a, u8 i) { return ((a+i) & 0xFF00) != ((a & 0xFF00)); } +inline void upd_cv(u8 x, u8 y, s16 r) { P.c = (r>0xFF); P.v = ~(x^y) & (x^r) & 0x80; } +inline void upd_nz(u8 x) { P.n = x & 0x80; P.z = (x == 0); } + +inline u8 wr(u16 a, u8 v) { T; return mem[a] = v; } +inline u8 rd(u16 a) { T; return mem[a]; } +inline u16 rd16_d(u16 a, u16 b) { return rd(a) | (rd(b) << 8); } +inline u16 rd16(u16 a) { return rd16_d(a, a+1); } +inline u8 push(u8 v) { return wr(0x100 | (S--), v); } +inline u8 pop() { return rd(0x100 | (++S)); } + +inline u16 imm() { return PC++; } +inline u16 imm16() { PC += 2; return PC - 2; } +inline u16 abs() { return rd16(imm16()); } +inline u16 _abx() { T; return abs() + X; } +inline u16 abx() { u16 a = abs(); if (cross(a, X)) { T; }; return a + X; } +inline u16 aby() { u16 a = abs(); if (cross(a, Y)) { T; }; return a + Y; } +inline u16 zp() { return rd(imm()); } +inline u16 zpx() { T; return (zp() + X) % 0x100; } +inline u16 zpy() { T; return (zp() + Y) % 0x100; } +inline u16 izx() { u8 i = zpx(); return rd16_d(i, (i+1) % 0x100); } +inline u16 _izy() { u8 i = zp(); return rd16_d(i, (i+1) % 0x100) + Y; } +inline u16 izy() { u16 a = _izy(); if (cross(a-Y, Y)) { T; }; return a; } + +template void st() { wr( m() , r); } +template<> void st() { wr(_izy() , A); T; } +template<> void st() { wr( abs() + X, A); T; } +template<> void st() { wr( abs() + Y, A); T; } + +#define G u16 a = m(); u8 p = rd(a) +template void ld() { G; upd_nz(r = p); } +template void cmp() { G; upd_nz(r - p); P.c = (r >= p); } +template void ADC() { G ; s16 r = A + p + P.c; upd_cv(A, p, r); upd_nz(A = r); } +template void SBC() { G ^ 0xFF; s16 r = A + p + P.c; upd_cv(A, p, r); upd_nz(A = r); } +template void BIT() { G; P.z = !(A & p); P.n = p & 0x80; P.v = p & 0x40; } +template void AND() { G; upd_nz(A &= p); } +template void EOR() { G; upd_nz(A ^= p); } +template void ORA() { G; upd_nz(A |= p); } +template void ASL() { G; P.c = p & 0x80; upd_nz(wr(a, p << 1)); T; } +template void LSR() { G; P.c = p & 0x01; upd_nz(wr(a, p >> 1)); T; } +template void ROL() { G; u8 c = P.c ; P.c = p & 0x80; upd_nz(wr(a, (p << 1) | c) ); T; } +template void ROR() { G; u8 c = P.c << 7; P.c = p & 0x01; upd_nz(wr(a, c | (p >> 1)) ); T; } +template void DEC() { G; upd_nz(wr(a, --p)); T; } +template void INC() { G; upd_nz(wr(a, ++p)); T; } +#undef G + +template void dec() { upd_nz(--r); T; } +template void inc() { upd_nz(++r); T; } +void ASL_A() { P.c = A & 0x80; upd_nz(A <<= 1); T; } +void LSR_A() { P.c = A & 0x01; upd_nz(A >>= 1); T; } +void ROL_A() { u8 c = P.c ; P.c = A & 0x80; upd_nz(A = ((A << 1) | c) ); T; } +void ROR_A() { u8 c = P.c << 7; P.c = A & 0x01; upd_nz(A = (c | (A >> 1)) ); T; } + +template void tr() { upd_nz(d = s); T; } +template<> void tr() { S = X; T; } + +void PLP() { P.reg = (pop() & 0b11001111) | (P.reg & 0b00110000); T; T; } +void PHP() { push(P.reg | (1 << 4)); T; } +void PLA() { A = pop(); upd_nz(A); T; T; } +void PHA() { push(A); T; } + +void NOP() { T; } +void BRK() { exit(0); } +void RTI() { PLP(); PC = pop() | (pop() << 8); } +void RTS() { PC = (pop() | (pop() << 8)) + 1; T; T; T; } + +template void flag() { P.set(f, v); T; } +template void br() { s8 j = rd(imm()); if (P.get(f) == v) { PC += j; T; } } +void JMP_IND() { u16 i = rd16(imm16()); PC = rd16_d(i, (i&0xFF00) | ((i+1) % 0x100)); } +void JMP() { PC = rd16(imm16()); } +void JSR() { u16 t = PC+1; T; push(t >> 8); push(t); PC = rd16(imm16()); } + +void step() +{ + switch (rd(PC++)) + { + case 0x00: return BRK() ; case 0x01: return ORA() ; + case 0x05: return ORA() ; case 0x06: return ASL() ; + case 0x08: return PHP() ; case 0x09: return ORA() ; + case 0x0A: return ASL_A() ; case 0x0D: return ORA() ; + case 0x0E: return ASL() ; case 0x10: return br() ; + case 0x11: return ORA() ; case 0x15: return ORA() ; + case 0x16: return ASL() ; case 0x18: return flag() ; + case 0x19: return ORA() ; case 0x1D: return ORA() ; + case 0x1E: return ASL<_abx>() ; case 0x20: return JSR() ; + case 0x21: return AND() ; case 0x24: return BIT() ; + case 0x25: return AND() ; case 0x26: return ROL() ; + case 0x28: return PLP() ; case 0x29: return AND() ; + case 0x2A: return ROL_A() ; case 0x2C: return BIT() ; + case 0x2D: return AND() ; case 0x2E: return ROL() ; + case 0x30: return br() ; case 0x31: return AND() ; + case 0x35: return AND() ; case 0x36: return ROL() ; + case 0x38: return flag() ; case 0x39: return AND() ; + case 0x3D: return AND() ; case 0x3E: return ROL<_abx>() ; + case 0x40: return RTI() ; case 0x41: return EOR() ; + case 0x45: return EOR() ; case 0x46: return LSR() ; + case 0x48: return PHA() ; case 0x49: return EOR() ; + case 0x4A: return LSR_A() ; case 0x4C: return JMP() ; + case 0x4D: return EOR() ; case 0x4E: return LSR() ; + case 0x50: return br() ; case 0x51: return EOR() ; + case 0x55: return EOR() ; case 0x56: return LSR() ; + case 0x58: return flag() ; case 0x59: return EOR() ; + case 0x5D: return EOR() ; case 0x5E: return LSR<_abx>() ; + case 0x60: return RTS() ; case 0x61: return ADC() ; + case 0x65: return ADC() ; case 0x66: return ROR() ; + case 0x68: return PLA() ; case 0x69: return ADC() ; + case 0x6A: return ROR_A() ; case 0x6C: return JMP_IND() ; + case 0x6D: return ADC() ; case 0x6E: return ROR() ; + case 0x70: return br() ; case 0x71: return ADC() ; + case 0x75: return ADC() ; case 0x76: return ROR() ; + case 0x78: return flag() ; case 0x79: return ADC() ; + case 0x7D: return ADC() ; case 0x7E: return ROR<_abx>() ; + case 0x81: return st() ; case 0x84: return st() ; + case 0x85: return st() ; case 0x86: return st() ; + case 0x88: return dec() ; case 0x8A: return tr() ; + case 0x8C: return st() ; case 0x8D: return st() ; + case 0x8E: return st() ; case 0x90: return br() ; + case 0x91: return st() ; case 0x94: return st() ; + case 0x95: return st() ; case 0x96: return st() ; + case 0x98: return tr() ; case 0x99: return st() ; + case 0x9A: return tr() ; case 0x9D: return st() ; + case 0xA0: return ld() ; case 0xA1: return ld() ; + case 0xA2: return ld() ; case 0xA4: return ld() ; + case 0xA5: return ld() ; case 0xA6: return ld() ; + case 0xA8: return tr() ; case 0xA9: return ld() ; + case 0xAA: return tr() ; case 0xAC: return ld() ; + case 0xAD: return ld() ; case 0xAE: return ld() ; + case 0xB0: return br() ; case 0xB1: return ld() ; + case 0xB4: return ld() ; case 0xB5: return ld() ; + case 0xB6: return ld() ; case 0xB8: return flag() ; + case 0xB9: return ld() ; case 0xBA: return tr() ; + case 0xBC: return ld() ; case 0xBD: return ld() ; + case 0xBE: return ld() ; case 0xC0: return cmp(); + case 0xC1: return cmp(); case 0xC4: return cmp() ; + case 0xC5: return cmp() ; case 0xC6: return DEC() ; + case 0xC8: return inc() ; case 0xC9: return cmp(); + case 0xCA: return dec() ; case 0xCC: return cmp(); + case 0xCD: return cmp(); case 0xCE: return DEC() ; + case 0xD0: return br() ; case 0xD1: return cmp(); + case 0xD5: return cmp(); case 0xD6: return DEC() ; + case 0xD8: return flag() ; case 0xD9: return cmp(); + case 0xDD: return cmp(); case 0xDE: return DEC<_abx>() ; + case 0xE0: return cmp(); case 0xE1: return SBC() ; + case 0xE4: return cmp() ; case 0xE5: return SBC() ; + case 0xE6: return INC() ; case 0xE8: return inc() ; + case 0xE9: return SBC() ; case 0xEA: return NOP() ; + case 0xEC: return cmp(); case 0xED: return SBC() ; + case 0xEE: return INC() ; case 0xF0: return br() ; + case 0xF1: return SBC() ; case 0xF5: return SBC() ; + case 0xF6: return INC() ; case 0xF8: return flag() ; + case 0xF9: return SBC() ; case 0xFD: return SBC() ; + case 0xFE: return INC<_abx>() ; default: return exit(1) ; + } +} + +void reset() +{ + A = X = Y = 0x00; + P.reg = 0b00100100; + PC = 0xC000; + S = 0xFD; +} + +int main(int argc, char const *argv[]) +{ + reset(); + + FILE* f = fopen(argv[1], "rb"); + fseek(f, 0, SEEK_END); + int s = ftell(f); + fseek(f, 16, SEEK_SET); + fread(mem + 0xC000, 0x4000, 1, f); + + u64 old_clock = 0; + while(1) + { + printf("[PC: $%.4x] - | A: $%.2x | X: $%.2x | Y: $%.2x | P: $%.2x | S: $%.2x | CYC: %lu\n", PC, A, X, Y, P.reg, S, (clock - old_clock) * 3); + fflush(stdout); + + old_clock = clock; + step(); + } + + return 0; +} diff --git a/src/cpu.hpp b/src/cpu.hpp new file mode 100644 index 0000000..e15afb0 --- /dev/null +++ b/src/cpu.hpp @@ -0,0 +1,30 @@ +#include "types.hpp" + +#ifndef CPU_HPP +#define CPU_HPP + + +enum {C, Z, I, D, B, UNUSED, V, N}; +union Flags +{ + struct + { + bool c : 1; + bool z : 1; + bool i : 1; + bool d : 1; + bool b : 1; + bool unused : 1; + bool v : 1; + bool n : 1; + }; + u8 reg; + + bool get(u8 i) { return reg & (1 << i); } + void set(u8 i, bool v) { reg = v ? (reg | (1 << i)) : (reg & ~(1 << i)); } +}; + +typedef u16 (*Mode)(void); + + +#endif diff --git a/src/types.hpp b/src/types.hpp new file mode 100644 index 0000000..21e7afc --- /dev/null +++ b/src/types.hpp @@ -0,0 +1,13 @@ +#include + +#ifndef TYPES_HPP +#define TYPES_HPP + + +typedef uint8_t u8; typedef int8_t s8; +typedef uint16_t u16; typedef int16_t s16; +typedef uint32_t u32; typedef int32_t s32; +typedef uint64_t u64; typedef int64_t s64; + + +#endif