Initial Commit

This commit is contained in:
scientiist
2023-01-30 01:10:28 -06:00
commit 6cef1c2f2d
6 changed files with 8336 additions and 0 deletions

28
Bus.h Normal file
View 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
View 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)

1311
j6502.cpp Normal file

File diff suppressed because it is too large Load Diff

101
j6502.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff