commit 88bebe70bbf6950b64d83c69c1c86c8c18d21e00
parent 097af219ccf7d31216828178597065c3b130764c
Author: Christos Margiolis <christos@margiolis.net>
Date: Wed, 28 Apr 2021 17:38:43 +0300
added warn() and made minor stylistic changes
Diffstat:
M | LICENSE | | | 41 | ++++++++++++++++------------------------- |
M | README.md | | | 15 | +++------------ |
M | chip8.1 | | | 2 | +- |
M | chip8.c | | | 814 | ++++++++++++++++++++++++++++++++++++++++--------------------------------------- |
M | config.mk | | | 7 | +++---- |
5 files changed, 437 insertions(+), 442 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -1,29 +1,20 @@
-BSD 3-Clause License
+MIT License
-Copyright (c) 2020-present, Christos Margiolis.
-All rights reserved.
+(c) 2020-Present Christos Margiolis <christos@christosmarg.xyz>
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the “Software”), to deal in
+the Software without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
-* Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-* Neither the name of images.weserv.nl nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
@@ -1,23 +1,14 @@
# CHIP-8 Emulator
-Your typical CHIP-8 copy. This one uses SDL2 as a rendering API.
-
-## Dependencies
-
-* `make`
-* `SDL2`
+CHIP-8 emulator using SDL2.
## Usage
```shell
-$ cd path/to/chip8
$ make
-$ ./chip8 [../path/to/ROM]
-$ make clean # optional
+# make install clean
```
-
-You can install `chip8` by running `sudo make install clean`.
-The binary will be installed at `/usr/local/bin`
+CHIP-8 will be installed at `/usr/local/bin`
## Screenshots
diff --git a/chip8.1 b/chip8.1
@@ -6,7 +6,7 @@
.Nd a minimal CHIP\-8 emulator
.Sh SYNOPSIS
.Nm
-.Op CHIP8_FILE
+.Ar rom
.Sh DESCRIPTION
.Nm
reads a CHIP\-8 ROM and executes it. It uses the SDL2
diff --git a/chip8.c b/chip8.c
@@ -9,341 +9,333 @@
#ifdef _WIN_32
#include <Windows.h>
-typedef unsigned int u_int32_t
+typedef unsigned int u_int32_t
typedef unsigned short u_int16_t
-typedef unsigned char u_int8_t
+typedef unsigned char u_int8_t
#else /* !_WIN_32 */
#include <sys/types.h>
#include <unistd.h>
#endif /* _WIN_32 */
-#define VX_MASK(x) ((x & 0x0f00) >> 8)
-#define VY_MASK(x) ((x & 0x00f0) >> 4)
-#define NN_MASK(x) (x & 0x00ff)
-#define NNN_MASK(x) (x & 0x0fff)
-#define ROM_SIZE_MAX (4096 - 512)
-#define EXECUTE(pc) do { pc += 2; } while (0)
-
-struct Chip8 {
- u_int16_t I;
- u_int16_t opcode;
- u_int16_t pc;
- u_int16_t sp;
- u_int16_t stack[16];
- u_int8_t delaytimer;
- u_int8_t drawflag;
- u_int8_t gfx[64 * 32];
- u_int8_t keys[16];
- u_int8_t memory[4096];
- u_int8_t soundtimer;
- u_int8_t V[16];
+#define VX_MASK(x) ((x & 0x0f00) >> 8)
+#define VY_MASK(x) ((x & 0x00f0) >> 4)
+#define NN_MASK(x) (x & 0x00ff)
+#define NNN_MASK(x) (x & 0x0fff)
+#define EXECUTE(pc) do { pc += 2; } while (0)
+
+struct chip8 {
+ u_int16_t stack[16];
+ u_int16_t I;
+ u_int16_t opcode;
+ u_int16_t pc;
+ u_int16_t sp;
+ u_int8_t mem[4096];
+ u_int8_t gfx[64 * 32];
+ u_int8_t V[16];
+ u_int8_t keys[16];
+ u_int8_t delaytimer;
+ u_int8_t soundtimer;
+ u_int8_t drawflag;
};
+static void chip8_init(struct chip8 *);
+static void romload(struct chip8 *, const char *);
+static void emulate(struct chip8 *);
+static int decode(struct chip8 *);
+static int evhandle(struct chip8 *);
+static void render(SDL_Renderer *, SDL_Texture *, struct chip8 *);
+static void warn(const char *, ...);
+static void die(const char *, ...);
+
+static char *argv0;
static const u_int8_t keymap[16] = {
- SDLK_1, SDLK_2, SDLK_3, SDLK_4,
- SDLK_q, SDLK_w, SDLK_e, SDLK_r,
- SDLK_a, SDLK_s, SDLK_d, SDLK_f,
- SDLK_z, SDLK_x, SDLK_c, SDLK_v
+ SDLK_1, SDLK_2, SDLK_3, SDLK_4,
+ SDLK_q, SDLK_w, SDLK_e, SDLK_r,
+ SDLK_a, SDLK_s, SDLK_d, SDLK_f,
+ SDLK_z, SDLK_x, SDLK_c, SDLK_v
};
-static void chip8_init(struct Chip8 *);
-static void rom_load(struct Chip8 *, const char *);
-static void emulate(struct Chip8 *);
-static int decode(struct Chip8 *);
-static void timers_update(struct Chip8 *);
-static int evts(struct Chip8 *);
-static void render(SDL_Renderer *, SDL_Texture *, struct Chip8 *);
-static void die(const char *, ...);
-
-#define I chip8->I
-#define opcode chip8->opcode
-#define pc chip8->pc
-#define sp chip8->sp
-#define stack chip8->stack
-#define delaytimer chip8->delaytimer
-#define drawflag chip8->drawflag
-#define gfx chip8->gfx
-#define keys chip8->keys
-#define memory chip8->memory
-#define soundtimer chip8->soundtimer
-#define V chip8->V
-
-void
-chip8_init(struct Chip8 *chip8)
-{
- int i;
- u_int8_t fontset[80] = {
- 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
- 0x20, 0x60, 0x20, 0x20, 0x70, // 1
- 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
- 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
- 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
- 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
- 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
- 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
- 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
- 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
- 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
- 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
- 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
- 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
- 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
- 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
- };
- pc = 0x200;
- opcode = 0;
- I = 0;
- sp = 0;
- delaytimer = 0;
- soundtimer = 0;
- memset(V, 0, 16 * sizeof(u_int8_t));
- memset(keys, 0, 16 * sizeof(u_int8_t));
- memset(stack, 0, 16 * sizeof(u_int16_t));
- memset(gfx, 0, 2048 * sizeof(u_int8_t));
- memset(memory, 0, 4096 * sizeof(u_int8_t));
- for (i = 0; i < 80; memory[i] = fontset[i], i++)
- ;
-}
-
-void
-rom_load(struct Chip8 *chip8, const char *fpath)
+#define stack chip8->stack
+#define I chip8->I
+#define opcode chip8->opcode
+#define pc chip8->pc
+#define sp chip8->sp
+#define mem chip8->mem
+#define gfx chip8->gfx
+#define V chip8->V
+#define keys chip8->keys
+#define delaytimer chip8->delaytimer
+#define soundtimer chip8->soundtimer
+#define drawflag chip8->drawflag
+
+static void
+chip8_init(struct chip8 *chip8)
{
- FILE *rom;
- size_t res;
- long romsize;
- int i;
- char *buf;
-
- if ((rom = fopen(fpath, "rb")) == NULL)
- die("error loading ROM (%s).", fpath);
- fseek(rom, 0, SEEK_END);
- romsize = ftell(rom);
- rewind(rom);
-
- if ((buf = malloc(romsize * sizeof(char))) == NULL)
- die("cannot allocate memory.");
- if ((res = fread(buf, sizeof(char), (size_t)romsize, rom)) != romsize)
- die("error reading ROM.");
- if (romsize < ROM_SIZE_MAX)
- for (i = 0; i < romsize; i++)
- memory[i + 512] = (u_int8_t)buf[i];
- else
- die("ROM cannot fit into memory.");
-
- fclose(rom);
- free(buf);
+ u_int8_t fontset[80] = {
+ 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
+ 0x20, 0x60, 0x20, 0x20, 0x70, // 1
+ 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
+ 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
+ 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
+ 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
+ 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
+ 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
+ 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
+ 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
+ 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
+ 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
+ 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
+ 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
+ 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
+ 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
+ };
+ int i;
+
+ pc = 0x200;
+ opcode = 0;
+ I = 0;
+ sp = 0;
+ delaytimer = 0;
+ soundtimer = 0;
+
+ memset(V, 0, sizeof(V));
+ memset(keys, 0, sizeof(keys));
+ memset(stack, 0, sizeof(stack));
+ memset(gfx, 0, sizeof(gfx));
+ memset(mem, 0, sizeof(mem));
+
+ for (i = 0; i < 80; i++)
+ mem[i] = fontset[i];
}
-
-void
-emulate(struct Chip8 *chip8)
+static void
+romload(struct chip8 *chip8, const char *fpath)
{
- opcode = memory[pc] << 8 | memory[pc + 1];
- if (decode(chip8)) {
- EXECUTE(pc);
- timers_update(chip8);
- }
- printf("Opcode: %x\tMemory: %x\tI: %x\tSP: %x\tPC: %d\n",
- opcode, memory[pc] << 8 | memory[pc + 1], I, sp, pc);
+ FILE *rom;
+ char *buf;
+ size_t res, romsize;
+ int i;
+
+ if ((rom = fopen(fpath, "rb")) == NULL)
+ die("fopen: %s", fpath);
+ fseek(rom, 0, SEEK_END);
+ romsize = ftell(rom);
+ rewind(rom);
+
+ if ((buf = malloc(romsize + 1)) == NULL)
+ die("malloc:");
+ if ((res = fread(buf, sizeof(char), romsize, rom)) != romsize)
+ die("fread:");
+ buf[romsize] = '\0';
+ if (romsize < (4092 - 512))
+ for (i = 0; i < romsize; i++)
+ mem[i + 512] = buf[i];
+ else
+ die("ROM cannot fit into memory");
+
+ fclose(rom);
+ free(buf);
}
-int
-decode(struct Chip8 *chip8)
+static void
+emulate(struct chip8 *chip8)
{
- int i;
-
- switch (opcode & 0xF000) {
- case 0x0000: // 00E_
- switch (NN_MASK(opcode)) {
- case 0xE0: // 00E0 - Clear screen
- memset(gfx, 0, 2048 * sizeof(u_int8_t));
- drawflag = 1;
- break;
- case 0xEE: // 00EE - Return from subroutine
- pc = stack[--sp];
- break;
- default:
- fprintf(stderr, "Unknown opcode: %x\n", opcode);
- return 0;
- }
- break;
- case 0x1000: // 1NNN - Jump to address NNN
- pc = NNN_MASK(opcode) - 2;
- break;
- case 0x2000: // 2NNN - Call subroutine at NNN
- stack[sp++] = pc;
- pc = NNN_MASK(opcode) - 2;
- case 0x3000: // 3NNN - Skip next instruction if VX == NN
- if (V[VX_MASK(opcode)] == NN_MASK(opcode))
- EXECUTE(pc);
- break;
- case 0x4000: // 4NNN - Skip next instruction if VX != NN
- if (V[VX_MASK(opcode)] != NN_MASK(opcode))
- EXECUTE(pc);
- break;
- case 0x5000: // 5XY0 - Skip next instruction if VX == VY
- if (V[VX_MASK(opcode)] == V[VY_MASK(opcode)])
- EXECUTE(pc);
- break;
- case 0x6000: // 6XNN - Set VX to NN
- V[VX_MASK(opcode)] = NN_MASK(opcode);
- break;
- case 0x7000: // 7XNN - Add NN to VX
- V[VX_MASK(opcode)] += NN_MASK(opcode);
- break;
- case 0x8000: // 8XY_
- switch (opcode & 0x000F) {
- case 0x0000: // 8XY0 - Set VX to VY
- V[VX_MASK(opcode)] = V[VY_MASK(opcode)];
- break;
- case 0x0001: // 8XY1 - Set VX to (VX OR VY)
- V[VX_MASK(opcode)] |= V[VY_MASK(opcode)];
- break;
- case 0x0002: // 8XY2 - Set VX to (VX AND VY)
- V[VX_MASK(opcode)] &= V[VY_MASK(opcode)];
- break;
- case 0x0003: // 8XY3 - Set VX to (VX XOR VY)
- V[VX_MASK(opcode)] ^= V[VY_MASK(opcode)];
- break;
- case 0x0004: // 8XY4 - Add VY to VX, VF = 1 if there is a carry
- V[VX_MASK(opcode)] += V[VY_MASK(opcode)];
- V[0xF] = (V[VY_MASK(opcode)] > (0xFF - V[VX_MASK(opcode)])) ? 1 : 0;
- break;
- case 0x0005: // 8XY5 - Sub VY from VX, VF = 0 if there is a borrow
- V[0xF] = (V[VY_MASK(opcode)] > V[VX_MASK(opcode)]) ? 0 : 1;
- V[VX_MASK(opcode)] -= V[VY_MASK(opcode)];
- break;
- case 0x0006: // 8XY6 - Shift VX right by 1. VF = LSB of VX before shift
- V[0xF] = V[VX_MASK(opcode)] & 0x1;
- V[VX_MASK(opcode)] >>= 1;
- break;
- case 0x0007: // 8XY7 - Set VX to VY-VX. VF = 0 if there is a borrow
- V[0xF] = (V[VX_MASK(opcode)] > V[VY_MASK(opcode)]) ? 0 : 1;
- V[VX_MASK(opcode)] = V[VY_MASK(opcode)] - V[VX_MASK(opcode)];
- break;
- case 0x000E: // 8XYE - Shift VX left by 1. VF = MSB of VX before shift
- V[0xF] = V[VX_MASK(opcode)] >> 7;
- V[VX_MASK(opcode)] <<= 1;
- break;
- default:
- fprintf(stderr, "Unknown opcode: %x\n", opcode);
- return 0;
- }
- break;
- case 0x9000: // 9XY0 - Skip next instruction if VX != VY
- if (V[VX_MASK(opcode)] != V[VY_MASK(opcode)])
- EXECUTE(pc);
- break;
- case 0xA000: // ANNN - Set I to the address NNN
- I = NNN_MASK(opcode);
- break;
- case 0xB000: // BNNN - Jump to NNN + V0
- pc = ((NNN_MASK(opcode)) + V[0]) - 2;
- break;
- case 0xC000: // CNNN - Set VX to random number masked by NN
- V[VX_MASK(opcode)] = (rand() % (0xFF + 1)) & (NN_MASK(opcode));
- break;
- case 0xD000: { // Draw an 8 pixel sprite at (VX, VY)
- int yl, xl;
- u_int16_t h = opcode & 0x000F;
- u_int16_t pixel;
- u_int8_t VX = V[VX_MASK(opcode)];
- u_int8_t VY = V[VY_MASK(opcode)];
-
- V[0xF] = 0;
- for (yl = 0; yl < h; yl++) {
- pixel = memory[I + yl];
- for (xl = 0; xl < 8; xl++) {
- if ((pixel & (0x80 >> xl)) != 0) {
- if (gfx[VX + xl + ((VY + yl) * 64)] == 1)
- V[0xF] = 1;
- gfx[VX + xl + ((VY + yl) * 64)] ^= 1;
- }
- }
- }
- drawflag = 1;
- }
- break;
- case 0xE000: // EX__
- switch (NN_MASK(opcode)) {
- case 0x009E: // EX9E - Skip next instruction if key in VX is pressed
- if (keys[V[VX_MASK(opcode)]])
- EXECUTE(pc);
- break;
- case 0x00A1: // EXA1 - Skip next instruction if key in VX isn't pressed
- if (!keys[V[VX_MASK(opcode)]])
- EXECUTE(pc);
- break;
- default:
- fprintf(stderr, "Unknown opcode: %x\n", opcode);
- return 0;
- }
- break;
- case 0xF000: // FX__
- switch (NN_MASK(opcode)) {
- case 0x0007: // FX07 - Set VX to delaytimer
- V[VX_MASK(opcode)] = delaytimer;
- break;
- case 0x000A: { // FX0A - Wait for key press and then store it in VX
- int keypressed = 0;
-
- for (i = 0; i < 16; i++) {
- if (keys[i]) {
- V[VX_MASK(opcode)] = i;
- keypressed = 1;
- }
- }
- if (!keypressed)
- return 0;
- }
- break;
- case 0x0015: // FX15 - Set the delaytimer to VX
- delaytimer = V[VX_MASK(opcode)];
- break;
- case 0x0018: // FX18 - Set the soundtimer to VX
- soundtimer = V[VX_MASK(opcode)];
- break;
- case 0x001E: // FX1E - Add VX to I
- V[0xF] = ((I + V[VX_MASK(opcode)]) > 0xFFF) ? 1 : 0;
- I += V[VX_MASK(opcode)];
- break;
- case 0x0029: // FX29 - Set I to the location of the sprite for char VX
- I = V[VX_MASK(opcode)] * 0x5;
- break;
- case 0x0033: // FX33 - Store bin coded decimal of VX at I, I+1 and I+2
- memory[I] = V[VX_MASK(opcode)] / 100;
- memory[I+1] = (V[VX_MASK(opcode)] / 10) % 10;
- memory[I+2] = V[VX_MASK(opcode)] % 10;
- break;
- case 0x0055: // FX55 - Store V0 to VX in memory starting at I
- for (i = 0; i <= (VX_MASK(opcode)); i++)
- memory[I + i] = V[i];
- I += (VX_MASK(opcode)) + 1;
- break;
- case 0x0065: // FX65 - Fill V0 to VX with vals from memory starting at I
- for (i = 0; i <= (VX_MASK(opcode)); i++)
- V[i] = memory[I + i];
- I += (VX_MASK(opcode)) + 1;
- break;
- default:
- fprintf(stderr, "Unknown opcode: %x\n", opcode);
- return 0;
- }
- break;
- default:
- fputs("Unimplemented opcode\n", stderr);
- return 0;
- }
- return 1;
+ opcode = mem[pc] << 8 | mem[pc + 1];
+ if (decode(chip8)) {
+ EXECUTE(pc);
+ if (delaytimer > 0)
+ delaytimer--;
+ if (soundtimer > 0)
+ soundtimer--;
+ }
+ printf("Opcode: %x\tMemory: %x\tI: %x\tSP: %x\tPC: %d\n",
+ opcode, mem[pc] << 8 | mem[pc + 1], I, sp, pc);
}
-void
-timers_update(struct Chip8 *chip8)
+static int
+decode(struct chip8 *chip8)
{
- if (delaytimer > 0)
- delaytimer--;
- if (soundtimer > 0)
- soundtimer--;
+ int yl, xl, i, keypress = 0;
+ u_int16_t h, pixel;
+ u_int8_t VX, VY;
+
+ switch (opcode & 0xF000) {
+ case 0x0000: // 00E_
+ switch (NN_MASK(opcode)) {
+ case 0xE0: // 00E0 - Clear screen
+ memset(gfx, 0, sizeof(gfx));
+ drawflag = 1;
+ break;
+ case 0xEE: // 00EE - Return from subroutine
+ pc = stack[--sp];
+ break;
+ default:
+ warn("unknown opcode: %x\n", opcode);
+ return 0;
+ }
+ break;
+ case 0x1000: // 1NNN - Jump to address NNN
+ pc = NNN_MASK(opcode) - 2;
+ break;
+ case 0x2000: // 2NNN - Call subroutine at NNN
+ stack[sp++] = pc;
+ pc = NNN_MASK(opcode) - 2;
+ case 0x3000: // 3NNN - Skip next instruction if VX == NN
+ if (V[VX_MASK(opcode)] == NN_MASK(opcode))
+ EXECUTE(pc);
+ break;
+ case 0x4000: // 4NNN - Skip next instruction if VX != NN
+ if (V[VX_MASK(opcode)] != NN_MASK(opcode))
+ EXECUTE(pc);
+ break;
+ case 0x5000: // 5XY0 - Skip next instruction if VX == VY
+ if (V[VX_MASK(opcode)] == V[VY_MASK(opcode)])
+ EXECUTE(pc);
+ break;
+ case 0x6000: // 6XNN - Set VX to NN
+ V[VX_MASK(opcode)] = NN_MASK(opcode);
+ break;
+ case 0x7000: // 7XNN - Add NN to VX
+ V[VX_MASK(opcode)] += NN_MASK(opcode);
+ break;
+ case 0x8000: // 8XY_
+ switch (opcode & 0x000F) {
+ case 0x0000: // 8XY0 - Set VX to VY
+ V[VX_MASK(opcode)] = V[VY_MASK(opcode)];
+ break;
+ case 0x0001: // 8XY1 - Set VX to (VX OR VY)
+ V[VX_MASK(opcode)] |= V[VY_MASK(opcode)];
+ break;
+ case 0x0002: // 8XY2 - Set VX to (VX AND VY)
+ V[VX_MASK(opcode)] &= V[VY_MASK(opcode)];
+ break;
+ case 0x0003: // 8XY3 - Set VX to (VX XOR VY)
+ V[VX_MASK(opcode)] ^= V[VY_MASK(opcode)];
+ break;
+ case 0x0004: // 8XY4 - Add VY to VX, VF = 1 if there is a carry
+ V[VX_MASK(opcode)] += V[VY_MASK(opcode)];
+ V[0xF] = (V[VY_MASK(opcode)] > (0xFF - V[VX_MASK(opcode)])) ? 1 : 0;
+ break;
+ case 0x0005: // 8XY5 - Sub VY from VX, VF = 0 if there is a borrow
+ V[0xF] = (V[VY_MASK(opcode)] > V[VX_MASK(opcode)]) ? 0 : 1;
+ V[VX_MASK(opcode)] -= V[VY_MASK(opcode)];
+ break;
+ case 0x0006: // 8XY6 - Shift VX right by 1. VF = LSB of VX before shift
+ V[0xF] = V[VX_MASK(opcode)] & 0x1;
+ V[VX_MASK(opcode)] >>= 1;
+ break;
+ case 0x0007: // 8XY7 - Set VX to VY-VX. VF = 0 if there is a borrow
+ V[0xF] = (V[VX_MASK(opcode)] > V[VY_MASK(opcode)]) ? 0 : 1;
+ V[VX_MASK(opcode)] = V[VY_MASK(opcode)] - V[VX_MASK(opcode)];
+ break;
+ case 0x000E: // 8XYE - Shift VX left by 1. VF = MSB of VX before shift
+ V[0xF] = V[VX_MASK(opcode)] >> 7;
+ V[VX_MASK(opcode)] <<= 1;
+ break;
+ default:
+ warn("unknown opcode: %x\n", opcode);
+ return 0;
+ }
+ break;
+ case 0x9000: // 9XY0 - Skip next instruction if VX != VY
+ if (V[VX_MASK(opcode)] != V[VY_MASK(opcode)])
+ EXECUTE(pc);
+ break;
+ case 0xA000: // ANNN - Set I to the address NNN
+ I = NNN_MASK(opcode);
+ break;
+ case 0xB000: // BNNN - Jump to NNN + V0
+ pc = ((NNN_MASK(opcode)) + V[0]) - 2;
+ break;
+ case 0xC000: // CNNN - Set VX to random number masked by NN
+ V[VX_MASK(opcode)] = (rand() % (0xFF + 1)) & (NN_MASK(opcode));
+ break;
+ case 0xD000: // Draw an 8 pixel sprite at (VX, VY)
+ h = opcode & 0x000F;
+ VX = V[VX_MASK(opcode)];
+ VY = V[VY_MASK(opcode)];
+ V[0xF] = 0;
+
+ for (yl = 0; yl < h; yl++) {
+ pixel = mem[I + yl];
+ for (xl = 0; xl < 8; xl++) {
+ if ((pixel & (0x80 >> xl)) != 0) {
+ if (gfx[VX + xl + ((VY + yl) * 64)] == 1)
+ V[0xF] = 1;
+ gfx[VX + xl + ((VY + yl) * 64)] ^= 1;
+ }
+ }
+ }
+ drawflag = 1;
+ break;
+ case 0xE000: // EX__
+ switch (NN_MASK(opcode)) {
+ case 0x009E: // EX9E - Skip next instruction if key in VX is pressed
+ if (keys[V[VX_MASK(opcode)]])
+ EXECUTE(pc);
+ break;
+ case 0x00A1: // EXA1 - Skip next instruction if key in VX isn't pressed
+ if (!keys[V[VX_MASK(opcode)]])
+ EXECUTE(pc);
+ break;
+ default:
+ warn("unknown opcode: %x\n", opcode);
+ return 0;
+ }
+ break;
+ case 0xF000: // FX__
+ switch (NN_MASK(opcode)) {
+ case 0x0007: // FX07 - Set VX to delaytimer
+ V[VX_MASK(opcode)] = delaytimer;
+ break;
+ case 0x000A: // FX0A - Wait for key press and then store it in VX
+ for (i = 0; i < 16; i++) {
+ if (keys[i]) {
+ V[VX_MASK(opcode)] = i;
+ keypress = 1;
+ }
+ }
+ if (!keypress)
+ return 0;
+ break;
+ case 0x0015: // FX15 - Set the delaytimer to VX
+ delaytimer = V[VX_MASK(opcode)];
+ break;
+ case 0x0018: // FX18 - Set the soundtimer to VX
+ soundtimer = V[VX_MASK(opcode)];
+ break;
+ case 0x001E: // FX1E - Add VX to I
+ V[0xF] = ((I + V[VX_MASK(opcode)]) > 0xFFF) ? 1 : 0;
+ I += V[VX_MASK(opcode)];
+ break;
+ case 0x0029: // FX29 - Set I to the location of the sprite for char VX
+ I = V[VX_MASK(opcode)] * 0x5;
+ break;
+ case 0x0033: // FX33 - Store bin coded decimal of VX at I, I+1 and I+2
+ mem[I] = V[VX_MASK(opcode)] / 100;
+ mem[I+1] = (V[VX_MASK(opcode)] / 10) % 10;
+ mem[I+2] = V[VX_MASK(opcode)] % 10;
+ break;
+ case 0x0055: // FX55 - Store V0 to VX in memory starting at I
+ for (i = 0; i <= (VX_MASK(opcode)); i++)
+ mem[I + i] = V[i];
+ I += (VX_MASK(opcode)) + 1;
+ break;
+ case 0x0065: // FX65 - Fill V0 to VX with vals from memory starting at I
+ for (i = 0; i <= (VX_MASK(opcode)); i++)
+ V[i] = mem[I + i];
+ I += (VX_MASK(opcode)) + 1;
+ break;
+ default:
+ warn("unknown opcode: %x\n", opcode);
+ return 0;
+ }
+ break;
+ default:
+ warn("unimplemented opcode\n");
+ return 0;
+ }
+ return 1;
}
#undef V
@@ -351,7 +343,7 @@ timers_update(struct Chip8 *chip8)
#undef opcode
#undef I
#undef sp
-#undef memory
+#undef mem
#undef gfx
#undef stack
#undef keys
@@ -359,106 +351,128 @@ timers_update(struct Chip8 *chip8)
#undef soundtimer
#undef drawflag
-int
-evts(struct Chip8 *chip8)
+static int
+evhandle(struct chip8 *chip8)
{
- SDL_Event e;
- int i;
-
- while (SDL_PollEvent(&e)) {
- if (e.type == SDL_QUIT || e.key.keysym.sym == SDLK_ESCAPE)
- return 0;
- if (e.type == SDL_KEYDOWN)
- for (i = 0; i < 16; i++)
- if (e.key.keysym.sym == keymap[i])
- chip8->keys[i] = 1;
- if (e.type == SDL_KEYUP)
- for (i = 0; i < 16; i++)
- if (e.key.keysym.sym == keymap[i])
- chip8->keys[i] = 0;
- }
- return 1;
+ SDL_Event e;
+ int i;
+
+ while (SDL_PollEvent(&e)) {
+ if (e.type == SDL_QUIT || e.key.keysym.sym == SDLK_ESCAPE)
+ return 0;
+ if (e.type == SDL_KEYUP || e.type == SDL_KEYDOWN) {
+ for (i = 0; i < 16; i++) {
+ if (e.key.keysym.sym == keymap[i]) {
+ if (e.type == SDL_KEYUP)
+ chip8->keys[i] = 0;
+ else if (e.type == SDL_KEYDOWN)
+ chip8->keys[i] = 1;
+ }
+ }
+ }
+ }
+ return 1;
}
-void
-render(SDL_Renderer *ren, SDL_Texture *tex, struct Chip8 *chip8)
+static void
+render(SDL_Renderer *ren, SDL_Texture *tex, struct chip8 *chip8)
{
- int i;
- u_int32_t pixels[2048];
- u_int8_t pixel;
-
- chip8->drawflag = 0;
- for (i = 0; i < 2048; i++) {
- pixel = chip8->gfx[i];
- pixels[i] = (0x00FFFFFF * pixel) | 0xFF000000;
- }
- SDL_UpdateTexture(tex, NULL, pixels, 64 * sizeof(Uint32));
- SDL_RenderClear(ren);
- SDL_RenderCopy(ren, tex, NULL, NULL);
- SDL_RenderPresent(ren);
+ u_int32_t pixels[2048];
+ int i;
+
+ chip8->drawflag = 0;
+ for (i = 0; i < 2048; i++)
+ pixels[i] = (0x00FFFFFF * chip8->gfx[i]) | 0xFF000000;
+ SDL_UpdateTexture(tex, NULL, pixels, 64 * sizeof(Uint32));
+ SDL_RenderClear(ren);
+ SDL_RenderCopy(ren, tex, NULL, NULL);
+ SDL_RenderPresent(ren);
}
-void
+static void
+warn(const char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "%s: ", argv0);
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+}
+
+static void
die(const char *fmt, ...)
{
- va_list args;
-
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
-
- if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
- fputc(' ', stderr);
- perror(NULL);
- } else
- fputc('\n', stderr);
-
- exit(EXIT_FAILURE);
+ va_list args;
+
+ fprintf(stderr, "%s: ", argv0);
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else
+ fputc('\n', stderr);
+
+ exit(EXIT_FAILURE);
}
int
main(int argc, char *argv[])
{
- SDL_Window *win;
- SDL_Renderer *ren;
- SDL_Texture *tex;
- struct Chip8 chip8;
- int w = 1024, h = 512;
-
- srand(time(NULL));
- if (argc != 2)
- die("usage: %s [ROM]", argv[0]);
- if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
- die("cannot initalize SDL: %s", SDL_GetError());
-
- win = SDL_CreateWindow("CHIP-8 Emulator", SDL_WINDOWPOS_UNDEFINED,
- SDL_WINDOWPOS_UNDEFINED, w, h,
- SDL_WINDOW_SHOWN);
- ren = SDL_CreateRenderer(win, -1, 0);
- SDL_RenderSetLogicalSize(ren, w, h);
- tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888,
- SDL_TEXTUREACCESS_STREAMING, 64, 32);
-
- if (!win || !ren || !tex)
- die("SDL error: %s", SDL_GetError());
-
- chip8_init(&chip8);
- rom_load(&chip8, argv[1]);
-
- while (evts(&chip8)) {
- emulate(&chip8);
- if (chip8.drawflag)
- render(ren, tex, &chip8);
+ SDL_Window *win;
+ SDL_Renderer *ren;
+ SDL_Texture *tex;
+ struct chip8 *chip8;
+ int w = 1024, h = 512;
+
+ argv0 = *argv;
+ srand(time(NULL));
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s rom", argv0);
+ return 1;
+ }
+ if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
+ die("SDL_Init: %s", SDL_GetError());
+
+ win = SDL_CreateWindow("CHIP-8 Emulator", SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED, w, h,
+ SDL_WINDOW_SHOWN);
+ ren = SDL_CreateRenderer(win, -1, 0);
+ SDL_RenderSetLogicalSize(ren, w, h);
+ tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888,
+ SDL_TEXTUREACCESS_STREAMING, 64, 32);
+
+ if (!win || !ren || !tex)
+ die("SDL error: %s", SDL_GetError());
+ if ((chip8 = malloc(sizeof(struct chip8))) == NULL)
+ die("malloc:");
+
+ chip8_init(chip8);
+ romload(chip8, argv[1]);
+
+ for (;;) {
+ if (!evhandle(chip8))
+ break;
+ emulate(chip8);
+ if (chip8->drawflag)
+ render(ren, tex, chip8);
#ifdef _WIN_32
- Sleep(1);
+ Sleep(1);
#else /* !_WIN_32 */
- usleep(1500);
+ usleep(1500);
#endif /* _WIN_32 */
- }
+ }
+
+ free(chip8);
+ SDL_DestroyTexture(tex);
+ SDL_DestroyRenderer(ren);
+ SDL_DestroyWindow(win);
+ SDL_Quit();
- SDL_DestroyTexture(tex);
- SDL_DestroyRenderer(ren);
- SDL_DestroyWindow(win);
- SDL_Quit();
- return EXIT_SUCCESS;
+ return EXIT_SUCCESS;
}
diff --git a/config.mk b/config.mk
@@ -4,7 +4,7 @@ VERSION = 0
# paths
PREFIX = /usr/local
-MAN_DIR = ${PREFIX}/man/man1
+MAN_DIR = ${PREFIX}/share/man/man1
BIN_DIR = ${PREFIX}/bin
# includes and libs
@@ -13,9 +13,8 @@ LIBS = -Llib -lSDL2
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \
- -DVERSION=\"${VERSION}\"
-CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations \
- -O3 ${INCS} ${CPPFLAGS}
+ -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\"
+CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = ${LIBS}
# utils