Initial commit
commit
588ce89b19
|
@ -0,0 +1,3 @@
|
||||||
|
.idea/
|
||||||
|
.idea/.gitignore
|
||||||
|
cmake-build-debug
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
|
@ -0,0 +1,21 @@
|
||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
project(chip8)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
|
||||||
|
find_package(SDL2 REQUIRED)
|
||||||
|
|
||||||
|
add_executable(chip8 main.cpp
|
||||||
|
include/chip8.h
|
||||||
|
src/chip8.cpp
|
||||||
|
include/logger.h
|
||||||
|
include/date.h
|
||||||
|
src/date.cpp
|
||||||
|
include/display.h
|
||||||
|
src/display.cpp
|
||||||
|
include/instruction.h
|
||||||
|
src/instruction.cpp
|
||||||
|
include/instruction_handler.h
|
||||||
|
src/instruction_handler.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(chip8 ${SDL2_LIBRARIES})
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef CHIP8_CHIP8_H
|
||||||
|
#define CHIP8_CHIP8_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class InstructionHandler;
|
||||||
|
class Display;
|
||||||
|
|
||||||
|
class Chip8 {
|
||||||
|
private:
|
||||||
|
unsigned char memory[0xFFF] = {};
|
||||||
|
unsigned char v[0x10] = {};
|
||||||
|
unsigned short pc;
|
||||||
|
Display* display;
|
||||||
|
InstructionHandler* instructionHandler;
|
||||||
|
public:
|
||||||
|
Chip8();
|
||||||
|
~Chip8();
|
||||||
|
void loadRom(const std::string& file);
|
||||||
|
[[nodiscard]] Display* getDisplay() const;
|
||||||
|
void emulateCycle();
|
||||||
|
};
|
||||||
|
#endif //CHIP8_CHIP8_H
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef CHIP8_DATE_H
|
||||||
|
#define CHIP8_DATE_H
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
class Date {
|
||||||
|
public:
|
||||||
|
explicit Date(std::chrono::system_clock::time_point tp);
|
||||||
|
static Date now();
|
||||||
|
std::string to_string() const;
|
||||||
|
private:
|
||||||
|
std::chrono::system_clock::time_point tp;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //CHIP8_DATE_H
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef CHIP8_DISPLAY_H
|
||||||
|
#define CHIP8_DISPLAY_H
|
||||||
|
|
||||||
|
#define STATE_ON 1
|
||||||
|
#define STATE_OFF 0
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include "chip8.h"
|
||||||
|
|
||||||
|
class Display {
|
||||||
|
private:
|
||||||
|
bool flagRedraw = false;
|
||||||
|
bool display[64 * 32] = {};
|
||||||
|
public:
|
||||||
|
void draw(SDL_Renderer*, const Chip8&);
|
||||||
|
[[nodiscard]] bool needsRedraw() const;
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
#endif //CHIP8_DISPLAY_H
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef CHIP8_INSTRUCTION_H
|
||||||
|
#define CHIP8_INSTRUCTION_H
|
||||||
|
class Instruction {
|
||||||
|
private:
|
||||||
|
const unsigned short opcode;
|
||||||
|
public:
|
||||||
|
explicit Instruction(unsigned short);
|
||||||
|
[[nodiscard]] unsigned short nnn() const;
|
||||||
|
[[nodiscard]] unsigned short n() const;
|
||||||
|
[[nodiscard]] unsigned short kk() const;
|
||||||
|
[[nodiscard]] unsigned char x() const;
|
||||||
|
[[nodiscard]] unsigned char y() const;
|
||||||
|
};
|
||||||
|
#endif //CHIP8_INSTRUCTION_H
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef CHIP8_INSTRUCTION_HANDLER_H
|
||||||
|
#define CHIP8_INSTRUCTION_HANDLER_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include "chip8.h"
|
||||||
|
#include "instruction.h"
|
||||||
|
|
||||||
|
class InstructionHandler {
|
||||||
|
private:
|
||||||
|
typedef void (*Handler)(const Chip8&, Instruction);
|
||||||
|
std::map<unsigned short, Handler> handlers = {};
|
||||||
|
static void handle0000(const Chip8&, Instruction);
|
||||||
|
public:
|
||||||
|
InstructionHandler();
|
||||||
|
void handleInstruction(unsigned short, const Chip8&, Instruction);
|
||||||
|
};
|
||||||
|
#endif //CHIP8_INSTRUCTION_HANDLER_H
|
|
@ -0,0 +1,60 @@
|
||||||
|
#ifndef CHIP8_LOGGER_H
|
||||||
|
#define CHIP8_LOGGER_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include "date.h"
|
||||||
|
|
||||||
|
enum LogType {
|
||||||
|
INFO,
|
||||||
|
DEBUG,
|
||||||
|
WARN,
|
||||||
|
ERROR
|
||||||
|
};
|
||||||
|
class Logger {
|
||||||
|
private:
|
||||||
|
static const char* logTypeToString(LogType logType) {
|
||||||
|
switch (logType) {
|
||||||
|
case LogType::INFO:
|
||||||
|
return "INFO";
|
||||||
|
case LogType::DEBUG:
|
||||||
|
return "DEBUG";
|
||||||
|
case LogType::WARN:
|
||||||
|
return "WARN";
|
||||||
|
case LogType::ERROR:
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
template<typename... Args>
|
||||||
|
static void log(LogType logType, Args&&... args) {
|
||||||
|
std::ostream& stream = (logType == LogType::ERROR) ? std::cerr : std::cout;
|
||||||
|
stream << "[" << logTypeToString(logType) << "] [" << Date::now().to_string() << "]";
|
||||||
|
((stream << " " << args), ...);
|
||||||
|
stream << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
static void info(Args&&... args) {
|
||||||
|
log(LogType::INFO, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
static void debug(Args&&... args) {
|
||||||
|
log(LogType::DEBUG, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
static void warn(Args&&... args) {
|
||||||
|
log(LogType::WARN, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
static void error(Args&&... args) {
|
||||||
|
log(LogType::ERROR, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //CHIP8_LOGGER_H
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include "include/logger.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
|
||||||
|
Logger::error("Cannot initialize SDL:", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Window* window = SDL_CreateWindow("Chip8 Emulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 512, SDL_WINDOW_SHOWN);
|
||||||
|
|
||||||
|
if (window == nullptr) {
|
||||||
|
Logger::error("Cannot create SDL window:", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||||
|
|
||||||
|
if (renderer == nullptr) {
|
||||||
|
Logger::error("Cannot create SDL renderer:", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run = true;
|
||||||
|
SDL_Event e;
|
||||||
|
|
||||||
|
while (run) {
|
||||||
|
while (SDL_PollEvent(&e) != 0) {
|
||||||
|
if (e.type == SDL_QUIT) {
|
||||||
|
run = false;
|
||||||
|
}
|
||||||
|
else if (e.type == SDL_KEYDOWN) {
|
||||||
|
if (e.key.keysym.sym == SDLK_ESCAPE) {
|
||||||
|
run = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_DestroyRenderer(renderer);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
SDL_Quit();
|
||||||
|
return 0;
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,36 @@
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include "../include/chip8.h"
|
||||||
|
#include "../include/instruction.h"
|
||||||
|
#include "../include/instruction_handler.h"
|
||||||
|
#include "../include/display.h"
|
||||||
|
|
||||||
|
Chip8::Chip8() : instructionHandler(), display() {
|
||||||
|
this->pc = 0x200;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chip8::~Chip8() {
|
||||||
|
delete display;
|
||||||
|
delete instructionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chip8::loadRom(const std::string& file) {
|
||||||
|
FILE* game = fopen(("roms/" + file).c_str(), "rb");
|
||||||
|
if (game == nullptr) {
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(this->memory + 0x200, sizeof(char), 0xFFF - 0x200, game);
|
||||||
|
fclose(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
Display* Chip8::getDisplay() const {
|
||||||
|
return this->display;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chip8::emulateCycle() {
|
||||||
|
unsigned short opcode = (this->memory[this->pc] << 8 | this->memory[this->pc + 1]);
|
||||||
|
this->instructionHandler->handleInstruction(opcode & 0xF000, *this, Instruction(opcode));
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include "../include/date.h"
|
||||||
|
|
||||||
|
Date::Date(std::chrono::system_clock::time_point tp) {
|
||||||
|
this->tp = tp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Date Date::now() {
|
||||||
|
return Date(std::chrono::system_clock::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Date::to_string() const {
|
||||||
|
auto now_c = std::chrono::system_clock::to_time_t(tp);
|
||||||
|
std::tm* now_tm = std::localtime(&now_c);
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << std::put_time(now_tm, "%Y-%m-%d %H:%M:%S");
|
||||||
|
return oss.str();
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include "../include/display.h"
|
||||||
|
|
||||||
|
void Display::draw(SDL_Renderer * renderer, const Chip8& chip8) {
|
||||||
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
||||||
|
|
||||||
|
for (int y = 0; y < 32; y++) {
|
||||||
|
for (int x = 0; x < 64; x++) {
|
||||||
|
if (display[(y * 64) + x] != STATE_OFF) {
|
||||||
|
SDL_Rect r;
|
||||||
|
r.x = x + 10;
|
||||||
|
r.y = y + 10;
|
||||||
|
r.w = 10;
|
||||||
|
r.h = 10;
|
||||||
|
SDL_RenderFillRect(renderer, &r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
this->flagRedraw = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Display::needsRedraw() const {
|
||||||
|
return this->flagRedraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Display::clear() {
|
||||||
|
std::fill(display, display + 64 * 32, false);
|
||||||
|
this->flagRedraw = true;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "../include/instruction.h"
|
||||||
|
|
||||||
|
Instruction::Instruction(unsigned short opcode) : opcode(opcode) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short Instruction::nnn() const {
|
||||||
|
return this->opcode & 0x0FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short Instruction::n() const {
|
||||||
|
return this->opcode & 0x000F;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short Instruction::kk() const {
|
||||||
|
return this->opcode & 0x00FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char Instruction::x() const {
|
||||||
|
return (this->opcode & 0x0F00) >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char Instruction::y() const {
|
||||||
|
return (this->opcode & 0x00F0) >> 4;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include "../include/instruction_handler.h"
|
||||||
|
#include "../include/logger.h"
|
||||||
|
#include "../include/display.h"
|
||||||
|
|
||||||
|
InstructionHandler::InstructionHandler() {
|
||||||
|
this->handlers[0x0000] = handle0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstructionHandler::handleInstruction(unsigned short instructionNumber, const Chip8& chip8, Instruction instruction) {
|
||||||
|
if (!this->handlers.contains(instructionNumber)) {
|
||||||
|
Logger::error("Unregistered instruction", instructionNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->handlers[instructionNumber](chip8, instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstructionHandler::handle0000(const Chip8& chip8, Instruction instruction) {
|
||||||
|
switch (instruction.kk()) {
|
||||||
|
case 0x00E0:
|
||||||
|
chip8.getDisplay()->clear();
|
||||||
|
break;
|
||||||
|
case 0x00EE:
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue