initial commit

This commit is contained in:
c1m5j 2021-07-30 13:44:51 +02:00
commit ae2b3d3451
40 changed files with 55111 additions and 0 deletions

40
src/audio.cpp Normal file
View file

@ -0,0 +1,40 @@
#include <iostream>
#include "memory.hpp"
#include "audio.hpp"
#include "bus.hpp"
Audio::Audio(Bus* bus): bus(bus), spec({}), device(0)
{
SDL_AudioSpec spec = {};
spec.freq = 15360;
spec.format = AUDIO_U8;
spec.channels = 1;
spec.samples = 256;
this->device = SDL_OpenAudioDevice(nullptr, 0, &spec, &this->spec, 0);
if (!this->device) {
std::cerr << "I had trouble creating the audio device!\nSDL error: " << SDL_GetError() << std::endl;
exit(1);
}
SDL_PauseAudioDevice(this->device, 0);
}
Audio::~Audio()
{
if (this->device) {
SDL_PauseAudioDevice(this->device, 1);
SDL_CloseAudioDevice(this->device);
this->device = 0;
}
this->spec = {};
this->bus = nullptr;
}
void Audio::Play()
{
uint32_t location = (uint32_t)this->bus->memory->mem[6] << 16 | this->bus->memory->mem[7] << 8;
if (SDL_QueueAudio(this->device, this->bus->memory->mem + location, 256)) {
std::cerr << "I had trouble playing audio!\nSDL error: " << SDL_GetError() << std::endl;
}
}

13
src/bus.cpp Normal file
View file

@ -0,0 +1,13 @@
#include "bus.hpp"
Bus::Bus(): memory(nullptr), cpu(nullptr), display(nullptr), audio(nullptr), input(nullptr)
{}
Bus::~Bus()
{
this->memory = nullptr;
this->cpu = nullptr;
this->display = nullptr;
this->audio = nullptr;
this->input = nullptr;
}

49
src/cpu.cpp Normal file
View file

@ -0,0 +1,49 @@
#include "cpu.hpp"
#include "bus.hpp"
#include "memory.hpp"
// i'm sorry but there's no way i'm typing this every time i want to access memory
// (which is a lot of times in this file)
#define MEMORY this->bus->memory->mem
CPU::CPU(Bus* bus): bus(bus)
{
this->Reset();
}
CPU::~CPU()
{
this->bus = nullptr;
}
void CPU::Step()
{
uint32_t destination = (uint32_t)this->pc[3] << 16 | (uint32_t)this->pc[4] << 8 | this->pc[5];
uint32_t source = (uint32_t)this->pc[0] << 16 | (uint32_t)this->pc[1] << 8 | this->pc[2];
uint32_t jump = (uint32_t)this->pc[6] << 16 | (uint32_t)this->pc[7] << 8 | this->pc[8];
MEMORY[destination] = MEMORY[source];
this->pc = MEMORY + jump;
}
void CPU::Reset()
{
this->pc = MEMORY + ((uint32_t)MEMORY[2]<<16 | (uint32_t)MEMORY[3]<<8 | MEMORY[4]);
}
void CPU::setKeyBit(uint8_t index)
{
uint32_t* keyboard = (uint32_t*)MEMORY;
if (index < 8) *keyboard |= 1 << (index + 8);
else *keyboard |= 1 << (index - 8);
}
void CPU::clearKeyBit(uint8_t index)
{
uint32_t* keyboard = (uint32_t*)MEMORY;
if (index < 8) *keyboard &= ~(1 << (index + 8));
else *keyboard &= ~(1 << (index - 8));
}
#undef MEMORY
// goodbye

55
src/display.cpp Normal file
View file

@ -0,0 +1,55 @@
#include <iostream>
#include "memory.hpp"
#include "display.hpp"
#include "bus.hpp"
Display::Display(Bus* bus, SDL_Renderer* ren): bus(bus)
{
this->Reset();
this->displayTexture = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, width, height);
if (!displayTexture) {
std::cerr << "Texture creation error: " << SDL_GetError() << std::endl;
exit(1);
}
this->PrepareColorPalette();
}
void Display::Reset()
{
for (auto& el: this->display) el = {0, 0, 0, 255};
}
void Display::PrepareColorPalette()
{
size_t i = 0;
for (size_t r = 0; r < 256; r += 0x33) {
for (size_t g = 0; g < 256; g += 0x33) {
for (size_t b = 0; b < 256; b += 0x33) {
this->colorPalette[i].r = r;
this->colorPalette[i].g = g;
this->colorPalette[i].b = b;
i++;
}
}
}
}
void Display::UpdateTexture()
{
uint32_t location = (uint32_t)this->bus->memory->mem[5];
for (size_t y = 0; y < 256; y++) {
for (size_t x = 0; x < 256; x++) {
this->display[(y * 256) + x] = this->colorPalette[this->bus->memory->mem[location << 16 | y << 8 | x ]];
}
}
SDL_UpdateTexture(this->displayTexture, NULL, display, width * 4);
}
Display::~Display()
{
SDL_DestroyTexture(this->displayTexture);
}

