Austin Morlan 1 year ago
parent
commit
52c86bee81
Signed by: Austin Morlan <mail@austinmorlan.com> GPG Key ID: FD6B27654AF5E348
6 changed files with 1053 additions and 0 deletions
  1. 16
    0
      CMakeLists.txt
  2. 583
    0
      Source/Chip8.cpp
  3. 155
    0
      Source/Chip8.hpp
  4. 47
    0
      Source/Main.cpp
  5. 229
    0
      Source/Platform.cpp
  6. 23
    0
      Source/Platform.hpp

+ 16
- 0
CMakeLists.txt View File

@@ -0,0 +1,16 @@
1
+cmake_minimum_required(VERSION 3.14)
2
+project(chip8)
3
+
4
+set(CMAKE_CXX_STANDARD 11)
5
+
6
+find_package(SDL2 REQUIRED)
7
+
8
+add_executable(
9
+	chip8
10
+	Source/Chip8.cpp
11
+	Source/Main.cpp
12
+	Source/Platform.cpp)
13
+
14
+target_compile_options(chip8 PRIVATE -Wall)
15
+
16
+target_link_libraries(chip8 PRIVATE SDL2::SDL2)

+ 583
- 0
Source/Chip8.cpp View File

@@ -0,0 +1,583 @@
1
+#include "Chip8.hpp"
2
+#include <chrono>
3
+#include <cstdint>
4
+#include <cstring>
5
+#include <fstream>
6
+#include <random>
7
+
8
+
9
+const unsigned int FONTSET_SIZE = 80;
10
+const unsigned int FONTSET_START_ADDRESS = 0x50;
11
+const unsigned int START_ADDRESS = 0x200;
12
+
13
+
14
+uint8_t fontset[FONTSET_SIZE] =
15
+	{
16
+		0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
17
+		0x20, 0x60, 0x20, 0x20, 0x70, // 1
18
+		0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
19
+		0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
20
+		0x90, 0x90, 0xF0, 0x10, 0x10, // 4
21
+		0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
22
+		0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
23
+		0xF0, 0x10, 0x20, 0x40, 0x40, // 7
24
+		0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
25
+		0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
26
+		0xF0, 0x90, 0xF0, 0x90, 0x90, // A
27
+		0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
28
+		0xF0, 0x80, 0x80, 0x80, 0xF0, // C
29
+		0xE0, 0x90, 0x90, 0x90, 0xE0, // D
30
+		0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
31
+		0xF0, 0x80, 0xF0, 0x80, 0x80  // F
32
+	};
33
+
34
+
35
+Chip8::Chip8()
36
+	: randGen(std::chrono::system_clock::now().time_since_epoch().count())
37
+{
38
+	// Initialize PC
39
+	pc = START_ADDRESS;
40
+
41
+	// Load fonts into memory
42
+	for (unsigned int i = 0; i < FONTSET_SIZE; ++i)
43
+	{
44
+		memory[FONTSET_START_ADDRESS + i] = fontset[i];
45
+	}
46
+
47
+	// Initialize RNG
48
+	randByte = std::uniform_int_distribution<uint8_t>(0, 255U);
49
+
50
+	// Set up function pointer table
51
+	table[0x0] = &Chip8::Table0;
52
+	table[0x1] = &Chip8::OP_1nnn;
53
+	table[0x2] = &Chip8::OP_2nnn;
54
+	table[0x3] = &Chip8::OP_3xkk;
55
+	table[0x4] = &Chip8::OP_4xkk;
56
+	table[0x5] = &Chip8::OP_5xy0;
57
+	table[0x6] = &Chip8::OP_6xkk;
58
+	table[0x7] = &Chip8::OP_7xkk;
59
+	table[0x8] = &Chip8::Table8;
60
+	table[0x9] = &Chip8::OP_9xy0;
61
+	table[0xA] = &Chip8::OP_Annn;
62
+	table[0xB] = &Chip8::OP_Bnnn;
63
+	table[0xC] = &Chip8::OP_Cxkk;
64
+	table[0xD] = &Chip8::OP_Dxyn;
65
+	table[0xE] = &Chip8::TableE;
66
+	table[0xF] = &Chip8::TableF;
67
+
68
+	table0[0x0] = &Chip8::OP_00E0;
69
+	table0[0xE] = &Chip8::OP_00EE;
70
+
71
+	table8[0x0] = &Chip8::OP_8xy0;
72
+	table8[0x1] = &Chip8::OP_8xy1;
73
+	table8[0x2] = &Chip8::OP_8xy2;
74
+	table8[0x3] = &Chip8::OP_8xy3;
75
+	table8[0x4] = &Chip8::OP_8xy4;
76
+	table8[0x5] = &Chip8::OP_8xy5;
77
+	table8[0x6] = &Chip8::OP_8xy6;
78
+	table8[0x7] = &Chip8::OP_8xy7;
79
+	table8[0xE] = &Chip8::OP_8xyE;
80
+
81
+	tableE[0x1] = &Chip8::OP_ExA1;
82
+	tableE[0xE] = &Chip8::OP_Ex9E;
83
+
84
+	tableF[0x07] = &Chip8::OP_Fx07;
85
+	tableF[0x0A] = &Chip8::OP_Fx0A;
86
+	tableF[0x15] = &Chip8::OP_Fx15;
87
+	tableF[0x18] = &Chip8::OP_Fx18;
88
+	tableF[0x1E] = &Chip8::OP_Fx1E;
89
+	tableF[0x29] = &Chip8::OP_Fx29;
90
+	tableF[0x33] = &Chip8::OP_Fx33;
91
+	tableF[0x55] = &Chip8::OP_Fx55;
92
+	tableF[0x65] = &Chip8::OP_Fx65;
93
+}
94
+
95
+void Chip8::LoadROM(char const* filename)
96
+{
97
+	std::ifstream file(filename, std::ios::binary | std::ios::ate);
98
+
99
+	if (file.is_open())
100
+	{
101
+		std::streampos size = file.tellg();
102
+		char* buffer = new char[size];
103
+		file.seekg(0, std::ios::beg);
104
+		file.read(buffer, size);
105
+		file.close();
106
+
107
+		for (long i = 0; i < size; ++i)
108
+		{
109
+			memory[START_ADDRESS + i] = buffer[i];
110
+		}
111
+
112
+		delete[] buffer;
113
+	}
114
+}
115
+
116
+void Chip8::Cycle()
117
+{
118
+	// Fetch
119
+	opcode = (memory[pc] << 8u) | memory[pc + 1];
120
+
121
+	// Increment the PC before we execute anything
122
+	pc += 2;
123
+
124
+	// Decode and Execute
125
+	((*this).*(table[(opcode & 0xF000u) >> 12u]))();
126
+
127
+	// Decrement the delay timer if it's been set
128
+	if (delayTimer > 0)
129
+	{
130
+		--delayTimer;
131
+	}
132
+
133
+	// Decrement the sound timer if it's been set
134
+	if (soundTimer > 0)
135
+	{
136
+		--soundTimer;
137
+	}
138
+}
139
+
140
+void Chip8::Table0()
141
+{
142
+	((*this).*(table0[opcode & 0x000Fu]))();
143
+}
144
+
145
+void Chip8::Table8()
146
+{
147
+	((*this).*(table8[opcode & 0x000Fu]))();
148
+}
149
+
150
+void Chip8::TableE()
151
+{
152
+	((*this).*(tableE[opcode & 0x000Fu]))();
153
+}
154
+
155
+void Chip8::TableF()
156
+{
157
+	((*this).*(tableF[opcode & 0x00FFu]))();
158
+}
159
+
160
+void Chip8::OP_NULL()
161
+{}
162
+
163
+void Chip8::OP_00E0()
164
+{
165
+	memset(video, 0, VIDEO_HEIGHT * VIDEO_WIDTH);
166
+}
167
+
168
+void Chip8::OP_00EE()
169
+{
170
+	--sp;
171
+	pc = stack[sp];
172
+}
173
+
174
+void Chip8::OP_1nnn()
175
+{
176
+	uint16_t address = opcode & 0x0FFFu;
177
+
178
+	pc = address;
179
+}
180
+
181
+void Chip8::OP_2nnn()
182
+{
183
+	uint16_t address = opcode & 0x0FFFu;
184
+
185
+	stack[sp] = pc;
186
+	++sp;
187
+	pc = address;
188
+}
189
+
190
+void Chip8::OP_3xkk()
191
+{
192
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
193
+	uint8_t byte = opcode & 0x00FFu;
194
+
195
+	if (registers[Vx] == byte)
196
+	{
197
+		pc += 2;
198
+	}
199
+}
200
+
201
+void Chip8::OP_4xkk()
202
+{
203
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
204
+	uint8_t byte = opcode & 0x00FFu;
205
+
206
+	if (registers[Vx] != byte)
207
+	{
208
+		pc += 2;
209
+	}
210
+}
211
+
212
+void Chip8::OP_5xy0()
213
+{
214
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
215
+	uint8_t Vy = (opcode & 0x00F0u) >> 4u;
216
+
217
+	if (registers[Vx] == registers[Vy])
218
+	{
219
+		pc += 2;
220
+	}
221
+}
222
+
223
+void Chip8::OP_6xkk()
224
+{
225
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
226
+	uint8_t byte = opcode & 0x00FFu;
227
+
228
+	registers[Vx] = byte;
229
+}
230
+
231
+void Chip8::OP_7xkk()
232
+{
233
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
234
+	uint8_t byte = opcode & 0x00FFu;
235
+
236
+	registers[Vx] += byte;
237
+}
238
+
239
+void Chip8::OP_8xy0()
240
+{
241
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
242
+	uint8_t Vy = (opcode & 0x00F0u) >> 4u;
243
+
244
+	registers[Vx] = registers[Vy];
245
+}
246
+
247
+void Chip8::OP_8xy1()
248
+{
249
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
250
+	uint8_t Vy = (opcode & 0x00F0u) >> 4u;
251
+
252
+	registers[Vx] |= registers[Vy];
253
+}
254
+
255
+void Chip8::OP_8xy2()
256
+{
257
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
258
+	uint8_t Vy = (opcode & 0x00F0u) >> 4u;
259
+
260
+	registers[Vx] &= registers[Vy];
261
+}
262
+
263
+void Chip8::OP_8xy3()
264
+{
265
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
266
+	uint8_t Vy = (opcode & 0x00F0u) >> 4u;
267
+
268
+	registers[Vx] ^= registers[Vy];
269
+}
270
+
271
+void Chip8::OP_8xy4()
272
+{
273
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
274
+	uint8_t Vy = (opcode & 0x00F0u) >> 4u;
275
+
276
+	uint16_t sum = registers[Vx] + registers[Vy];
277
+
278
+	if (sum > 255U)
279
+	{
280
+		registers[0xF] = 1;
281
+	}
282
+	else
283
+	{
284
+		registers[0xF] = 0;
285
+	}
286
+
287
+	registers[Vx] = sum & 0xFFu;
288
+}
289
+
290
+void Chip8::OP_8xy5()
291
+{
292
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
293
+	uint8_t Vy = (opcode & 0x00F0u) >> 4u;
294
+
295
+	if (registers[Vx] > registers[Vy])
296
+	{
297
+		registers[0xF] = 1;
298
+	}
299
+	else
300
+	{
301
+		registers[0xF] = 0;
302
+	}
303
+
304
+	registers[Vx] -= registers[Vy];
305
+}
306
+
307
+void Chip8::OP_8xy6()
308
+{
309
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
310
+
311
+	// Save LSB in VF
312
+	registers[0xF] = (registers[Vx] & 0x1u);
313
+
314
+	registers[Vx] >>= 1;
315
+}
316
+
317
+void Chip8::OP_8xy7()
318
+{
319
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
320
+	uint8_t Vy = (opcode & 0x00F0u) >> 4u;
321
+
322
+	if (registers[Vy] > registers[Vx])
323
+	{
324
+		registers[0xF] = 1;
325
+	}
326
+	else
327
+	{
328
+		registers[0xF] = 0;
329
+	}
330
+
331
+	registers[Vx] = registers[Vy] - registers[Vx];
332
+}
333
+
334
+void Chip8::OP_8xyE()
335
+{
336
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
337
+
338
+	// Save MSB in VF
339
+	registers[0xF] = (registers[Vx] & 0x80u) >> 7u;
340
+
341
+	registers[Vx] <<= 1;
342
+}
343
+
344
+void Chip8::OP_9xy0()
345
+{
346
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
347
+	uint8_t Vy = (opcode & 0x00F0u) >> 4u;
348
+
349
+	if (registers[Vx] != registers[Vy])
350
+	{
351
+		pc += 2;
352
+	}
353
+}
354
+
355
+void Chip8::OP_Annn()
356
+{
357
+	uint16_t address = opcode & 0x0FFFu;
358
+
359
+	index = address;
360
+}
361
+
362
+void Chip8::OP_Bnnn()
363
+{
364
+	uint16_t address = opcode & 0x0FFFu;
365
+
366
+	pc = registers[0] + address;
367
+}
368
+
369
+void Chip8::OP_Cxkk()
370
+{
371
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
372
+	uint8_t byte = opcode & 0x00FFu;
373
+
374
+	registers[Vx] = randByte(randGen) & byte;
375
+}
376
+
377
+void Chip8::OP_Dxyn()
378
+{
379
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
380
+	uint8_t Vy = (opcode & 0x00F0u) >> 4u;
381
+	uint8_t height = opcode & 0x000Fu;
382
+
383
+	// Wrap if going beyond screen boundaries
384
+	uint8_t xPos = registers[Vx] % VIDEO_WIDTH;
385
+	uint8_t yPos = registers[Vy] % VIDEO_HEIGHT;
386
+
387
+	registers[0xF] = 0;
388
+
389
+	for (unsigned int row = 0; row < height; ++row)
390
+	{
391
+		uint8_t spriteByte = memory[index + row];
392
+
393
+		for (unsigned int col = 0; col < 8; ++col)
394
+		{
395
+			uint8_t spritePixel = spriteByte & (0x80u >> col);
396
+			uint32_t* screenPixel = &video[(yPos + row) * VIDEO_WIDTH + (xPos + col)];
397
+
398
+			// Sprite pixel is on
399
+			if (spritePixel)
400
+			{
401
+				// Screen pixel also on - collision
402
+				if (*screenPixel == 0xFFFFFFFF)
403
+				{
404
+					registers[0xF] = 1;
405
+				}
406
+
407
+				// Effectively XOR with the sprite pixel
408
+				*screenPixel ^= 0xFFFFFFFF;
409
+			}
410
+		}
411
+	}
412
+}
413
+
414
+void Chip8::OP_Ex9E()
415
+{
416
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
417
+
418
+	uint8_t key = registers[Vx];
419
+
420
+	if (keypad[key])
421
+	{
422
+		pc += 2;
423
+	}
424
+}
425
+
426
+void Chip8::OP_ExA1()
427
+{
428
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
429
+
430
+	uint8_t key = registers[Vx];
431
+
432
+	if (!keypad[key])
433
+	{
434
+		pc += 2;
435
+	}
436
+}
437
+
438
+void Chip8::OP_Fx07()
439
+{
440
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
441
+
442
+	registers[Vx] = delayTimer;
443
+}
444
+
445
+void Chip8::OP_Fx0A()
446
+{
447
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
448
+
449
+	if (keypad[0])
450
+	{
451
+		registers[Vx] = 0;
452
+	}
453
+	else if (keypad[1])
454
+	{
455
+		registers[Vx] = 1;
456
+	}
457
+	else if (keypad[2])
458
+	{
459
+		registers[Vx] = 2;
460
+	}
461
+	else if (keypad[3])
462
+	{
463
+		registers[Vx] = 3;
464
+	}
465
+	else if (keypad[4])
466
+	{
467
+		registers[Vx] = 4;
468
+	}
469
+	else if (keypad[5])
470
+	{
471
+		registers[Vx] = 5;
472
+	}
473
+	else if (keypad[6])
474
+	{
475
+		registers[Vx] = 6;
476
+	}
477
+	else if (keypad[7])
478
+	{
479
+		registers[Vx] = 7;
480
+	}
481
+	else if (keypad[8])
482
+	{
483
+		registers[Vx] = 8;
484
+	}
485
+	else if (keypad[9])
486
+	{
487
+		registers[Vx] = 9;
488
+	}
489
+	else if (keypad[10])
490
+	{
491
+		registers[Vx] = 10;
492
+	}
493
+	else if (keypad[11])
494
+	{
495
+		registers[Vx] = 11;
496
+	}
497
+	else if (keypad[12])
498
+	{
499
+		registers[Vx] = 12;
500
+	}
501
+	else if (keypad[13])
502
+	{
503
+		registers[Vx] = 13;
504
+	}
505
+	else if (keypad[14])
506
+	{
507
+		registers[Vx] = 14;
508
+	}
509
+	else if (keypad[15])
510
+	{
511
+		registers[Vx] = 15;
512
+	}
513
+	else
514
+	{
515
+		pc -= 2;
516
+	}
517
+}
518
+
519
+void Chip8::OP_Fx15()
520
+{
521
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
522
+
523
+	delayTimer = registers[Vx];
524
+}
525
+
526
+void Chip8::OP_Fx18()
527
+{
528
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
529
+
530
+	soundTimer = registers[Vx];
531
+}
532
+
533
+void Chip8::OP_Fx1E()
534
+{
535
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
536
+
537
+	index += registers[Vx];
538
+}
539
+
540
+void Chip8::OP_Fx29()
541
+{
542
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
543
+	uint8_t digit = registers[Vx];
544
+
545
+	index = FONTSET_START_ADDRESS + (5 * digit);
546
+}
547
+
548
+void Chip8::OP_Fx33()
549
+{
550
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
551
+	uint8_t value = registers[Vx];
552
+
553
+	// Ones-place
554
+	memory[index + 2] = value % 10;
555
+	value /= 10;
556
+
557
+	// Tens-place
558
+	memory[index + 1] = value % 10;
559
+	value /= 10;
560
+
561
+	// Hundreds-place
562
+	memory[index] = value % 10;
563
+}
564
+
565
+void Chip8::OP_Fx55()
566
+{
567
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
568
+
569
+	for (uint8_t i = 0; i <= Vx; ++i)
570
+	{
571
+		memory[index + i] = registers[i];
572
+	}
573
+}
574
+
575
+void Chip8::OP_Fx65()
576
+{
577
+	uint8_t Vx = (opcode & 0x0F00u) >> 8u;
578
+
579
+	for (uint8_t i = 0; i <= Vx; ++i)
580
+	{
581
+		registers[i] = memory[index + i];
582
+	}
583
+}

