Initial commit

main
Tiger 2023-09-09 19:57:57 +02:00
commit 588ce89b19
16 changed files with 368 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea/
.idea/.gitignore
cmake-build-debug

8
.idea/.gitignore vendored Normal file
View File

@ -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

21
CMakeLists.txt Normal file
View File

@ -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})

23
include/chip8.h Normal file
View File

@ -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

15
include/date.h Normal file
View File

@ -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

19
include/display.h Normal file
View File

@ -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

14
include/instruction.h Normal file
View File

@ -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

View File

@ -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

60
include/logger.h Normal file
View File

@ -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

49
main.cpp Normal file
View File

@ -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.

36
src/chip8.cpp Normal file
View File

@ -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));
}

18
src/date.cpp Normal file
View File

@ -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();
}

32
src/display.cpp Normal file
View File

@ -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;
}

25
src/instruction.cpp Normal file
View File

@ -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;
}

View File

@ -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;
}
}