74
src/input.cpp Normal file
View file

@ -0,0 +1,74 @@
#include "input.hpp"
#include "memory.hpp"
#include "bus.hpp"
Input::Input(Bus* bus): bus(bus) {}
Input::~Input()
{
this->bus = nullptr;
}
void Input::HandleKeyDown(SDL_Keycode keycode)
{
switch (keycode)
{
case SDLK_1: this->SetKeyBit(1); break;
case SDLK_2: this->SetKeyBit(2); break;
case SDLK_3: this->SetKeyBit(3); break;
case SDLK_4: this->SetKeyBit(12); break;
case SDLK_q: this->SetKeyBit(4); break;
case SDLK_w: this->SetKeyBit(5); break;
case SDLK_e: this->SetKeyBit(6); break;
case SDLK_r: this->SetKeyBit(13); break;
case SDLK_a: this->SetKeyBit(7); break;
case SDLK_s: this->SetKeyBit(8); break;
case SDLK_d: this->SetKeyBit(9); break;
case SDLK_f: this->SetKeyBit(14); break;
case SDLK_z: this->SetKeyBit(10); break;
case SDLK_x: this->SetKeyBit(0); break;
case SDLK_c: this->SetKeyBit(11); break;
case SDLK_v: this->SetKeyBit(15); break;
default: break;
}
}
void Input::HandleKeyUp(SDL_Keycode keycode)
{
switch (keycode)
{
case SDLK_1: this->UnsetKeyBit(1); break;
case SDLK_2: this->UnsetKeyBit(2); break;
case SDLK_3: this->UnsetKeyBit(3); break;
case SDLK_4: this->UnsetKeyBit(12); break;
case SDLK_q: this->UnsetKeyBit(4); break;
case SDLK_w: this->UnsetKeyBit(5); break;
case SDLK_e: this->UnsetKeyBit(6); break;
case SDLK_r: this->UnsetKeyBit(13); break;
case SDLK_a: this->UnsetKeyBit(7); break;
case SDLK_s: this->UnsetKeyBit(8); break;
case SDLK_d: this->UnsetKeyBit(9); break;
case SDLK_f: this->UnsetKeyBit(14); break;
case SDLK_z: this->UnsetKeyBit(10); break;
case SDLK_x: this->UnsetKeyBit(0); break;
case SDLK_c: this->UnsetKeyBit(11); break;
case SDLK_v: this->UnsetKeyBit(15); break;
default: break;
}
}
void Input::SetKeyBit(uint8_t bit)
{
uint32_t* keyboard = (uint32_t*)this->bus->memory->mem;
if (bit < 8) *keyboard |= (1 << (bit + 8));
else *keyboard |= (1 << (bit - 8));
}
void Input::UnsetKeyBit(uint8_t bit)
{
uint32_t* keyboard = (uint32_t*)this->bus->memory->mem;
if (bit < 8) *keyboard &= ~(1 << (bit + 8));
else *keyboard &= ~(1 << (bit - 8));
}

247
src/main.cpp Normal file
View file