+ 155
- 0
Source/Chip8.hpp View File

@@ -0,0 +1,155 @@
1
+#pragma once
2
+
3
+#include <cstdint>
4
+#include <random>
5
+
6
+
7
+const unsigned int KEY_COUNT = 16;
8
+const unsigned int MEMORY_SIZE = 4096;
9
+const unsigned int REGISTER_COUNT = 16;
10
+const unsigned int STACK_LEVELS = 16;
11
+const unsigned int VIDEO_HEIGHT = 32;
12
+const unsigned int VIDEO_WIDTH = 64;
13
+
14
+
15
+class Chip8
16
+{
17
+public:
18
+	Chip8();
19
+	void LoadROM(char const* filename);
20
+	void Cycle();
21
+
22
+	uint8_t keypad[KEY_COUNT]{};
23
+	uint32_t video[VIDEO_WIDTH * VIDEO_HEIGHT]{};
24
+
25
+private:
26
+	void Table0();
27
+	void Table8();
28
+	void TableE();
29
+	void TableF();
30
+
31
+	// Do nothing
32
+	void OP_NULL();
33
+
34
+	// CLS
35
+	void OP_00E0();
36
+
37
+	// RET
38
+	void OP_00EE();
39
+
40
+	// JP address
41
+	void OP_1nnn();
42
+
43
+	// CALL address
44
+	void OP_2nnn();
45
+
46
+	// SE Vx, byte
47
+	void OP_3xkk();
48
+
49
+	// SNE Vx, byte
50
+	void OP_4xkk();
51
+
52
+	// SE Vx, Vy
53
+	void OP_5xy0();
54
+
55
+	// LD Vx, byte
56
+	void OP_6xkk();
57
+
58
+	// ADD Vx, byte
59
+	void OP_7xkk();
60
+
61
+	// LD Vx, Vy
62
+	void OP_8xy0();
63
+
64
+	// OR Vx, Vy
65
+	void OP_8xy1();
66
+
67
+	// AND Vx, Vy
68
+	void OP_8xy2();
69
+
70
+	// XOR Vx, Vy
71
+	void OP_8xy3();
72
+
73
+	// ADD Vx, Vy
74
+	void OP_8xy4();
75
+
76
+	// SUB Vx, Vy
77
+	void OP_8xy5();
78
+
79
+	// SHR Vx
80
+	void OP_8xy6();
81
+
82
+	// SUBN Vx, Vy
83
+	void OP_8xy7();
84
+
85
+	// SHL Vx
86
+	void OP_8xyE();
87
+
88
+	// SNE Vx, Vy
89
+	void OP_9xy0();
90
+
91
+	// LD I, address
92
+	void OP_Annn();
93
+
94
+	// JP V0, address
95
+	void OP_Bnnn();
96
+
97
+	// RND Vx, byte
98
+	void OP_Cxkk();
99
+
100
+	// DRW Vx, Vy, height
101
+	void OP_Dxyn();
102
+
103
+	// SKP Vx
104
+	void OP_Ex9E();
105
+
106
+	// SKNP Vx
107
+	void OP_ExA1();
108
+
109
+	// LD Vx, DT
110
+	void OP_Fx07();
111
+
112
+	// LD Vx, K
113
+	void OP_Fx0A();
114
+
115
+	// LD DT, Vx
116
+	void OP_Fx15();
117
+
118
+	// LD ST, Vx
119
+	void OP_Fx18();
120
+
121
+	// ADD I, Vx
122
+	void OP_Fx1E();
123
+
124
+	// LD F, Vx
125
+	void OP_Fx29();
126
+
127
+	// LD B, Vx
128
+	void OP_Fx33();
129
+
130
+	// LD [I], Vx
131
+	void OP_Fx55();
132
+
133
+	// LD Vx, [I]
134
+	void OP_Fx65();
135
+
136
+	uint8_t memory[MEMORY_SIZE]{};
137
+	uint8_t registers[REGISTER_COUNT]{};
138
+	uint16_t index{};
139
+	uint16_t pc{};
140
+	uint8_t delayTimer{};
141
+	uint8_t soundTimer{};
142
+	uint16_t stack[STACK_LEVELS]{};
143
+	uint8_t sp{};
144
+	uint16_t opcode{};
145
+
146
+	std::default_random_engine randGen;
147
+	std::uniform_int_distribution<uint8_t> randByte;
148
+
149
+	typedef void (Chip8::*Chip8Func)();
150
+	Chip8Func table[0xF + 1]{&Chip8::OP_NULL};
151
+	Chip8Func table0[0xE + 1]{&Chip8::OP_NULL};
152
+	Chip8Func table8[0xE + 1]{&Chip8::OP_NULL};
153
+	Chip8Func tableE[0xE + 1]{&Chip8::OP_NULL};
154
+	Chip8Func tableF[0x65 + 1]{&Chip8::OP_NULL};
155
+};

