2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2021 Alessio Chiapperini.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
34 #include "../src/chip8.h"
36 static int count_pass
;
37 static int count_fail
;
38 static uint8_t fontset
[FONTSIZ
] = {
39 0xF0, 0x90, 0x90, 0x90, 0xF0, /* 0 */
40 0x20, 0x60, 0x20, 0x20, 0x70, /* 1 */
41 0xF0, 0x10, 0xF0, 0x80, 0xF0, /* 2 */
42 0xF0, 0x10, 0xF0, 0x10, 0xF0, /* 3 */
43 0x90, 0x90, 0xF0, 0x10, 0x10, /* 4 */
44 0xF0, 0x80, 0xF0, 0x10, 0xF0, /* 5 */
45 0xF0, 0x80, 0xF0, 0x90, 0xF0, /* 6 */
46 0xF0, 0x10, 0x20, 0x40, 0x40, /* 7 */
47 0xF0, 0x90, 0xF0, 0x90, 0xF0, /* 8 */
48 0xF0, 0x90, 0xF0, 0x10, 0xF0, /* 9 */
49 0xF0, 0x90, 0xF0, 0x90, 0x90, /* A */
50 0xE0, 0x90, 0xE0, 0x90, 0xE0, /* B */
51 0xF0, 0x80, 0x80, 0x80, 0xF0, /* C */
52 0xE0, 0x90, 0x90, 0x90, 0xE0, /* D */
53 0xF0, 0x80, 0xF0, 0x80, 0xF0, /* E */
54 0xF0, 0x80, 0xF0, 0x80, 0x80 /* F */
57 #define TEST(exp, test_name) \
59 (void)printf("[ %-8s ] " test_name "\n", "RUN"); \
61 (void)printf("[ %8s ] " test_name "\n", "OK"); \
64 (void)printf("[ %8s ] " test_name "\n", "NOK"); \
70 setup(struct chip8
*c
)
73 srand((unsigned int) time(0));
74 printf("[----------] Test environment set-up.\n");
80 printf("[----------] Test environment teardown.\n");
84 putopcode(uint16_t opcode
, struct chip8
*c
, size_t pos
)
86 c
->memory
[pos
] = opcode
>> 8;
87 c
->memory
[pos
+ 1] = opcode
& 0xFF;
97 (void)printf("[==========] Running test cases.\n");
100 /* PC must be set to 0x200 */
103 TEST(c
.pc
== 0x200, "PC set to 0x200");
106 /* Font is loaded into memory */
111 for (size_t i
= 0; i
< FONTSIZ
&& isloaded
!= 0; i
++) {
112 if (c
.memory
[FONT_STARTADDR
+ i
] != fontset
[i
]) {
116 TEST(isloaded
== 1, "Font is loaded");
119 /* 00E0: display should be cleared */
124 putopcode(0x00E0, &c
, c
.pc
);
126 for (size_t px
= 0; px
< WIDTH
* HEIGHT
&& isclear
!= 0; px
++) {
127 if (c
.display
[px
] != 0) {
131 TEST(isclear
== 1, "00E0: display cleared");
134 /* 00EE: return from subroutine */
136 uint8_t osp
= ++c
.sp
;
139 putopcode(0x00EE, &c
, c
.pc
);
141 TEST((--osp
== c
.sp
) && (c
.pc
== c
.stack
[c
.sp
]),
142 "00EE: RET from subroutine");
145 /* 1123: jmp to location 0x123, pc = 0x123 */
149 putopcode(0x1123, &c
, c
.pc
);
151 TEST(c
.pc
== 0x123, "1123: JMP to location 0x123");
154 /* 2456: call subroutine at addr 0x456 */
160 putopcode(0x2456, &c
, c
.pc
);
162 TEST((c
.stack
[0] == 0x57) && (c
.sp
== 1) &&
163 (c
.pc
== 0x456), "2456: call subroutine at addr 0x456");
166 /* 3879: skip next instruction if V8 = 0x79 */
169 c
.registers
[8] = 0x79;
172 putopcode(0x3879, &c
, c
.pc
);
174 TEST(c
.pc
== 4, "3879: skip next instruction, V8 = 0x79");
177 /* 4123: skip next instruction if V1 != 0x23 */
180 c
.registers
[1] = 0x45;
183 putopcode(0x4123, &c
, c
.pc
);
185 TEST(c
.pc
== 4, "4123: skip next instruction, V1 != 0x23");
188 /* 5210: skip next instruction if V2 = V1 */
191 c
.registers
[2] = 0x69;
192 c
.registers
[1] = c
.registers
[2];
195 putopcode(0x5210, &c
, c
.pc
);
197 TEST(c
.pc
== 4, "5210: skip next instruction, V2 = V1");
200 /* 6456: set V4 = 0x56 */
204 putopcode(0x6456, &c
, c
.pc
);
206 TEST(c
.registers
[4] == 0x56, "6456: set V4 = 0x56");
209 /* 7789: set V7 = V7 + 0x89 */
212 c
.registers
[7] = 0x0;
214 putopcode(0x7789, &c
, c
.pc
);
216 TEST(c
.registers
[7] == 0x89, "7789: set V7 = 0x89");
219 /* 8980: set V9 = V8 */
222 c
.registers
[8] = 0x98;
224 putopcode(0x8980, &c
, c
.pc
);
226 TEST(c
.registers
[9] == c
.registers
[8], "8980: set V9 = V8");
229 /* 8981: set V9 = V9 OR V8 */
232 c
.registers
[9] = 0x32;
233 c
.registers
[8] = 0x58;
235 putopcode(0x8981, &c
, c
.pc
);
237 TEST(c
.registers
[9] == 0x7a, "8981: set V9 = V9 OR V8");
240 /* 8982: set V9 = V9 AND V8 */
243 c
.registers
[9] = 0x32;
244 c
.registers
[8] = 0x58;
246 putopcode(0x8982, &c
, c
.pc
);
248 TEST(c
.registers
[9] == 0x10, "8982: set V9 = V9 AND V8");
251 /* 8983: set V9 = V9 XOR V8 */
254 c
.registers
[9] = 0x32;
255 c
.registers
[8] = 0x58;
257 putopcode(0x8983, &c
, c
.pc
);
259 TEST(c
.registers
[9] == 0x6a, "8983: set V9 = V9 XOR V8");
262 /* 8984: set V9 = V9 + V8, VF = carry */
265 c
.registers
[9] = 0x32;
266 c
.registers
[8] = 0xFA;
268 putopcode(0x8984, &c
, c
.pc
);
270 TEST(c
.registers
[9] == 0x2c && c
.registers
[0xF] == 1,
271 "8984: set V9 = V9 + V8, VF = carry");
274 /* 8985: set V9 = V9 - V8, VF = not borrow */
277 c
.registers
[9] = 0xFA;
278 c
.registers
[8] = 0x32;
280 putopcode(0x8985, &c
, c
.pc
);
282 TEST(c
.registers
[9] == 0xc8 && c
.registers
[0xF] == 1,
283 "8985: set V9 = V9 - V8, VF = not borrow");
286 /* 8986: set V9 = V9 SHR 1 */
289 c
.registers
[9] = 0xFA;
291 putopcode(0x8986, &c
, c
.pc
);
293 TEST(c
.registers
[9] == 0x7D && c
.registers
[0xF] == 0,
294 "8986: set V9 = V9 SHR 1");
297 /* 8987: set V9 = V8 - V9, VF = not borrow */
300 c
.registers
[9] = 0x32;
301 c
.registers
[8] = 0xFA;
303 putopcode(0x8987, &c
, c
.pc
);
305 TEST(c
.registers
[9] == 0xc8 && c
.registers
[0xF] == 1,
306 "8987: set V9 = V8 - V9, VF = not borrow");
309 /* 898E: V9 = V9 SHL 1 */
312 c
.registers
[9] = 0x32;
314 putopcode(0x898E, &c
, c
.pc
);
316 TEST(c
.registers
[9] == 0x64 && c
.registers
[0xF] == 0,
317 "898E: set V9 = V9 SHL 1");
320 /* 9120: skip next instruction if V1 != V2 */
323 c
.registers
[1] = 0x45;
324 c
.registers
[2] = 0x32;
327 putopcode(0x9120, &c
, c
.pc
);
329 TEST(c
.pc
== 4, "9120: skip next instruction, V1 != V2");
332 /* A123: set I to 0x123 */
336 putopcode(0xA123, &c
, c
.pc
);
338 TEST(c
.index
== 0x123, "A123: set I to 0x123");
341 /* B345: jump to location 0x345 + V0 */
344 c
.registers
[0] = 0x12;
347 putopcode(0xB345, &c
, c
.pc
);
349 TEST(c
.pc
== 0x357, "B345: jump to location 0x345 + 0x12 (V0)");
352 /* E69E: skip next instruction if key with the value of V6 is pressed */
356 c
.keypad
[c
.registers
[6]] = 1;
359 putopcode(0xE69E, &c
, c
.pc
);
361 TEST(c
.pc
== 4, "E69E: skip next instruction, key with value of V6"
366 * E1A1: skip next instruction if key with the value of V1 is not
372 c
.keypad
[c
.registers
[1]] = 0;
375 putopcode(0xE1A1, &c
, c
.pc
);
377 TEST(c
.pc
== 4, "E1A1: skip next instruction, "
378 "key with value of V1 is not pressed");
381 /* F507: set V5 = delay timer value */
387 putopcode(0xF507, &c
, c
.pc
);
389 TEST(c
.pc
== 2 && c
.registers
[5] == 4,
390 "F507: set V5 to delay timer value");
393 /* F20A: wait for a key press, store the value of the key in V2 */
397 (void)memset(c
.keypad
, 0, sizeof c
.keypad
);
400 putopcode(0xF20A, &c
, c
.pc
);
402 TEST(c
.pc
== 2 && c
.registers
[2] == 0,
403 "F20A: key 'x' is pressed, V2 = 0");
406 /* F515: set delay timer = V5 */
412 putopcode(0xF515, &c
, c
.pc
);
414 TEST(c
.pc
== 2 && c
.delay_timer
== 5,
415 "F515: set delay timer = V5");
418 /* F718: set sound timer = V7 */
424 putopcode(0xF718, &c
, c
.pc
);
426 TEST(c
.pc
== 2 && c
.sound_timer
== 7,
427 "F718: set sound timer = V7");
430 /* F01E: set I = I + V0 */
432 uint16_t oindex
= c
.index
;
438 putopcode(0xF01E, &c
, c
.pc
);
440 TEST(c
.pc
== 2 && c
.index
== oindex
+ 1,
441 "F01E: set I = I + V0");
444 /* F929: set I to location of sprite for digit V9 */
450 putopcode(0xF929, &c
, c
.pc
);
452 TEST(c
.pc
== 2 && c
.index
== 0x5A,
453 "F929: set I to location of sprite of '2'");
457 * F333: store BCD representation of V3 in memory at locations I, I+1
463 c
.registers
[3] = 231;
465 putopcode(0xF333, &c
, c
.pc
);
467 TEST(c
.pc
== 2 && c
.memory
[c
.index
+ 2] == 1 &&
468 c
.memory
[c
.index
+ 1] == 3 && c
.memory
[c
.index
] == 2,
469 "F333: store BCD representation of V3 in memory locations "
473 /* F255: store registers V0-V2 in memory starting from I */
483 putopcode(0xF255, &c
, c
.pc
);
487 for (uint8_t r
= 0; r
<= 2 && res
!= 0; r
++) {
488 if (c
.memory
[c
.index
+ r
] != c
.registers
[r
]) {
492 TEST(c
.pc
== 2 && res
== 1,
493 "F255: store registers V0-V2 in memory starting from "
497 /* F165: read registers V0-V1 in memory starting from I */
506 putopcode(0xF165, &c
, c
.pc
);
510 for (uint8_t r
= 0; r
<= 1 && res
!= 0; r
++) {
511 if (c
.memory
[c
.index
+ r
] != c
.registers
[r
]) {
515 TEST(c
.pc
== 2 && res
== 1,
516 "F165: read registers V0-V1 in memory starting from "
520 (void)printf("[==========] %d test cases ran.\n", test
);
521 (void)printf("[ PASSED ] %d tests.\n", count_pass
);
522 (void)printf("[ FAILED ] %d tests.\n", count_fail
);