@ -0,0 +1,247 @@
#include <iostream>
#include <filesystem>
#include <bitset>
#include <SDL2/SDL.h>
#include <imgui.h>
#include <ImGuiFileDialog.h>
#include <imgui_sdl.h>
#include <ctime>
#include "bus.hpp"
#include "cpu.hpp"
#include "display.hpp"
#include "audio.hpp"
#include "memory.hpp"
#include "input.hpp"
#define MILLISECONDS std::milli::den
#define FRAMERATE 60.0f
#define FRAMELENGTH (MILLISECONDS / FRAMERATE)
int main(int argc, char const *argv[])
{
// SDL initialisation
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window* window = SDL_CreateWindow("Bytepusher++", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1000, 700, SDL_WINDOW_RESIZABLE);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
// VM initialisation
Bus* bus = new Bus();
Memory* memory = new Memory(bus);
bus->memory = memory;
CPU* cpu = new CPU(bus);
bus->cpu = cpu;
Display* display = new Display(bus, renderer);
bus->display = display;
Audio* audio = new Audio(bus);
bus->audio = audio;
Input* input = new Input(bus);
bus->input = input;
ImGui::CreateContext();
ImGuiSDL::Initialize(renderer, 900, 700);
ImGui::StyleColorsDark();
// Clears screen
SDL_Texture* clear = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, 100, 100);
{
SDL_SetRenderTarget(renderer, clear);
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
SDL_RenderClear(renderer);
SDL_SetRenderTarget(renderer, nullptr);
}
std::time_t res = std::time(nullptr);
std::string date = std::ctime(&res);
date = date.substr(0, date.length() - 1);
int snapshotIncrement = 1;
bool run = true;
bool paused = false;
bool showBytepusher = true;
bool showDebug = true;
while (run)
{
size_t begin = SDL_GetTicks();
ImGuiIO& io = ImGui::GetIO();
int wheel = 0;
SDL_Event e;
while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT) run = false;
else if (e.type == SDL_KEYDOWN) {
if (!paused && memory->ROMLoaded)
input->HandleKeyDown(e.key.keysym.sym);
} else if (e.type == SDL_KEYUP) {
input->HandleKeyUp(e.key.keysym.sym);
} else if (e.type == SDL_WINDOWEVENT) {
if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
io.DisplaySize.x = static_cast<float>(e.window.data1);
io.DisplaySize.y = static_cast<float>(e.window.data2);
}
} else if (e.type == SDL_MOUSEWHEEL) {
wheel = e.wheel.y;
}
}
int mouseX, mouseY;
const int buttons = SDL_GetMouseState(&mouseX, &mouseY);
// Setup low-level inputs (e.g. on Win32, GetKeyboardState(), or write to those fields from your Windows message loop handlers, etc.)
io.DeltaTime = 1.0f / 60.0f;
io.MousePos = ImVec2(static_cast<float>(mouseX), static_cast<float>(mouseY));
io.MouseDown[0] = buttons & SDL_BUTTON(SDL_BUTTON_LEFT);
io.MouseDown[1] = buttons & SDL_BUTTON(SDL_BUTTON_RIGHT);
io.MouseWheel = static_cast<float>(wheel);
// Bytepusher code
if (!paused && memory->ROMLoaded) {
cpu->Reset();
size_t i = 65536;
do {
cpu->Step();
} while (i--);
audio->Play();
display->UpdateTexture();
}
// ImGui code
ImGui::NewFrame();
// window minimizers
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open")) {
ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose BytePusher ROM", ".bytepusher,.bp", "", 1);
}
if (ImGui::MenuItem("Close ROM")) {
memory->Reset();
display->Reset();
display->UpdateTexture();
}
if (ImGui::MenuItem("Quit")) {
run = false;
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Debug")) {
ImGui::Checkbox("Show debug window", &showDebug);
ImGui::Separator();
if (ImGui::MenuItem(paused? "Resume emulation###pausebtn" : "Pause emulation###pausebtn")) {
paused = !paused;
}
if (ImGui::MenuItem("Snapshot RAM")) {
// candidate for ugliest piece of code in history?
std::string filepath = std::filesystem::path(argv[0])
.parent_path()
.parent_path()
.string()
+ "/snapshots/"
+ date
+ ": "
+ std::to_string(snapshotIncrement)
+ ".bytepusher";
if (!memory->SnapshotRAM(filepath)) {
if (ImGui::BeginPopupModal("RAM snapshotting error")) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "%s", memory->snapshotError.c_str());
ImGui::EndPopup();
}
}
snapshotIncrement++;
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
if (ImGuiFileDialog::Instance()->Display("ChooseFileDlgKey")) {
if (ImGuiFileDialog::Instance()->IsOk()) {
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
ImGuiFileDialog::Instance()->Close();
if (!memory->LoadROM(filePathName)) {
if (ImGui::BeginPopupModal("ROM loading error")) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "%s", memory->ROMLoadError.c_str());
ImGui::EndPopup();
}
}
}
}
if (showDebug) {
ImGui::Begin("Debug", &showDebug, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize);
ImGui::Text("-- ROM info --");
if (ImGui::BeginTable("ROM info", 2)) {
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Has loaded ROM");
ImGui::TableNextColumn(); ImGui::Text(memory->ROMLoaded? "Yes" : "No");
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "ROM name");
ImGui::TableNextColumn(); ImGui::Text("%s", std::filesystem::path(memory->ROMFilepath).filename().c_str());
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "ROM size");
ImGui::TableNextColumn(); ImGui::Text("%zu B", memory->ROMSize);
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Is paused");
ImGui::TableNextColumn(); ImGui::Text(paused? "Yes" : "No");
ImGui::EndTable();
}
ImGui::Separator();
ImGui::Text("-- Emulation info --");
if (ImGui::BeginTable("Emulation info", 2)) {
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Keyboard state");
ImGui::TableNextColumn(); ImGui::Text("%s", std::bitset<16>((uint32_t)memory->mem[0] << 8 | memory->mem[1]).to_string().c_str());
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "GFX block location");
ImGui::TableNextColumn(); ImGui::Text("0x%02x", memory->mem[5]);
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Sound block location");
ImGui::TableNextColumn(); ImGui::Text("0x%06x", (uint32_t)memory->mem[6] << 16 | memory->mem[7] << 8);
ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Memory errors");
ImGui::TableNextColumn(); ImGui::Text("%s\n%s", memory->ROMLoadError.c_str(), memory->snapshotError.c_str());
ImGui::EndTable();
}
ImGui::End();
}
ImGui::Begin(paused? "[PAUSED] Bytepusher###bptitle" : "Bytepusher###bptitle", &showBytepusher, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize);
ImGui::Image(display->displayTexture, ImVec2(512, 512));
ImGui::End();
SDL_SetRenderDrawColor(renderer, 114, 144, 154, 255);
SDL_RenderClear(renderer);
ImGui::Render();
ImGuiSDL::Render(ImGui::GetDrawData());
SDL_RenderPresent(renderer);
size_t length = SDL_GetTicks() - begin;
if (length < FRAMELENGTH) {
SDL_Delay(FRAMELENGTH - length);
}
}
ImGuiSDL::Deinitialize();
SDL_DestroyTexture(clear);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
delete cpu;
delete memory;
delete display;
delete audio;
delete bus;
ImGui::DestroyContext();
return EXIT_SUCCESS;
}