+ 47
- 0
Source/Main.cpp View File

@@ -0,0 +1,47 @@
1
+#include "Chip8.hpp"
2
+#include "Platform.hpp"
3
+#include <chrono>
4
+#include <iostream>
5
+
6
+
7
+int main(int argc, char** argv)
8
+{
9
+	if (argc != 4)
10
+	{
11
+		std::cerr << "Usage: " << argv[0] << " <Scale> <Delay> <ROM>\n";
12
+		std::exit(EXIT_FAILURE);
13
+	}
14
+
15
+	int videoScale = std::stoi(argv[1]);
16
+	int cycleDelay = std::stoi(argv[2]);
17
+	char const* romFilename = argv[3];
18
+
19
+	Platform platform("CHIP-8 Emulator", VIDEO_WIDTH * videoScale, VIDEO_HEIGHT * videoScale, VIDEO_WIDTH, VIDEO_HEIGHT);
20
+
21
+	Chip8 chip8;
22
+	chip8.LoadROM(romFilename);
23
+
24
+	int videoPitch = sizeof(chip8.video[0]) * VIDEO_WIDTH;
25
+
26
+	auto lastCycleTime = std::chrono::high_resolution_clock::now();
27
+	bool quit = false;
28
+
29
+	while (!quit)
30
+	{
31
+		quit = platform.ProcessInput(chip8.keypad);
32
+
33
+		auto currentTime = std::chrono::high_resolution_clock::now();
34
+		float dt = std::chrono::duration<float, std::chrono::milliseconds::period>(currentTime - lastCycleTime).count();
35
+
36
+		if (dt > cycleDelay)
37
+		{
38
+			lastCycleTime = currentTime;
39
+
40
+			chip8.Cycle();
41
+
42
+			platform.Update(chip8.video, videoPitch);
43
+		}
44
+	}
45
+
46
+	return 0;
47
+}

