From 830ab9eda7376f38b335e87892f150ff823d5f95 Mon Sep 17 00:00:00 2001 From: Pablo Rodriguez Date: Fri, 29 Nov 2024 18:44:31 -0500 Subject: [PATCH] Implemented most FXXX instructions. It now passes the Corax+ test. --- Interpreter.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++------ main.cpp | 49 +++++++++++++++++++++++----------------------- 2 files changed, 70 insertions(+), 31 deletions(-) diff --git a/Interpreter.cpp b/Interpreter.cpp index dba54f0..d1dbcba 100644 --- a/Interpreter.cpp +++ b/Interpreter.cpp @@ -4,6 +4,7 @@ namespace chocochip8 { +// converts any 8-bit sprite row to its high-res 16-bit equivalent constexpr uint16_t gcvLowResToHighResRowLookupTable[256] = { 0x0000, 0x0003, 0x000C, 0x000F, 0x0030, 0x0033, 0x003C, 0x003F, 0x00C0, 0x00C3, 0x00CC, 0x00CF, 0x00F0, 0x00F3, 0x00FC, 0x00FF, 0x0300, 0x0303, 0x030C, 0x030F, 0x0330, 0x0333, 0x033C, 0x033F, 0x03C0, 0x03C3, 0x03CC, 0x03CF, 0x03F0, 0x03F3, 0x03FC, 0x03FF, @@ -23,6 +24,7 @@ constexpr uint16_t gcvLowResToHighResRowLookupTable[256] = { 0xFF00, 0xFF03, 0xFF0C, 0xFF0F, 0xFF30, 0xFF33, 0xFF3C, 0xFF3F, 0xFFC0, 0xFFC3, 0xFFCC, 0xFFCF, 0xFFF0, 0xFFF3, 0xFFFC, 0xFFFF }; +// 4x5 sprites for hex digits 0-F constexpr uint8_t gcvLowResFontData[80] = { 0xF0, 0x90, 0x90, 0x90, 0xF0, 0x20, 0x60, 0x20, 0x20, 0x70, @@ -68,8 +70,11 @@ void Interpreter::tick() { }; // fetch instruction - sreg_t pc = mvSpecialReg[SR_PC]; - unsigned inst = (mvMemory[pc] << 8) | mvMemory[pc + 1]; + sreg_t iInstAddr = mvSpecialReg[SR_PC]; + unsigned inst = (mvMemory[iInstAddr] << 8) | mvMemory[iInstAddr + 1]; + + // increment program counter + mvSpecialReg[SR_PC] += 2; // extract fields unsigned iRegDst = (inst & 0x0F00) >> 8; // destination register index @@ -99,7 +104,7 @@ void Interpreter::tick() { mvSpecialReg[SR_PC] = imm12; break; case 0x2000: // 2NNN - call subroutine - mCallStack.push(pc); + mCallStack.push(mvSpecialReg[SR_PC]); mvSpecialReg[SR_PC] = imm12; break; case 0x3000: // 3XNN - skip if equal immediate @@ -138,12 +143,47 @@ void Interpreter::tick() { case 0xE000: // EX9E, EXA1 - keypad access break; case 0xF000: // several unique instructions + switch(inst & 0xF0FF) { + case 0xF007: // FX07 - read timer register + mvReg[iRegDst] = (mvSpecialReg[SR_T1] + mcTicksPerSecond - 1) / mcTicksPerSecond; + break; + case 0xF015: // FX15 - set timer register + mvSpecialReg[SR_T1] = mcTicksPerSecond * mvReg[iRegDst]; + break; + case 0xF018: // FX18 - set sound timer register + if(mvSpecialReg[SR_T1] == 0 && mvReg[iRegDst] != 0) { + mrBuzzer.on(); + } + mvSpecialReg[SR_T1] = mcTicksPerSecond * mvReg[iRegDst]; + break; + case 0xF01E: // FX1E - add to I + mvSpecialReg[SR_I] += mvReg[iRegDst]; + break; + case 0xF029: // FX29 - set I to address of font sprite data for digit X + mvSpecialReg[SR_I] = scLowRestFontAddr + 5 * mvReg[iRegDst]; + break; + case 0xF033: // FX33 - convert to bcd + mvMemory[mvSpecialReg[SR_I]] = (mvReg[iRegDst] / 100) % 10; + mvMemory[mvSpecialReg[SR_I] + 1] = (mvReg[iRegDst] / 10) % 10; + mvMemory[mvSpecialReg[SR_I] + 2] = mvReg[iRegDst] % 10; + break; + case 0xF055: // FX55 - dump registers + for(int i = 0; i <= iRegDst - R_V0; i++) { + mvMemory[mvSpecialReg[SR_I]++] = mvReg[R_V0 + i]; + } + break; + case 0xF065: // FX65 - restore registers + for(int i = 0; i <= iRegDst - R_V0; i++) { + mvReg[R_V0 + i] = mvMemory[mvSpecialReg[SR_I]++]; + } + break; + default: + throw std::invalid_argument("not implemented"); + break; + } break; } - // increment PC - mvSpecialReg[SR_PC] += 2; - // decrement timers if(mvSpecialReg[SR_T1] > 0) { mvSpecialReg[SR_T1] -= 1; diff --git a/main.cpp b/main.cpp index d176cd3..5e6b5d0 100644 --- a/main.cpp +++ b/main.cpp @@ -1,12 +1,18 @@ #include +#include +#include +#include + #include #include "BuzzerSDL.hpp" #include "DisplaySDL.hpp" #include "Interpreter.hpp" #include "Peripherals.hpp" +#include "SDL_events.h" +#include "SDL_timer.h" -int main(int argc, char* args[]) { +int main(int argc, char* argv[]) { if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { std::cerr << "Couldn't initialize SDL: " << SDL_GetError() << '\n'; return 1; @@ -21,33 +27,26 @@ int main(int argc, char* args[]) { TestKeypad keypad; chocochip8::Interpreter chip8(90, display, buzzer, keypad); - uint8_t prog[] = { - 0xA0, 0x00, // LD I,0 - 0x60, 0x00, // LD $0,0 - 0x61, 0x00, // LD $1,0 - 0xD0, 0x15, // DRW $0, $1, 5 - 0xA0, 0x05, // LD I,5 - 0x60, 0x3C, // LD $0,60 - 0x61, 0x00, // LD $1,0 - 0xD0, 0x15, // DRW $0, $1, 5 - 0xA0, 0x0A, // LD I,10 - 0x60, 0x00, // LD $0,0 - 0x61, 0x1B, // LD $1,27 - 0xD0, 0x15, // DRW $0, $1, 5 - 0xA0, 0x0F, // LD I,15 - 0x60, 0x3C, // LD $0,60 - 0x61, 0x1B, // LD $1,27 - 0xD0, 0x15, // DRW $0, $1, 5 - }; - - chip8.loadProgram((char*)prog, sizeof(prog)); - for(int i = 0; i < sizeof(prog) / 2; i++) { - chip8.tick(); - } + auto rom = std::vector(); + auto romfile = std::ifstream(argv[1] != NULL ? argv[1] : "/dev/stdin", std::ios_base::in | std::ios_base::binary); + std::copy( + std::istreambuf_iterator(romfile), + std::istreambuf_iterator(), + std::back_insert_iterator(rom) + ); + chip8.loadProgram(rom.data(), rom.size()); SDL_Event event; - while(SDL_WaitEvent(&event) && event.type != SDL_QUIT) { + bool done = false; + while(!done) { + while(SDL_PollEvent(&event)) { + if(event.type == SDL_QUIT) { + done = true; + } + } + chip8.tick(); display.updateWindow(); + SDL_Delay(1000.0 / 60); } buzzer.off();