68
src/memory.cpp Normal file
View file

@ -0,0 +1,68 @@
#include <iostream>
#include <filesystem>
#include <fstream>
#include <ctime>
#include "memory.hpp"
#include "bus.hpp"
Memory::Memory(Bus* bus): bus(bus)
{
this->Reset();
}
Memory::~Memory()
{
this->bus = nullptr;
}
void Memory::Reset()
{
this->ROMLoaded = false;
this->ROMFilepath = "";
this->ROMSize = 0;
this->ROMLoadError = "";
this->snapshotError = "";
this->snapshotLast = "";
for (auto& el: this->mem) el = 0;
}
bool Memory::LoadROM(std::string filepath)
{
std::fstream stream(filepath, std::ios::in|std::ios::binary);
if (stream) {
size_t filesize = std::filesystem::file_size(filepath);
if (filesize > MEMORY_SIZE) {
std::cerr << "Error: chosen ROM is larger than RAM" << std::endl;
this->ROMLoadError = "Error: chosen ROM is larger than RAM";
return 0;
}
stream.read((char*)this->mem, filesize);
this->ROMLoaded = true;
this->ROMFilepath = filepath;
this->ROMSize = filesize;
this->ROMLoadError = "";
stream.close();
return 1;
} else {
std::cerr << "Error: ROM could not be found or opened" << std::endl;
this->ROMLoadError = "Error: ROM could not be found or opened";
return 0;
}
}
bool Memory::SnapshotRAM(std::string filepath)
{
std::fstream stream(filepath, std::ios::out|std::ios::binary);
if (stream) {
stream.write((char*)this->mem, MEMORY_SIZE);
this->snapshotError = "";
std::time_t res = std::time(nullptr);
this->snapshotLast = std::ctime(&res);
stream.close();
return 1;
} else {
std::cerr << "Error: could not write RAM contents to filepath " << filepath << std::endl;
this->snapshotError = "Error: could not write RAM contents to filepath " + filepath;
return 0;
}
}