+ 229
- 0
Source/Platform.cpp View File

@@ -0,0 +1,229 @@
1
+#include "Platform.hpp"
2
+#include <SDL2/SDL.h>
3
+
4
+
5
+Platform::Platform(char const* title, int windowWidth, int windowHeight, int textureWidth, int textureHeight)
6
+{
7
+	SDL_Init(SDL_INIT_VIDEO);
8
+
9
+	window = SDL_CreateWindow(title, 0, 0, windowWidth, windowHeight, SDL_WINDOW_SHOWN);
10
+
11
+	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
12
+
13
+	texture = SDL_CreateTexture(
14
+		renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, textureWidth, textureHeight);
15
+}
16
+
17
+Platform::~Platform()
18
+{
19
+	SDL_DestroyTexture(texture);
20
+	SDL_DestroyRenderer(renderer);
21
+	SDL_DestroyWindow(window);
22
+	SDL_Quit();
23
+}
24
+
25
+void Platform::Update(void const* buffer, int pitch)
26
+{
27
+	SDL_UpdateTexture(texture, nullptr, buffer, pitch);
28
+	SDL_RenderClear(renderer);
29
+	SDL_RenderCopy(renderer, texture, nullptr, nullptr);
30
+	SDL_RenderPresent(renderer);
31
+}
32
+
33
+bool Platform::ProcessInput(uint8_t* keys)
34
+{
35
+	bool quit = false;
36
+
37
+	SDL_Event event;
38
+
39
+	while (SDL_PollEvent(&event))
40
+	{
41
+		switch (event.type)
42
+		{
43
+			case SDL_QUIT:
44
+			{
45
+				quit = true;
46
+			} break;
47
+
48
+			case SDL_KEYDOWN:
49
+			{
50
+				switch (event.key.keysym.sym)
51
+				{
52
+					case SDLK_ESCAPE:
53
+					{
54
+						quit = true;
55
+					} break;
56
+
57
+					case SDLK_x:
58
+					{
59
+						keys[0] = 1;
60
+					} break;
61
+
62
+					case SDLK_1:
63
+					{
64
+						keys[1] = 1;
65
+					} break;
66
+
67
+					case SDLK_2:
68
+					{
69
+						keys[2] = 1;
70
+					} break;
71
+
72
+					case SDLK_3:
73
+					{
74
+						keys[3] = 1;
75
+					} break;
76
+
77
+					case SDLK_q:
78
+					{
79
+						keys[4] = 1;
80
+					} break;
81
+
82
+					case SDLK_w:
83
+					{
84
+						keys[5] = 1;
85
+					} break;
86
+
87
+					case SDLK_e:
88
+					{
89
+						keys[6] = 1;
90
+					} break;
91
+
92
+					case SDLK_a:
93
+					{
94
+						keys[7] = 1;
95
+					} break;
96
+
97
+					case SDLK_s:
98
+					{
99
+						keys[8] = 1;
100
+					} break;
101
+
102
+					case SDLK_d:
103
+					{
104
+						keys[9] = 1;
105
+					} break;
106
+
107
+					case SDLK_z:
108
+					{
109
+						keys[0xA] = 1;
110
+					} break;
111
+
112
+					case SDLK_c:
113
+					{
114
+						keys[0xB] = 1;
115
+					} break;
116
+
117
+					case SDLK_4:
118
+					{
119
+						keys[0xC] = 1;
120
+					} break;
121
+
122
+					case SDLK_r:
123
+					{
124
+						keys[0xD] = 1;
125
+					} break;
126
+
127
+					case SDLK_f:
128
+					{
129
+						keys[0xE] = 1;
130
+					} break;
131
+
132
+					case SDLK_v:
133
+					{
134
+						keys[0xF] = 1;
135
+					} break;
136
+				}
137
+			} break;
138
+
139
+			case SDL_KEYUP:
140
+			{
141
+				switch (event.key.keysym.sym)
142
+				{
143
+					case SDLK_x:
144
+					{
145
+						keys[0] = 0;
146
+					} break;
147
+
148
+					case SDLK_1:
149
+					{
150
+						keys[1] = 0;
151
+					} break;
152
+
153
+					case SDLK_2:
154
+					{
155
+						keys[2] = 0;
156
+					} break;
157
+
158
+					case SDLK_3:
159
+					{
160
+						keys[3] = 0;
161
+					} break;
162
+
163
+					case SDLK_q:
164
+					{
165
+						keys[4] = 0;
166
+					} break;
167
+
168
+					case SDLK_w:
169
+					{
170
+						keys[5] = 0;
171
+					} break;
172
+
173
+					case SDLK_e:
174
+					{
175
+						keys[6] = 0;
176
+					} break;
177
+
178
+					case SDLK_a:
179
+					{
180
+						keys[7] = 0;
181
+					} break;
182
+
183
+					case SDLK_s:
184
+					{
185
+						keys[8] = 0;
186
+					} break;
187
+
188
+					case SDLK_d:
189
+					{
190
+						keys[9] = 0;
191
+					} break;
192
+
193
+					case SDLK_z:
194
+					{
195
+						keys[0xA] = 0;
196
+					} break;
197
+
198
+					case SDLK_c:
199
+					{
200
+						keys[0xB] = 0;
201
+					} break;
202
+
203
+					case SDLK_4:
204
+					{
205
+						keys[0xC] = 0;
206
+					} break;
207
+
208
+					case SDLK_r:
209
+					{
210
+						keys[0xD] = 0;
211
+					} break;
212
+
213
+					case SDLK_f:
214
+					{
215
+						keys[0xE] = 0;
216
+					} break;
217
+
218
+					case SDLK_v:
219
+					{
220
+						keys[0xF] = 0;
221
+					} break;
222
+				}
223
+			} break;
224
+		}
225
+	}
226
+
227
+	return quit;
228
+}
229
+

+ 23
- 0
Source/Platform.hpp View File

@@ -0,0 +1,23 @@
1
+#pragma once
2
+
3
+#include <cstdint>
4
+
5
+
6
+class SDL_Window;
7
+class SDL_Renderer;
8
+class SDL_Texture;
9
+
10
+
11
+class Platform
12
+{
13
+public:
14
+	Platform(char const* title, int windowWidth, int windowHeight, int textureWidth, int textureHeight);
15
+	~Platform();
16
+	void Update(void const* buffer, int pitch);
17
+	bool ProcessInput(uint8_t* keys);
18
+
19
+private:
20
+	SDL_Window* window{};
21
+	SDL_Renderer* renderer{};
22
+	SDL_Texture* texture{};
23
+};

Loading…
Cancel
Save