1 ![CHIP-8 logo](https://i.imgur.com/fHXLgX0.png "CHIP-8 logo")
4 CHIP-8 emulator implemented in C99 using the SDL2 multimedia library. It implements all the opcodes of a standard
5 CHIP-8 but without sound.
9 * Compiled with security hardening flags.
10 * Static analysis integrated using clang's `scan-build` using checkers `alpha.security`, `alpha.core.CastSize`,
11 `alpha.core.CastToStruct`, `alpha.core.IdenticalExpr`, `alpha.core.PointerArithm`, `alpha.core.PointerSub`,
12 `alpha.core.SizeofPtr`, `alpha.core.TestAfterDivZero`, `alpha.unix`.
13 * Follows [FreeBSD coding style](https://www.freebsd.org/cgi/man.cgi?query=style&sektion=9).
14 * Test harness: opcodes are unit tested.
17 * Uses [SDL2](https://www.libsdl.org) as the only dependency.
18 * Easy to compile and uses POSIX make.
21 * Audio is not supported
24 Other than [SDL2](https://www.libsdl.org), the only other dependency is a toolchain supporting the following flags:
27 CFLAGS = -std=c99 -O2 -Wall -Wextra -Wpedantic \
28 -Walloca -Wcast-qual -Wconversion -Wformat=2 -Wformat-security \
29 -Wnull-dereference -Wstack-protector -Wvla -Warray-bounds \
30 -Wbad-function-cast -Wconversion -Wshadow -Wstrict-overflow=4 -Wundef \
31 -Wstrict-prototypes -Wswitch-default -Wfloat-equal \
32 -Wpointer-arith -Wswitch-enum \
34 `sdl2-config --cflags` \
35 -fstack-protector-strong -fPIE -fstack-clash-protection
37 LDFLAGS = -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wl,-z,separate-code
40 Otherwise you can just remove the security flags and compile it with
42 CFLAGS = -std=c99 -O2 -Wall -Wextra -Wpedantic `sdl2-config --cflags`
46 or pass your own flags to make
48 make CC=gcc CFLAGS=... LDFLAGS=...
52 Clone this repository then
55 $ make PREFIX=/usr install
58 This will install the compiled binary under `PREFIX` (`/usr/bin`) in this case, if not specified `PREFIX` will default
59 to `/usr/local`. For staged installs, `DESTDIR` is also supported.
62 **c8emul** receives as input a ROM file and executes it displaying the output in a window of default size 64x32 and
63 with a delay between each cycle of 3ms. The ROM file however, can be omitted, in this case the program takes the input
64 from the standard input until `EOF` or `^D` is reached.
66 The options are as follows:
68 * **-s** Specify the `scaling_factor`, i.e. a multiplier that controls how to scale the default 64x32 window size.
69 * **-d** Specify the delay in milliseconds between cycles.
72 Load ROM from stdin and display it in 640x320 with the default 3ms cycle delay:
76 Load ROM from file and scale the window to 640x320 with a 2ms cycle delay:
78 $ c8emul -s 10 -d 2 Chip8_Picture.ch8
82 The test suite consists of a series of unit tests, one for each opcode (excluding Cxkk and Dxyn) and for the
83 intialization of the emulator. The suite is contained in the `test` folder. It's output is similar to
84 [googletest](https://github.com/google/googletest)'s and it can be invoked with `make check` which, if
85 everything is working should output something similar to
88 [----------] Test environment set-up.
89 [==========] Running test cases.
90 [ RUN ] PC set to 0x200
91 [ OK ] PC set to 0x200
92 [ RUN ] Font is loaded
94 [ RUN ] 00E0: display cleared
95 [ OK ] 00E0: display cleared
96 [ RUN ] 00EE: RET from subroutine
97 [ OK ] 00EE: RET from subroutine
98 [ RUN ] 1123: JMP to location 0x123
99 [ OK ] 1123: JMP to location 0x123
100 [ RUN ] 2456: call subroutine at addr 0x456
101 [ OK ] 2456: call subroutine at addr 0x456
102 [ RUN ] 3879: skip next instruction, V8 = 0x79
103 [ OK ] 3879: skip next instruction, V8 = 0x79
104 [ RUN ] 4123: skip next instruction, V1 != 0x23
105 [ OK ] 4123: skip next instruction, V1 != 0x23
106 [ RUN ] 5210: skip next instruction, V2 = V1
107 [ OK ] 5210: skip next instruction, V2 = V1
108 [ RUN ] 6456: set V4 = 0x56
109 [ OK ] 6456: set V4 = 0x56
110 [ RUN ] 7789: set V7 = 0x89
111 [ OK ] 7789: set V7 = 0x89
112 [ RUN ] 8980: set V9 = V8
113 [ OK ] 8980: set V9 = V8
114 [ RUN ] 8981: set V9 = V9 OR V8
115 [ OK ] 8981: set V9 = V9 OR V8
116 [ RUN ] 8982: set V9 = V9 AND V8
117 [ OK ] 8982: set V9 = V9 AND V8
118 [ RUN ] 8983: set V9 = V9 XOR V8
119 [ OK ] 8983: set V9 = V9 XOR V8
120 [ RUN ] 8984: set V9 = V9 + V8, VF = carry
121 [ OK ] 8984: set V9 = V9 + V8, VF = carry
122 [ RUN ] 8985: set V9 = V9 - V8, VF = not borrow
123 [ OK ] 8985: set V9 = V9 - V8, VF = not borrow
124 [ RUN ] 8986: set V9 = V9 SHR 1
125 [ OK ] 8986: set V9 = V9 SHR 1
126 [ RUN ] 8987: set V9 = V8 - V9, VF = not borrow
127 [ OK ] 8987: set V9 = V8 - V9, VF = not borrow
128 [ RUN ] 898E: set V9 = V9 SHL 1
129 [ OK ] 898E: set V9 = V9 SHL 1
130 [ RUN ] 9120: skip next instruction, V1 != V2
131 [ OK ] 9120: skip next instruction, V1 != V2
132 [ RUN ] A123: set I to 0x123
133 [ OK ] A123: set I to 0x123
134 [ RUN ] B345: jump to location 0x345 + 0x12 (V0)
135 [ OK ] B345: jump to location 0x345 + 0x12 (V0)
136 [ RUN ] E69E: skip next instruction, key with value of V6 is pressed
137 [ OK ] E69E: skip next instruction, key with value of V6 is pressed
138 [ RUN ] E1A1: skip next instruction, key with value of V1 is not pressed
139 [ OK ] E1A1: skip next instruction, key with value of V1 is not pressed
140 [ RUN ] F507: set V5 to delay timer value
141 [ OK ] F507: set V5 to delay timer value
142 [ RUN ] F20A: key 'x' is pressed, V2 = 0
143 [ OK ] F20A: key 'x' is pressed, V2 = 0
144 [ RUN ] F515: set delay timer = V5
145 [ OK ] F515: set delay timer = V5
146 [ RUN ] F718: set sound timer = V7
147 [ OK ] F718: set sound timer = V7
148 [ RUN ] F01E: set I = I + V0
149 [ OK ] F01E: set I = I + V0
150 [ RUN ] F929: set I to location of sprite of '2'
151 [ OK ] F929: set I to location of sprite of '2'
152 [ RUN ] F333: store BCD representation of V3 in memory locations I, I+1, I+2
153 [ OK ] F333: store BCD representation of V3 in memory locations I, I+1, I+2
154 [ RUN ] F255: store registers V0-V2 in memory starting from location I
155 [ OK ] F255: store registers V0-V2 in memory starting from location I
156 [ RUN ] F165: read registers V0-V1 in memory starting from location I
157 [ OK ] F165: read registers V0-V1 in memory starting from location I
158 [==========] 34 test cases ran.
161 [----------] Test environment teardown.
165 Static analysis on the code base is done by using clang's static analyzer run through `scan-build.sh` which wraps the
166 `scan-build` utility. The checkers used are part of the
167 [Experimental Checkers](https://releases.llvm.org/12.0.0/tools/clang/docs/analyzer/checkers.html#alpha-checkers)
168 (aka *alpha* checkers):
171 * `alpha.core.CastSize`
172 * `alpha.core.CastToStruct`
173 * `alpha.core.IdenticalExpr`
174 * `alpha.core.PointerArithm`
175 * `alpha.core.PointerSub`
176 * `alpha.core.SizeofPtr`
177 * `alpha.core.TestAfterDivZero`
181 Send patches on the [mailing list](https://www.freelists.org/list/chip8-dev), report bugs on the [issue tracker](https://todo.sr.ht/~spidernet/chip8).
184 BSD 2-Clause FreeBSD License, see LICENSE.