Initial Commit
This commit is contained in:
28
Bus.h
Normal file
28
Bus.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Created by josh on 1/22/23.
|
||||
//
|
||||
|
||||
#ifndef EMU6502_BUS_H
|
||||
#define EMU6502_BUS_H
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
|
||||
|
||||
#include "j6502.h"
|
||||
|
||||
class Bus {
|
||||
public:
|
||||
Bus();
|
||||
~Bus();
|
||||
public:
|
||||
j6502 cpu;
|
||||
std::array<uint8_t, 64*1024> ram;
|
||||
|
||||
void write(uint16_t addr, uint8_t data);
|
||||
|
||||
uint8_t read(uint16_t addr, bool readOnly);
|
||||
};
|
||||
|
||||
|
||||
#endif //EMU6502_BUS_H
|
11
CMakeLists.txt
Normal file
11
CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(emu6502)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
find_package(X11 REQUIRED)
|
||||
include_directories(${X11_INCLUDE_DIR})
|
||||
link_directories(${X11_LIBRARIES})
|
||||
|
||||
add_executable(emu6502 main.cpp j6502.cpp j6502.h Bus.cpp Bus.h olcPixelGameEngine.h)
|
||||
target_link_libraries(emu6502 X11 GL png stdc++fs pthread)
|
101
j6502.h
Normal file
101
j6502.h
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Created by josh on 1/22/23.
|
||||
//
|
||||
|
||||
|
||||
#ifndef EMU6502_J6502_H
|
||||
#define EMU6502_J6502_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
|
||||
// Get Forward Declared
|
||||
class Bus;
|
||||
|
||||
class j6502 {
|
||||
public:
|
||||
enum FLAGS {
|
||||
C = (1 << 0), // Carry Bit
|
||||
Z = (1 << 1), // Zero
|
||||
I = (1 << 2), // Disable Interrupts
|
||||
D = (1 << 3), // Decimal Mode (unimplemented)
|
||||
B = (1 << 4), // Break
|
||||
U = (1 << 5), // Unused
|
||||
V = (1 << 6), // Overflow
|
||||
N = (1 << 7), // Negative
|
||||
};
|
||||
j6502();
|
||||
~j6502();
|
||||
u8 status = 0x00; // Status Register
|
||||
u8 a = 0x00; // Accumulator
|
||||
u8 x = 0x00; // X Register
|
||||
u8 y = 0x00; // Y Register
|
||||
u8 sp = 0x00; // Stack Pointer
|
||||
u16 pc = 0x0000; // Program Counter
|
||||
// Facilitate emulation
|
||||
u8 fetched = 0x00;
|
||||
u16 addr_abs = 0x0000;
|
||||
u16 addr_rel = 0x00;
|
||||
u8 opcode = 0x00;
|
||||
u8 cycles = 0;
|
||||
uint32_t clock_count = 0;
|
||||
u16 temp = 0x00;
|
||||
|
||||
Bus *bus = nullptr;
|
||||
struct INSTRUCTION {
|
||||
std::string name;
|
||||
|
||||
u8 (j6502::*operate)(void) = nullptr;
|
||||
|
||||
u8 (j6502::*addrmode)(void) = nullptr;
|
||||
|
||||
u8 cycles = 0;
|
||||
};
|
||||
std::vector<INSTRUCTION> lookup;
|
||||
|
||||
void ConnectBus(Bus *n){ bus = n; };
|
||||
u8 read(u16 addr);
|
||||
void write(u16 addr, u8 d);
|
||||
u8 GetFlag(FLAGS f);
|
||||
void SetFlag(FLAGS f, bool v);
|
||||
u8 fetch();
|
||||
|
||||
std::map<u16, std::string> disassemble(u16 start, u16 stop);
|
||||
|
||||
void nmi();
|
||||
void irq();
|
||||
void reset();
|
||||
void clock();
|
||||
u8 CLC();u8 CLD();
|
||||
u8 ZP0();u8 ZPX();u8 ABS();u8 REL();
|
||||
u8 ZPY();u8 IMM();u8 IMP();u8 ABX();
|
||||
u8 STX();u8 STA();u8 SED();u8 SEC();
|
||||
u8 RTS();u8 RTI();u8 ROR();u8 ROL();
|
||||
u8 PLP();u8 PLA();u8 PHP();u8 PHA();
|
||||
u8 BVS();u8 BVC();u8 BRK();u8 BPL();
|
||||
u8 BNE();u8 BMI();u8 BIT();u8 BEQ();
|
||||
u8 BCS();u8 BCC();u8 ASL();u8 SBC();
|
||||
u8 ADC();u8 AND();u8 DEY();u8 DEX();
|
||||
u8 DEC();u8 CPY();u8 EOR();u8 INC();
|
||||
u8 INX();u8 INY();u8 JMP();u8 JSR();
|
||||
u8 LDA();u8 LDX();u8 LDY();u8 LSR();
|
||||
u8 ORA();u8 ABY();u8 IND();u8 IZX();
|
||||
u8 IZY();u8 CLI();u8 CLV();u8 CMP();
|
||||
u8 CPX();u8 SEI();u8 XXX();u8 NOP();
|
||||
|
||||
u8 TYA();u8 TXS();u8 TXA();
|
||||
u8 TSX();
|
||||
u8 TAY();u8 TAX();u8 STY();
|
||||
|
||||
bool complete();
|
||||
};
|
||||
|
||||
|
||||
#endif //EMU6502_J6502_H
|
189
main.cpp
Normal file
189
main.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "Bus.h"
|
||||
#include "j6502.h"
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
|
||||
|
||||
class Emulator : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
Emulator() { sAppName = "olc6502 Demonstration"; }
|
||||
|
||||
Bus nes;
|
||||
std::map<uint16_t, std::string> mapAsm;
|
||||
|
||||
std::string hex(uint32_t n, uint8_t d)
|
||||
{
|
||||
std::string s(d, '0');
|
||||
for (int i = d - 1; i >= 0; i--, n >>= 4)
|
||||
s[i] = "0123456789ABCDEF"[n & 0xF];
|
||||
return s;
|
||||
};
|
||||
|
||||
void DrawRam(int x, int y, uint16_t nAddr, int nRows, int nColumns)
|
||||
{
|
||||
int nRamX = x, nRamY = y;
|
||||
for (int row = 0; row < nRows; row++)
|
||||
{
|
||||
std::string sOffset = "$" + hex(nAddr, 4) + ":";
|
||||
for (int col = 0; col < nColumns; col++)
|
||||
{
|
||||
sOffset += " " + hex(nes.read(nAddr, true), 2);
|
||||
nAddr += 1;
|
||||
}
|
||||
DrawString(nRamX, nRamY, sOffset);
|
||||
nRamY += 10;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCpu(int x, int y)
|
||||
{
|
||||
std::string status = "STATUS: ";
|
||||
DrawString(x , y , "STATUS:", olc::WHITE);
|
||||
DrawString(x + 64, y, "N", nes.cpu.status & j6502::N ? olc::GREEN : olc::RED);
|
||||
DrawString(x + 80, y , "V", nes.cpu.status & j6502::V ? olc::GREEN : olc::RED);
|
||||
DrawString(x + 96, y , "-", nes.cpu.status & j6502::U ? olc::GREEN : olc::RED);
|
||||
DrawString(x + 112, y , "B", nes.cpu.status & j6502::B ? olc::GREEN : olc::RED);
|
||||
DrawString(x + 128, y , "D", nes.cpu.status & j6502::D ? olc::GREEN : olc::RED);
|
||||
DrawString(x + 144, y , "I", nes.cpu.status & j6502::I ? olc::GREEN : olc::RED);
|
||||
DrawString(x + 160, y , "Z", nes.cpu.status & j6502::Z ? olc::GREEN : olc::RED);
|
||||
DrawString(x + 178, y , "C", nes.cpu.status & j6502::C ? olc::GREEN : olc::RED);
|
||||
DrawString(x , y + 10, "PC: $" + hex(nes.cpu.pc, 4));
|
||||
DrawString(x , y + 20, "A: $" + hex(nes.cpu.a, 2) + " [" + std::to_string(nes.cpu.a) + "]");
|
||||
DrawString(x , y + 30, "X: $" + hex(nes.cpu.x, 2) + " [" + std::to_string(nes.cpu.x) + "]");
|
||||
DrawString(x , y + 40, "Y: $" + hex(nes.cpu.y, 2) + " [" + std::to_string(nes.cpu.y) + "]");
|
||||
DrawString(x , y + 50, "Stack P: $" + hex(nes.cpu.sp, 4));
|
||||
}
|
||||
|
||||
void DrawCode(int x, int y, int nLines)
|
||||
{
|
||||
auto it_a = mapAsm.find(nes.cpu.pc);
|
||||
int nLineY = (nLines >> 1) * 10 + y;
|
||||
if (it_a != mapAsm.end())
|
||||
{
|
||||
DrawString(x, nLineY, (*it_a).second, olc::CYAN);
|
||||
while (nLineY < (nLines * 10) + y)
|
||||
{
|
||||
nLineY += 10;
|
||||
if (++it_a != mapAsm.end())
|
||||
{
|
||||
DrawString(x, nLineY, (*it_a).second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it_a = mapAsm.find(nes.cpu.pc);
|
||||
nLineY = (nLines >> 1) * 10 + y;
|
||||
if (it_a != mapAsm.end())
|
||||
{
|
||||
while (nLineY > y)
|
||||
{
|
||||
nLineY -= 10;
|
||||
if (--it_a != mapAsm.end())
|
||||
{
|
||||
DrawString(x, nLineY, (*it_a).second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Load Program (assembled at https://www.masswerk.at/6502/assembler.html)
|
||||
/*
|
||||
*=$8000
|
||||
LDX #10
|
||||
STX $0000
|
||||
LDX #3
|
||||
STX $0001
|
||||
LDY $0000
|
||||
LDA #0
|
||||
CLC
|
||||
loop
|
||||
ADC $0001
|
||||
DEY
|
||||
BNE loop
|
||||
STA $0002
|
||||
NOP
|
||||
NOP
|
||||
NOP
|
||||
*/
|
||||
|
||||
// Convert hex string into bytes for RAM
|
||||
std::stringstream ss;
|
||||
ss << "A2 0A 8E 00 00 A2 03 8E 01 00 AC 00 00 A9 00 18 6D 01 00 88 D0 FA 8D 02 00 EA EA EA";
|
||||
uint16_t nOffset = 0x8000;
|
||||
while (!ss.eof())
|
||||
{
|
||||
std::string b;
|
||||
ss >> b;
|
||||
nes.ram[nOffset++] = (uint8_t)std::stoul(b, nullptr, 16);
|
||||
}
|
||||
|
||||
// Set Reset Vector
|
||||
nes.ram[0xFFFC] = 0x00;
|
||||
nes.ram[0xFFFD] = 0x80;
|
||||
|
||||
// Dont forget to set IRQ and NMI vectors if you want to play with those
|
||||
|
||||
// Extract dissassembly
|
||||
mapAsm = nes.cpu.disassemble(0x0000, 0xFFFF);
|
||||
|
||||
// Reset
|
||||
nes.cpu.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime)
|
||||
{
|
||||
Clear(olc::DARK_BLUE);
|
||||
|
||||
|
||||
if (GetKey(olc::Key::SPACE).bPressed)
|
||||
{
|
||||
do
|
||||
{
|
||||
nes.cpu.clock();
|
||||
}
|
||||
while (!nes.cpu.complete());
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::R).bPressed)
|
||||
nes.cpu.reset();
|
||||
|
||||
if (GetKey(olc::Key::I).bPressed)
|
||||
nes.cpu.irq();
|
||||
|
||||
if (GetKey(olc::Key::N).bPressed)
|
||||
nes.cpu.nmi();
|
||||
|
||||
// Draw Ram Page 0x00
|
||||
DrawRam(2, 2, 0x0000, 16, 16);
|
||||
DrawRam(2, 182, 0x8000, 16, 16);
|
||||
DrawCpu(448, 2);
|
||||
DrawCode(448, 72, 26);
|
||||
|
||||
|
||||
DrawString(10, 370, "SPACE = Step Instruction R = RESET I = IRQ N = NMI");
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
int main() {
|
||||
Emulator demo;
|
||||
demo.Construct(680, 480, 2, 2);
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
6696
olcPixelGameEngine.h
Normal file
6696
olcPixelGameEngine.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user