disassembler, assembler, bug fixes, and more

This commit is contained in:
Zoe
2025-03-07 15:05:22 +00:00
parent d64f63b165
commit 587fda2d49
14 changed files with 4307 additions and 1082 deletions

17
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug assembler",
"type": "gdb",
"request": "launch",
"target": "./bin/assembler",
"arguments": "${workspaceRoot}/examples/syntax.asm ${workspaceRoot}/bin/test.ch8",
"cwd": "${workspaceRoot}",
"valuesFormatting": "parseText"
}
]
}

View File

@@ -1,19 +1,22 @@
CXX := g++ CXX := g++
CXXFLAGS := -Wall -Wextra -std=c++23 -g -Werror -Ilibs `sdl2-config --cflags` CXXFLAGS := -Wall -Wextra -std=c++23 -g -Werror -Ilibs
LDFLAGS := `sdl2-config --libs` LDFLAGS := `sdl2-config --libs`
BIN_DIR := bin BIN_DIR := bin
all: voidEmu disassembler all: voidEmu assembler disassembler
run: all run: all
./bin/voidEmu $(FILE) ./bin/voidEmu $(FILE)
assembler: $(wildcard assembler/*.cpp) | $(BIN_DIR)
$(CXX) $(CXXFLAGS) $^ -o ${BIN_DIR}/$@
disassembler: $(wildcard disassembler/*.cpp) | $(BIN_DIR) disassembler: $(wildcard disassembler/*.cpp) | $(BIN_DIR)
$(CXX) $(CXXFLAGS) $^ -o ${BIN_DIR}/$@ $(CXX) $(CXXFLAGS) $^ -o ${BIN_DIR}/$@
voidEmu: $(wildcard src/*.cpp) | $(BIN_DIR) voidEmu: $(wildcard src/*.cpp) | $(BIN_DIR)
$(CXX) $(CXXFLAGS) $^ -o ${BIN_DIR}/$@ $(LDFLAGS) $(CXX) $(CXXFLAGS) `sdl2-config --cflags` $^ -o ${BIN_DIR}/$@ $(LDFLAGS)
$(BIN_DIR): $(BIN_DIR):
mkdir -p $@ mkdir -p $@

View File

@@ -2,15 +2,13 @@
Current state: This project is a Chip8 emulator implemented according to [Cowgod's Chip-8 Technical Reference](http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#2.2). Current state: This project is a Chip8 emulator implemented according to [Cowgod's Chip-8 Technical Reference](http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#2.2).
This is a simple emulator for the Game Boy. It is written in C++ and uses SDL for rendering.
## TODO ## TODO
- [X] Implement sound - [X] Implement sound
- [X] Implement keyboard input - [X] Implement keyboard input
- [ ] Fix games like Tetris and pong - [X] Fix games like Tetris and pong
- [X] Implement a disassembler
- [ ] Implement better e2e testing for visuals and other things - [ ] Implement better e2e testing for visuals and other things
- [ ] Implement a disassembler
- [ ] Get better debugging - [ ] Get better debugging
## Why ## Why
@@ -21,7 +19,10 @@ I wanted to learn how to use C++ and SDL, and I recently saw a youtube video lis
* [Cowgod's Chip-8 Technical Reference](http://devernay.free.fr/hacks/chip8/C8TECH10.HTM) * [Cowgod's Chip-8 Technical Reference](http://devernay.free.fr/hacks/chip8/C8TECH10.HTM)
- [Chip-8 Test Suite](https://github.com/Timendus/chip8-test-suite) - [Chip-8 Test Suite](https://github.com/Timendus/chip8-test-suite)
- [Wernsey's Chip-8 Assembler](https://github.com/wernsey/chip8), for syntax reference **only**
# License # License
This is free and unencumbered software released into the public domain, much to the detriment of my "heirs and successors". Unlicense everything All code in this repository that is not under The Unlicense has its license at the top of the file.
This is free and unencumbered software released into the public domain, much to the detriment of my "heirs and successors". Unlicense everything.

8
assembler/.clangd Normal file
View File

@@ -0,0 +1,8 @@
CompileFlags:
Add:
- -I../libs
- -Wall
- -Wextra
- -std=c++23
- -g
- -Werror

1825
assembler/assember.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,533 @@
#include <bits/stdc++.h>
#include <cstddef>
#include <cstdint>
#include <fcntl.h>
#include <format>
#include <queue>
#include <string>
#include <sys/types.h>
#include <unistd.h>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#define BYTECODE_READER_IMPLEMENTATION
#include "reader.hpp"
#include <cstdio>
struct label {
enum type {
type_instruction,
type_byte,
} type;
uint16_t length;
std::string name;
};
uint16_t *find_closest_label(const std::unordered_map<uint16_t, label> &map,
uint16_t target) {
uint16_t *closest_label = nullptr;
for (const auto &pair : map) {
if (pair.first <= target) {
if (closest_label != nullptr && pair.first < *closest_label) {
continue;
}
if (closest_label == nullptr) {
closest_label = (uint16_t *)calloc(1, sizeof(uint16_t));
if (closest_label == NULL) {
throw std::runtime_error("Failed to allocate memory");
}
}
*closest_label = pair.first;
} else {
break;
}
}
return closest_label;
}
std::string to_hex(size_t number) { return std::format("{:04x}", number); }
std::string
get_label_for_byte(uint16_t byte,
std::unordered_map<uint16_t, struct label> labels,
enum label::type target_type) {
std::string operand_str;
operand_str.append("0x");
operand_str.append(to_hex(byte));
if (labels.find(byte - 0x200) != labels.end()) {
operand_str.clear();
operand_str = labels.at(byte - 0x200).name;
} else {
// try to see if the operand is offset from a label
if (byte > 0x200) {
uint16_t *closest_label = find_closest_label(labels, byte - 0x200);
if (closest_label != nullptr &&
labels.at(*closest_label).type == target_type) {
uint16_t offset = (byte - 0x200) - (*closest_label);
// discard our result if the offset is greater than the
// length of the label
if (offset > labels.at(*closest_label).length) {
return operand_str;
}
operand_str.clear();
operand_str = labels.at(*closest_label).name;
operand_str.append(" + 0x");
operand_str.append(to_hex(offset));
operand_str.append("");
}
}
}
return operand_str;
}
// I could emit something like omni assembly, nut that is significantly more
// complex than just emitting the assembly like this, so I am just going to
// emit the assembly like this for now
void print_instruction(Bytecode bytecode, uint16_t pc,
std::unordered_map<uint16_t, struct label> labels) {
switch (bytecode.instruction_type) {
case HLT:
printf("halt\n");
break;
case EXIT:
printf("exit 0x%02x\n", bytecode.operand.byte);
break;
case SYS:
if (labels.find(bytecode.operand.word - 0x200) == labels.end()) {
fprintf(stderr, "No label found for %04x\n",
bytecode.operand.word - 0x200);
exit(1);
}
printf("sys %s\n",
labels.find(bytecode.operand.word - 0x200)->second.name.c_str());
break;
case CLS:
printf("cls\n");
break;
case RET:
printf("ret\n");
break;
case JP:
if (pc == 0 && bytecode.operand.word == 0x260) {
printf("hires\n");
break;
}
if (labels.find(bytecode.operand.word - 0x200) == labels.end()) {
fprintf(stderr, "No label found for %04x\n",
bytecode.operand.word - 0x200);
exit(1);
}
printf("jp %s\n",
labels.find(bytecode.operand.word - 0x200)->second.name.c_str());
break;
case CALL:
if (labels.find(bytecode.operand.word - 0x200) == labels.end()) {
fprintf(stderr, "No label found for %04x\n",
bytecode.operand.word - 0x200);
exit(1);
}
printf("call %s\n",
labels.find(bytecode.operand.word - 0x200)->second.name.c_str());
break;
case SKIP_INSTRUCTION_BYTE:
printf("se v%x, 0x%02x\n", bytecode.operand.byte_reg.reg,
bytecode.operand.byte_reg.byte);
break;
case SKIP_INSTRUCTION_NE_BYTE:
printf("sne v%x, 0x%02x\n", bytecode.operand.byte_reg.reg,
bytecode.operand.byte_reg.byte);
break;
case SKIP_INSTRUCTION_REG:
printf("se v%x, v%x\n", bytecode.operand.reg_reg.x,
bytecode.operand.reg_reg.y);
break;
case SKIP_INSTRUCTION_NE_REG:
printf("sne v%x, v%x\n", bytecode.operand.reg_reg.x,
bytecode.operand.reg_reg.y);
break;
case LOAD_BYTE: {
printf("ld v%x, 0x%02x\n", bytecode.operand.byte_reg.reg,
bytecode.operand.byte_reg.byte);
break;
}
case ADD_BYTE:
printf("add v%x, 0x%02x\n", bytecode.operand.byte_reg.reg,
bytecode.operand.byte_reg.byte);
break;
case LOAD_REG:
printf("ld v%x, v%x\n", bytecode.operand.reg_reg.x,
bytecode.operand.reg_reg.y);
break;
case ADD_REG:
printf("add v%x, v%x\n", bytecode.operand.reg_reg.x,
bytecode.operand.reg_reg.y);
break;
case OR_REG:
printf("or v%x, v%x\n", bytecode.operand.reg_reg.x,
bytecode.operand.reg_reg.y);
break;
case AND_REG:
printf("and v%x, v%x\n", bytecode.operand.reg_reg.x,
bytecode.operand.reg_reg.y);
break;
case XOR_REG:
printf("xor v%x, v%x\n", bytecode.operand.reg_reg.x,
bytecode.operand.reg_reg.y);
break;
case SHR_REG:
printf("shr v%x\n", bytecode.operand.reg_reg.x);
break;
case SUB_REG:
printf("sub v%x, v%x\n", bytecode.operand.reg_reg.x,
bytecode.operand.reg_reg.y);
break;
case SUBN_REG:
printf("subn v%x, v%x\n", bytecode.operand.reg_reg.x,
bytecode.operand.reg_reg.y);
break;
case SHL_REG:
printf("shl v%x\n", bytecode.operand.reg_reg.x);
break;
case LOAD_I_BYTE: {
printf("ld I, %s\n", get_label_for_byte(bytecode.operand.word, labels,
label::type_byte)
.c_str());
break;
}
case JP_V0_BYTE: {
printf("jp v0, %s\n", get_label_for_byte(bytecode.operand.word, labels,
label::type_instruction)
.c_str());
break;
}
case RND:
printf("rnd v%x, 0x%02x\n", bytecode.operand.byte_reg.reg,
bytecode.operand.byte_reg.byte);
break;
case DRW:
printf("drw v%x, v%x, 0x%x\n", bytecode.operand.reg_reg_nibble.x,
bytecode.operand.reg_reg_nibble.y,
bytecode.operand.reg_reg_nibble.nibble);
break;
case SKIP_PRESSED_REG:
printf("skp v%x\n", bytecode.operand.byte);
break;
case SKIP_NOT_PRESSED_REG:
printf("sknp v%x\n", bytecode.operand.byte);
break;
case LD_REG_DT:
printf("ld v%x, dt\n", bytecode.operand.byte);
break;
case LD_REG_K:
printf("ld v%x, k\n", bytecode.operand.byte);
break;
case LD_DT_REG:
printf("ld dt, v%x\n", bytecode.operand.byte);
break;
case LD_ST_REG:
printf("ld st, v%x\n", bytecode.operand.byte);
break;
case ADD_I_REG:
printf("add I, v%x\n", bytecode.operand.byte);
break;
case LD_F_REG:
printf("ld f, v%x\n", bytecode.operand.byte);
break;
case LD_B_REG:
printf("ld b, v%x\n", bytecode.operand.byte);
break;
case LD_PTR_I_REG:
printf("ld [I], v%x\n", bytecode.operand.byte);
break;
case LD_REG_PTR_I:
printf("ld v%x, [I]\n", bytecode.operand.byte);
break;
case UNKNOWN_INSTRUCTION:
printf("?\n");
}
}
struct hole {
uint16_t start;
uint16_t end;
};
struct assembly_node {
uint16_t address;
enum type {
type_byte,
type_instruction,
} type;
union {
uint8_t byte;
Bytecode instruction;
};
};
void write_assembly(std::vector<struct assembly_node> assembly,
std::unordered_map<uint16_t, struct label> labels) {
std::sort(assembly.begin(), assembly.end(),
[](const struct assembly_node &a, const struct assembly_node &b) {
return a.address < b.address;
});
uint16_t last_byte_address = 0x0000;
for (auto &node : assembly) {
if (node.type != assembly_node::type_byte && node.address != 0 &&
node.address - 1 == last_byte_address) {
printf("\n");
}
if (labels.find(node.address) != labels.end()) {
if (node.address != 0x0000) {
// add whitespacing between labels, but not for the _start label
printf("\n");
}
printf("%s:\n", labels.at(node.address).name.c_str());
}
switch (node.type) {
case assembly_node::type_byte:
if (node.address != last_byte_address + 1) {
printf("db ");
} else {
printf(",\n ");
}
printf("0x%02x", node.byte);
last_byte_address = node.address;
break;
case assembly_node::type_instruction:
// if previous assembly was a byte, then we need to emit a new line
print_instruction(node.instruction, node.address, labels);
break;
}
}
}
void disassemble(uint8_t *rom, int rom_size) {
// evaluate the bytecode, but dont actually execute it, just print it out,
// and when we reach a branching instruction, follow it. Make sure that if
// we enter an inifinite loop we dont just loop forever, so make sure we
// keep track of what we have already visited
std::unordered_set<uint16_t> addresses_visited;
std::queue<uint16_t> work_queue;
std::vector<struct hole> holes;
std::unordered_map<uint16_t, struct label> labels;
size_t label_idx = 0;
uint16_t *stack = (uint16_t *)calloc(16, sizeof(uint16_t));
if (stack == NULL) {
fprintf(stderr, "Failed to allocate stack!");
exit(1);
}
// holds the start of a label
size_t stack_idx = 0;
std::vector<struct assembly_node> assembly;
// start at the beginning of the rom
work_queue.push(0x0000);
labels.emplace(
0x0000,
label{.type = label::type_instruction, .length = 0, .name = "_start"});
while (!work_queue.empty()) {
uint16_t pc = work_queue.front();
work_queue.pop();
if (pc >= (uint16_t)rom_size) {
// if we are reading past the end of the rom, we are done
break;
}
if (addresses_visited.find(pc) != addresses_visited.end())
continue;
addresses_visited.insert(pc);
uint16_t opcode = (rom[pc] << 8) | rom[pc + 1];
Bytecode bytecode = parse(opcode);
assembly.push_back({.address = pc,
.type = assembly_node::type_instruction,
.instruction = bytecode});
switch (bytecode.instruction_type) {
case JP: {
if (pc == 0 && bytecode.operand.word == 0x260) {
work_queue.push(0x2C0);
break;
}
if (!labels.contains(bytecode.operand.word - 0x200)) {
labels.emplace(
bytecode.operand.word - 0x200,
label{.type = label::type_instruction,
.length = 0,
.name = "_" + std::to_string(label_idx++)});
}
work_queue.push(bytecode.operand.word - 0x200);
break;
}
case CALL: {
if (stack_idx == 16) {
fprintf(stderr, "Stack overflow!\n");
exit(1);
}
if (!labels.contains(bytecode.operand.word - 0x200)) {
labels.emplace(
bytecode.operand.word - 0x200,
label{.type = label::type_instruction,
.length = 0,
.name = "_" + std::to_string(label_idx++)});
}
stack[stack_idx++] = pc + 2;
work_queue.push(bytecode.operand.word - 0x200);
break;
}
case SKIP_INSTRUCTION_BYTE:
case SKIP_INSTRUCTION_NE_BYTE:
case SKIP_INSTRUCTION_REG:
case SKIP_INSTRUCTION_NE_REG:
case SKIP_PRESSED_REG:
case SKIP_NOT_PRESSED_REG: {
work_queue.push(pc + 2);
work_queue.push(pc + 4);
break;
}
case RET: {
if (stack_idx == 0) {
fprintf(stderr, "Stack underflow!\n");
exit(1);
}
uint16_t ret_pc = stack[--stack_idx];
work_queue.push(ret_pc);
break;
}
case HLT: { // Stop following
break;
}
case UNKNOWN_INSTRUCTION: {
fprintf(stderr, "Unknown instruction: %04x\n", opcode);
// we failed at disassembling smartly
break;
}
default:
work_queue.push(pc + 2);
}
}
bool skip = false;
uint16_t *last_seen_byte_array = nullptr;
uint16_t start_of_last_contiguous_block = 0x0000;
for (uint16_t pc = 0x00; pc < rom_size; pc++) {
if (skip) {
skip = false;
continue;
}
if (addresses_visited.find(pc) != addresses_visited.end()) {
// when there is an instruction that we have already visited, we
// want to skip this byte and the next byte, but we cant rely of the
// instructions being aligned to 0x02 bytes, so instead we tell the
// next run of the loop to skip
skip = true;
continue;
}
// this seems scary, but it's fine because the if block will jump down
// if the first condition is met, so it will never dereference a null
// pointer
if (last_seen_byte_array == nullptr ||
*last_seen_byte_array != pc - 1) {
if (last_seen_byte_array == nullptr) {
last_seen_byte_array = new uint16_t;
}
start_of_last_contiguous_block = pc;
// we are not in a contiguous block of bytes, so we need to add a
// label
if (!labels.contains(pc)) {
labels.emplace(
pc, label{.type = label::type_byte,
.length = 1,
.name = "_" + std::to_string(label_idx++)});
}
} else {
// we are in a contiguous block of bytes, so we need to update the
// label's length by one
labels[start_of_last_contiguous_block].length++;
}
*last_seen_byte_array = pc;
assembly.push_back(
{.address = pc, .type = assembly_node::type_byte, .byte = rom[pc]});
}
for (auto &pair : labels) {
uint16_t pc = pair.first;
uint16_t label_length = 0;
// while we havent reached the end of the rom, and we havent crossed
// into a new label
while (pc < rom_size) {
label_length++;
switch (pair.second.type) {
case label::type_byte:
pc++;
break;
case label::type_instruction:
pc += 2;
break;
}
if (labels.find(pc) != labels.end())
break;
}
pair.second.length = label_length;
}
write_assembly(assembly, labels);
}
int main(int argc, char **argv) {
if (argc < 2) {
printf("Usage: %s <file>\n", argv[0]);
return 1;
}
int rom_fd = open(argv[1], O_RDONLY);
if (rom_fd < 0) {
fprintf(stderr, "Failed to open file: %s\n", argv[1]);
return 1;
}
int rom_size = lseek(rom_fd, 0, SEEK_END);
(void)lseek(rom_fd, 0, SEEK_SET);
uint8_t *rom = (uint8_t *)calloc(rom_size, sizeof(uint8_t));
if (rom == NULL) {
fprintf(stderr, "Failed to allocate memory!\n");
return 1;
}
read(rom_fd, rom, rom_size);
disassemble(rom, rom_size);
return 0;
}

View File

@@ -1,9 +0,0 @@
#define BYTECODE_READER_IMPLEMENTATION
#include "reader.hpp"
#include <cstdio>
int main() {
printf("Hello World!\n");
return 0;
}

401
examples/font2.asm Normal file
View File

@@ -0,0 +1,401 @@
; This file is a modification of font2.asm from Wernsey's Chip-8 Assembler
; Apache License
; Version 2.0, January 2004
; http://www.apache.org/licenses/
;
; TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
;
; 1. Definitions.
;
; "License" shall mean the terms and conditions for use, reproduction,
; and distribution as defined by Sections 1 through 9 of this document.
;
; "Licensor" shall mean the copyright owner or entity authorized by
; the copyright owner that is granting the License.
;
; "Legal Entity" shall mean the union of the acting entity and all
; other entities that control, are controlled by, or are under common
; control with that entity. For the purposes of this definition,
; "control" means (i) the power, direct or indirect, to cause the
; direction or management of such entity, whether by contract or
; otherwise, or (ii) ownership of fifty percent (50%) or more of the
; outstanding shares, or (iii) beneficial ownership of such entity.
;
; "You" (or "Your") shall mean an individual or Legal Entity
; exercising permissions granted by this License.
;
; "Source" form shall mean the preferred form for making modifications,
; including but not limited to software source code, documentation
; source, and configuration files.
;
; "Object" form shall mean any form resulting from mechanical
; transformation or translation of a Source form, including but
; not limited to compiled object code, generated documentation,
; and conversions to other media types.
;
; "Work" shall mean the work of authorship, whether in Source or
; Object form, made available under the License, as indicated by a
; copyright notice that is included in or attached to the work
; (an example is provided in the Appendix below).
;
; "Derivative Works" shall mean any work, whether in Source or Object
; form, that is based on (or derived from) the Work and for which the
; editorial revisions, annotations, elaborations, or other modifications
; represent, as a whole, an original work of authorship. For the purposes
; of this License, Derivative Works shall not include works that remain
; separable from, or merely link (or bind by name) to the interfaces of,
; the Work and Derivative Works thereof.
;
; "Contribution" shall mean any work of authorship, including
; the original version of the Work and any modifications or additions
; to that Work or Derivative Works thereof, that is intentionally
; submitted to Licensor for inclusion in the Work by the copyright owner
; or by an individual or Legal Entity authorized to submit on behalf of
; the copyright owner. For the purposes of this definition, "submitted"
; means any form of electronic, verbal, or written communication sent
; to the Licensor or its representatives, including but not limited to
; communication on electronic mailing lists, source code control systems,
; and issue tracking systems that are managed by, or on behalf of, the
; Licensor for the purpose of discussing and improving the Work, but
; excluding communication that is conspicuously marked or otherwise
; designated in writing by the copyright owner as "Not a Contribution."
;
; "Contributor" shall mean Licensor and any individual or Legal Entity
; on behalf of whom a Contribution has been received by Licensor and
; subsequently incorporated within the Work.
;
; 2. Grant of Copyright License. Subject to the terms and conditions of
; this License, each Contributor hereby grants to You a perpetual,
; worldwide, non-exclusive, no-charge, royalty-free, irrevocable
; copyright license to reproduce, prepare Derivative Works of,
; publicly display, publicly perform, sublicense, and distribute the
; Work and such Derivative Works in Source or Object form.
;
; 3. Grant of Patent License. Subject to the terms and conditions of
; this License, each Contributor hereby grants to You a perpetual,
; worldwide, non-exclusive, no-charge, royalty-free, irrevocable
; (except as stated in this section) patent license to make, have made,
; use, offer to sell, sell, import, and otherwise transfer the Work,
; where such license applies only to those patent claims licensable
; by such Contributor that are necessarily infringed by their
; Contribution(s) alone or by combination of their Contribution(s)
; with the Work to which such Contribution(s) was submitted. If You
; institute patent litigation against any entity (including a
; cross-claim or counterclaim in a lawsuit) alleging that the Work
; or a Contribution incorporated within the Work constitutes direct
; or contributory patent infringement, then any patent licenses
; granted to You under this License for that Work shall terminate
; as of the date such litigation is filed.
;
; 4. Redistribution. You may reproduce and distribute copies of the
; Work or Derivative Works thereof in any medium, with or without
; modifications, and in Source or Object form, provided that You
; meet the following conditions:
;
; (a) You must give any other recipients of the Work or
; Derivative Works a copy of this License; and
;
; (b) You must cause any modified files to carry prominent notices
; stating that You changed the files; and
;
; (c) You must retain, in the Source form of any Derivative Works
; that You distribute, all copyright, patent, trademark, and
; attribution notices from the Source form of the Work,
; excluding those notices that do not pertain to any part of
; the Derivative Works; and
;
; (d) If the Work includes a "NOTICE" text file as part of its
; distribution, then any Derivative Works that You distribute must
; include a readable copy of the attribution notices contained
; within such NOTICE file, excluding those notices that do not
; pertain to any part of the Derivative Works, in at least one
; of the following places: within a NOTICE text file distributed
; as part of the Derivative Works; within the Source form or
; documentation, if provided along with the Derivative Works; or,
; within a display generated by the Derivative Works, if and
; wherever such third-party notices normally appear. The contents
; of the NOTICE file are for informational purposes only and
; do not modify the License. You may add Your own attribution
; notices within Derivative Works that You distribute, alongside
; or as an addendum to the NOTICE text from the Work, provided
; that such additional attribution notices cannot be construed
; as modifying the License.
;
; You may add Your own copyright statement to Your modifications and
; may provide additional or different license terms and conditions
; for use, reproduction, or distribution of Your modifications, or
; for any such Derivative Works as a whole, provided Your use,
; reproduction, and distribution of the Work otherwise complies with
; the conditions stated in this License.
;
; 5. Submission of Contributions. Unless You explicitly state otherwise,
; any Contribution intentionally submitted for inclusion in the Work
; by You to the Licensor shall be under the terms and conditions of
; this License, without any additional terms or conditions.
; Notwithstanding the above, nothing herein shall supersede or modify
; the terms of any separate license agreement you may have executed
; with Licensor regarding such Contributions.
;
; 6. Trademarks. This License does not grant permission to use the trade
; names, trademarks, service marks, or product names of the Licensor,
; except as required for reasonable and customary use in describing the
; origin of the Work and reproducing the content of the NOTICE file.
;
; 7. Disclaimer of Warranty. Unless required by applicable law or
; agreed to in writing, Licensor provides the Work (and each
; Contributor provides its Contributions) on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
; implied, including, without limitation, any warranties or conditions
; of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
; PARTICULAR PURPOSE. You are solely responsible for determining the
; appropriateness of using or redistributing the Work and assume any
; risks associated with Your exercise of permissions under this License.
;
; 8. Limitation of Liability. In no event and under no legal theory,
; whether in tort (including negligence), contract, or otherwise,
; unless required by applicable law (such as deliberate and grossly
; negligent acts) or agreed to in writing, shall any Contributor be
; liable to You for damages, including any direct, indirect, special,
; incidental, or consequential damages of any character arising as a
; result of this License or out of the use or inability to use the
; Work (including but not limited to damages for loss of goodwill,
; work stoppage, computer failure or malfunction, or any and all
; other commercial damages or losses), even if such Contributor
; has been advised of the possibility of such damages.
;
; 9. Accepting Warranty or Additional Liability. While redistributing
; the Work or Derivative Works thereof, You may choose to offer,
; and charge a fee for, acceptance of support, warranty, indemnity,
; or other liability obligations and/or rights consistent with this
; License. However, in accepting such obligations, You may act only
; on Your own behalf and on Your sole responsibility, not on behalf
; of any other Contributor, and only if You agree to indemnify,
; defend, and hold each Contributor harmless for any liability
; incurred by, or claims asserted against, such Contributor by reason
; of your accepting any such warranty or additional liability.
;
; END OF TERMS AND CONDITIONS
;
; APPENDIX: How to apply the Apache License to your work.
;
; To apply the Apache License to your work, attach the following
; boilerplate notice, with the fields enclosed by brackets "{}"
; replaced with your own identifying information. (Don't include
; the brackets!) The text should be enclosed in the appropriate
; comment syntax for the file format. We also recommend that a
; file or class name and description of purpose be included on the
; same "printed page" as the copyright notice for easier
; identification within third-party archives.
;
; Copyright {yyyy} {name of copyright owner}
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
; http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
; This is the 8*10 font for SCHIP.
; Run it through the assembler to get the
; hex codes for the fonts that you can copy
; and paste into chip8.c
font:
db ; '0'
0b01111100,
0b10000010,
0b10000010,
0b10000010,
0b10000010,
0b10000010,
0b10000010,
0b10000010,
0b01111100,
0b00000000
db ; '1'
0b00001000,
0b00011000,
0b00111000,
0b00001000,
0b00001000,
0b00001000,
0b00001000,
0b00001000,
0b00111100,
0b00000000
db ; '2'
0b01111100,
0b10000010,
0b00000010,
0b00000010,
0b00000100,
0b00011000,
0b00100000,
0b01000000,
0b11111110,
0b00000000
db ; '3'
0b01111100,
0b10000010,
0b00000010,
0b00000010,
0b00111100,
0b00000010,
0b00000010,
0b10000010,
0b01111100,
0b00000000
db ; '4'
0b10000100,
0b10000100,
0b10000100,
0b10000100,
0b11111110,
0b00000100,
0b00000100,
0b00000100,
0b00000100,
0b00000000
db ; '5'
0b11111110,
0b10000000,
0b10000000,
0b10000000,
0b11111100,
0b00000010,
0b00000010,
0b10000010,
0b01111100,
0b00000000
db ; '6'
0b01111100,
0b10000010,
0b10000000,
0b10000000,
0b11111100,
0b10000010,
0b10000010,
0b10000010,
0b01111100,
0b00000000
db ; '7'
0b11111110,
0b00000010,
0b00000100,
0b00001000,
0b00010000,
0b00100000,
0b00100000,
0b00100000,
0b00100000,
0b00000000
db ; '8'
0b01111100,
0b10000010,
0b10000010,
0b10000010,
0b01111100,
0b10000010,
0b10000010,
0b10000010,
0b01111100,
0b00000000
db ; '9'
0b01111100,
0b10000010,
0b10000010,
0b10000010,
0b01111110,
0b00000010,
0b00000010,
0b10000010,
0b01111100,
0b00000000
db ; 'A'
0b00010000,
0b00101000,
0b01000100,
0b10000010,
0b10000010,
0b11111110,
0b10000010,
0b10000010,
0b10000010,
0b00000000
db ; 'B'
0b11111100,
0b10000010,
0b10000010,
0b10000010,
0b11111100,
0b10000010,
0b10000010,
0b10000010,
0b11111100,
0b00000000
db ; 'C'
0b01111100,
0b10000010,
0b10000000,
0b10000000,
0b10000000,
0b10000000,
0b10000000,
0b10000010,
0b01111100,
0b00000000
db ; 'D'
0b11111100,
0b10000010,
0b10000010,
0b10000010,
0b10000010,
0b10000010,
0b10000010,
0b10000010,
0b11111100,
0b00000000
db ; 'E'
0b11111110,
0b10000000,
0b10000000,
0b10000000,
0b11111000,
0b10000000,
0b10000000,
0b10000000,
0b11111110,
0b00000000
db ; 'F'
0b11111110,
0b10000000,
0b10000000,
0b10000000,
0b11111000,
0b10000000,
0b10000000,
0b10000000,
0b10000000,
0b00000000

44
examples/sink.asm Normal file
View File

@@ -0,0 +1,44 @@
; kitchen sink for instructions
_start:
exit 0x00 ; 001N
cls ; 00E0
ret ; 00EE
sys 0x00 ; 0NNN
jp 0x123 ; 1NNN
jp v0, 0x123 ; BNNN
call foo ; 2NNN
se V1, 0xAA ; 3xkk
sne V2, 0xAA ; 4xkk
se V1, V2 ; 5xy0
sne V1, V2 ; 9xy0
add v1, 0x12 ; 7xkk
add v1, v2 ; 8xy4
add I, V8 ; Fx1E
or V2, v3 ; 8xy1
and VA, vb ; 8xy2
xor VA, vb ; 8xy3
sub v1, v2 ; 8xy5
shr VA, vb ; 8xy6 shifts VA right by vb
shr VA ; 8xx6
subn VA, vb ; 8xy7
shl VA, vb ; 8xyE
shl VA ; 8xxE
rnd VD, 0xFF ; Cxkk
drw VE, VF, 0x4 ; Dxxy
skp VE ; Ex9E
sknp VA ; ExA1
ld V1, 0xAA ; 6xkk
ld V2, v3 ; 8xy0
ld I, 0xAA ; ANNN
ld v2, DT ; Fx07
ld v2, K ; Fx0A
ld DT, V5 ; Fx15
ld ST, V5 ; Fx18
ld F, V5 ; Fx29
ld B, V5 ; Fx33
ld [I], VA ; Fx55
ld VA, [I] ; Fx65
foo:
add v8, v9
ret

299
examples/syntax.asm Normal file
View File

@@ -0,0 +1,299 @@
; basically stolen from https://github.com/wernsey/chip8/blob/master/examples/syntax.asm
; This file is a modification of syntax.asm from Wernsey's Chip-8 Assembler
; Apache License
; Version 2.0, January 2004
; http://www.apache.org/licenses/
;
; TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
;
; 1. Definitions.
;
; "License" shall mean the terms and conditions for use, reproduction,
; and distribution as defined by Sections 1 through 9 of this document.
;
; "Licensor" shall mean the copyright owner or entity authorized by
; the copyright owner that is granting the License.
;
; "Legal Entity" shall mean the union of the acting entity and all
; other entities that control, are controlled by, or are under common
; control with that entity. For the purposes of this definition,
; "control" means (i) the power, direct or indirect, to cause the
; direction or management of such entity, whether by contract or
; otherwise, or (ii) ownership of fifty percent (50%) or more of the
; outstanding shares, or (iii) beneficial ownership of such entity.
;
; "You" (or "Your") shall mean an individual or Legal Entity
; exercising permissions granted by this License.
;
; "Source" form shall mean the preferred form for making modifications,
; including but not limited to software source code, documentation
; source, and configuration files.
;
; "Object" form shall mean any form resulting from mechanical
; transformation or translation of a Source form, including but
; not limited to compiled object code, generated documentation,
; and conversions to other media types.
;
; "Work" shall mean the work of authorship, whether in Source or
; Object form, made available under the License, as indicated by a
; copyright notice that is included in or attached to the work
; (an example is provided in the Appendix below).
;
; "Derivative Works" shall mean any work, whether in Source or Object
; form, that is based on (or derived from) the Work and for which the
; editorial revisions, annotations, elaborations, or other modifications
; represent, as a whole, an original work of authorship. For the purposes
; of this License, Derivative Works shall not include works that remain
; separable from, or merely link (or bind by name) to the interfaces of,
; the Work and Derivative Works thereof.
;
; "Contribution" shall mean any work of authorship, including
; the original version of the Work and any modifications or additions
; to that Work or Derivative Works thereof, that is intentionally
; submitted to Licensor for inclusion in the Work by the copyright owner
; or by an individual or Legal Entity authorized to submit on behalf of
; the copyright owner. For the purposes of this definition, "submitted"
; means any form of electronic, verbal, or written communication sent
; to the Licensor or its representatives, including but not limited to
; communication on electronic mailing lists, source code control systems,
; and issue tracking systems that are managed by, or on behalf of, the
; Licensor for the purpose of discussing and improving the Work, but
; excluding communication that is conspicuously marked or otherwise
; designated in writing by the copyright owner as "Not a Contribution."
;
; "Contributor" shall mean Licensor and any individual or Legal Entity
; on behalf of whom a Contribution has been received by Licensor and
; subsequently incorporated within the Work.
;
; 2. Grant of Copyright License. Subject to the terms and conditions of
; this License, each Contributor hereby grants to You a perpetual,
; worldwide, non-exclusive, no-charge, royalty-free, irrevocable
; copyright license to reproduce, prepare Derivative Works of,
; publicly display, publicly perform, sublicense, and distribute the
; Work and such Derivative Works in Source or Object form.
;
; 3. Grant of Patent License. Subject to the terms and conditions of
; this License, each Contributor hereby grants to You a perpetual,
; worldwide, non-exclusive, no-charge, royalty-free, irrevocable
; (except as stated in this section) patent license to make, have made,
; use, offer to sell, sell, import, and otherwise transfer the Work,
; where such license applies only to those patent claims licensable
; by such Contributor that are necessarily infringed by their
; Contribution(s) alone or by combination of their Contribution(s)
; with the Work to which such Contribution(s) was submitted. If You
; institute patent litigation against any entity (including a
; cross-claim or counterclaim in a lawsuit) alleging that the Work
; or a Contribution incorporated within the Work constitutes direct
; or contributory patent infringement, then any patent licenses
; granted to You under this License for that Work shall terminate
; as of the date such litigation is filed.
;
; 4. Redistribution. You may reproduce and distribute copies of the
; Work or Derivative Works thereof in any medium, with or without
; modifications, and in Source or Object form, provided that You
; meet the following conditions:
;
; (a) You must give any other recipients of the Work or
; Derivative Works a copy of this License; and
;
; (b) You must cause any modified files to carry prominent notices
; stating that You changed the files; and
;
; (c) You must retain, in the Source form of any Derivative Works
; that You distribute, all copyright, patent, trademark, and
; attribution notices from the Source form of the Work,
; excluding those notices that do not pertain to any part of
; the Derivative Works; and
;
; (d) If the Work includes a "NOTICE" text file as part of its
; distribution, then any Derivative Works that You distribute must
; include a readable copy of the attribution notices contained
; within such NOTICE file, excluding those notices that do not
; pertain to any part of the Derivative Works, in at least one
; of the following places: within a NOTICE text file distributed
; as part of the Derivative Works; within the Source form or
; documentation, if provided along with the Derivative Works; or,
; within a display generated by the Derivative Works, if and
; wherever such third-party notices normally appear. The contents
; of the NOTICE file are for informational purposes only and
; do not modify the License. You may add Your own attribution
; notices within Derivative Works that You distribute, alongside
; or as an addendum to the NOTICE text from the Work, provided
; that such additional attribution notices cannot be construed
; as modifying the License.
;
; You may add Your own copyright statement to Your modifications and
; may provide additional or different license terms and conditions
; for use, reproduction, or distribution of Your modifications, or
; for any such Derivative Works as a whole, provided Your use,
; reproduction, and distribution of the Work otherwise complies with
; the conditions stated in this License.
;
; 5. Submission of Contributions. Unless You explicitly state otherwise,
; any Contribution intentionally submitted for inclusion in the Work
; by You to the Licensor shall be under the terms and conditions of
; this License, without any additional terms or conditions.
; Notwithstanding the above, nothing herein shall supersede or modify
; the terms of any separate license agreement you may have executed
; with Licensor regarding such Contributions.
;
; 6. Trademarks. This License does not grant permission to use the trade
; names, trademarks, service marks, or product names of the Licensor,
; except as required for reasonable and customary use in describing the
; origin of the Work and reproducing the content of the NOTICE file.
;
; 7. Disclaimer of Warranty. Unless required by applicable law or
; agreed to in writing, Licensor provides the Work (and each
; Contributor provides its Contributions) on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
; implied, including, without limitation, any warranties or conditions
; of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
; PARTICULAR PURPOSE. You are solely responsible for determining the
; appropriateness of using or redistributing the Work and assume any
; risks associated with Your exercise of permissions under this License.
;
; 8. Limitation of Liability. In no event and under no legal theory,
; whether in tort (including negligence), contract, or otherwise,
; unless required by applicable law (such as deliberate and grossly
; negligent acts) or agreed to in writing, shall any Contributor be
; liable to You for damages, including any direct, indirect, special,
; incidental, or consequential damages of any character arising as a
; result of this License or out of the use or inability to use the
; Work (including but not limited to damages for loss of goodwill,
; work stoppage, computer failure or malfunction, or any and all
; other commercial damages or losses), even if such Contributor
; has been advised of the possibility of such damages.
;
; 9. Accepting Warranty or Additional Liability. While redistributing
; the Work or Derivative Works thereof, You may choose to offer,
; and charge a fee for, acceptance of support, warranty, indemnity,
; or other liability obligations and/or rights consistent with this
; License. However, in accepting such obligations, You may act only
; on Your own behalf and on Your sole responsibility, not on behalf
; of any other Contributor, and only if You agree to indemnify,
; defend, and hold each Contributor harmless for any liability
; incurred by, or claims asserted against, such Contributor by reason
; of your accepting any such warranty or additional liability.
;
; END OF TERMS AND CONDITIONS
;
; APPENDIX: How to apply the Apache License to your work.
;
; To apply the Apache License to your work, attach the following
; boilerplate notice, with the fields enclosed by brackets "{}"
; replaced with your own identifying information. (Don't include
; the brackets!) The text should be enclosed in the appropriate
; comment syntax for the file format. We also recommend that a
; file or class name and description of purpose be included on the
; same "printed page" as the copyright notice for easier
; identification within third-party archives.
;
; Copyright {yyyy} {name of copyright owner}
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
; http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
; This program just demonstrates and tests all the instructions in the assembler.
; Don't try to run it
; Comments start with semicolons
_start: ; labels are identifiers followed by colons
cls
jp _start
jp 0x123 ; Hexadecimal numbers are preceded by 0x
jp V0, 0x123 ; jumps to 0x123 + v0 in memory space
jp V0, end
call end
call 0x203
se V1, 0xAA
se V2, v3
sne V1, 0xAA
sne V2, v3
end:
ret
add V1, 0xAA
add V2, v3
ld V1, 0xAA
ld V2, v3
or V2, v3
and VA, vb
xor VA, vb
shr VA, vb
shr VA
subn VA, vb
shl VA, vb
shl VA
rnd VD, 0xFF
drw VE, VF, 0x4
skp VE
sknp VA
add I, V8
ld I, 0xAAA
ld V5, DT
ld V5, K
ld DT, V5
ld ST, V5
ld F, V5
ld B, V5
ld [I], VA
ld VA, [I]
; "define" can be used to define constants
define aaa 0x222
; this instruction will be directly after the ld VA, [I] instruction
jp aaa
; "define" can also be used to define aliases for registers
define bbb vd
ld bbb, 0b01010101 ; Binary literals start with 0b
jp 0b101001010101
jp x
ld I, x
; You can enable hires mode by defining the `hires` directive
hires
ld VA, 0x12
ld VB, 0x34
drw VA, VB, 0x1
se VF, 0x01
jp 0x238
jp 0x5b6
offset 0x280 ; interst 0x280 zero bytes
; This is how you can define sprites:
; "db" emits raw bytes, separated by commas.
; "dw" can emit 16-bit words.
x: db 0x11, 0x22, 0x33, 0x44
y: db
0b00100100,
0b11111111,
0b01011010,
0b00111100,
0b00100100
cls
; You can load text data into the output through the `text` directive:
string1:
text "hello"
; (The label `string1` can be used later like so: `ld I, string1`)
; The above is equivalent to this `db` directive
; db 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x00
; The string can also contain special symbols escaped with '\'
string2: text "\"\e\r\n"
; This is how you can include another file:
include "font2.asm"

View File

@@ -96,6 +96,8 @@ enum instruction {
LD_PTR_I_REG, LD_PTR_I_REG,
// operand is a pointer to a uint8_t // operand is a pointer to a uint8_t
LD_REG_PTR_I, LD_REG_PTR_I,
// no operand
HLT,
UNKNOWN_INSTRUCTION, UNKNOWN_INSTRUCTION,
}; };
@@ -136,11 +138,16 @@ inline Bytecode parse(uint16_t opcode) {
bytecode.instruction_type = RET; bytecode.instruction_type = RET;
break; break;
} }
case 0x0000: {
// HLT 0x0000
bytecode.instruction_type = HLT;
break;
}
default: default:
// SYS NNN // SYS NNN
//? NOTE: This is an outdated opcode, but it's still //? NOTE: This is an outdated opcode, but it's still
//? important for completeness. It's not clear what the //? important for completeness. It's not clear what the
//? difference is between it and the JMP NNN 0x1NNN opcode. //? difference is between it and the JP NNN 0x1NNN opcode.
bytecode.instruction_type = SYS; bytecode.instruction_type = SYS;
break; break;
} }
@@ -207,7 +214,7 @@ inline Bytecode parse(uint16_t opcode) {
// meant set VX equal to VY bitshifted right 1 but emulators and // meant set VX equal to VY bitshifted right 1 but emulators and
// software seem to ignore VY now. Note: This instruction was // software seem to ignore VY now. Note: This instruction was
// originally undocumented but functional due to how the 8XXX // originally undocumented but functional due to how the 8XXX
// instructions were implemented on teh COSMAC VIP. // instructions were implemented on the COSMAC VIP.
bytecode.instruction_type = SHR_REG; bytecode.instruction_type = SHR_REG;
break; break;
} }
@@ -316,6 +323,7 @@ inline Bytecode parse(uint16_t opcode) {
switch (bytecode.instruction_type) { switch (bytecode.instruction_type) {
case UNKNOWN_INSTRUCTION: case UNKNOWN_INSTRUCTION:
case HLT:
case RET: case RET:
case CLS: { case CLS: {
// no operand // no operand

File diff suppressed because it is too large Load Diff

1156
src/voidemu.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,7 @@ extern_roms=(
"https://github.com/kripod/chip8-roms/raw/refs/heads/master/hires/Hires%20Stars%20%5BSergey%20Naydenov,%202010%5D.ch8" "https://github.com/kripod/chip8-roms/raw/refs/heads/master/hires/Hires%20Stars%20%5BSergey%20Naydenov,%202010%5D.ch8"
"https://github.com/kripod/chip8-roms/raw/refs/heads/master/programs/Random%20Number%20Test%20%5BMatthew%20Mikolay,%202010%5D.ch8" "https://github.com/kripod/chip8-roms/raw/refs/heads/master/programs/Random%20Number%20Test%20%5BMatthew%20Mikolay,%202010%5D.ch8"
"https://github.com/kripod/chip8-roms/raw/refs/heads/master/hires/Hires%20Sierpinski%20%5BSergey%20Naydenov,%202010%5D.ch8" "https://github.com/kripod/chip8-roms/raw/refs/heads/master/hires/Hires%20Sierpinski%20%5BSergey%20Naydenov,%202010%5D.ch8"
"https://github.com/kripod/chip8-roms/raw/refs/heads/master/games/Worm%20V4%20%5BRB-Revival%20Studios,%202007%5D.ch8"
) )
for rom in "${extern_roms[@]}"; do for rom in "${extern_roms[@]}"; do