├── .DS_Store
├── LICENSE
├── Makefile
├── README.md
├── Screenshots
├── .DS_Store
├── Pong-Chip8 Mac.png
└── linux-screenshot.png
├── beep.wav
├── c8games
├── .DS_Store
├── 15PUZZLE
├── BLINKY
├── BLITZ
├── BRIX
├── CONNECT4
├── GUESS
├── HIDDEN
├── INVADERS
├── KALEID
├── MAZE
├── MERLIN
├── MISSILE
├── PONG
├── PONG2
├── PUZZLE
├── SYZYGY
├── TANK
├── TETRIS
├── TICTAC
├── UFO
├── VBRIX
├── VERS
├── WIPEOFF
├── breakout.ch8
├── connnect4.ch8
├── invaders.ch8
├── maze.ch8
├── pong.ch8
├── tetris.ch8
└── tictac.ch8
├── ch8
├── ch8.c
├── ch8.dSYM
└── Contents
│ ├── Info.plist
│ └── Resources
│ └── DWARF
│ └── ch8
├── cpu.c
├── cpu.h
├── dbg.h
└── opcodes.h
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/.DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Casey Williams
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CFLAGS=-Wall -g -I/System/Library/Frameworks/GLUT.framework/Headers -I/System/Library/Frameworks/SDL2_mixer.framework/Headers
2 | LDFLAGS=-framework OpenGL -framework GLUT -framework SDL2_mixer
3 |
4 |
5 | build: ch8
6 |
7 | ch8: ch8.c cpu.c
8 |
9 | clean:
10 | rm -f main
11 | rm -f cpu
12 | rm -f ch8
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chip-Emul8
2 |
My first little emulator of a Chip-8
3 |
4 |
5 |
The Idea
6 | So I first decided to code this Chip-8 emulator around a week ago as I've really wanted to get into emulation and as far as I was aware - writing one is the best way to learn about it. And I can definitely agree. I learnt a lot when going through this and searching the net for info, how to do certain things, using libraries etc and it has all been a great experience. The plan was that this would introduce me to the basics of emulation so I can progress to coding a Gameboy DMG-01 emulator... eventually.
7 |
8 | What I Did
9 | I started where most people probably would: how to code a chip 8 emulator search in Google. I found the multigesture post linked at the bottom and I got going. I modelled the CPU, added the emulators basic functions and it was all good. I then tweaked around with some of the modelling for example using the header so I could model the registers as uint8_t rather than unsigned char. These little things just made it a bit easier to understand and keep track of everything. After that I went on to begin decoding the opcodes using the Cowgod's technical reference linked down below (as best as I could) and then took a look at a couple of the other emulators down below as reference on some of the instructions I was unsure about. When I finished writing them for the first time and had fixed the small syntax errors etc. I ran the program and it was fine! I couldn't believe that it actually worked (wasn't what I was expecting but then again there are only 36 instructions). I went on to looking at how I was going to draw the graphics. From what I had looked up I was either going to use SDL2 or GLUT. After playing round with both I decided to go with GLUT - primarily because it ships with OSX but also because it just seemed a bit easier to setup and initialise. To draw the graphics on screen was fairly simple as the Chip-8 has monochrome graphics and a 64 x 32 pixel display. Just iterate through my screen array and draw the pixels. On first run the picture was a mess. So I spent around an hour going back through my opcodes optimising some and correcting others and now that is all done so here we have the Chip-Emul8.
10 |
11 | Plans
12 | The only thing I have planned for now is to implement sounds, that's it.
13 |
14 |
15 | Screenshots
16 | Mac
17 |
18 |
19 | Linux
20 |
21 |
22 | How To Use It
23 | Currently I've only tested this on Mac and I believe you have to download GLUT on other OSs. First off, download the repo and open it up. Navigate to the directory and run
24 | Mac
25 | FYI: If you have updated to sierra, some of the GLUT library has been fully deprecated and so will no longer compile.
26 | ```make build```
27 | After that, to use the emulator type
28 | ```./ch8 ./```
29 | Controls... I'm not exactly sure as to the controls for all the games but the keys will be one of: 1, 2, 3, 4, q, w, e, r, t, y, a, s, d, f, z, x, c, v. To quit the emulator press esc.
30 |
31 | Windows
32 | To be done..
33 |
34 | Linux
35 | Exact same as Mac.
36 |
37 | Feel free to use the code so long as you give credit to me.
38 |
39 | Here are some useful links:
40 |
41 | About the CHIP-8:
42 | https://en.wikipedia.org/wiki/CHIP-8#Virtual_machine_description
43 |
44 | Chip-8 Opcodes:
45 | http://devernay.free.fr/hacks/chip8/C8TECH10.HTM
46 |
47 | Writing an emulator:
48 | http://www.multigesture.net/articles/how-to-write-an-emulator-chip-8-interpreter/
49 | http://mattmik.com/files/chip8/mastering/chip8.html
50 | http://emulator101.com/
51 | http://fms.komkon.org/EMUL8/HOWTO.html
52 |
53 | ROMS:
54 | http://www.zophar.net/
55 |
56 | Reading in files as byte arrays:
57 | http://www.linuxquestions.org/questions/programming-9/c-howto-read-binary-file-into-buffer-172985/
58 |
59 | Some other good emulators:
60 | https://github.com/eshyong/Chip-8-Emulator
61 | https://github.com/bryangarza/chip8
62 | https://github.com/prophittcorey/iC8
63 |
64 |
--------------------------------------------------------------------------------
/Screenshots/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/Screenshots/.DS_Store
--------------------------------------------------------------------------------
/Screenshots/Pong-Chip8 Mac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/Screenshots/Pong-Chip8 Mac.png
--------------------------------------------------------------------------------
/Screenshots/linux-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/Screenshots/linux-screenshot.png
--------------------------------------------------------------------------------
/beep.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/beep.wav
--------------------------------------------------------------------------------
/c8games/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/.DS_Store
--------------------------------------------------------------------------------
/c8games/15PUZZLE:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/15PUZZLE
--------------------------------------------------------------------------------
/c8games/BLINKY:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/BLINKY
--------------------------------------------------------------------------------
/c8games/BLITZ:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/BLITZ
--------------------------------------------------------------------------------
/c8games/BRIX:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/BRIX
--------------------------------------------------------------------------------
/c8games/CONNECT4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/CONNECT4
--------------------------------------------------------------------------------
/c8games/GUESS:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/GUESS
--------------------------------------------------------------------------------
/c8games/HIDDEN:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/HIDDEN
--------------------------------------------------------------------------------
/c8games/INVADERS:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/INVADERS
--------------------------------------------------------------------------------
/c8games/KALEID:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/KALEID
--------------------------------------------------------------------------------
/c8games/MAZE:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/MAZE
--------------------------------------------------------------------------------
/c8games/MERLIN:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/MERLIN
--------------------------------------------------------------------------------
/c8games/MISSILE:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/MISSILE
--------------------------------------------------------------------------------
/c8games/PONG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/PONG
--------------------------------------------------------------------------------
/c8games/PONG2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/PONG2
--------------------------------------------------------------------------------
/c8games/PUZZLE:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/PUZZLE
--------------------------------------------------------------------------------
/c8games/SYZYGY:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/SYZYGY
--------------------------------------------------------------------------------
/c8games/TANK:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/TANK
--------------------------------------------------------------------------------
/c8games/TETRIS:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/TETRIS
--------------------------------------------------------------------------------
/c8games/TICTAC:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/TICTAC
--------------------------------------------------------------------------------
/c8games/UFO:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/UFO
--------------------------------------------------------------------------------
/c8games/VBRIX:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/VBRIX
--------------------------------------------------------------------------------
/c8games/VERS:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/VERS
--------------------------------------------------------------------------------
/c8games/WIPEOFF:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/WIPEOFF
--------------------------------------------------------------------------------
/c8games/breakout.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/breakout.ch8
--------------------------------------------------------------------------------
/c8games/connnect4.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/connnect4.ch8
--------------------------------------------------------------------------------
/c8games/invaders.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/invaders.ch8
--------------------------------------------------------------------------------
/c8games/maze.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/maze.ch8
--------------------------------------------------------------------------------
/c8games/pong.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/pong.ch8
--------------------------------------------------------------------------------
/c8games/tetris.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/tetris.ch8
--------------------------------------------------------------------------------
/c8games/tictac.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/c8games/tictac.ch8
--------------------------------------------------------------------------------
/ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/ch8
--------------------------------------------------------------------------------
/ch8.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "cpu.h"
5 | #include "dbg.h"
6 |
7 |
8 |
9 | chip8 cpu;
10 |
11 | void initGL(); //initialise GL
12 |
13 | void draw_square(float x_coord, float y_coord); //draw pixel
14 |
15 | void render(); //update render
16 |
17 | void idle(); //idling function
18 |
19 | void handle_key_press(unsigned char key, int x, int y);
20 |
21 | void handle_key_release(unsigned char key, int x, int y);
22 |
23 | int main(int argc, char const *argv[])
24 | {
25 |
26 | const char * game = argv[1];
27 | if(!game) {
28 | log_info("Usage: ./ch8 ./");
29 | exit(1);
30 | }
31 |
32 | initialise_cpu(&cpu);
33 | load_rom(&cpu, game);
34 |
35 |
36 | glutInit(&argc, argv); //initialise glut
37 |
38 | //initialise display and window
39 |
40 | glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
41 | glutInitWindowPosition(100, 100);
42 | glutInitWindowSize(640, 320);
43 | glutCreateWindow("Chip-Emul8");
44 |
45 | initGL();
46 |
47 | //handle key presses and releases
48 | glutKeyboardFunc(handle_key_press);
49 | glutKeyboardUpFunc(handle_key_release);
50 |
51 | //draw function
52 | glutDisplayFunc(render);
53 |
54 | //idle function (to redraw)
55 | glutIdleFunc(idle);
56 |
57 | glutMainLoop();
58 |
59 | return 0;
60 | }
61 |
62 | void handle_key_press(unsigned char key, int x, int y) {
63 | unsigned char input;
64 | switch(key) {
65 | case 27:
66 | log_info("Exiting emulator...");
67 | exit(0);
68 | case '1':
69 | // only certain key values are passed to the chip
70 | input = 0x0001;
71 | break;
72 | case '2':
73 | input = 0x0002;
74 | break;
75 | case '3':
76 | input = 0x0003;
77 | break;
78 | case '4':
79 | input = 0x000C;
80 | break;
81 | case 'q':
82 | input = 0x0004;
83 | break;
84 | case 'w':
85 | input = 0x0005;
86 | break;
87 | case 'e':
88 | input = 0x0006;
89 | break;
90 | case 'r':
91 | input = 0x000D;
92 | break;
93 | case 'a':
94 | input = 0x0007;
95 | break;
96 | case 's':
97 | input = 0x0008;
98 | break;
99 | case 'd':
100 | input = 0x0009;
101 | break;
102 | case 'f':
103 | input = 0x000E;
104 | break;
105 | case 'z':
106 | input = 0x000A;
107 | break;
108 | case 'x':
109 | input = 0x0000;
110 | break;
111 | case 'c':
112 | input = 0x000B;
113 | break;
114 | case 'v':
115 | input = 0x000F;
116 | break;
117 | default:
118 | break;
119 | }
120 | handle_input(&cpu, input);
121 |
122 |
123 | }
124 |
125 | void handle_key_release(unsigned char key, int x, int y) {
126 | handle_input(&cpu, 0x00FF);
127 | }
128 |
129 | void draw_square(float x_coord, float y_coord) {
130 | glBegin(GL_QUADS);
131 | glColor3f(1.0f, 1.0f, 1.0f);
132 | glVertex2f(x_coord, y_coord);
133 | glVertex2f(x_coord + 10, y_coord);
134 | glVertex2f(x_coord + 10, y_coord + 10);
135 | glVertex2f(x_coord, y_coord + 10);
136 | glEnd();
137 | }
138 |
139 | void render() {
140 | glClear(GL_COLOR_BUFFER_BIT); //clears screen
141 |
142 | glMatrixMode(GL_MODELVIEW);
143 |
144 | glLoadIdentity();
145 |
146 | emulate_cycle(&cpu);
147 |
148 | int i, j;
149 | for(i = 0; i < 640; i++) {
150 | for(j = 0; j < 320; j++) {
151 | if(cpu.gfx[i][j] == 1) {
152 | draw_square((float)(i * 10), (float) (j * 10));
153 | }
154 | }
155 | }
156 |
157 | glutSwapBuffers(); //smooth animation
158 | }
159 |
160 | void idle() {
161 | glutPostRedisplay(); //calls to redraw screen
162 | }
163 |
164 | void initGL() {
165 | // sets up GLUT window for 2D drawing
166 | glMatrixMode(GL_PROJECTION);
167 | glLoadIdentity();
168 | gluOrtho2D(0.0, 640, 320, 0.0);
169 |
170 | glMatrixMode(GL_MODELVIEW);
171 | glLoadIdentity();
172 |
173 | // clears screen color
174 | glClearColor(0.f, 0.f, 0.f, 1.f);
175 | }
176 |
177 |
178 |
--------------------------------------------------------------------------------
/ch8.dSYM/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | English
7 | CFBundleIdentifier
8 | com.apple.xcode.dsym.ch8
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundlePackageType
12 | dSYM
13 | CFBundleSignature
14 | ????
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleVersion
18 | 1
19 |
20 |
21 |
--------------------------------------------------------------------------------
/ch8.dSYM/Contents/Resources/DWARF/ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shnupta/Chip-Emul8/71a8f562ebb3dd1040924d53447720f3ee69c5c6/ch8.dSYM/Contents/Resources/DWARF/ch8
--------------------------------------------------------------------------------
/cpu.c:
--------------------------------------------------------------------------------
1 | #include "dbg.h"
2 | #include "cpu.h"
3 | #include "opcodes.h"
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | Mix_Music * beep_sound = NULL;
10 | Mix_Chunk * beep_scratch = NULL;
11 |
12 | unsigned char font_set[80] =
13 | {
14 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
15 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
16 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
17 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
18 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
19 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
20 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
21 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
22 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
23 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
24 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
25 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
26 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
27 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
28 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
29 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
30 | };
31 |
32 |
33 | void initialise_cpu(chip8 * cpu) {
34 | memset(cpu->memory, 0, 4096); //reset memory
35 | memset(cpu->V, 0, 16); //reset registers
36 | memset(cpu->stack, 0, 16); //reset stack
37 | memset(cpu->keys, 0, 16); //reset keys
38 | //defaults
39 | cpu->I = 0;
40 | cpu->pc = 0x200;
41 | cpu->sp = 0;
42 | cpu->delay_timer = 0;
43 | cpu->sound_timer = 0;
44 | cpu->opcode = 0;
45 |
46 | // load fonts
47 | for (int i = 0; i < 80; i++) {
48 | cpu->memory[i] = font_set[i];
49 | }
50 |
51 | clear_screen(cpu);
52 |
53 | srand(time(NULL)); //reset random seed
54 |
55 |
56 |
57 | if(Mix_OpenAudio(441000, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
58 | log_err("SDL_mixer error.");
59 | }
60 |
61 |
62 | beep_sound = Mix_LoadMUS("beep.wav");
63 |
64 | if(beep_sound == NULL) {
65 | log_err("Loading of sound failed.");
66 | }
67 |
68 |
69 |
70 | }
71 |
72 |
73 | bool load_rom(chip8 * cpu, const char *rom_name) {
74 | FILE * file = fopen(rom_name, "rb"); //open file in binary mode
75 | if(!file) {
76 | log_err("File does not exist.");
77 | return false;
78 | }
79 |
80 | fseek(file, 0, SEEK_END);
81 | unsigned long buffer_size = ftell(file);
82 | rewind(file);
83 |
84 | log_info("Read %lu bytes from %s", buffer_size, rom_name);
85 |
86 | char *buffer = (char *) malloc((buffer_size + 1) * sizeof(char)); //allocate memory for buffer
87 | fread(buffer, buffer_size, 1, file);
88 |
89 | for(int i = 0; i < buffer_size; i++) {
90 | cpu->memory[512 + i] = buffer[i];
91 | }
92 |
93 | return true;
94 |
95 | }
96 |
97 |
98 | void emulate_cycle(chip8 * cpu) {
99 |
100 | update_timers(cpu);
101 | //fetch opcode
102 | cpu->opcode = cpu->memory[cpu->pc] << 8 | cpu->memory[cpu->pc + 1];
103 |
104 | decode_opcode(cpu);
105 |
106 |
107 | }
108 |
109 | void clear_screen(chip8 * cpu) {
110 | memset(cpu->gfx, 0, 64 * 32);
111 |
112 | cpu->draw_flag = true;
113 |
114 | }
115 |
116 | void update_timers(chip8 * cpu) {
117 | if(cpu->delay_timer > 0) {
118 | --cpu->delay_timer;
119 | }
120 |
121 | if(cpu->sound_timer > 0) {
122 | if(cpu->sound_timer == 1) {
123 | printf("BEEP\n");
124 | Mix_PlayMusic(beep_sound, -1);
125 | --cpu->sound_timer;
126 | }
127 | }
128 | }
129 |
130 | void handle_input(chip8 * cpu, unsigned char key) {
131 | cpu->key = key;
132 | }
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/cpu.h:
--------------------------------------------------------------------------------
1 | #ifndef _CPU_H
2 | #define _CPU_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | typedef struct {
9 | uint16_t opcode; //an opcode is two bytes
10 |
11 | uint8_t memory[4096]; //has 4K memory
12 |
13 | uint8_t V[16]; //general purpose regsters, VF doubles as a a carry flag
14 |
15 | uint16_t I; //index register
16 | uint16_t pc; //program counter
17 |
18 | uint8_t gfx[64][32]; //pixels of screen
19 |
20 | uint8_t delay_timer;
21 | uint8_t sound_timer;
22 |
23 | uint16_t stack[16];
24 | uint16_t sp;
25 |
26 | uint8_t keys[16]; //hex keypad
27 |
28 | unsigned char key;
29 |
30 | bool draw_flag;
31 |
32 | } chip8;
33 |
34 | //0x000-0x1FF - Chip 8 interpreter (contains font set in emu)
35 | //0x050-0x0A0 - Used for the built in 4x5 pixel font set (0-F)
36 | //0x200-0xFFF - Program ROM and work RAM
37 |
38 |
39 | void initialise_cpu(chip8 * cpu);
40 | bool load_rom(chip8 * cpu, const char *name);
41 | void emulate_cycle(chip8 * cpu);
42 | void clear_screen(chip8 *cpu);
43 | void update_timers(chip8 * cpu);
44 |
45 | #endif
--------------------------------------------------------------------------------
/dbg.h:
--------------------------------------------------------------------------------
1 | #ifndef __dbg_h__
2 | #define __dbg_h__
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #ifndef NDEBUG
9 | #define debug(M, ...)
10 | #else
11 | #define debug(M, ...) fprintf(stderr, "DEBUG function:%s %s: %d: " M "\n", __FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__)
12 | #endif
13 |
14 | #define clean_errno() (errno == 0 ? "None" : strerror(errno))
15 |
16 | #define log_err(M, ...) fprintf(stderr, "[ERROR] (function:%s %s:%d: errno: %s) " M "\n", __FUNCTION__, __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
17 |
18 | #define log_warn(M, ...) fprintf(stderr, "[WARN] (function:%s %s:%d: errno: %s) " M "\n", __FUNCTION__, __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
19 |
20 | #define log_info(M, ...) fprintf(stderr, "[INFO] (function:%s %s:%d) " M "\n", __FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__)
21 |
22 | #define check(A, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }
23 |
24 | #define sentinel(M, ...) {log_err(M, ##__VA_ARGS__); errno=0; goto error; }
25 |
26 | #define check_mem(A) check((A), "Out of memory.")
27 |
28 | #define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__); errno=0; goto error; }
29 |
30 | #endif
--------------------------------------------------------------------------------
/opcodes.h:
--------------------------------------------------------------------------------
1 | #ifndef _opcodes_h
2 | #define _opcodes_h
3 | #include
4 | #include
5 |
6 | //instructions site: http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#00E0
7 |
8 | #define X(x) ((x & 0x0F00) >> 8)
9 | #define Y(x) ((x & 0x00F0) >> 4)
10 | #define N(x) (x & 0x000F)
11 | #define KK(x) (x & 0x00FF)
12 | #define NNN(x) (x & 0x0FFF)
13 |
14 |
15 | void decode_opcode(chip8 * cpu);
16 |
17 | void decode_opcode(chip8 * cpu) {
18 | switch(cpu->opcode & 0xF000) {
19 | case 0x0000: //0nnn
20 | switch(cpu->opcode) {
21 | case 0x00E0: //00E0: CLS - clear the display
22 | clear_screen(cpu);
23 | cpu->pc += 2;
24 | break;
25 |
26 | case 0x00EE: //00EE: RET - return from a subroutine, sets PC = stack[sp] then sp--
27 | cpu->sp -= 1;
28 | cpu->pc = cpu->stack[cpu->sp];
29 | cpu->pc += 2;
30 |
31 |
32 | break;
33 |
34 | default:
35 | log_err("Unknown opcode: %X", cpu->opcode);
36 | cpu->pc += 2;
37 | break;
38 |
39 | }
40 | break;
41 |
42 | case 0x1000: //1nnn: JP addr - jump to location nnn
43 | cpu->pc = NNN(cpu->opcode);
44 | break;
45 |
46 | case 0x2000: //2nnn: CALL addr - call subroutine at nnn
47 | //add error checking to prevent stck overflow
48 |
49 |
50 | cpu->stack[cpu->sp] = cpu->pc;
51 | cpu->sp += 1;
52 | cpu->pc = NNN(cpu->opcode);
53 | break;
54 |
55 | case 0x3000: //3xkk: SE Vx, byte - skip next instruction if Vx == kk
56 | cpu->pc += cpu->V[X(cpu->opcode)] == KK(cpu->opcode) ? 4 : 2; //Ternary truth test, read X ? Y : Z as "if X then Y else Z".
57 | break;
58 |
59 | case 0x4000: //4XKK: SNE Vx, byte - skip next instruction if Vx != kk
60 | cpu->pc += cpu->V[X(cpu->opcode)] != KK(cpu->opcode) ? 4 : 2;
61 | break;
62 |
63 | case 0x5000: //5XY0: SE Vx, Vy - skips next instrution if Vx == Vy
64 | cpu->pc += cpu->V[X(cpu->opcode)] == cpu->V[Y(cpu->opcode)] ? 4 : 2;
65 | break;
66 |
67 | case 0x6000: //6XKK: LD Vx, byte - set V == KK
68 | cpu->V[X(cpu->opcode)] = KK(cpu->opcode);
69 | cpu->pc += 2;
70 | break;
71 |
72 | case 0x7000: //7XKK: ADD Vx, byte - set Vx = Vx + kk
73 | cpu->V[X(cpu->opcode)] += KK(cpu->opcode);
74 | cpu->pc += 2;
75 | break;
76 |
77 | case 0x8000:
78 | switch(cpu->opcode & 0x000F) {
79 | case 0x0000: //8XY0: LD Vx, Vy - set Vx = Vy
80 | cpu->V[X(cpu->opcode)] = cpu->V[Y(cpu->opcode)];
81 | cpu->pc += 2;
82 | break;
83 |
84 | case 0x0001: //8XY1: OR Vx, Vy - set Vx = Vx Or Vy
85 | cpu->V[X(cpu->opcode)] |= cpu->V[Y(cpu->opcode)];
86 | cpu->pc += 2;
87 | break;
88 |
89 | case 0x0002: //8XY2: AND Vx, Vy - set Vx = Vx AND Vy
90 | cpu->V[X(cpu->opcode)] &= cpu->V[Y(cpu->opcode)];
91 | cpu->pc += 2;
92 | break;
93 |
94 | case 0x0003: //8XY3: XOR Vx, Vy - set Vx = Vx XOR Vy
95 | cpu->V[X(cpu->opcode)] ^= cpu->V[Y(cpu->opcode)];
96 | cpu->pc += 2;
97 | break;
98 |
99 |
100 | case 0x0004: //8XY4: ADD Vx, Vy - set Vx = Vx + Vy, set VF = carry
101 | {
102 | int vx = cpu->V[X(cpu->opcode)];
103 | int vy = cpu->V[Y(cpu->opcode)];
104 |
105 | int result = vx + vy;
106 |
107 | cpu->V[0xF] = result > 255 ? 1 : 0; //VF is doubled as a carry flag register
108 |
109 | //only the lower 8 bits are stored ad kept in Vx
110 | cpu->V[X(cpu->opcode)] = result &0xFF;
111 |
112 | cpu->pc += 2;
113 | }
114 | break;
115 |
116 | case 0x0005: //8XY5: SUB Vx, Vy - set Vx = Vx - Vy, set VF = NOT borrow
117 | {
118 | int vx = cpu->V[X(cpu->opcode)];
119 | int vy = cpu->V[Y(cpu->opcode)];
120 |
121 | cpu->V[0xF] = vx < vy ? 0 : 1;
122 |
123 | cpu->V[X(cpu->opcode)] = vx -vy;
124 |
125 | cpu->pc += 2;
126 | }
127 | break;
128 |
129 |
130 | case 0x0006: //8XY6: SHR Vx {, Vy} - Vx = Vx SHR 1.
131 | cpu->V[0xF] = cpu->V[X(cpu->opcode)] & 0x01;
132 | cpu->V[X(cpu->opcode)] >>= 1;
133 | cpu->pc += 2;
134 | break;
135 |
136 | case 0x0007: //8XY7: SUBN Vx, Vy - set Vx = Vy - Vx, set VF NOT borrow
137 | {
138 | int vx = cpu->V[X(cpu->opcode)];
139 | int vy = cpu->V[Y(cpu->opcode)];
140 |
141 | cpu->V[0xF] = vx > vy ? 0 : 1;
142 |
143 | cpu->V[X(cpu->opcode)] = vy - vx;
144 |
145 | cpu->pc += 2;
146 | }
147 | break;
148 |
149 |
150 | case 0x000E: //8XYE: SHL Vx {, Vy} - set Vx = Vx SHL 1
151 | cpu->V[0xF] = (cpu->V[X(cpu->opcode)] & 0x80) >> 7;
152 | cpu->V[X(cpu->opcode)] <<= 1;
153 | cpu->pc += 2;
154 | break;
155 |
156 | default:
157 | log_err("Unknown opcode: %X", cpu->opcode);
158 | cpu->pc += 2;
159 | }
160 | break;
161 |
162 | case 0x9000: //9XY0: SNE Vx,Vy - skip next instruction if Vx != VY
163 | cpu->pc += cpu->V[X(cpu->opcode)] != cpu->V[Y(cpu->opcode)] ? 4 : 2;
164 | break;
165 |
166 | case 0xA000: //ANNN: LD I, addr set = NNN
167 | cpu->I = NNN(cpu->opcode);
168 | cpu->pc += 2;
169 | break;
170 |
171 | case 0xB000: //BNNN: JP v0, addr - jump to location nnn + v0
172 | cpu->pc = NNN(cpu->opcode) + cpu->V[0x0];
173 | break;
174 |
175 | case 0xC000: //CXKK - RNDVx, byte - set Vx = random byte AND KK
176 | cpu->V[X(cpu->opcode)] = (rand() % 256) & KK(cpu->opcode);
177 | cpu->pc += 2;
178 | break;
179 |
180 | case 0xD000: //DXYN: DRW Vx, Vy, nibble - display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.
181 | {
182 | int height = cpu->opcode &0x000F;
183 | int x_coord = cpu->V[X(cpu->opcode)];
184 | int y_coord = cpu->V[Y(cpu->opcode)];
185 |
186 | // because the sprite is represented by hexadecimal numbers
187 | // bitwise operators are necessary to obtain each pixel
188 | int ands[8] = { 128, 64, 32, 16, 8, 4, 2, 1 };
189 |
190 | cpu->V[0xF] = 0;
191 |
192 | for(int i = 0; i < height; i++) {
193 | for(int j = 0; j < 8; j++) {
194 | if(x_coord + j == 64) {
195 | x_coord = -j;
196 | }
197 | if(y_coord + i == 32) {
198 | y_coord = -i;
199 | }
200 |
201 | if(cpu->gfx[x_coord + j][y_coord + i] == 1 && ((cpu->memory[cpu->I + i] & ands[j]) >> (8 - j - 1)) == 1) {
202 | cpu->V[0xF] = 1;
203 | }
204 |
205 | cpu->gfx[x_coord + j][y_coord + i] = cpu->gfx[x_coord + j][y_coord + i] ^ ((cpu->memory[cpu->I + i] & ands[j]) >> (8 - j - 1));
206 | }
207 | x_coord = cpu->V[X(cpu->opcode)];
208 | y_coord = cpu->V[Y(cpu->opcode)];
209 | }
210 |
211 | cpu->draw_flag = true;
212 | cpu->pc += 2;
213 | }
214 | break;
215 |
216 | case 0xE000:
217 | switch(cpu->opcode & 0x00FF) {
218 | case 0x009E: //EX9E: SKP Vx - skip next instruction if key with the value of Vx is pressed.
219 | cpu->pc += cpu->key == cpu->V[X(cpu->opcode)] ? 4 : 2;
220 | break;
221 |
222 | case 0x00A1: //EXA1: SKNP Vx - Skip next instruction if key with the value of Vx is not pressed.
223 | cpu->pc += cpu->key != cpu->V[X(cpu->opcode)] ? 4 : 2;
224 | break;
225 |
226 | default:
227 | log_err("Unknown opcode %X", cpu->opcode);
228 | exit(1);
229 | }
230 | break;
231 |
232 |
233 | case 0xF000:
234 | switch(cpu->opcode & 0x00FF) {
235 | case 0x0007: //FX07: LD Vx, DT - Set Vx = delay timer value.
236 | cpu->V[X(cpu->opcode)] = cpu->delay_timer;
237 | cpu->pc += 2;
238 | break;
239 |
240 | case 0x000A: //Ld Vx, K - Wait for a key press, store the value of the key in Vx.
241 | {
242 | if(cpu->key == 0) {
243 | return;
244 | } else {
245 | cpu->V[X(cpu->opcode)] = cpu->key;
246 | }
247 | cpu->pc += 2;
248 | }
249 | break;
250 |
251 | case 0x0015: //FX15: LD T, Vx - set delay timer = Vx
252 | cpu->delay_timer = cpu->V[X(cpu->opcode)];
253 | cpu->pc += 2;
254 | break;
255 |
256 | case 0x0018: //FX18: LD ST, Vx - set sound timer = Vx
257 | cpu->sound_timer = cpu->V[X(cpu->opcode)];
258 | cpu->pc += 2;
259 | break;
260 |
261 | case 0x001E: //FX1E: ADD I, Vx - set I = I + Vx
262 |
263 | cpu->I += cpu->V[X(cpu->opcode)];
264 | cpu->pc += 2;
265 | break;
266 |
267 | case 0x0029: //FX29: LD , Vx - set I = location of sprite for digit Vx.
268 | cpu->I = cpu->V[X(cpu->opcode)] * 5;
269 | cpu->pc += 2;
270 | break;
271 |
272 | case 0x0033: //FX33: LD B, Vx - Store BCD representation of Vx in memory locations I, I+1, and I+2.
273 | cpu->memory[cpu->I] = cpu->V[X(cpu->opcode)] / 100; //store the hundreds value of vx in memory location I
274 | cpu->memory[cpu->I + 1] = (cpu->V[X(cpu->opcode)] % 10) % 10; //store the tens value of vx in memory location I + 1
275 | cpu->memory[cpu->I + 2] = cpu->V[X(cpu->opcode)] % 10; //store the units value of vx in memory location I + 2
276 | cpu->pc += 2;
277 | break;
278 |
279 | case 0x0055: //FX55: LD [I], Vx - Store registers V0 through Vx in memory starting at location I.
280 | for (int i = 0; i < X(cpu->opcode) + 1; i++) {
281 | cpu->memory[cpu->I + i] = cpu->V[i];
282 | }
283 | cpu->I = cpu->I + X(cpu->opcode) + 1;
284 | cpu->pc += 2;
285 | break;
286 |
287 | case 0x0065: //FX6: Ld Vx, [I] - Read registers V0 through Vx from memory starting at location I.
288 | for (int i = 0; i < X(cpu->opcode) + 1; i++) {
289 | cpu->V[i] = cpu->memory[cpu->I + i];
290 | }
291 | cpu->I = cpu->I + X(cpu->opcode) + 1;
292 | cpu->pc += 2;
293 | break;
294 |
295 | default:
296 | log_err("Unknown opcode %X", cpu->opcode);
297 | exit(1);
298 |
299 |
300 | }
301 | break;
302 |
303 | default:
304 | log_err("Unknown opcode %X", cpu->opcode);
305 | exit(1);
306 |
307 | }
308 | }
309 |
310 |
311 | #endif
--------------------------------------------------------------------------------