Skip to content

Commit

Permalink
high-level chip-8e support
Browse files Browse the repository at this point in the history
  • Loading branch information
gulrak committed Jan 6, 2024
1 parent e24c04a commit a6e1681
Show file tree
Hide file tree
Showing 11 changed files with 375 additions and 6 deletions.
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.1.8]
## [1.1.9] (wip)

### Added

- New test for CHIP-8E in `test-roms`
- New high-level variant `CHIP-8E`, the emulator will by default still use the real `VIP-CHIP-8E`
due to more accurate timing, but the generic one will take up less resources for more constrained
platforms

### Fixed

- The VIP-CHIP-8E interpreter had a typo leading to `BBnn` not working ([#12](https://github.com/gulrak/cadmium/issues/12))
- Disassembling 0x7C, 0x7D or 0x7F generated single byte opcodes instead two byte ones ([#13](https://github.com/gulrak/cadmium/issues/12))
- Octo-Assembler would hang on macro definitions without name or parameter

## [1.1.8] - 2024-01-01

### Added

Expand All @@ -21,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
and the one from the CHIP-8 test suite v4.1, the SCHIPC is now moreoriented on the
behavior of Chromatophores SCHIPC
- Added support for CHIP-8E on the VIP core
- Realistic buzzer sound from HP48 based variants (CHIP-48, SCHIP1.x, SCHIPC)

### Changed

Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)

project(cadmium VERSION 1.1.8 LANGUAGES C CXX)
project(cadmium VERSION 1.1.9 LANGUAGES C CXX)
cmake_policy(VERSION 3.16)

include(cmake/BuildSettings.cmake)
Expand Down
4 changes: 2 additions & 2 deletions src/cadmium.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1554,7 +1554,7 @@ class Cadmium : public emu::Chip8EmuHostEx
//SetNextWidth(_screenWidth - 383);
Begin();
Label("CHIP-8 variant / Core:");
if(DropdownBox("CHIP-8;CHIP-8-STRICT;CHIP-10;CHIP-8X;CHIP-48;SCHIP 1.0;SCHIP 1.1;SCHIP-COMP;SCHIP-MODERN;MEGACHIP8;XO-CHIP;VIP-CHIP-8;VIP-CHIP-8 64x64;VIP-HI-RES-CHIP-8;VIP-CHIP-8E;VIP-CHIP-8X;VIP-CHIP-8X-64x64;VIP-HI-RES-CHIP-8X;CHIP-8 DREAM6800", &_behaviorSel)) {
if(DropdownBox("CHIP-8;CHIP-8-STRICT;CHIP-10;CHIP-8E;CHIP-8X;CHIP-48;SCHIP 1.0;SCHIP 1.1;SCHIP-COMP;SCHIP-MODERN;MEGACHIP8;XO-CHIP;VIP-CHIP-8;VIP-CHIP-8 64x64;VIP-HI-RES-CHIP-8;VIP-CHIP-8E;VIP-CHIP-8X;VIP-CHIP-8X-64x64;VIP-HI-RES-CHIP-8X;CHIP-8 DREAM6800", &_behaviorSel)) {
auto preset = static_cast<emu::Chip8EmulatorOptions::SupportedPreset>(_behaviorSel);
_frameBoost = 1;
updateEmulatorOptions(emu::Chip8EmulatorOptions::optionsOfPreset(preset));
Expand Down Expand Up @@ -2673,7 +2673,7 @@ int main(int argc, char* argv[])
exit(0);
}
if(opcodeJSON) {
dumpOpcodeJSON(std::cout, emu::C8V::CHIP_8|emu::C8V::CHIP_8_I|emu::C8V::CHIP_8X|emu::C8V::CHIP_10|emu::C8V::CHIP_8_D6800|emu::C8V::CHIP_48|emu::C8V::SCHIP_1_0|emu::C8V::SCHIP_1_1|emu::C8V::SCHIPC|emu::C8V::MEGA_CHIP|emu::C8V::XO_CHIP);
dumpOpcodeJSON(std::cout, emu::C8V::CHIP_8|emu::C8V::CHIP_8_I|emu::C8V::CHIP_8X|emu::C8V::CHIP_8E|emu::C8V::CHIP_10|emu::C8V::CHIP_8_D6800|emu::C8V::CHIP_48|emu::C8V::SCHIP_1_0|emu::C8V::SCHIP_1_1|emu::C8V::SCHIPC|emu::C8V::MEGA_CHIP|emu::C8V::XO_CHIP);
exit(0);
}
if(!dumpInterpreter.empty()) {
Expand Down
1 change: 0 additions & 1 deletion src/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ void Debugger::render(Font& font, std::function<void(Rectangle,int)> drawScreen)
EndPanel();
static Vector2 memScroll{0,0};
static uint8_t memPage{0};
SetNextWidth(163);
BeginPanel(memPage ? TextFormat("Memory [%02X....]", memPage) : "Memory", {0,0});
{
auto pos = GetCurrentPos();
Expand Down
99 changes: 99 additions & 0 deletions src/emulation/chip8cores.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,22 @@ void Chip8EmulatorFP::setHandler()
on(0xF0FF, 0xF075, &Chip8EmulatorFP::opFx75);
on(0xF0FF, 0xF085, &Chip8EmulatorFP::opFx85);
break;
case Chip8EmulatorOptions::eCHIP8E:
on(0xFFFF, 0x00ED, &Chip8EmulatorFP::op00ED_c8e);
on(0xFFFF, 0x00F2, &Chip8EmulatorFP::opNop);
on(0xFFFF, 0x0151, &Chip8EmulatorFP::op0151_c8e);
on(0xFFFF, 0x0188, &Chip8EmulatorFP::op0188_c8e);
on(0xF00F, 0x5001, &Chip8EmulatorFP::op5xy1_c8e);
on(0xF00F, 0x5002, &Chip8EmulatorFP::op5xy2_c8e);
on(0xF00F, 0x5003, &Chip8EmulatorFP::op5xy3_c8e);
on(0xFF00, 0xBB00, &Chip8EmulatorFP::opBBnn_c8e);
on(0xFF00, 0xBF00, &Chip8EmulatorFP::opBFnn_c8e);
on(0xF0FF, 0xF003, &Chip8EmulatorFP::opNop);
on(0xF0FF, 0xF01B, &Chip8EmulatorFP::opFx1B_c8e);
on(0xF0FF, 0xF04F, &Chip8EmulatorFP::opFx4F_c8e);
on(0xF0FF, 0xF0E3, &Chip8EmulatorFP::opNop);
on(0xF0FF, 0xF0E7, &Chip8EmulatorFP::opNop);
break;
case Chip8EmulatorOptions::eCHIP8X:
on(0xFFFF, 0x02A0, &Chip8EmulatorFP::op02A0_c8x);
on(0xF00F, 0x5001, &Chip8EmulatorFP::op5xy1_c8x);
Expand Down Expand Up @@ -523,6 +539,11 @@ void Chip8EmulatorFP::op00E0_megachip(uint16_t opcode)
_cycleCounter = calcNextFrame() - 1;
}

void Chip8EmulatorFP::op00ED_c8e(uint16_t opcode)
{
halt();
}

void Chip8EmulatorFP::op00EE(uint16_t opcode)
{
if(!_rSP)
Expand Down Expand Up @@ -662,6 +683,22 @@ void Chip8EmulatorFP::op00FF_megachip(uint16_t opcode)
}
}

void Chip8EmulatorFP::op0151_c8e(uint16_t opcode)
{
if(_rDT) {
_rPC -= 2;
_cpuState = eWAITING;
}
else {
_cpuState = eNORMAL;
}
}

void Chip8EmulatorFP::op0188_c8e(uint16_t opcode)
{
_rPC = (_rPC + 2) & ADDRESS_MASK;
}

void Chip8EmulatorFP::op01nn(uint16_t opcode)
{
_rI = ((opcode & 0xFF) << 16 | (_memory[_rPC & ADDRESS_MASK] << 8) | _memory[(_rPC + 1) & ADDRESS_MASK]) & ADDRESS_MASK;
Expand Down Expand Up @@ -824,6 +861,13 @@ void Chip8EmulatorFP::op5xy0_with_01nn(uint16_t opcode)
}
}

void Chip8EmulatorFP::op5xy1_c8e(uint16_t opcode)
{
if (_rV[(opcode >> 8) & 0xF] > _rV[(opcode >> 4) & 0xF]) {
_rPC = (_rPC + 2) & ADDRESS_MASK;
}
}

void Chip8EmulatorFP::op5xy1_c8x(uint16_t opcode)
{
_rV[(opcode >> 8) & 0xF] = ((_rV[(opcode >> 8) & 0xF] & 0x77) + (_rV[(opcode >> 4) & 0xF] & 0x77)) & 0x77;
Expand All @@ -840,6 +884,20 @@ void Chip8EmulatorFP::op5xy2(uint16_t opcode)
fixupSafetyPad();
}

void Chip8EmulatorFP::op5xy2_c8e(uint16_t opcode)
{
auto x = (opcode >> 8) & 0xF;
auto y = (opcode >> 4) & 0xF;
if(x < y) {
auto l = y - x;
for(int i=0; i <= l; ++i)
_memory[(_rI + i) & ADDRESS_MASK] = _rV[x + i];
if(_rI + l >= ADDRESS_MASK)
fixupSafetyPad();
_rI = (_rI + l + 1) & ADDRESS_MASK;
}
}

void Chip8EmulatorFP::op5xy3(uint16_t opcode)
{
auto x = (opcode >> 8) & 0xF;
Expand All @@ -848,6 +906,18 @@ void Chip8EmulatorFP::op5xy3(uint16_t opcode)
_rV[x < y ? x + i : x - i] = _memory[(_rI + i) & ADDRESS_MASK];
}

void Chip8EmulatorFP::op5xy3_c8e(uint16_t opcode)
{
auto x = (opcode >> 8) & 0xF;
auto y = (opcode >> 4) & 0xF;
if(x < y) {
auto l = y - x;
for(int i=0; i <= l; ++i)
_rV[x + i] = _memory[(_rI + i) & ADDRESS_MASK];
_rI = (_rI + l + 1) & ADDRESS_MASK;
}
}

void Chip8EmulatorFP::op5xy4(uint16_t opcode)
{
auto x = (opcode >> 8) & 0xF;
Expand Down Expand Up @@ -985,6 +1055,16 @@ void Chip8EmulatorFP::opAnnn(uint16_t opcode)
_rI = opcode & 0xFFF;
}

void Chip8EmulatorFP::opBBnn_c8e(uint16_t opcode)
{
_rPC = (_rPC - 2 - (opcode & 0xff)) & ADDRESS_MASK;
}

void Chip8EmulatorFP::opBFnn_c8e(uint16_t opcode)
{
_rPC = (_rPC - 2 + (opcode & 0xff)) & ADDRESS_MASK;
}

void Chip8EmulatorFP::opBxy0_c8x(uint16_t opcode)
{
auto rx = _rV[(opcode >> 8) & 0xF];
Expand Down Expand Up @@ -1296,6 +1376,11 @@ void Chip8EmulatorFP::opFx18(uint16_t opcode)
if(!_rST) _wavePhase = 0;
}

void Chip8EmulatorFP::opFx1B_c8e(uint16_t opcode)
{
_rPC = (_rPC + _rV[(opcode >> 8) & 0xF]) & ADDRESS_MASK;
}

void Chip8EmulatorFP::opFx1E(uint16_t opcode)
{
_rI = (_rI + _rV[(opcode >> 8) & 0xF]) & ADDRESS_MASK;
Expand Down Expand Up @@ -1330,6 +1415,20 @@ void Chip8EmulatorFP::opFx3A(uint16_t opcode)
_xoPitch.store(_rV[(opcode >> 8) & 0xF]);
}

void Chip8EmulatorFP::opFx4F_c8e(uint16_t opcode)
{
if(_cpuState != eWAITING) {
_rDT = _rV[(opcode >> 8) & 0xF];
_cpuState = eWAITING;
}
if(_rDT && (_cpuState == eWAITING)) {
_rPC -= 2;
}
else {
_cpuState = eNORMAL;
}
}

void Chip8EmulatorFP::opFx55(uint16_t opcode)
{
uint8_t upto = (opcode >> 8) & 0xF;
Expand Down
10 changes: 10 additions & 0 deletions src/emulation/chip8cores.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ class Chip8EmulatorFP : public Chip8EmulatorBase
void op00Dn_masked(uint16_t opcode);
void op00E0(uint16_t opcode);
void op00E0_megachip(uint16_t opcode);
void op00ED_c8e(uint16_t opcode);
void op00EE(uint16_t opcode);
void op00EE_cyclic(uint16_t opcode);
void op00FB(uint16_t opcode);
Expand All @@ -549,6 +550,8 @@ class Chip8EmulatorFP : public Chip8EmulatorBase
void op00FF_withClear(uint16_t opcode);
void op00FF_megachip(uint16_t opcode);
void op01nn(uint16_t opcode);
void op0151_c8e(uint16_t opcode);
void op0188_c8e(uint16_t opcode);
void op02A0_c8x(uint16_t opcode);
void op02nn(uint16_t opcode);
void op03nn(uint16_t opcode);
Expand All @@ -570,9 +573,12 @@ class Chip8EmulatorFP : public Chip8EmulatorBase
void op5xy0(uint16_t opcode);
void op5xy0_with_F000(uint16_t opcode);
void op5xy0_with_01nn(uint16_t opcode);
void op5xy1_c8e(uint16_t opcode);
void op5xy1_c8x(uint16_t opcode);
void op5xy2(uint16_t opcode);
void op5xy2_c8e(uint16_t opcode);
void op5xy3(uint16_t opcode);
void op5xy3_c8e(uint16_t opcode);
void op5xy4(uint16_t opcode);
void op6xnn(uint16_t opcode);
void op7xnn(uint16_t opcode);
Expand All @@ -595,6 +601,8 @@ class Chip8EmulatorFP : public Chip8EmulatorBase
void op9xy0_with_01nn(uint16_t opcode);
void opAnnn(uint16_t opcode);
void opBnnn(uint16_t opcode);
void opBBnn_c8e(uint16_t opcode);
void opBFnn_c8e(uint16_t opcode);
void opBxy0_c8x(uint16_t opcode);
void opBxyn_c8x(uint16_t opcode);
void opBxnn(uint16_t opcode);
Expand All @@ -617,12 +625,14 @@ class Chip8EmulatorFP : public Chip8EmulatorBase
void opFx0A(uint16_t opcode);
void opFx15(uint16_t opcode);
void opFx18(uint16_t opcode);
void opFx1B_c8e(uint16_t opcode);
void opFx1E(uint16_t opcode);
void opFx29(uint16_t opcode);
void opFx29_ship10Beta(uint16_t opcode);
void opFx30(uint16_t opcode);
void opFx33(uint16_t opcode);
void opFx3A(uint16_t opcode);
void opFx4F_c8e(uint16_t opcode);
void opFx55(uint16_t opcode);
void opFx55_loadStoreIncIByX(uint16_t opcode);
void opFx55_loadStoreDontIncI(uint16_t opcode);
Expand Down
6 changes: 6 additions & 0 deletions src/emulation/chip8options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Chip8Variant Chip8EmulatorOptions::variantForPreset(SupportedPreset preset)
case eCHIP8: return Chip8Variant::CHIP_8;
case eCHIP8TE: return Chip8Variant::CHIP_8;
case eCHIP10: return Chip8Variant::CHIP_10;
case eCHIP8E: return Chip8Variant::CHIP_8E;
case eCHIP8X: return Chip8Variant::CHIP_8X;
case eCHIP48: return Chip8Variant::CHIP_48;
case eSCHIP10: return Chip8Variant::SCHIP_1_0;
Expand Down Expand Up @@ -71,6 +72,7 @@ std::string Chip8EmulatorOptions::nameOfPreset(SupportedPreset preset)
case eCHIP8: return "CHIP-8";
case eCHIP8TE: return "CHIP-8-STRICT";
case eCHIP10: return "CHIP-10";
case eCHIP8E: return "CHIP-8E";
case eCHIP8X: return "CHIP-8X";
case eCHIP48: return "CHIP-48";
case eSCHIP10: return "SUPER-CHIP 1.0";
Expand Down Expand Up @@ -100,6 +102,7 @@ const char* Chip8EmulatorOptions::shortNameOfPreset(SupportedPreset preset)
case eCHIP8: return "CHIP8";
case eCHIP8TE: return "CHIP8ST";
case eCHIP10: return "CHIP10";
case eCHIP8E: return "CHIP8E";
case eCHIP8X: return "CHIP8X";
case eCHIP48: return "CHIP48";
case eSCHIP10: return "SCHIP10";
Expand Down Expand Up @@ -171,6 +174,7 @@ static std::map<std::string, emu::Chip8EmulatorOptions::SupportedPreset> presetM
{"vipchip864x128", Opts::eCHIP8VIP_FPD},
{"viphireschip8", Opts::eCHIP8VIP_FPD},
{"hireschip8vip", Opts::eCHIP8VIP_FPD},
{"vipchip8e", Opts::eCHIP8EVIP},
{"vipchip8x", Opts::eCHIP8XVIP},
{"chip8evip", Opts::eCHIP8EVIP},
{"chip8xvip", Opts::eCHIP8XVIP},
Expand All @@ -195,6 +199,7 @@ static std::map<Opts::SupportedPreset,std::string> presetOptionsProtoMap = {
{Opts::eCHIP8, R"({})"},
{Opts::eCHIP8TE, R"({})"},
{Opts::eCHIP10, R"({"optAllowHires":true,"optOnlyHires":true})"},
{Opts::eCHIP8E, R"({})"},
{Opts::eCHIP8X, R"({"startAddress":768,"instructionsPerFrame":18,"advanced":{"palette":["#000080","#000000","#008000","#800000","#181818","#FF0000","#0000FF","#FF00FF","#00FF00","#FFFF00","#00FFFF","#FFFFFF","#000000","#000000","#000000","#000000"]}})"},
{Opts::eCHIP48, R"({"optJustShiftVx":true,"optDontResetVf":true,"optLoadStoreIncIByX":true,"optInstantDxyn":false,"optJump0Bxnn":true,"instructionsPerFrame":15,"frameRate":64})"},
{Opts::eSCHIP10, R"({"optJustShiftVx":true,"optDontResetVf":true,"optLoadStoreIncIByX":true,"optInstantDxyn":false,"optLoresDxy0Is8x16":true,"optSCLoresDrawing":true,"optJump0Bxnn":true,"optAllowHires":true,"instructionsPerFrame":15,"frameRate":64})"},
Expand Down Expand Up @@ -243,6 +248,7 @@ Chip8EmulatorOptions::SupportedPreset Chip8EmulatorOptions::presetForVariant(chi
else if(variant == chip8::Variant::XO_CHIP) return eXOCHIP;
else if(variant == chip8::Variant::CHIP_8_TPD) return eCHIP8VIP_TPD;
else if(variant == chip8::Variant::CHIP_8_COSMAC_VIP) return eCHIP8VIP;
else if(variant == chip8::Variant::CHIP_8E) return eCHIP8EVIP;
else if(variant == chip8::Variant::CHIP_8X) return eCHIP8XVIP;
else if(variant == chip8::Variant::CHIP_8X_TPD) return eCHIP8XVIP_TPD;
else if(variant == chip8::Variant::HI_RES_CHIP_8X) return eCHIP8XVIP_FPD;
Expand Down
2 changes: 1 addition & 1 deletion src/emulation/chip8options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace emu {

struct Chip8EmulatorOptions {
~Chip8EmulatorOptions();
enum SupportedPreset { eCHIP8, eCHIP8TE, eCHIP10, eCHIP8X, eCHIP48, eSCHIP10, eSCHIP11, eSCHPC, eSCHIP_MODERN, eMEGACHIP, eXOCHIP, eCHIP8VIP, eCHIP8VIP_TPD, eCHIP8VIP_FPD, eCHIP8EVIP, eCHIP8XVIP, eCHIP8XVIP_TPD, eCHIP8XVIP_FPD, eCHIP8DREAM, eC8D68CHIPOSLO, eCHICUEYI, ePORTABLE, eNUM_PRESETS };
enum SupportedPreset { eCHIP8, eCHIP8TE, eCHIP10, eCHIP8E, eCHIP8X, eCHIP48, eSCHIP10, eSCHIP11, eSCHPC, eSCHIP_MODERN, eMEGACHIP, eXOCHIP, eCHIP8VIP, eCHIP8VIP_TPD, eCHIP8VIP_FPD, eCHIP8EVIP, eCHIP8XVIP, eCHIP8XVIP_TPD, eCHIP8XVIP_FPD, eCHIP8DREAM, eC8D68CHIPOSLO, eCHICUEYI, ePORTABLE, eNUM_PRESETS };
SupportedPreset behaviorBase{eCHIP8};
uint16_t startAddress{0x200};
bool optJustShiftVx{false};
Expand Down
1 change: 1 addition & 0 deletions src/librarian.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ static KnownRomInfo g_knownRoms[] = {
{"788661c6a49c4e081492416bf2ce86342116bb1d", emu::chip8::Variant::CHIP_8, "Crack Me (Pawel Lukasik, 2017)", nullptr, nullptr},
//{"7a4a89870f2ab23c28024dd1c3dd52cf1af1ad00", {emu::Chip8EmulatorOptions::eSCHIP11}},
{"7a4a89870f2ab23c28024dd1c3dd52cf1af1ad00", emu::chip8::Variant::SCHIPC, "Octopeg (Chromatophore, 2015-10-29)", R"({"instructionsPerFrame": 200, "advanced": {"col1": "#acd5ff", "col2": "#FF6600", "col3": "#662200", "col0": "#113152", "buzzColor": "#264c74", "quietColor": "#000000"}})", nullptr},
{"7ad8b4814c37eb7b84d002a2b7e7ae64ec6fed5f", emu::chip8::Variant::CHIP_8E, "CHIP-8E Test (Gulrak, 2024)", nullptr, nullptr},
{"7b6f54169a9511c976fdaa85db3822d9c707077f", emu::chip8::Variant::GENERIC_CHIP_8, "6-keypad (Timendus, 2023-04-12)", nullptr,"@GH/Timendus/chip8-test-suite/v4.0/bin/6-keypad.ch8"},
{"7c680cd427c2d0eecde208ebbce667707c0f13a2", emu::chip8::Variant::CHIP_8, "Simple Snek (John Earnest, 2021)", nullptr, nullptr},
{"7cd0334fc30cbbb21d3c5a909fa2c69927ec4a6c", emu::chip8::Variant::CHIP_8, "Alternate (TCNJ S.572.3)", nullptr, nullptr},
Expand Down
Binary file added test-roms/bin/chip8e-test.c8e
Binary file not shown.
Loading

0 comments on commit a6e1681

Please sign in to comment.