├── .gitignore
├── README.md
├── emulator_chip8
├── Part 1 - Emulation and Bitwise
│ ├── readme.txt
│ ├── src assignment
│ │ ├── chip
│ │ │ └── Chip.java
│ │ └── emu
│ │ │ ├── ChipFrame.java
│ │ │ ├── ChipPanel.java
│ │ │ └── Main.java
│ └── src
│ │ ├── chip
│ │ └── Chip.java
│ │ └── emu
│ │ └── Main.java
├── Part 10 - Beep, Boop
│ └── src
│ │ ├── chip
│ │ ├── Audio.java
│ │ ├── Chip.java
│ │ └── ChipData.java
│ │ └── emu
│ │ ├── ChipFrame.java
│ │ ├── ChipPanel.java
│ │ └── Main.java
├── Part 11 - Final
│ └── src
│ │ ├── chip
│ │ ├── Audio.java
│ │ ├── Chip.java
│ │ └── ChipData.java
│ │ └── emu
│ │ ├── ChipFrame.java
│ │ ├── ChipPanel.java
│ │ └── Main.java
├── Part 2 - Begin
│ ├── src assignment
│ │ └── chip
│ │ │ └── ChipData.java
│ └── src
│ │ ├── chip
│ │ └── Chip.java
│ │ └── emu
│ │ ├── ChipFrame.java
│ │ ├── ChipPanel.java
│ │ └── Main.java
├── Part 3 - Drawing
│ └── src
│ │ ├── chip
│ │ ├── Chip.java
│ │ └── ChipData.java
│ │ └── emu
│ │ ├── ChipFrame.java
│ │ ├── ChipPanel.java
│ │ └── Main.java
├── Part 4 - Jumping
│ └── src
│ │ ├── chip
│ │ ├── Chip.java
│ │ └── ChipData.java
│ │ └── emu
│ │ ├── ChipFrame.java
│ │ ├── ChipPanel.java
│ │ └── Main.java
├── Part 5 - Coding the F codes
│ ├── src assignment
│ │ ├── chip
│ │ │ ├── Chip.java
│ │ │ └── ChipData.java
│ │ └── emu
│ │ │ ├── ChipFrame.java
│ │ │ ├── ChipPanel.java
│ │ │ └── Main.java
│ └── src
│ │ ├── chip
│ │ ├── Chip.java
│ │ └── ChipData.java
│ │ └── emu
│ │ ├── ChipFrame.java
│ │ ├── ChipPanel.java
│ │ └── Main.java
├── Part 6 - Opcodes
│ └── src
│ │ ├── chip
│ │ ├── Chip.java
│ │ └── ChipData.java
│ │ └── emu
│ │ ├── ChipFrame.java
│ │ ├── ChipPanel.java
│ │ └── Main.java
├── Part 8 - Debugging
│ └── src
│ │ ├── chip
│ │ ├── Chip.java
│ │ └── ChipData.java
│ │ └── emu
│ │ ├── ChipFrame.java
│ │ ├── ChipPanel.java
│ │ └── Main.java
├── Part 9 - Pong
│ └── src
│ │ ├── chip
│ │ ├── Chip.java
│ │ └── ChipData.java
│ │ └── emu
│ │ ├── ChipFrame.java
│ │ ├── ChipPanel.java
│ │ └── Main.java
├── links and data.txt
└── programs
│ ├── invaders.c8
│ ├── pong2.c8
│ └── tetris.c8
└── emulator_gameboy
├── GBCPUman.pdf
├── chip
├── Condition.java
├── Flag.java
├── Register.java
├── Z80.java
└── opcode
│ ├── IOpcode.java
│ ├── Opcode00NOP.java
│ ├── Opcode01Load.java
│ ├── Opcode02Load.java
│ ├── Opcode0FRotateRight.java
│ ├── Opcode1000Stop.java
│ └── Opcode1FRotateRight.java
├── command_map.txt
└── emu
├── ChipFrame.java
├── ChipPanel.java
└── Main.java
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | # Package Files #
4 | *.jar
5 | *.war
6 | *.ear
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Youtube-Tutorials
2 | =================
3 |
4 | All coding related to my youtube channel (youtube.com/JohnneiGaming)
--------------------------------------------------------------------------------
/emulator_chip8/Part 1 - Emulation and Bitwise/readme.txt:
--------------------------------------------------------------------------------
1 | Part 1 - Emulation and Bitwise Operations
2 | - Coverage
3 |
4 | In this part we'll start by putting together all information of the chipset into an chip class.
5 | This class will be the location in which we will emulate the chip.
6 |
7 | - Assignement for part 2
8 |
9 | Make a Frame to display the display array visually. This could be a JFrame or OpenGL (LWJGL advised) or whatever.
10 | I'll show my solution in part 2.
11 | Hint: Scale the display up to 640x320 or you'll have a really small screen.
12 |
13 | - Binary (Base 2) and Hexadecimal (Base 16)
14 |
15 | There are many ways to display an decimal number (1, 2, 3, 3.14, etc).
16 | A computer is working with "bits", which is binary.
17 | A bit will represent a "bool" value: It's either on or off
18 | All the values are made up by bits and the amount of available bits will limit the maximums.
19 | The values of binary are all powers of 2 and the lowest value bit will be on the most right side.
20 | The maximum value will be reached with all bits "on" (set to 1) and the lowest with all bits off.
21 | To get an idea of how large each type is: the smallest type we can work with is Byte (byte) which is 8-bit (0 - 255),
22 | Short (short) is 16-bit (0 - 65'535), Integer (int) is 32-bit (0 - 4'294'967'295), Long (long) is 64-bit (0 - 18'446'744'073'709'551'615).
23 | The maximum value which you can reach with x bits is: 2^x - 1 (-1 because 0 is the first value).
24 | To display a value in binary you can use this method (ex. with putting 67 into a byte)
25 | Well start with noting the values of that bit above the row
26 | 1
27 | 2 6 3 1 8 4 2 1
28 | 8 4 2 6
29 | ---------
30 | 0 0 0 0 0 0 0 0
31 | but for readability the standard is: group in four-bit (nibble)
32 | 0000 0000
33 | so well start checking our value:
34 | Does 128 fit in 67? No: We'll write down a zero: 0xxx xxxx
35 | Does 64 fit in 67?: Yes: We'll write down an one: 01xx xxxx, We'll also subtract 64 from 67: remaining with 3
36 | Does 32 fit in 3?: No: We'll write down a zero: 010x xxxx
37 | Does 16 fit in 3?: No: We'll write down a zero: 0100 xxxx
38 | Does 8 fit in 3?: No: We'll write down a zero: 0100 0xxx
39 | Does 4 fit in 3?: No: We'll write down a zero: 0100 00xx
40 | Does 2 fit in 3?: Yes: We'll write down an one: 0100 001x, We'll also subtract 2 from 3: remaining with 1
41 | Does 1 fit in 1?: Yes: We'll write down an one: 0100 0011
42 |
43 | so lets do some simple binary counting, and to keep it simple we'll use an example with nibbles (4 bit) and one with bytes.
44 | 0101 = 5 in decimal
45 | 1010 = 10 in decimal
46 | ---- +
47 | 1111 = 15 in decimal
48 | To make it easy you could translate the binary into decimal and then add up the decimals and then back into binary or you can use shifting of bits, but you'll get that after you work with bits a bit.
49 | The basis of that is 1 (1 in decimal) and 1 (1 in decimal) will become 10 (2 in decimal) and work with that on, it sounds maybe complicated but you'll get it in time.
50 |
51 | Ok so on to the hexadecimal: I can be a lot shorter about this.
52 | In hexadecimal we use 1 digit to represent 0 up to and including 15: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A (10), B (11), C (12), D (13), E (14), F (15).
53 | So with 1 digit we can represent 1 nibble (4 bit), that means that we can write down a full byte in just 2 characters.
54 | The rule to let people know you are working in hexadecimal is: add 0x infront of the number.
55 | So we'll rewrite the values of the type ranges: Byte (0x00 - 0xFF), Short (0x0000, 0xFFFF), Integer (0x00000000, 0xFFFFFFFF), Long (0x0000000000000000 - 0xFFFFFFFFFFFFFFFF).
56 | It is used to have to write down less and it is a bit more clear in bitwise operations.
57 | How to translate hexadecimal into decimal:
58 | 0xFE - (F * 16) + (E * 1) = 254
59 | 0x102 - (1 * 16 * 16) + (0 * 16) + (2 * 1) = 258
60 | etc.
61 |
62 | [Continue the video from here]
63 |
64 | - Bitwise Operations
65 |
66 | This chapter is only used as reference.
67 |
68 | Legenda: [NAME (JAVA OPERATOR)]
69 |
70 | [Left Shift (<<)]
71 | 0000 0001 (1)
72 | ---- ---- 3 (Left shift 3 positions)
73 | 0000 1000 (8)
74 |
75 | [Right Shift (>>)]
76 | 0010 0010 (34)
77 | ---- ---- 1 (Right Shift 1 position)
78 | 0001 0001 (17)
79 |
80 | [OR Operation (|)]
81 | 0010 1001 (41)
82 | 0101 1010 (90)
83 | ---- ---- OR
84 | 0111 1011 (123)
85 |
86 | True/False Schema:
87 | A | B | Result
88 | 0 | 0 | 0
89 | 1 | 0 | 1
90 | 0 | 1 | 1
91 | 1 | 1 | 1
92 |
93 | [AND Operation (&)]
94 | 0010 1001 (41)
95 | 0101 1010 (90)
96 | ---- ---- AND
97 | 0000 1000 (8)
98 |
99 | True/False Schema
100 | A | B | Result
101 | 0 | 0 | 0
102 | 1 | 0 | 0
103 | 0 | 1 | 0
104 | 1 | 1 | 1
105 |
106 | [XOR (Exclusive OR) Operation (^)]
107 | 0010 1001 (41)
108 | 0101 1010 (90)
109 | ---- ---- XOR
110 | 0111 0011 (115)
111 |
112 | True/False Schema
113 | A | B | Result
114 | 0 | 0 | 0
115 | 1 | 0 | 1
116 | 0 | 1 | 1
117 | 1 | 1 | 0
118 |
119 | [Unary (~)] (Invert)
120 | 0010 1001 (41)
121 | ---- ---- UNARY
122 | 1101 0110 (214)
--------------------------------------------------------------------------------
/emulator_chip8/Part 1 - Emulation and Bitwise/src assignment/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class Chip {
4 |
5 | /**
6 | * 4kB of 8-bit memory
7 | * At position 0x50: The "bios" fontset
8 | * At position 0x200: The start of every program
9 | */
10 | private char[] memory;
11 | /**
12 | * 16 8-bit registers.
13 | * They will be used to store data which is used in several operation
14 | * Register 0xF is used for Carry, Borrow and collision detection
15 | */
16 | private char[] V;
17 | /**
18 | * 16-bit (only 12 are used) to point to a specific point in the memory
19 | */
20 | private char I;
21 | /**
22 | * The 16-bit (only 12 are used) to point to the current operation
23 | */
24 | private char pc;
25 |
26 | /**
27 | * Subroutine callstack
28 | * Allows up to 16 levels of nesting
29 | */
30 | private char stack[];
31 | /**
32 | * Points to the next free slot int the stack
33 | */
34 | private int stackPointer;
35 |
36 | /**
37 | * This timer is used to delay events in programs/games
38 | */
39 | private int delay_timer;
40 | /**
41 | * This timer is used to make a beeping sound
42 | */
43 | private int sound_timer;
44 |
45 | /**
46 | * This array will be our keyboard state
47 | */
48 | private byte[] keys;
49 |
50 | /**
51 | * The 64x32 pixel monochrome (black/white) display
52 | */
53 | private byte[] display;
54 |
55 | /**
56 | * Reset the Chip 8 memory and pointers
57 | */
58 | public void init() {
59 | memory = new char[4096];
60 | V = new char[16];
61 | I = 0x0;
62 | pc = 0x200;
63 |
64 | stack = new char[16];
65 | stackPointer = 0;
66 |
67 | delay_timer = 0;
68 | sound_timer = 0;
69 |
70 | keys = new byte[16];
71 |
72 | display = new byte[64 * 32];
73 | }
74 |
75 | /**
76 | * Executes a single Operation Code (Opcode)
77 | */
78 | public void run() {
79 | //fetch Opcode
80 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
81 | System.out.print(Integer.toHexString(opcode) + ": ");
82 | //decode opcode
83 | switch(opcode & 0xF000) {
84 |
85 | case 0x8000: //Contains more data in last nibble
86 |
87 | switch(opcode & 0x000F) {
88 |
89 | case 0x0000: //8XY0: Sets VX to the value of VY.
90 | default:
91 | System.err.println("Unsupported Opcode!");
92 | System.exit(0);
93 | break;
94 | }
95 |
96 | break;
97 |
98 | default:
99 | System.err.println("Unsupported Opcode!");
100 | System.exit(0);
101 | }
102 | //execute opcode
103 | }
104 |
105 | /**
106 | * Returns the display data
107 | * @return
108 | * Current state of the 64x32 display
109 | */
110 | public byte[] getDisplay() {
111 | return display;
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 1 - Emulation and Bitwise/src assignment/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 |
6 | import javax.swing.JFrame;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipFrame extends JFrame {
11 |
12 | private ChipPanel panel;
13 |
14 | public ChipFrame(Chip c) {
15 | setPreferredSize(new Dimension(640, 320));
16 | pack();
17 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
18 | panel = new ChipPanel(c);
19 | setLayout(new BorderLayout());
20 | add(panel, BorderLayout.CENTER);
21 | setDefaultCloseOperation(EXIT_ON_CLOSE);
22 | setTitle("Chip 8 Emulator");
23 | pack();
24 | setVisible(true);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 1 - Emulation and Bitwise/src assignment/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private Chip chip;
13 |
14 | public ChipPanel(Chip chip) {
15 | this.chip = chip;
16 | }
17 |
18 | public void paint(Graphics g) {
19 | byte[] display = chip.getDisplay();
20 | for(int i = 0; i < display.length; i++) {
21 | if(display[i] == 0)
22 | g.setColor(Color.BLACK);
23 | else
24 | g.setColor(Color.WHITE);
25 |
26 | int x = (i % 64);
27 | int y = (int)Math.floor(i / 64);
28 |
29 | g.fillRect(x * 10, y * 10, 10, 10);
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 1 - Emulation and Bitwise/src assignment/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main {
6 |
7 | public static void main(String[] args) {
8 | Chip c = new Chip();
9 | c.init();
10 | //c.run();
11 | ChipFrame frame = new ChipFrame(c);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 1 - Emulation and Bitwise/src/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class Chip {
4 |
5 | /**
6 | * 4kB of 8-bit memory
7 | * At position 0x50: The "bios" fontset
8 | * At position 0x200: The start of every program
9 | */
10 | private char[] memory;
11 | /**
12 | * 16 8-bit registers.
13 | * They will be used to store data which is used in several operation
14 | * Register 0xF is used for Carry, Borrow and collision detection
15 | */
16 | private char[] V;
17 | /**
18 | * 16-bit (only 12 are used) to point to a specific point in the memory
19 | */
20 | private char I;
21 | /**
22 | * The 16-bit (only 12 are used) to point to the current operation
23 | */
24 | private char pc;
25 |
26 | /**
27 | * Subroutine callstack
28 | * Allows up to 16 levels of nesting
29 | */
30 | private char stack[];
31 | /**
32 | * Points to the next free slot int the stack
33 | */
34 | private int stackPointer;
35 |
36 | /**
37 | * This timer is used to delay events in programs/games
38 | */
39 | private int delay_timer;
40 | /**
41 | * This timer is used to make a beeping sound
42 | */
43 | private int sound_timer;
44 |
45 | /**
46 | * This array will be our keyboard state
47 | */
48 | private byte[] keys;
49 |
50 | /**
51 | * The 64x32 pixel monochrome (black/white) display
52 | */
53 | private byte[] display;
54 |
55 | /**
56 | * Reset the Chip 8 memory and pointers
57 | */
58 | public void init() {
59 | memory = new char[4096];
60 | V = new char[16];
61 | I = 0x0;
62 | pc = 0x200;
63 |
64 | stack = new char[16];
65 | stackPointer = 0;
66 |
67 | delay_timer = 0;
68 | sound_timer = 0;
69 |
70 | keys = new byte[16];
71 |
72 | display = new byte[64 * 32];
73 | }
74 |
75 | /**
76 | * Executes a single Operation Code (Opcode)
77 | */
78 | public void run() {
79 | //fetch Opcode
80 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
81 | System.out.print(Integer.toHexString(opcode) + ": ");
82 | //decode opcode
83 | switch(opcode & 0xF000) {
84 |
85 | case 0x8000: //Contains more data in last nibble
86 |
87 | switch(opcode & 0x000F) {
88 |
89 | case 0x0000: //8XY0: Sets VX to the value of VY.
90 | default:
91 | System.err.println("Unsupported Opcode!");
92 | System.exit(0);
93 | break;
94 | }
95 |
96 | break;
97 |
98 | default:
99 | System.err.println("Unsupported Opcode!");
100 | System.exit(0);
101 | }
102 | //execute opcode
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 1 - Emulation and Bitwise/src/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main {
6 |
7 | public static void main(String[] args) {
8 | Chip c = new Chip();
9 | c.init();
10 | c.run();
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 10 - Beep, Boop/src/chip/Audio.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.File;
4 |
5 | import javax.sound.sampled.AudioInputStream;
6 | import javax.sound.sampled.AudioSystem;
7 | import javax.sound.sampled.Clip;
8 |
9 | public class Audio {
10 |
11 | public static void playSound(String file) {
12 | try {
13 | Clip clip = AudioSystem.getClip();
14 | AudioInputStream audioInput = AudioSystem.getAudioInputStream(new File(file));
15 | clip.open(audioInput);
16 | clip.start();
17 | System.out.println("Beep");
18 | } catch (Exception e) {
19 | System.err.println("Failed to play audio file: " + e.getMessage());
20 | }
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 10 - Beep, Boop/src/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 | import java.util.Random;
8 |
9 | public class Chip {
10 |
11 | /**
12 | * 4kB of 8-bit memory
13 | * At position 0x50: The "bios" fontset
14 | * At position 0x200: The start of every program
15 | */
16 | private char[] memory;
17 | /**
18 | * 16 8-bit registers.
19 | * They will be used to store data which is used in several operation
20 | * Register 0xF is used for Carry, Borrow and collision detection
21 | */
22 | private char[] V;
23 | /**
24 | * 16-bit (only 12 are used) to point to a specific point in the memory
25 | */
26 | private char I;
27 | /**
28 | * The 16-bit (only 12 are used) to point to the current operation
29 | */
30 | private char pc;
31 |
32 | /**
33 | * Subroutine callstack
34 | * Allows up to 16 levels of nesting
35 | */
36 | private char stack[];
37 | /**
38 | * Points to the next free slot int the stack
39 | */
40 | private int stackPointer;
41 |
42 | /**
43 | * This timer is used to delay events in programs/games
44 | */
45 | private int delay_timer;
46 | /**
47 | * This timer is used to make a beeping sound
48 | */
49 | private int sound_timer;
50 |
51 | /**
52 | * This array will be our keyboard state
53 | */
54 | private byte[] keys;
55 | /**
56 | * The 64x32 pixel monochrome (black/white) display
57 | */
58 | private byte[] display;
59 |
60 | private boolean needRedraw;
61 |
62 | /**
63 | * Reset the Chip 8 memory and pointers
64 | */
65 | public void init() {
66 | memory = new char[4096];
67 | V = new char[16];
68 | I = 0x0;
69 | pc = 0x200;
70 |
71 | stack = new char[16];
72 | stackPointer = 0;
73 |
74 | delay_timer = 0;
75 | sound_timer = 0;
76 |
77 | keys = new byte[16];
78 |
79 | display = new byte[64 * 32];
80 |
81 | needRedraw = false;
82 | loadFontset();
83 | }
84 |
85 | /**
86 | * Executes a single Operation Code (Opcode)
87 | */
88 | public void run() {
89 | //fetch Opcode
90 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
91 | System.out.print(Integer.toHexString(opcode).toUpperCase() + ": ");
92 | //decode opcode
93 | switch(opcode & 0xF000) {
94 |
95 | case 0x0000: //Multi-case
96 | switch(opcode & 0x00FF) {
97 | case 0x00E0: //00E0: Clear Screen
98 | for(int i = 0; i < display.length; i++) {
99 | display[i] = 0;
100 | }
101 | pc += 2;
102 | needRedraw = true;
103 | break;
104 |
105 | case 0x00EE: //00EE: Returns from subroutine
106 | stackPointer--;
107 | pc = (char)(stack[stackPointer] + 2);
108 | System.out.println("Returning to " + Integer.toHexString(pc).toUpperCase());
109 | break;
110 |
111 | default: //0NNN: Calls RCA 1802 Program at address NNN
112 | System.err.println("Unsupported Opcode!");
113 | System.exit(0);
114 | break;
115 | }
116 | break;
117 |
118 | case 0x1000: //1NNN: Jumps to address NNN
119 | int nnn = opcode & 0x0FFF;
120 | pc = (char)nnn;
121 | System.out.println("Jumping to " + Integer.toHexString(pc).toUpperCase());
122 | break;
123 |
124 | case 0x2000: //2NNN: Calls subroutine at NNN
125 | stack[stackPointer] = pc;
126 | stackPointer++;
127 | pc = (char)(opcode & 0x0FFF);
128 | System.out.println("Calling " + Integer.toHexString(pc).toUpperCase() + " from " + Integer.toHexString(stack[stackPointer - 1]).toUpperCase());
129 | break;
130 |
131 | case 0x3000: { //3XNN: Skips the next instruction if VX equals NN
132 | int x = (opcode & 0x0F00) >> 8;
133 | int nn = (opcode & 0x00FF);
134 | if(V[x] == nn) {
135 | pc += 4;
136 | System.out.println("Skipping next instruction (V[" + x +"] == " + nn + ")");
137 | } else {
138 | pc += 2;
139 | System.out.println("Not skipping next instruction (V[" + x +"] != " + nn + ")");
140 | }
141 | break;
142 | }
143 |
144 | case 0x4000: { //4XNN: Skip the next instruction if VX != NN
145 | int x = (opcode & 0x0F00) >> 8;
146 | int nn = opcode & 0x00FF;
147 | if(V[x] != nn) {
148 | System.out.println("Skipping next instruction V[" + x + "] = " + (int)V[x] + " != " + nn);
149 | pc += 4;
150 | } else {
151 | System.out.println("Not skipping next instruction V[" + x + "] = " + (int)V[x] + " == " + nn);
152 | pc += 2;
153 | }
154 | break;
155 | }
156 |
157 | case 0x6000: { //6XNN: Set VX to NN
158 | int x = (opcode & 0x0F00) >> 8;
159 | V[x] = (char)(opcode & 0x00FF);
160 | pc += 2;
161 | System.out.println("Setting V[" + x + "] to " + (int)V[x]);
162 | break;
163 | }
164 |
165 | case 0x7000: { //7XNN: Adds NN to VX
166 | int x = (opcode & 0x0F00) >> 8;
167 | int nn = (opcode & 0x00FF);
168 | V[x] = (char)((V[x] + nn) & 0xFF);
169 | pc += 2;
170 | System.out.println("Adding " + nn + " to V["+ x + "] = " + (int)V[x]);
171 | break;
172 | }
173 |
174 | case 0x8000: //Contains more data in last nibble
175 |
176 | switch(opcode & 0x000F) {
177 |
178 | case 0x0000: { //8XY0: Sets VX to the value of VY
179 | int x = (opcode & 0x0F00) >> 8;
180 | int y = (opcode & 0x00F0) >> 4;
181 | System.out.println("Setting V[" + x + "] to " + (int)V[y]);
182 | V[x] = V[y];
183 | pc += 2;
184 | break;
185 | }
186 |
187 | case 0x0002: { //8XY2: Sets VX to VX AND VY
188 | int x = (opcode & 0x0F00) >> 8;
189 | int y = (opcode & 0x00F0) >> 4;
190 | System.out.println("Set V[" + x + "] to V[" + x + "] = " + (int)V[x] + " & V[" + y + "] = " + (int)V[y] + " = " + (int)(V[x] & V[y]));
191 | V[x] = (char)(V[x] & V[y]);
192 | pc += 2;
193 | break;
194 | }
195 |
196 | case 0x0004: { //Adds VY to VX. VF is set to 1 when carry applies else to 0
197 | int x = (opcode & 0x0F00) >> 8;
198 | int y = (opcode & 0x00F0) >> 4;
199 | System.out.print("Adding V[" + x + "] (" + (int)V[x] + ") to V[" + y + "] (" + (int)V[y] + ") = " + ((V[x] + V[y]) & 0xFF) + ", ");
200 | if(V[y] > 0xFF - V[x]) {
201 | V[0xF] = 1;
202 | System.out.println("Carry!");
203 | } else {
204 | V[0xF] = 0;
205 | System.out.println("No Carry");
206 | }
207 | V[x] = (char)((V[x] + V[y]) & 0xFF);
208 | pc += 2;
209 | break;
210 | }
211 |
212 | case 0x0005: { //VY is subtracted from VX. VF is set to 0 when there is a borrow else 1
213 | int x = (opcode & 0x0F00) >> 8;
214 | int y = (opcode & 0x00F0) >> 4;
215 | System.out.print("V[" + x + "] = " + (int)V[x] + " V[" + y + "] = " + (int)V[y] + ", ");
216 | if(V[x] > V[y]) {
217 | V[0xF] = 1;
218 | System.out.println("No Borrow");
219 | } else {
220 | V[0xF] = 0;
221 | System.out.println("Borrow");
222 | }
223 | V[x] = (char)((V[x] - V[y]) & 0xFF);
224 | pc += 2;
225 | break;
226 | }
227 |
228 | case 0x0006: { //8XY6: Shift VX right by one, VF is set to the least significant bit of VX
229 | int x = (opcode & 0x0F00) >> 8;
230 | V[0xF] = (char)(V[x] & 0x1);
231 | V[x] = (char)(V[x] << 1);
232 | pc += 2;
233 | System.out.println("Shift V[ " + x + "] << 1 and VF to LSB of VX");
234 | break;
235 | }
236 |
237 | default:
238 | System.err.println("Unsupported Opcode!");
239 | System.exit(0);
240 | break;
241 | }
242 |
243 | break;
244 |
245 | case 0xA000: //ANNN: Set I to NNN
246 | I = (char)(opcode & 0x0FFF);
247 | pc += 2;
248 | System.out.println("Set I to " + Integer.toHexString(I).toUpperCase());
249 | break;
250 |
251 | case 0xC000: { //CXNN: Set VX to a random number and NN
252 | int x = (opcode & 0x0F00) >> 8;
253 | int nn = (opcode & 0x00FF);
254 | int randomNumber = new Random().nextInt(255) & nn;
255 | System.out.println("V[" + x + "] has been set to (randomised) " + randomNumber);
256 | V[x] = (char)randomNumber;
257 | pc += 2;
258 | break;
259 | }
260 |
261 | case 0xD000: { //DXYN: Draw a sprite (X, Y) size (8, N). Sprite is located at I
262 | int x = V[(opcode & 0x0F00) >> 8];
263 | int y = V[(opcode & 0x00F0) >> 4];
264 | int height = opcode & 0x000F;
265 |
266 | V[0xF] = 0;
267 |
268 | for(int _y = 0; _y < height; _y++) {
269 | int line = memory[I + _y];
270 | for(int _x = 0; _x < 8; _x++) {
271 | int pixel = line & (0x80 >> _x);
272 | if(pixel != 0) {
273 | int totalX = x + _x;
274 | int totalY = y + _y;
275 |
276 | totalX = totalX % 64;
277 | totalY = totalY % 32;
278 |
279 | int index = (totalY * 64) + totalX;
280 |
281 | if(display[index] == 1)
282 | V[0xF] = 1;
283 |
284 | display[index] ^= 1;
285 | }
286 | }
287 | }
288 | pc += 2;
289 | needRedraw = true;
290 | System.out.println("Drawing at V[" + ((opcode & 0x0F00) >> 8) + "] = " + x + ", V[" + ((opcode & 0x00F0) >> 4) + "] = " + y);
291 | break;
292 | }
293 |
294 | case 0xE000: {
295 | switch (opcode & 0x00FF) {
296 | case 0x009E: { //EX9E Skip the next instruction if the Key VX is pressed
297 | int x = (opcode & 0x0F00) >> 8;
298 | int key = V[x];
299 | if(keys[key] == 1) {
300 | pc += 4;
301 | } else {
302 | pc += 2;
303 | }
304 | System.out.println("Skipping next instruction if V[" + x + "] = " + ((int)V[x])+ " is pressed");
305 | break;
306 | }
307 |
308 | case 0x00A1: { //EXA1 Skip the next instruction if the Key VX is NOT pressed
309 | int x = (opcode & 0x0F00) >> 8;
310 | int key = V[x];
311 | if(keys[key] == 0) {
312 | pc += 4;
313 | } else {
314 | pc += 2;
315 | }
316 | System.out.println("Skipping next instruction if V[" + x + "] = " + (int)V[x] + " is NOT pressed");
317 | break;
318 | }
319 |
320 | default:
321 | System.err.println("Unexisting opcode");
322 | System.exit(0);
323 | return;
324 | }
325 | break;
326 | }
327 |
328 | case 0xF000:
329 |
330 | switch(opcode & 0x00FF) {
331 |
332 | case 0x0007: { //FX07: Set VX to the value of delay_timer
333 | int x = (opcode & 0x0F00) >> 8;
334 | V[x] = (char)delay_timer;
335 | pc += 2;
336 | System.out.println("V[" + x + "] has been set to " + delay_timer);
337 | break;
338 | }
339 |
340 | case 0x0015: { //FX15: Set delay timer to V[x]
341 | int x = (opcode & 0x0F00) >> 8;
342 | delay_timer = V[x];
343 | pc += 2;
344 | System.out.println("Set delay_timer to V[" + x + "] = " + (int)V[x]);
345 | break;
346 | }
347 |
348 | case 0x0018: { //FX18: Set the sound timer to V[x]
349 | int x = (opcode & 0x0F00) >> 8;
350 | sound_timer = V[x];
351 | pc += 2;
352 | break;
353 | }
354 |
355 | case 0x001E: { //FX1E: Adds VX to I
356 | int x = (opcode & 0x0F00) >> 8;
357 | I = (char)(I + V[x]);
358 | System.out.println("Adding V[" + x + "] = " + (int)V[x] + " to I");
359 | pc += 2;
360 | break;
361 | }
362 |
363 | case 0x0029: { //FX29: Sets I to the location of the sprite for the character VX (Fontset)
364 | int x = (opcode & 0x0F00) >> 8;
365 | int character = V[x];
366 | I = (char)(0x050 + (character * 5));
367 | System.out.println("Setting I to Character V[" + x + "] = " + (int)V[x] + " Offset to 0x" + Integer.toHexString(I).toUpperCase());
368 | pc += 2;
369 | break;
370 | }
371 |
372 | case 0x0033: { //FX33 Store a binary-coded decimal value VX in I, I + 1 and I + 2
373 | int x = (opcode & 0x0F00) >> 8;
374 | int value = V[x];
375 | int hundreds = (value - (value % 100)) / 100;
376 | value -= hundreds * 100;
377 | int tens = (value - (value % 10))/ 10;
378 | value -= tens * 10;
379 | memory[I] = (char)hundreds;
380 | memory[I + 1] = (char)tens;
381 | memory[I + 2] = (char)value;
382 | System.out.println("Storing Binary-Coded Decimal V[" + x + "] = " + (int)(V[(opcode & 0x0F00) >> 8]) + " as { " + hundreds+ ", " + tens + ", " + value + "}");
383 | pc += 2;
384 | break;
385 | }
386 |
387 | case 0x0065: { //FX65 Filss V0 to VX with values from I
388 | int x = (opcode & 0x0F00) >> 8;
389 | for(int i = 0; i <= x; i++) {
390 | V[i] = memory[I + i];
391 | }
392 | System.out.println("Setting V[0] to V[" + x + "] to the values of memory[0x" + Integer.toHexString(I & 0xFFFF).toUpperCase() + "]");
393 | I = (char)(I + x + 1);
394 | pc += 2;
395 | break;
396 | }
397 |
398 | default:
399 | System.err.println("Unsupported Opcode!");
400 | System.exit(0);
401 | }
402 | break;
403 |
404 | default:
405 | System.err.println("Unsupported Opcode!");
406 | System.exit(0);
407 | }
408 |
409 | if(sound_timer > 0) {
410 | sound_timer--;
411 | Audio.playSound("./beep.wav");
412 | }
413 | if(delay_timer > 0)
414 | delay_timer--;
415 | }
416 |
417 | /**
418 | * Returns the display data
419 | * @return
420 | * Current state of the 64x32 display
421 | */
422 | public byte[] getDisplay() {
423 | return display;
424 | }
425 |
426 | /**
427 | * Checks if there is a redraw needed
428 | * @return
429 | * If a redraw is needed
430 | */
431 | public boolean needsRedraw() {
432 | return needRedraw;
433 | }
434 |
435 | /**
436 | * Notify the chip that is has been redrawn
437 | */
438 | public void removeDrawFlag() {
439 | needRedraw = false;
440 | }
441 |
442 | /**
443 | * Loads the program into the memory
444 | * @param file
445 | * The location of the program
446 | */
447 | public void loadProgram(String file) {
448 | DataInputStream input = null;
449 | try {
450 | input = new DataInputStream(new FileInputStream(new File(file)));
451 |
452 | int offset = 0;
453 | while(input.available() > 0) {
454 | memory[0x200 + offset] = (char)(input.readByte() & 0xFF);
455 | offset++;
456 | }
457 |
458 | } catch (IOException e) {
459 | e.printStackTrace();
460 | System.exit(0);
461 | } finally {
462 | if(input != null) {
463 | try { input.close(); } catch (IOException ex) {}
464 | }
465 | }
466 | }
467 |
468 | /**
469 | * Loads the fontset into the memory
470 | */
471 | public void loadFontset() {
472 | for(int i = 0; i < ChipData.fontset.length; i++) {
473 | memory[0x50 + i] = (char)(ChipData.fontset[i] & 0xFF);
474 | }
475 | }
476 |
477 | public void setKeyBuffer(int[] keyBuffer) {
478 | for(int i = 0; i < keys.length; i++) {
479 | keys[i] = (byte)keyBuffer[i];
480 | }
481 | }
482 |
483 | }
484 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 10 - Beep, Boop/src/chip/ChipData.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class ChipData {
4 |
5 | /**
6 | * Fontset in bytes
7 | * Memory position 0x50
8 | */
9 | public static int[] fontset =
10 | {
11 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
12 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
13 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
14 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
15 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
16 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
17 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
18 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
19 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
20 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
21 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
22 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
23 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
24 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
25 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
26 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 10 - Beep, Boop/src/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 | import java.awt.event.KeyEvent;
6 | import java.awt.event.KeyListener;
7 |
8 | import javax.swing.JFrame;
9 |
10 | import chip.Chip;
11 |
12 | public class ChipFrame extends JFrame implements KeyListener {
13 |
14 | private static final long serialVersionUID = 1L;
15 | private ChipPanel panel;
16 | private int[] keyBuffer;
17 | private int[] keyIdToKey;
18 |
19 | public ChipFrame(Chip c) {
20 | setPreferredSize(new Dimension(640, 320));
21 | pack();
22 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
23 | panel = new ChipPanel(c);
24 | setLayout(new BorderLayout());
25 | add(panel, BorderLayout.CENTER);
26 | setDefaultCloseOperation(EXIT_ON_CLOSE);
27 | setTitle("Chip 8 Emulator");
28 | pack();
29 | setVisible(true);
30 | addKeyListener(this);
31 |
32 | keyIdToKey = new int[256];
33 | keyBuffer = new int[16];
34 | fillKeyIds();
35 | }
36 |
37 | private void fillKeyIds() {
38 | for(int i = 0; i < keyIdToKey.length; i++) {
39 | keyIdToKey[i] = -1;
40 | }
41 | keyIdToKey['1'] = 1;
42 | keyIdToKey['2'] = 2;
43 | keyIdToKey['3'] = 3;
44 | keyIdToKey['Q'] = 4;
45 | keyIdToKey['W'] = 5;
46 | keyIdToKey['E'] = 6;
47 | keyIdToKey['A'] = 7;
48 | keyIdToKey['S'] = 8;
49 | keyIdToKey['D'] = 9;
50 | keyIdToKey['Z'] = 0xA;
51 | keyIdToKey['X'] = 0;
52 | keyIdToKey['C'] = 0xB;
53 | keyIdToKey['4'] = 0xC;
54 | keyIdToKey['R'] = 0xD;
55 | keyIdToKey['F'] = 0xE;
56 | keyIdToKey['V'] = 0xF;
57 | }
58 |
59 | @Override
60 | public void keyPressed(KeyEvent e) {
61 | if(keyIdToKey[e.getKeyCode()] != -1) {
62 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 1;
63 | }
64 | }
65 |
66 | @Override
67 | public void keyReleased(KeyEvent e) {
68 | if(keyIdToKey[e.getKeyCode()] != -1) {
69 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 0;
70 | }
71 | }
72 |
73 | @Override
74 | public void keyTyped(KeyEvent e) {
75 | }
76 |
77 | public int[] getKeyBuffer() {
78 | return keyBuffer;
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 10 - Beep, Boop/src/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private static final long serialVersionUID = 1L;
13 | private Chip chip;
14 |
15 | public ChipPanel(Chip chip) {
16 | this.chip = chip;
17 | }
18 |
19 | public void paint(Graphics g) {
20 | byte[] display = chip.getDisplay();
21 | for(int i = 0; i < display.length; i++) {
22 | if(display[i] == 0)
23 | g.setColor(Color.BLACK);
24 | else
25 | g.setColor(Color.WHITE);
26 |
27 | int x = (i % 64);
28 | int y = (int)Math.floor(i / 64);
29 |
30 | g.fillRect(x * 10, y * 10, 10, 10);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 10 - Beep, Boop/src/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main extends Thread {
6 |
7 | private Chip chip8;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | chip8 = new Chip();
12 | chip8.init();
13 | chip8.loadProgram("./invaders.c8");
14 | frame = new ChipFrame(chip8);
15 | }
16 |
17 | public void run() {
18 | //60 hz, 60 updates per second
19 | while(true) {
20 | chip8.setKeyBuffer(frame.getKeyBuffer());
21 | chip8.run();
22 | if(chip8.needsRedraw()) {
23 | frame.repaint();
24 | chip8.removeDrawFlag();
25 | }
26 | try {
27 | Thread.sleep(16);
28 | } catch (InterruptedException e) {
29 | //Unthrown exception
30 | }
31 | }
32 | }
33 |
34 | public static void main(String[] args) {
35 | Main main = new Main();
36 | main.start();
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 11 - Final/src/chip/Audio.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.File;
4 |
5 | import javax.sound.sampled.AudioInputStream;
6 | import javax.sound.sampled.AudioSystem;
7 | import javax.sound.sampled.Clip;
8 |
9 | public class Audio {
10 |
11 | public static void playSound(String file) {
12 | try {
13 | Clip clip = AudioSystem.getClip();
14 | AudioInputStream audioInput = AudioSystem.getAudioInputStream(new File(file));
15 | clip.open(audioInput);
16 | clip.start();
17 | System.out.println("Beep");
18 | } catch (Exception e) {
19 | System.err.println("Failed to play audio file: " + e.getMessage());
20 | }
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 11 - Final/src/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 | import java.util.Random;
8 |
9 | public class Chip {
10 |
11 | /**
12 | * 4kB of 8-bit memory
13 | * At position 0x50: The "bios" fontset
14 | * At position 0x200: The start of every program
15 | */
16 | private char[] memory;
17 | /**
18 | * 16 8-bit registers.
19 | * They will be used to store data which is used in several operation
20 | * Register 0xF is used for Carry, Borrow and collision detection
21 | */
22 | private char[] V;
23 | /**
24 | * 16-bit (only 12 are used) to point to a specific point in the memory
25 | */
26 | private char I;
27 | /**
28 | * The 16-bit (only 12 are used) to point to the current operation
29 | */
30 | private char pc;
31 |
32 | /**
33 | * Subroutine callstack
34 | * Allows up to 16 levels of nesting
35 | */
36 | private char stack[];
37 | /**
38 | * Points to the next free slot int the stack
39 | */
40 | private int stackPointer;
41 |
42 | /**
43 | * This timer is used to delay events in programs/games
44 | */
45 | private int delay_timer;
46 | /**
47 | * This timer is used to make a beeping sound
48 | */
49 | private int sound_timer;
50 |
51 | /**
52 | * This array will be our keyboard state
53 | */
54 | private byte[] keys;
55 | /**
56 | * The 64x32 pixel monochrome (black/white) display
57 | */
58 | private byte[] display;
59 |
60 | private boolean needRedraw;
61 |
62 | /**
63 | * Reset the Chip 8 memory and pointers
64 | */
65 | public void init() {
66 | memory = new char[4096];
67 | V = new char[16];
68 | I = 0x0;
69 | pc = 0x200;
70 |
71 | stack = new char[16];
72 | stackPointer = 0;
73 |
74 | delay_timer = 0;
75 | sound_timer = 0;
76 |
77 | keys = new byte[16];
78 |
79 | display = new byte[64 * 32];
80 |
81 | needRedraw = false;
82 | loadFontset();
83 | }
84 |
85 | /**
86 | * Executes a single Operation Code (Opcode)
87 | */
88 | public void run() {
89 | //fetch Opcode
90 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
91 | System.out.print(Integer.toHexString(opcode).toUpperCase() + ": ");
92 | //decode opcode
93 | switch(opcode & 0xF000) {
94 |
95 | case 0x0000: //Multi-case
96 | switch(opcode & 0x00FF) {
97 | case 0x00E0: //00E0: Clear Screen
98 | for(int i = 0; i < display.length; i++) {
99 | display[i] = 0;
100 | }
101 | pc += 2;
102 | needRedraw = true;
103 | break;
104 |
105 | case 0x00EE: //00EE: Returns from subroutine
106 | stackPointer--;
107 | pc = (char)(stack[stackPointer] + 2);
108 | System.out.println("Returning to " + Integer.toHexString(pc).toUpperCase());
109 | break;
110 |
111 | default: //0NNN: Calls RCA 1802 Program at address NNN
112 | System.err.println("Unsupported Opcode!");
113 | System.exit(0);
114 | break;
115 | }
116 | break;
117 |
118 | case 0x1000: { //1NNN: Jumps to address NNN
119 | int nnn = opcode & 0x0FFF;
120 | pc = (char)nnn;
121 | System.out.println("Jumping to " + Integer.toHexString(pc).toUpperCase());
122 | break;
123 | }
124 |
125 | case 0x2000: //2NNN: Calls subroutine at NNN
126 | stack[stackPointer] = pc;
127 | stackPointer++;
128 | pc = (char)(opcode & 0x0FFF);
129 | System.out.println("Calling " + Integer.toHexString(pc).toUpperCase() + " from " + Integer.toHexString(stack[stackPointer - 1]).toUpperCase());
130 | break;
131 |
132 | case 0x3000: { //3XNN: Skips the next instruction if VX equals NN
133 | int x = (opcode & 0x0F00) >> 8;
134 | int nn = (opcode & 0x00FF);
135 | if(V[x] == nn) {
136 | pc += 4;
137 | System.out.println("Skipping next instruction (V[" + x +"] == " + nn + ")");
138 | } else {
139 | pc += 2;
140 | System.out.println("Not skipping next instruction (V[" + x +"] =/= " + nn + ")");
141 | }
142 | break;
143 | }
144 |
145 | case 0x4000: { //4XNN: Skip the next instruction if VX != NN
146 | int x = (opcode & 0x0F00) >> 8;
147 | int nn = opcode & 0x00FF;
148 | if(V[x] != nn) {
149 | System.out.println("Skipping next instruction V[" + x + "] = " + (int)V[x] + " != " + nn);
150 | pc += 4;
151 | } else {
152 | System.out.println("Not skipping next instruction V[" + x + "] = " + (int)V[x] + " == " + nn);
153 | pc += 2;
154 | }
155 | break;
156 | }
157 |
158 | case 0x5000: { //5XY0 Skips the next instruction if VX equals VY.
159 | int x = (opcode & 0x0F00) >> 8;
160 | int y = (opcode & 0x00F0) >> 4;
161 | if(V[x] == V[y]) {
162 | System.out.println("Skipping next instruction V[" + x + "] == V[" + y + "]");
163 | pc += 4;
164 | } else {
165 | System.out.println("Skipping next instruction V[" + x + "] =/= V[" + y + "]");
166 | pc += 2;
167 | }
168 | break;
169 | }
170 |
171 | case 0x6000: { //6XNN: Set VX to NN
172 | int x = (opcode & 0x0F00) >> 8;
173 | V[x] = (char)(opcode & 0x00FF);
174 | pc += 2;
175 | System.out.println("Setting V[" + x + "] to " + (int)V[x]);
176 | break;
177 | }
178 |
179 | case 0x7000: { //7XNN: Adds NN to VX
180 | int x = (opcode & 0x0F00) >> 8;
181 | int nn = (opcode & 0x00FF);
182 | V[x] = (char)((V[x] + nn) & 0xFF);
183 | pc += 2;
184 | System.out.println("Adding " + nn + " to V["+ x + "] = " + (int)V[x]);
185 | break;
186 | }
187 |
188 | case 0x8000: //Contains more data in last nibble
189 |
190 | switch(opcode & 0x000F) {
191 |
192 | case 0x0000: { //8XY0: Sets VX to the value of VY
193 | int x = (opcode & 0x0F00) >> 8;
194 | int y = (opcode & 0x00F0) >> 4;
195 | System.out.println("Setting V[" + x + "] to " + (int)V[y]);
196 | V[x] = V[y];
197 | pc += 2;
198 | break;
199 | }
200 |
201 | case 0x0001: { //8XY1 Sets VX to VX or VY.
202 | int x = (opcode & 0x0F00) >> 8;
203 | int y = (opcode & 0x00F0) >> 4;
204 | System.out.println("Setting V[" + x + "] = V[" + x + "] | V[" + y + "]");
205 | V[x] = (char)((V[x] | V[y]) & 0xFF);
206 | pc += 2;
207 | break;
208 | }
209 |
210 | case 0x0002: { //8XY2: Sets VX to VX AND VY
211 | int x = (opcode & 0x0F00) >> 8;
212 | int y = (opcode & 0x00F0) >> 4;
213 | System.out.println("Set V[" + x + "] to V[" + x + "] = " + (int)V[x] + " & V[" + y + "] = " + (int)V[y] + " = " + (int)(V[x] & V[y]));
214 | V[x] = (char)(V[x] & V[y]);
215 | pc += 2;
216 | break;
217 | }
218 |
219 | case 0x0003: { //8XY3 Sets VX to VX xor VY.
220 | int x = (opcode & 0x0F00) >> 8;
221 | int y = (opcode & 0x00F0) >> 4;
222 | System.out.println("Setting V[" + x + "] = V[" + x + "] ^ V[" + y + "]");
223 | V[x] = (char)((V[x] ^ V[y]) & 0xFF);
224 | pc += 2;
225 | break;
226 | }
227 |
228 | case 0x0004: { //Adds VY to VX. VF is set to 1 when carry applies else to 0
229 | int x = (opcode & 0x0F00) >> 8;
230 | int y = (opcode & 0x00F0) >> 4;
231 | System.out.print("Adding V[" + x + "] (" + (int)V[x] + ") to V[" + y + "] (" + (int)V[y] + ") = " + ((V[x] + V[y]) & 0xFF) + ", ");
232 | if(V[y] > 0xFF - V[x]) {
233 | V[0xF] = 1;
234 | System.out.println("Carry!");
235 | } else {
236 | V[0xF] = 0;
237 | System.out.println("No Carry");
238 | }
239 | V[x] = (char)((V[x] + V[y]) & 0xFF);
240 | pc += 2;
241 | break;
242 | }
243 |
244 | case 0x0005: { //VY is subtracted from VX. VF is set to 0 when there is a borrow else 1
245 | int x = (opcode & 0x0F00) >> 8;
246 | int y = (opcode & 0x00F0) >> 4;
247 | System.out.print("V[" + x + "] = " + (int)V[x] + " V[" + y + "] = " + (int)V[y] + ", ");
248 | if(V[x] > V[y]) {
249 | V[0xF] = 1;
250 | System.out.println("No Borrow");
251 | } else {
252 | V[0xF] = 0;
253 | System.out.println("Borrow");
254 | }
255 | V[x] = (char)((V[x] - V[y]) & 0xFF);
256 | pc += 2;
257 | break;
258 | }
259 |
260 | case 0x0006: { //8XY6: Shift VX right by one, VF is set to the least significant bit of VX
261 | int x = (opcode & 0x0F00) >> 8;
262 | V[0xF] = (char)(V[x] & 0x1);
263 | V[x] = (char)(V[x] >> 1);
264 | pc += 2;
265 | System.out.println("Shift V[ " + x + "] >> 1 and VF to LSB of VX");
266 | break;
267 | }
268 |
269 | case 0x0007: { //8XY7 Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
270 | int x = (opcode & 0x0F00) >> 8;
271 | int y = (opcode & 0x00F0) >> 4;
272 |
273 | if(V[x] > V[y])
274 | V[0xF] = 0;
275 | else
276 | V[0xF] = 1;
277 |
278 | V[x] = (char)((V[y] - V[x]) & 0xFF);
279 | System.out.println("V[" + x + "] = V[" + y + "] - V[" + x + "], Applies Borrow if needed");
280 |
281 | pc += 2;
282 | break;
283 | }
284 |
285 | case 0x000E: { //8XYE Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift.
286 | int x = (opcode & 0x0F00) >> 8;
287 | V[0xF] = (char)(V[x] & 0x80);
288 | V[x] = (char)(V[x] << 1);
289 | pc += 2;
290 | System.out.println("Shift V[ " + x + "] << 1 and VF to MSB of VX");
291 | break;
292 | }
293 |
294 | default:
295 | System.err.println("Unsupported Opcode!");
296 | System.exit(0);
297 | break;
298 | }
299 |
300 | break;
301 |
302 | case 0x9000: { //9XY0 Skips the next instruction if VX doesn't equal VY.
303 | int x = (opcode & 0x0F00) >> 8;
304 | int y = (opcode & 0x00F0) >> 4;
305 | if(V[x] != V[y]) {
306 | System.out.println("Skipping next instruction V[" + x + "] != V[" + y + "]");
307 | pc += 4;
308 | } else {
309 | System.out.println("Skipping next instruction V[" + x + "] !/= V[" + y + "]");
310 | pc += 2;
311 | }
312 | break;
313 | }
314 |
315 | case 0xA000: //ANNN: Set I to NNN
316 | I = (char)(opcode & 0x0FFF);
317 | pc += 2;
318 | System.out.println("Set I to " + Integer.toHexString(I).toUpperCase());
319 | break;
320 |
321 | case 0xB000: { //BNNN Jumps to the address NNN plus V0.
322 | int nnn = opcode & 0x0FFF;
323 | int extra = V[0] & 0xFF;
324 | pc = (char)(nnn + extra);
325 | break;
326 | }
327 |
328 | case 0xC000: { //CXNN: Set VX to a random number and NN
329 | int x = (opcode & 0x0F00) >> 8;
330 | int nn = (opcode & 0x00FF);
331 | int randomNumber = new Random().nextInt(255) & nn;
332 | System.out.println("V[" + x + "] has been set to (randomised) " + randomNumber);
333 | V[x] = (char)randomNumber;
334 | pc += 2;
335 | break;
336 | }
337 |
338 | case 0xD000: { //DXYN: Draw a sprite (X, Y) size (8, N). Sprite is located at I
339 | int x = V[(opcode & 0x0F00) >> 8];
340 | int y = V[(opcode & 0x00F0) >> 4];
341 | int height = opcode & 0x000F;
342 |
343 | V[0xF] = 0;
344 |
345 | for(int _y = 0; _y < height; _y++) {
346 | int line = memory[I + _y];
347 | for(int _x = 0; _x < 8; _x++) {
348 | int pixel = line & (0x80 >> _x);
349 | if(pixel != 0) {
350 | int totalX = x + _x;
351 | int totalY = y + _y;
352 |
353 | totalX = totalX % 64;
354 | totalY = totalY % 32;
355 |
356 | int index = (totalY * 64) + totalX;
357 |
358 | if(display[index] == 1)
359 | V[0xF] = 1;
360 |
361 | display[index] ^= 1;
362 | }
363 | }
364 | }
365 | pc += 2;
366 | needRedraw = true;
367 | System.out.println("Drawing at V[" + ((opcode & 0x0F00) >> 8) + "] = " + x + ", V[" + ((opcode & 0x00F0) >> 4) + "] = " + y);
368 | break;
369 | }
370 |
371 | case 0xE000: {
372 | switch (opcode & 0x00FF) {
373 | case 0x009E: { //EX9E Skip the next instruction if the Key VX is pressed
374 | int x = (opcode & 0x0F00) >> 8;
375 | int key = V[x];
376 | if(keys[key] == 1) {
377 | pc += 4;
378 | } else {
379 | pc += 2;
380 | }
381 | System.out.println("Skipping next instruction if V[" + x + "] = " + ((int)V[x])+ " is pressed");
382 | break;
383 | }
384 |
385 | case 0x00A1: { //EXA1 Skip the next instruction if the Key VX is NOT pressed
386 | int x = (opcode & 0x0F00) >> 8;
387 | int key = V[x];
388 | if(keys[key] == 0) {
389 | pc += 4;
390 | } else {
391 | pc += 2;
392 | }
393 | System.out.println("Skipping next instruction if V[" + x + "] = " + (int)V[x] + " is NOT pressed");
394 | break;
395 | }
396 |
397 | default:
398 | System.err.println("Unexisting opcode");
399 | System.exit(0);
400 | return;
401 | }
402 | break;
403 | }
404 |
405 | case 0xF000:
406 |
407 | switch(opcode & 0x00FF) {
408 |
409 | case 0x0007: { //FX07: Set VX to the value of delay_timer
410 | int x = (opcode & 0x0F00) >> 8;
411 | V[x] = (char)delay_timer;
412 | pc += 2;
413 | System.out.println("V[" + x + "] has been set to " + delay_timer);
414 | break;
415 | }
416 |
417 | case 0x000A: { //FX0A A key press is awaited, and then stored in VX.
418 | int x = (opcode & 0x0F00) >> 8;
419 | for(int i = 0; i < keys.length; i++) {
420 | if(keys[i] == 1) {
421 | V[x] = (char)i;
422 | pc += 2;
423 | break;
424 | }
425 | }
426 | System.out.println("Awaiting key press to be stored in V[" + x + "]");
427 | break;
428 | }
429 |
430 | case 0x0015: { //FX15: Set delay timer to V[x]
431 | int x = (opcode & 0x0F00) >> 8;
432 | delay_timer = V[x];
433 | pc += 2;
434 | System.out.println("Set delay_timer to V[" + x + "] = " + (int)V[x]);
435 | break;
436 | }
437 |
438 | case 0x0018: { //FX18: Set the sound timer to V[x]
439 | int x = (opcode & 0x0F00) >> 8;
440 | sound_timer = V[x];
441 | pc += 2;
442 | break;
443 | }
444 |
445 | case 0x001E: { //FX1E: Adds VX to I
446 | int x = (opcode & 0x0F00) >> 8;
447 | I = (char)(I + V[x]);
448 | System.out.println("Adding V[" + x + "] = " + (int)V[x] + " to I");
449 | pc += 2;
450 | break;
451 | }
452 |
453 | case 0x0029: { //FX29: Sets I to the location of the sprite for the character VX (Fontset)
454 | int x = (opcode & 0x0F00) >> 8;
455 | int character = V[x];
456 | I = (char)(0x050 + (character * 5));
457 | System.out.println("Setting I to Character V[" + x + "] = " + (int)V[x] + " Offset to 0x" + Integer.toHexString(I).toUpperCase());
458 | pc += 2;
459 | break;
460 | }
461 |
462 | case 0x0033: { //FX33 Store a binary-coded decimal value VX in I, I + 1 and I + 2
463 | int x = (opcode & 0x0F00) >> 8;
464 | int value = V[x];
465 | int hundreds = (value - (value % 100)) / 100;
466 | value -= hundreds * 100;
467 | int tens = (value - (value % 10))/ 10;
468 | value -= tens * 10;
469 | memory[I] = (char)hundreds;
470 | memory[I + 1] = (char)tens;
471 | memory[I + 2] = (char)value;
472 | System.out.println("Storing Binary-Coded Decimal V[" + x + "] = " + (int)(V[(opcode & 0x0F00) >> 8]) + " as { " + hundreds+ ", " + tens + ", " + value + "}");
473 | pc += 2;
474 | break;
475 | }
476 |
477 | case 0x0055: { //FX55 Stores V0 to VX in memory starting at address I.
478 | int x = (opcode & 0x0F00) >> 8;
479 | for(int i = 0; i <= x; i++) {
480 | memory[I + i] = V[i];
481 | }
482 | System.out.println("Setting Memory[" + Integer.toHexString(I & 0xFFFF).toUpperCase() + " + n] = V[0] to V[x]");
483 | pc += 2;
484 | break;
485 | }
486 |
487 | case 0x0065: { //FX65 Fills V0 to VX with values from I
488 | int x = (opcode & 0x0F00) >> 8;
489 | for(int i = 0; i <= x; i++) {
490 | V[i] = memory[I + i];
491 | }
492 | System.out.println("Setting V[0] to V[" + x + "] to the values of memory[0x" + Integer.toHexString(I & 0xFFFF).toUpperCase() + "]");
493 | I = (char)(I + x + 1);
494 | pc += 2;
495 | break;
496 | }
497 |
498 | default:
499 | System.err.println("Unsupported Opcode!");
500 | System.exit(0);
501 | }
502 | break;
503 |
504 | default:
505 | System.err.println("Unsupported Opcode!");
506 | System.exit(0);
507 | }
508 |
509 | if(sound_timer > 0) {
510 | sound_timer--;
511 | Audio.playSound("./beep.wav");
512 | }
513 | if(delay_timer > 0)
514 | delay_timer--;
515 | }
516 |
517 | /**
518 | * Returns the display data
519 | * @return
520 | * Current state of the 64x32 display
521 | */
522 | public byte[] getDisplay() {
523 | return display;
524 | }
525 |
526 | /**
527 | * Checks if there is a redraw needed
528 | * @return
529 | * If a redraw is needed
530 | */
531 | public boolean needsRedraw() {
532 | return needRedraw;
533 | }
534 |
535 | /**
536 | * Notify the chip that is has been redrawn
537 | */
538 | public void removeDrawFlag() {
539 | needRedraw = false;
540 | }
541 |
542 | /**
543 | * Loads the program into the memory
544 | * @param file
545 | * The location of the program
546 | */
547 | public void loadProgram(String file) {
548 | DataInputStream input = null;
549 | try {
550 | input = new DataInputStream(new FileInputStream(new File(file)));
551 |
552 | int offset = 0;
553 | while(input.available() > 0) {
554 | memory[0x200 + offset] = (char)(input.readByte() & 0xFF);
555 | offset++;
556 | }
557 |
558 | } catch (IOException e) {
559 | e.printStackTrace();
560 | System.exit(0);
561 | } finally {
562 | if(input != null) {
563 | try { input.close(); } catch (IOException ex) {}
564 | }
565 | }
566 | }
567 |
568 | /**
569 | * Loads the fontset into the memory
570 | */
571 | public void loadFontset() {
572 | for(int i = 0; i < ChipData.fontset.length; i++) {
573 | memory[0x50 + i] = (char)(ChipData.fontset[i] & 0xFF);
574 | }
575 | }
576 |
577 | public void setKeyBuffer(int[] keyBuffer) {
578 | for(int i = 0; i < keys.length; i++) {
579 | keys[i] = (byte)keyBuffer[i];
580 | }
581 | }
582 |
583 | }
584 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 11 - Final/src/chip/ChipData.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class ChipData {
4 |
5 | /**
6 | * Fontset in bytes
7 | * Memory position 0x50
8 | */
9 | public static int[] fontset =
10 | {
11 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
12 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
13 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
14 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
15 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
16 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
17 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
18 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
19 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
20 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
21 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
22 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
23 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
24 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
25 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
26 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 11 - Final/src/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 | import java.awt.event.KeyEvent;
6 | import java.awt.event.KeyListener;
7 |
8 | import javax.swing.JFrame;
9 |
10 | import chip.Chip;
11 |
12 | public class ChipFrame extends JFrame implements KeyListener {
13 |
14 | private static final long serialVersionUID = 1L;
15 | private ChipPanel panel;
16 | private int[] keyBuffer;
17 | private int[] keyIdToKey;
18 |
19 | public ChipFrame(Chip c) {
20 | setPreferredSize(new Dimension(640, 320));
21 | pack();
22 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
23 | panel = new ChipPanel(c);
24 | setLayout(new BorderLayout());
25 | add(panel, BorderLayout.CENTER);
26 | setDefaultCloseOperation(EXIT_ON_CLOSE);
27 | setTitle("Chip 8 Emulator");
28 | pack();
29 | setVisible(true);
30 | addKeyListener(this);
31 |
32 | keyIdToKey = new int[256];
33 | keyBuffer = new int[16];
34 | fillKeyIds();
35 | }
36 |
37 | private void fillKeyIds() {
38 | for(int i = 0; i < keyIdToKey.length; i++) {
39 | keyIdToKey[i] = -1;
40 | }
41 | keyIdToKey['1'] = 1;
42 | keyIdToKey['2'] = 2;
43 | keyIdToKey['3'] = 3;
44 | keyIdToKey['Q'] = 4;
45 | keyIdToKey['W'] = 5;
46 | keyIdToKey['E'] = 6;
47 | keyIdToKey['A'] = 7;
48 | keyIdToKey['S'] = 8;
49 | keyIdToKey['D'] = 9;
50 | keyIdToKey['Z'] = 0xA;
51 | keyIdToKey['X'] = 0;
52 | keyIdToKey['C'] = 0xB;
53 | keyIdToKey['4'] = 0xC;
54 | keyIdToKey['R'] = 0xD;
55 | keyIdToKey['F'] = 0xE;
56 | keyIdToKey['V'] = 0xF;
57 | }
58 |
59 | @Override
60 | public void keyPressed(KeyEvent e) {
61 | if(keyIdToKey[e.getKeyCode()] != -1) {
62 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 1;
63 | }
64 | }
65 |
66 | @Override
67 | public void keyReleased(KeyEvent e) {
68 | if(keyIdToKey[e.getKeyCode()] != -1) {
69 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 0;
70 | }
71 | }
72 |
73 | @Override
74 | public void keyTyped(KeyEvent e) {
75 | }
76 |
77 | public int[] getKeyBuffer() {
78 | return keyBuffer;
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 11 - Final/src/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private static final long serialVersionUID = 1L;
13 | private Chip chip;
14 |
15 | public ChipPanel(Chip chip) {
16 | this.chip = chip;
17 | }
18 |
19 | public void paint(Graphics g) {
20 | byte[] display = chip.getDisplay();
21 | for(int i = 0; i < display.length; i++) {
22 | if(display[i] == 0)
23 | g.setColor(Color.BLACK);
24 | else
25 | g.setColor(Color.WHITE);
26 |
27 | int x = (i % 64);
28 | int y = (int)Math.floor(i / 64);
29 |
30 | g.fillRect(x * 10, y * 10, 10, 10);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 11 - Final/src/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main extends Thread {
6 |
7 | private Chip chip8;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | chip8 = new Chip();
12 | chip8.init();
13 | chip8.loadProgram("./invaders.c8");
14 | frame = new ChipFrame(chip8);
15 | }
16 |
17 | public void run() {
18 | //60 hz, 60 updates per second
19 | while(true) {
20 | chip8.setKeyBuffer(frame.getKeyBuffer());
21 | chip8.run();
22 | if(chip8.needsRedraw()) {
23 | frame.repaint();
24 | chip8.removeDrawFlag();
25 | }
26 | try {
27 | Thread.sleep(16);
28 | } catch (InterruptedException e) {
29 | //Unthrown exception
30 | }
31 | }
32 | }
33 |
34 | public static void main(String[] args) {
35 | Main main = new Main();
36 | main.start();
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 2 - Begin/src assignment/chip/ChipData.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class ChipData {
4 |
5 | /**
6 | * Fontset in bytes
7 | * Memory position 0x50
8 | */
9 | public static int[] fontset =
10 | {
11 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
12 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
13 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
14 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
15 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
16 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
17 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
18 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
19 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
20 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
21 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
22 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
23 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
24 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
25 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
26 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 2 - Begin/src/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 |
8 | public class Chip {
9 |
10 | /**
11 | * 4kB of 8-bit memory
12 | * At position 0x50: The "bios" fontset
13 | * At position 0x200: The start of every program
14 | */
15 | private char[] memory;
16 | /**
17 | * 16 8-bit registers.
18 | * They will be used to store data which is used in several operation
19 | * Register 0xF is used for Carry, Borrow and collision detection
20 | */
21 | private char[] V;
22 | /**
23 | * 16-bit (only 12 are used) to point to a specific point in the memory
24 | */
25 | private char I;
26 | /**
27 | * The 16-bit (only 12 are used) to point to the current operation
28 | */
29 | private char pc;
30 |
31 | /**
32 | * Subroutine callstack
33 | * Allows up to 16 levels of nesting
34 | */
35 | private char stack[];
36 | /**
37 | * Points to the next free slot int the stack
38 | */
39 | private int stackPointer;
40 |
41 | /**
42 | * This timer is used to delay events in programs/games
43 | */
44 | private int delay_timer;
45 | /**
46 | * This timer is used to make a beeping sound
47 | */
48 | private int sound_timer;
49 |
50 | /**
51 | * This array will be our keyboard state
52 | */
53 | private byte[] keys;
54 |
55 | /**
56 | * The 64x32 pixel monochrome (black/white) display
57 | */
58 | private byte[] display;
59 |
60 | private boolean needRedraw;
61 |
62 | /**
63 | * Reset the Chip 8 memory and pointers
64 | */
65 | public void init() {
66 | memory = new char[4096];
67 | V = new char[16];
68 | I = 0x0;
69 | pc = 0x200;
70 |
71 | stack = new char[16];
72 | stackPointer = 0;
73 |
74 | delay_timer = 0;
75 | sound_timer = 0;
76 |
77 | keys = new byte[16];
78 |
79 | display = new byte[64 * 32];
80 |
81 | needRedraw = false;
82 | loadFontset();
83 | }
84 |
85 | /**
86 | * Executes a single Operation Code (Opcode)
87 | */
88 | public void run() {
89 | //fetch Opcode
90 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
91 | System.out.print(Integer.toHexString(opcode) + ": ");
92 | //decode opcode
93 | switch(opcode & 0xF000) {
94 |
95 | case 0x1000: //1NNN: Jumps to address NNN
96 | break;
97 |
98 | case 0x2000: //2NNN: Calls subroutine at NNN
99 | stack[stackPointer] = pc;
100 | stackPointer++;
101 | pc = (char)(opcode & 0x0FFF);
102 | break;
103 |
104 | case 0x3000: //3XNN: Skips the next instruction if VX equals NN
105 | break;
106 |
107 | case 0x6000: //6XNN: Set VX to NN
108 | int x = (opcode & 0x0F00) >> 8;
109 | V[x] = (char)(opcode & 0x00FF);
110 | pc += 2;
111 | break;
112 |
113 | case 0x7000: //7XNN: Adds NN to VX
114 | int _x = (opcode & 0x0F00) >> 8;
115 | int nn = (opcode & 0x00FF);
116 | V[_x] = (char)((V[_x] + nn) & 0xFF);
117 | break;
118 |
119 | case 0x8000: //Contains more data in last nibble
120 |
121 | switch(opcode & 0x000F) {
122 |
123 | case 0x0000: //8XY0: Sets VX to the value of VY.
124 | default:
125 | System.err.println("Unsupported Opcode!");
126 | System.exit(0);
127 | break;
128 | }
129 |
130 | break;
131 |
132 | case 0xA000: //ANNN: Set I to NNN
133 | I = (char)(opcode & 0x0FFF);
134 | pc += 2;
135 | break;
136 |
137 | case 0xD000: //DXYN: Draw a sprite (X, Y) size (8, N). Sprite is located at I
138 | pc += 2;
139 | break;
140 |
141 | default:
142 | System.err.println("Unsupported Opcode!");
143 | System.exit(0);
144 | }
145 | }
146 |
147 | /**
148 | * Returns the display data
149 | * @return
150 | * Current state of the 64x32 display
151 | */
152 | public byte[] getDisplay() {
153 | return display;
154 | }
155 |
156 | /**
157 | * Checks if there is a redraw needed
158 | * @return
159 | * If a redraw is needed
160 | */
161 | public boolean needsRedraw() {
162 | return needRedraw;
163 | }
164 |
165 | /**
166 | * Notify the chip that is has been redrawn
167 | */
168 | public void removeDrawFlag() {
169 | needRedraw = false;
170 | }
171 |
172 | /**
173 | * Loads the program into the memory
174 | * @param file
175 | * The location of the program
176 | */
177 | public void loadProgram(String file) {
178 | DataInputStream input = null;
179 | try {
180 | input = new DataInputStream(new FileInputStream(new File(file)));
181 |
182 | int offset = 0;
183 | while(input.available() > 0) {
184 | memory[0x200 + offset] = (char)(input.readByte() & 0xFF);
185 | offset++;
186 | }
187 |
188 | } catch (IOException e) {
189 | e.printStackTrace();
190 | System.exit(0);
191 | } finally {
192 | if(input != null) {
193 | try { input.close(); } catch (IOException ex) {}
194 | }
195 | }
196 | }
197 |
198 | }
199 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 2 - Begin/src/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 |
6 | import javax.swing.JFrame;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipFrame extends JFrame {
11 |
12 | private ChipPanel panel;
13 |
14 | public ChipFrame(Chip c) {
15 | setPreferredSize(new Dimension(640, 320));
16 | pack();
17 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
18 | panel = new ChipPanel(c);
19 | setLayout(new BorderLayout());
20 | add(panel, BorderLayout.CENTER);
21 | setDefaultCloseOperation(EXIT_ON_CLOSE);
22 | setTitle("Chip 8 Emulator");
23 | pack();
24 | setVisible(true);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 2 - Begin/src/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private Chip chip;
13 |
14 | public ChipPanel(Chip chip) {
15 | this.chip = chip;
16 | }
17 |
18 | public void paint(Graphics g) {
19 | byte[] display = chip.getDisplay();
20 | for(int i = 0; i < display.length; i++) {
21 | if(display[i] == 0)
22 | g.setColor(Color.BLACK);
23 | else
24 | g.setColor(Color.WHITE);
25 |
26 | int x = (i % 64);
27 | int y = (int)Math.floor(i / 64);
28 |
29 | g.fillRect(x * 10, y * 10, 10, 10);
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 2 - Begin/src/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main extends Thread {
6 |
7 | private Chip chip8;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | chip8 = new Chip();
12 | chip8.init();
13 | chip8.loadProgram("./pong2.c8");
14 | frame = new ChipFrame(chip8);
15 | }
16 |
17 | public void run() {
18 | //60 hz, 60 updates per second
19 | while(true) {
20 | chip8.run();
21 | if(chip8.needsRedraw()) {
22 | frame.repaint();
23 | chip8.removeDrawFlag();
24 | }
25 | try {
26 | Thread.sleep(16);
27 | } catch (InterruptedException e) {
28 | //Unthrown exception
29 | }
30 | }
31 | }
32 |
33 | public static void main(String[] args) {
34 | Main main = new Main();
35 | main.start();
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 3 - Drawing/src/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 |
8 | public class Chip {
9 |
10 | /**
11 | * 4kB of 8-bit memory
12 | * At position 0x50: The "bios" fontset
13 | * At position 0x200: The start of every program
14 | */
15 | private char[] memory;
16 | /**
17 | * 16 8-bit registers.
18 | * They will be used to store data which is used in several operation
19 | * Register 0xF is used for Carry, Borrow and collision detection
20 | */
21 | private char[] V;
22 | /**
23 | * 16-bit (only 12 are used) to point to a specific point in the memory
24 | */
25 | private char I;
26 | /**
27 | * The 16-bit (only 12 are used) to point to the current operation
28 | */
29 | private char pc;
30 |
31 | /**
32 | * Subroutine callstack
33 | * Allows up to 16 levels of nesting
34 | */
35 | private char stack[];
36 | /**
37 | * Points to the next free slot int the stack
38 | */
39 | private int stackPointer;
40 |
41 | /**
42 | * This timer is used to delay events in programs/games
43 | */
44 | private int delay_timer;
45 | /**
46 | * This timer is used to make a beeping sound
47 | */
48 | private int sound_timer;
49 |
50 | /**
51 | * This array will be our keyboard state
52 | */
53 | private byte[] keys;
54 | /**
55 | * The 64x32 pixel monochrome (black/white) display
56 | */
57 | private byte[] display;
58 |
59 | private boolean needRedraw;
60 |
61 | /**
62 | * Reset the Chip 8 memory and pointers
63 | */
64 | public void init() {
65 | memory = new char[4096];
66 | V = new char[16];
67 | I = 0x0;
68 | pc = 0x200;
69 |
70 | stack = new char[16];
71 | stackPointer = 0;
72 |
73 | delay_timer = 0;
74 | sound_timer = 0;
75 |
76 | keys = new byte[16];
77 |
78 | display = new byte[64 * 32];
79 |
80 | needRedraw = false;
81 | loadFontset();
82 | }
83 |
84 | /**
85 | * Executes a single Operation Code (Opcode)
86 | */
87 | public void run() {
88 | //fetch Opcode
89 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
90 | System.out.print(Integer.toHexString(opcode).toUpperCase() + ": ");
91 | //decode opcode
92 | switch(opcode & 0xF000) {
93 |
94 | case 0x1000: //1NNN: Jumps to address NNN
95 | System.err.println("Unsupported Opcode!");
96 | System.exit(0);
97 | break;
98 |
99 | case 0x2000: //2NNN: Calls subroutine at NNN
100 | stack[stackPointer] = pc;
101 | stackPointer++;
102 | pc = (char)(opcode & 0x0FFF);
103 | System.out.println("Calling " + Integer.toHexString(pc).toUpperCase());
104 | break;
105 |
106 | case 0x3000: //3XNN: Skips the next instruction if VX equals NN
107 | System.err.println("Unsupported Opcode!");
108 | System.exit(0);
109 | break;
110 |
111 | case 0x6000: { //6XNN: Set VX to NN
112 | int x = (opcode & 0x0F00) >> 8;
113 | V[x] = (char)(opcode & 0x00FF);
114 | pc += 2;
115 | System.out.println("Setting V[" + x + "] to " + (int)V[x]);
116 | break;
117 | }
118 |
119 | case 0x7000: { //7XNN: Adds NN to VX
120 | int x = (opcode & 0x0F00) >> 8;
121 | int nn = (opcode & 0x00FF);
122 | V[x] = (char)((V[x] + nn) & 0xFF);
123 | pc += 2;
124 | System.out.println("Adding " + nn + " to V["+ x + "] = " + (int)V[x]);
125 | break;
126 | }
127 |
128 | case 0x8000: //Contains more data in last nibble
129 |
130 | switch(opcode & 0x000F) {
131 |
132 | case 0x0000: //8XY0: Sets VX to the value of VY.
133 | default:
134 | System.err.println("Unsupported Opcode!");
135 | System.exit(0);
136 | break;
137 | }
138 |
139 | break;
140 |
141 | case 0xA000: //ANNN: Set I to NNN
142 | I = (char)(opcode & 0x0FFF);
143 | pc += 2;
144 | System.out.println("Set I to " + Integer.toHexString(I).toUpperCase());
145 | break;
146 |
147 | case 0xD000: { //DXYN: Draw a sprite (X, Y) size (8, N). Sprite is located at I
148 | int x = V[(opcode & 0x0F00) >> 8];
149 | int y = V[(opcode & 0x00F0) >> 4];
150 | int height = opcode & 0x000F;
151 |
152 | V[0xF] = 0;
153 |
154 | for(int _y = 0; _y < height; _y++) {
155 | int line = memory[I + _y];
156 | for(int _x = 0; _x < 8; _x++) {
157 | int pixel = line & (0x80 >> _x);
158 | if(pixel != 0) {
159 | int totalX = x + _x;
160 | int totalY = y + _y;
161 | int index = totalY * 64 + totalX;
162 |
163 | if(display[index] == 1)
164 | V[0xF] = 1;
165 |
166 | display[index] ^= 1;
167 | }
168 | }
169 | }
170 | pc += 2;
171 | needRedraw = true;
172 | break;
173 | }
174 |
175 | default:
176 | System.err.println("Unsupported Opcode!");
177 | System.exit(0);
178 | }
179 | }
180 |
181 | /**
182 | * Returns the display data
183 | * @return
184 | * Current state of the 64x32 display
185 | */
186 | public byte[] getDisplay() {
187 | return display;
188 | }
189 |
190 | /**
191 | * Checks if there is a redraw needed
192 | * @return
193 | * If a redraw is needed
194 | */
195 | public boolean needsRedraw() {
196 | return needRedraw;
197 | }
198 |
199 | /**
200 | * Notify the chip that is has been redrawn
201 | */
202 | public void removeDrawFlag() {
203 | needRedraw = false;
204 | }
205 |
206 | /**
207 | * Loads the program into the memory
208 | * @param file
209 | * The location of the program
210 | */
211 | public void loadProgram(String file) {
212 | DataInputStream input = null;
213 | try {
214 | input = new DataInputStream(new FileInputStream(new File(file)));
215 |
216 | int offset = 0;
217 | while(input.available() > 0) {
218 | memory[0x200 + offset] = (char)(input.readByte() & 0xFF);
219 | offset++;
220 | }
221 |
222 | } catch (IOException e) {
223 | e.printStackTrace();
224 | System.exit(0);
225 | } finally {
226 | if(input != null) {
227 | try { input.close(); } catch (IOException ex) {}
228 | }
229 | }
230 | }
231 |
232 | /**
233 | * Loads the fontset into the memory
234 | */
235 | public void loadFontset() {
236 | for(int i = 0; i < ChipData.fontset.length; i++) {
237 | memory[0x50 + i] = (char)(ChipData.fontset[i] & 0xFF);
238 | }
239 | }
240 |
241 | }
242 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 3 - Drawing/src/chip/ChipData.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class ChipData {
4 |
5 | /**
6 | * Fontset in bytes
7 | * Memory position 0x50
8 | */
9 | public static int[] fontset =
10 | {
11 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
12 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
13 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
14 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
15 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
16 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
17 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
18 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
19 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
20 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
21 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
22 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
23 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
24 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
25 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
26 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 3 - Drawing/src/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 |
6 | import javax.swing.JFrame;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipFrame extends JFrame {
11 |
12 | private ChipPanel panel;
13 |
14 | public ChipFrame(Chip c) {
15 | setPreferredSize(new Dimension(640, 320));
16 | pack();
17 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
18 | panel = new ChipPanel(c);
19 | setLayout(new BorderLayout());
20 | add(panel, BorderLayout.CENTER);
21 | setDefaultCloseOperation(EXIT_ON_CLOSE);
22 | setTitle("Chip 8 Emulator");
23 | pack();
24 | setVisible(true);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 3 - Drawing/src/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private Chip chip;
13 |
14 | public ChipPanel(Chip chip) {
15 | this.chip = chip;
16 | }
17 |
18 | public void paint(Graphics g) {
19 | byte[] display = chip.getDisplay();
20 | for(int i = 0; i < display.length; i++) {
21 | if(display[i] == 0)
22 | g.setColor(Color.BLACK);
23 | else
24 | g.setColor(Color.WHITE);
25 |
26 | int x = (i % 64);
27 | int y = (int)Math.floor(i / 64);
28 |
29 | g.fillRect(x * 10, y * 10, 10, 10);
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 3 - Drawing/src/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main extends Thread {
6 |
7 | private Chip chip8;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | chip8 = new Chip();
12 | chip8.init();
13 | chip8.loadProgram("./pong2.c8");
14 | frame = new ChipFrame(chip8);
15 | }
16 |
17 | public void run() {
18 | //60 hz, 60 updates per second
19 | while(true) {
20 | chip8.run();
21 | if(chip8.needsRedraw()) {
22 | frame.repaint();
23 | chip8.removeDrawFlag();
24 | }
25 | try {
26 | Thread.sleep(16);
27 | } catch (InterruptedException e) {
28 | //Unthrown exception
29 | }
30 | }
31 | }
32 |
33 | public static void main(String[] args) {
34 | Main main = new Main();
35 | main.start();
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 4 - Jumping/src/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 |
8 | public class Chip {
9 |
10 | /**
11 | * 4kB of 8-bit memory
12 | * At position 0x50: The "bios" fontset
13 | * At position 0x200: The start of every program
14 | */
15 | private char[] memory;
16 | /**
17 | * 16 8-bit registers.
18 | * They will be used to store data which is used in several operation
19 | * Register 0xF is used for Carry, Borrow and collision detection
20 | */
21 | private char[] V;
22 | /**
23 | * 16-bit (only 12 are used) to point to a specific point in the memory
24 | */
25 | private char I;
26 | /**
27 | * The 16-bit (only 12 are used) to point to the current operation
28 | */
29 | private char pc;
30 |
31 | /**
32 | * Subroutine callstack
33 | * Allows up to 16 levels of nesting
34 | */
35 | private char stack[];
36 | /**
37 | * Points to the next free slot int the stack
38 | */
39 | private int stackPointer;
40 |
41 | /**
42 | * This timer is used to delay events in programs/games
43 | */
44 | private int delay_timer;
45 | /**
46 | * This timer is used to make a beeping sound
47 | */
48 | private int sound_timer;
49 |
50 | /**
51 | * This array will be our keyboard state
52 | */
53 | private byte[] keys;
54 | /**
55 | * The 64x32 pixel monochrome (black/white) display
56 | */
57 | private byte[] display;
58 |
59 | private boolean needRedraw;
60 |
61 | /**
62 | * Reset the Chip 8 memory and pointers
63 | */
64 | public void init() {
65 | memory = new char[4096];
66 | V = new char[16];
67 | I = 0x0;
68 | pc = 0x200;
69 |
70 | stack = new char[16];
71 | stackPointer = 0;
72 |
73 | delay_timer = 0;
74 | sound_timer = 0;
75 |
76 | keys = new byte[16];
77 |
78 | display = new byte[64 * 32];
79 |
80 | needRedraw = false;
81 | loadFontset();
82 | }
83 |
84 | /**
85 | * Executes a single Operation Code (Opcode)
86 | */
87 | public void run() {
88 | //fetch Opcode
89 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
90 | System.out.print(Integer.toHexString(opcode).toUpperCase() + ": ");
91 | //decode opcode
92 | switch(opcode & 0xF000) {
93 |
94 | case 0x0000: //Multi-case
95 | switch(opcode & 0x00FF) {
96 | case 0x00E0: //00E0: Clear Screen
97 | System.err.println("Unsupported Opcode!");
98 | System.exit(0);
99 | break;
100 |
101 | case 0x00EE: //00EE: Returns from subroutine
102 | stackPointer--;
103 | pc = (char)(stack[stackPointer] + 2);
104 | System.out.println("Returning to " + Integer.toHexString(pc).toUpperCase());
105 | break;
106 |
107 | default: //0NNN: Calls RCA 1802 Program at address NNN
108 | System.err.println("Unsupported Opcode!");
109 | System.exit(0);
110 | break;
111 | }
112 | break;
113 |
114 | case 0x1000: //1NNN: Jumps to address NNN
115 | int nnn = opcode & 0x0FFF;
116 | pc = (char)nnn;
117 | break;
118 |
119 | case 0x2000: //2NNN: Calls subroutine at NNN
120 | stack[stackPointer] = pc;
121 | stackPointer++;
122 | pc = (char)(opcode & 0x0FFF);
123 | System.out.println("Calling " + Integer.toHexString(pc).toUpperCase() + " from " + Integer.toHexString(stack[stackPointer - 1]).toUpperCase());
124 | break;
125 |
126 | case 0x3000: { //3XNN: Skips the next instruction if VX equals NN
127 | int x = (opcode & 0x0F00) >> 8;
128 | int nn = (opcode & 0x00FF);
129 | if(V[x] == nn) {
130 | pc += 4;
131 | System.out.println("Skipping next instruction (V[" + x +"] == " + nn + ")");
132 | } else {
133 | pc += 2;
134 | System.out.println("Not skipping next instruction (V[" + x +"] != " + nn + ")");
135 | }
136 | break;
137 | }
138 |
139 | case 0x6000: { //6XNN: Set VX to NN
140 | int x = (opcode & 0x0F00) >> 8;
141 | V[x] = (char)(opcode & 0x00FF);
142 | pc += 2;
143 | System.out.println("Setting V[" + x + "] to " + (int)V[x]);
144 | break;
145 | }
146 |
147 | case 0x7000: { //7XNN: Adds NN to VX
148 | int x = (opcode & 0x0F00) >> 8;
149 | int nn = (opcode & 0x00FF);
150 | V[x] = (char)((V[x] + nn) & 0xFF);
151 | pc += 2;
152 | System.out.println("Adding " + nn + " to V["+ x + "] = " + (int)V[x]);
153 | break;
154 | }
155 |
156 | case 0x8000: //Contains more data in last nibble
157 |
158 | switch(opcode & 0x000F) {
159 |
160 | case 0x0000: //8XY0: Sets VX to the value of VY.
161 | default:
162 | System.err.println("Unsupported Opcode!");
163 | System.exit(0);
164 | break;
165 | }
166 |
167 | break;
168 |
169 | case 0xA000: //ANNN: Set I to NNN
170 | I = (char)(opcode & 0x0FFF);
171 | pc += 2;
172 | System.out.println("Set I to " + Integer.toHexString(I).toUpperCase());
173 | break;
174 |
175 | case 0xD000: { //DXYN: Draw a sprite (X, Y) size (8, N). Sprite is located at I
176 | int x = V[(opcode & 0x0F00) >> 8];
177 | int y = V[(opcode & 0x00F0) >> 4];
178 | int height = opcode & 0x000F;
179 |
180 | V[0xF] = 0;
181 |
182 | for(int _y = 0; _y < height; _y++) {
183 | int line = memory[I + _y];
184 | for(int _x = 0; _x < 8; _x++) {
185 | int pixel = line & (0x80 >> _x);
186 | if(pixel != 0) {
187 | int totalX = x + _x;
188 | int totalY = y + _y;
189 | int index = totalY * 64 + totalX;
190 |
191 | if(display[index] == 1)
192 | V[0xF] = 1;
193 |
194 | display[index] ^= 1;
195 | }
196 | }
197 | }
198 | pc += 2;
199 | needRedraw = true;
200 | System.out.println("Drawing at V[" + ((opcode & 0x0F00) >> 8) + "] = " + x + ", V[" + ((opcode & 0x00F0) >> 4) + "] = " + y);
201 | break;
202 | }
203 |
204 | default:
205 | System.err.println("Unsupported Opcode!");
206 | System.exit(0);
207 | }
208 | }
209 |
210 | /**
211 | * Returns the display data
212 | * @return
213 | * Current state of the 64x32 display
214 | */
215 | public byte[] getDisplay() {
216 | return display;
217 | }
218 |
219 | /**
220 | * Checks if there is a redraw needed
221 | * @return
222 | * If a redraw is needed
223 | */
224 | public boolean needsRedraw() {
225 | return needRedraw;
226 | }
227 |
228 | /**
229 | * Notify the chip that is has been redrawn
230 | */
231 | public void removeDrawFlag() {
232 | needRedraw = false;
233 | }
234 |
235 | /**
236 | * Loads the program into the memory
237 | * @param file
238 | * The location of the program
239 | */
240 | public void loadProgram(String file) {
241 | DataInputStream input = null;
242 | try {
243 | input = new DataInputStream(new FileInputStream(new File(file)));
244 |
245 | int offset = 0;
246 | while(input.available() > 0) {
247 | memory[0x200 + offset] = (char)(input.readByte() & 0xFF);
248 | offset++;
249 | }
250 |
251 | } catch (IOException e) {
252 | e.printStackTrace();
253 | System.exit(0);
254 | } finally {
255 | if(input != null) {
256 | try { input.close(); } catch (IOException ex) {}
257 | }
258 | }
259 | }
260 |
261 | /**
262 | * Loads the fontset into the memory
263 | */
264 | public void loadFontset() {
265 | for(int i = 0; i < ChipData.fontset.length; i++) {
266 | memory[0x50 + i] = (char)(ChipData.fontset[i] & 0xFF);
267 | }
268 | }
269 |
270 | }
271 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 4 - Jumping/src/chip/ChipData.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class ChipData {
4 |
5 | /**
6 | * Fontset in bytes
7 | * Memory position 0x50
8 | */
9 | public static int[] fontset =
10 | {
11 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
12 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
13 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
14 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
15 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
16 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
17 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
18 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
19 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
20 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
21 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
22 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
23 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
24 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
25 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
26 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 4 - Jumping/src/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 |
6 | import javax.swing.JFrame;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipFrame extends JFrame {
11 |
12 | private ChipPanel panel;
13 |
14 | public ChipFrame(Chip c) {
15 | setPreferredSize(new Dimension(640, 320));
16 | pack();
17 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
18 | panel = new ChipPanel(c);
19 | setLayout(new BorderLayout());
20 | add(panel, BorderLayout.CENTER);
21 | setDefaultCloseOperation(EXIT_ON_CLOSE);
22 | setTitle("Chip 8 Emulator");
23 | pack();
24 | setVisible(true);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 4 - Jumping/src/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private Chip chip;
13 |
14 | public ChipPanel(Chip chip) {
15 | this.chip = chip;
16 | }
17 |
18 | public void paint(Graphics g) {
19 | byte[] display = chip.getDisplay();
20 | for(int i = 0; i < display.length; i++) {
21 | if(display[i] == 0)
22 | g.setColor(Color.BLACK);
23 | else
24 | g.setColor(Color.WHITE);
25 |
26 | int x = (i % 64);
27 | int y = (int)Math.floor(i / 64);
28 |
29 | g.fillRect(x * 10, y * 10, 10, 10);
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 4 - Jumping/src/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main extends Thread {
6 |
7 | private Chip chip8;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | chip8 = new Chip();
12 | chip8.init();
13 | chip8.loadProgram("./pong2.c8");
14 | frame = new ChipFrame(chip8);
15 | }
16 |
17 | public void run() {
18 | //60 hz, 60 updates per second
19 | while(true) {
20 | chip8.run();
21 | if(chip8.needsRedraw()) {
22 | frame.repaint();
23 | chip8.removeDrawFlag();
24 | }
25 | try {
26 | Thread.sleep(16);
27 | } catch (InterruptedException e) {
28 | //Unthrown exception
29 | }
30 | }
31 | }
32 |
33 | public static void main(String[] args) {
34 | Main main = new Main();
35 | main.start();
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 5 - Coding the F codes/src assignment/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 |
8 | public class Chip {
9 |
10 | /**
11 | * 4kB of 8-bit memory
12 | * At position 0x50: The "bios" fontset
13 | * At position 0x200: The start of every program
14 | */
15 | private char[] memory;
16 | /**
17 | * 16 8-bit registers.
18 | * They will be used to store data which is used in several operation
19 | * Register 0xF is used for Carry, Borrow and collision detection
20 | */
21 | private char[] V;
22 | /**
23 | * 16-bit (only 12 are used) to point to a specific point in the memory
24 | */
25 | private char I;
26 | /**
27 | * The 16-bit (only 12 are used) to point to the current operation
28 | */
29 | private char pc;
30 |
31 | /**
32 | * Subroutine callstack
33 | * Allows up to 16 levels of nesting
34 | */
35 | private char stack[];
36 | /**
37 | * Points to the next free slot int the stack
38 | */
39 | private int stackPointer;
40 |
41 | /**
42 | * This timer is used to delay events in programs/games
43 | */
44 | private int delay_timer;
45 | /**
46 | * This timer is used to make a beeping sound
47 | */
48 | private int sound_timer;
49 |
50 | /**
51 | * This array will be our keyboard state
52 | */
53 | private byte[] keys;
54 | /**
55 | * The 64x32 pixel monochrome (black/white) display
56 | */
57 | private byte[] display;
58 |
59 | private boolean needRedraw;
60 |
61 | /**
62 | * Reset the Chip 8 memory and pointers
63 | */
64 | public void init() {
65 | memory = new char[4096];
66 | V = new char[16];
67 | I = 0x0;
68 | pc = 0x200;
69 |
70 | stack = new char[16];
71 | stackPointer = 0;
72 |
73 | delay_timer = 0;
74 | sound_timer = 0;
75 |
76 | keys = new byte[16];
77 |
78 | display = new byte[64 * 32];
79 |
80 | needRedraw = false;
81 | loadFontset();
82 | }
83 |
84 | /**
85 | * Executes a single Operation Code (Opcode)
86 | */
87 | public void run() {
88 | //fetch Opcode
89 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
90 | System.out.print(Integer.toHexString(opcode).toUpperCase() + ": ");
91 | //decode opcode
92 | switch(opcode & 0xF000) {
93 |
94 | case 0x0000: //Multi-case
95 | switch(opcode & 0x00FF) {
96 | case 0x00E0: //00E0: Clear Screen
97 | System.err.println("Unsupported Opcode!");
98 | System.exit(0);
99 | break;
100 |
101 | case 0x00EE: //00EE: Returns from subroutine
102 | stackPointer--;
103 | pc = (char)(stack[stackPointer] + 2);
104 | System.out.println("Returning to " + Integer.toHexString(pc).toUpperCase());
105 | break;
106 |
107 | default: //0NNN: Calls RCA 1802 Program at address NNN
108 | System.err.println("Unsupported Opcode!");
109 | System.exit(0);
110 | break;
111 | }
112 | break;
113 |
114 | case 0x1000: //1NNN: Jumps to address NNN
115 | int nnn = opcode & 0x0FFF;
116 | pc = (char)nnn;
117 | break;
118 |
119 | case 0x2000: //2NNN: Calls subroutine at NNN
120 | stack[stackPointer] = pc;
121 | stackPointer++;
122 | pc = (char)(opcode & 0x0FFF);
123 | System.out.println("Calling " + Integer.toHexString(pc).toUpperCase() + " from " + Integer.toHexString(stack[stackPointer - 1]).toUpperCase());
124 | break;
125 |
126 | case 0x3000: { //3XNN: Skips the next instruction if VX equals NN
127 | int x = (opcode & 0x0F00) >> 8;
128 | int nn = (opcode & 0x00FF);
129 | if(V[x] == nn) {
130 | pc += 4;
131 | System.out.println("Skipping next instruction (V[" + x +"] == " + nn + ")");
132 | } else {
133 | pc += 2;
134 | System.out.println("Not skipping next instruction (V[" + x +"] != " + nn + ")");
135 | }
136 | break;
137 | }
138 |
139 | case 0x6000: { //6XNN: Set VX to NN
140 | int x = (opcode & 0x0F00) >> 8;
141 | V[x] = (char)(opcode & 0x00FF);
142 | pc += 2;
143 | System.out.println("Setting V[" + x + "] to " + (int)V[x]);
144 | break;
145 | }
146 |
147 | case 0x7000: { //7XNN: Adds NN to VX
148 | int x = (opcode & 0x0F00) >> 8;
149 | int nn = (opcode & 0x00FF);
150 | V[x] = (char)((V[x] + nn) & 0xFF);
151 | pc += 2;
152 | System.out.println("Adding " + nn + " to V["+ x + "] = " + (int)V[x]);
153 | break;
154 | }
155 |
156 | case 0x8000: //Contains more data in last nibble
157 |
158 | switch(opcode & 0x000F) {
159 |
160 | case 0x0000: //8XY0: Sets VX to the value of VY.
161 | default:
162 | System.err.println("Unsupported Opcode!");
163 | System.exit(0);
164 | break;
165 | }
166 |
167 | break;
168 |
169 | case 0xA000: //ANNN: Set I to NNN
170 | I = (char)(opcode & 0x0FFF);
171 | pc += 2;
172 | System.out.println("Set I to " + Integer.toHexString(I).toUpperCase());
173 | break;
174 |
175 | case 0xD000: { //DXYN: Draw a sprite (X, Y) size (8, N). Sprite is located at I
176 | int x = V[(opcode & 0x0F00) >> 8];
177 | int y = V[(opcode & 0x00F0) >> 4];
178 | int height = opcode & 0x000F;
179 |
180 | V[0xF] = 0;
181 |
182 | for(int _y = 0; _y < height; _y++) {
183 | int line = memory[I + _y];
184 | for(int _x = 0; _x < 8; _x++) {
185 | int pixel = line & (0x80 >> _x);
186 | if(pixel != 0) {
187 | int totalX = x + _x;
188 | int totalY = y + _y;
189 | int index = totalY * 64 + totalX;
190 |
191 | if(display[index] == 1)
192 | V[0xF] = 1;
193 |
194 | display[index] ^= 1;
195 | }
196 | }
197 | }
198 | pc += 2;
199 | needRedraw = true;
200 | System.out.println("Drawing at V[" + ((opcode & 0x0F00) >> 8) + "] = " + x + ", V[" + ((opcode & 0x00F0) >> 4) + "] = " + y);
201 | break;
202 | }
203 |
204 | case 0xF000:
205 |
206 | switch(opcode & 0x00FF) {
207 |
208 | case 0x0029: { //Sets I to the location of the sprite for the character VX (Fontset)
209 | int x = (opcode & 0x0F00) >> 8;
210 | int character = V[x];
211 | I = (char)(0x050 + (character * 5));
212 | System.out.println("Setting I to Character V[" + x + "] = " + (int)V[x] + " Offset to 0x" + Integer.toHexString(I).toUpperCase());
213 | pc += 2;
214 | break;
215 | }
216 |
217 | case 0x0033: { //FX33 Store a binary-coded decimal value VX in I, I + 1 and I + 2
218 | int x = (opcode & 0x0F00) >> 8;
219 | int value = V[x];
220 | int hundreds = (value - (value % 100)) / 100;
221 | value -= hundreds * 100;
222 | int tens = (value - (value % 10))/ 10;
223 | value -= tens * 10;
224 | memory[I] = (char)hundreds;
225 | memory[I + 1] = (char)tens;
226 | memory[I + 2] = (char)value;
227 | System.out.println("Storing Binary-Coded Decimal V[" + x + "] = " + (int)(V[(opcode & 0x0F00) >> 8]) + " as { " + hundreds+ ", " + tens + ", " + value + "}");
228 | pc += 2;
229 | break;
230 | }
231 |
232 | case 0x0065: { //FX65 Filss V0 to VX with values from I
233 | int x = (opcode & 0x0F00) >> 8;
234 | for(int i = 0; i < x; i++) {
235 | V[i] = memory[I + i];
236 | }
237 | System.out.println("Setting V[0] to V[" + x + "] to the values of merory[0x" + Integer.toHexString(I & 0xFFFF).toUpperCase() + "]");
238 | pc += 2;
239 | break;
240 | }
241 |
242 | default:
243 | System.err.println("Unsupported Opcode!");
244 | System.exit(0);
245 | }
246 | break;
247 |
248 | default:
249 | System.err.println("Unsupported Opcode!");
250 | System.exit(0);
251 | }
252 | }
253 |
254 | /**
255 | * Returns the display data
256 | * @return
257 | * Current state of the 64x32 display
258 | */
259 | public byte[] getDisplay() {
260 | return display;
261 | }
262 |
263 | /**
264 | * Checks if there is a redraw needed
265 | * @return
266 | * If a redraw is needed
267 | */
268 | public boolean needsRedraw() {
269 | return needRedraw;
270 | }
271 |
272 | /**
273 | * Notify the chip that is has been redrawn
274 | */
275 | public void removeDrawFlag() {
276 | needRedraw = false;
277 | }
278 |
279 | /**
280 | * Loads the program into the memory
281 | * @param file
282 | * The location of the program
283 | */
284 | public void loadProgram(String file) {
285 | DataInputStream input = null;
286 | try {
287 | input = new DataInputStream(new FileInputStream(new File(file)));
288 |
289 | int offset = 0;
290 | while(input.available() > 0) {
291 | memory[0x200 + offset] = (char)(input.readByte() & 0xFF);
292 | offset++;
293 | }
294 |
295 | } catch (IOException e) {
296 | e.printStackTrace();
297 | System.exit(0);
298 | } finally {
299 | if(input != null) {
300 | try { input.close(); } catch (IOException ex) {}
301 | }
302 | }
303 | }
304 |
305 | /**
306 | * Loads the fontset into the memory
307 | */
308 | public void loadFontset() {
309 | for(int i = 0; i < ChipData.fontset.length; i++) {
310 | memory[0x50 + i] = (char)(ChipData.fontset[i] & 0xFF);
311 | }
312 | }
313 |
314 | public void setKeyBuffer(int[] keyBuffer) {
315 | for(int i = 0; i < keys.length; i++) {
316 | keys[i] = (byte)keyBuffer[i];
317 | }
318 | }
319 |
320 | }
321 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 5 - Coding the F codes/src assignment/chip/ChipData.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class ChipData {
4 |
5 | /**
6 | * Fontset in bytes
7 | * Memory position 0x50
8 | */
9 | public static int[] fontset =
10 | {
11 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
12 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
13 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
14 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
15 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
16 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
17 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
18 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
19 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
20 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
21 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
22 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
23 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
24 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
25 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
26 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 5 - Coding the F codes/src assignment/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 | import java.awt.event.KeyEvent;
6 | import java.awt.event.KeyListener;
7 |
8 | import javax.swing.JFrame;
9 |
10 | import chip.Chip;
11 |
12 | public class ChipFrame extends JFrame implements KeyListener {
13 |
14 | private static final long serialVersionUID = 1L;
15 | private ChipPanel panel;
16 | private int[] keyBuffer;
17 | private int[] keyIdToKey;
18 |
19 | public ChipFrame(Chip c) {
20 | setPreferredSize(new Dimension(640, 320));
21 | pack();
22 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
23 | panel = new ChipPanel(c);
24 | setLayout(new BorderLayout());
25 | add(panel, BorderLayout.CENTER);
26 | setDefaultCloseOperation(EXIT_ON_CLOSE);
27 | setTitle("Chip 8 Emulator");
28 | pack();
29 | setVisible(true);
30 |
31 | keyIdToKey = new int[256];
32 | keyBuffer = new int[16];
33 | fillKeyIds();
34 | }
35 |
36 | private void fillKeyIds() {
37 | for(int i = 0; i < keyIdToKey.length; i++) {
38 | keyIdToKey[i] = -1;
39 | }
40 | keyIdToKey['1'] = 1;
41 | keyIdToKey['2'] = 2;
42 | keyIdToKey['3'] = 3;
43 | keyIdToKey['Q'] = 4;
44 | keyIdToKey['W'] = 5;
45 | keyIdToKey['E'] = 6;
46 | keyIdToKey['A'] = 7;
47 | keyIdToKey['S'] = 8;
48 | keyIdToKey['D'] = 9;
49 | keyIdToKey['Z'] = 0xA;
50 | keyIdToKey['X'] = 0;
51 | keyIdToKey['C'] = 0xB;
52 | keyIdToKey['4'] = 0xC;
53 | keyIdToKey['R'] = 0xD;
54 | keyIdToKey['F'] = 0xE;
55 | keyIdToKey['V'] = 0xF;
56 | }
57 |
58 | @Override
59 | public void keyPressed(KeyEvent e) {
60 | if(keyIdToKey[e.getKeyCode()] != -1) {
61 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 1;
62 | }
63 | }
64 |
65 | @Override
66 | public void keyReleased(KeyEvent e) {
67 | if(keyIdToKey[e.getKeyCode()] != -1) {
68 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 0;
69 | }
70 | }
71 |
72 | @Override
73 | public void keyTyped(KeyEvent e) {
74 | }
75 |
76 | public int[] getKeyBuffer() {
77 | return keyBuffer;
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 5 - Coding the F codes/src assignment/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private static final long serialVersionUID = 1L;
13 | private Chip chip;
14 |
15 | public ChipPanel(Chip chip) {
16 | this.chip = chip;
17 | }
18 |
19 | public void paint(Graphics g) {
20 | byte[] display = chip.getDisplay();
21 | for(int i = 0; i < display.length; i++) {
22 | if(display[i] == 0)
23 | g.setColor(Color.BLACK);
24 | else
25 | g.setColor(Color.WHITE);
26 |
27 | int x = (i % 64);
28 | int y = (int)Math.floor(i / 64);
29 |
30 | g.fillRect(x * 10, y * 10, 10, 10);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 5 - Coding the F codes/src assignment/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main extends Thread {
6 |
7 | private Chip chip8;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | chip8 = new Chip();
12 | chip8.init();
13 | chip8.loadProgram("./pong2.c8");
14 | frame = new ChipFrame(chip8);
15 | }
16 |
17 | public void run() {
18 | //60 hz, 60 updates per second
19 | while(true) {
20 | chip8.setKeyBuffer(frame.getKeyBuffer());
21 | chip8.run();
22 | if(chip8.needsRedraw()) {
23 | frame.repaint();
24 | chip8.removeDrawFlag();
25 | }
26 | try {
27 | Thread.sleep(16);
28 | } catch (InterruptedException e) {
29 | //Unthrown exception
30 | }
31 | }
32 | }
33 |
34 | public static void main(String[] args) {
35 | Main main = new Main();
36 | main.start();
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 5 - Coding the F codes/src/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 |
8 | public class Chip {
9 |
10 | /**
11 | * 4kB of 8-bit memory
12 | * At position 0x50: The "bios" fontset
13 | * At position 0x200: The start of every program
14 | */
15 | private char[] memory;
16 | /**
17 | * 16 8-bit registers.
18 | * They will be used to store data which is used in several operation
19 | * Register 0xF is used for Carry, Borrow and collision detection
20 | */
21 | private char[] V;
22 | /**
23 | * 16-bit (only 12 are used) to point to a specific point in the memory
24 | */
25 | private char I;
26 | /**
27 | * The 16-bit (only 12 are used) to point to the current operation
28 | */
29 | private char pc;
30 |
31 | /**
32 | * Subroutine callstack
33 | * Allows up to 16 levels of nesting
34 | */
35 | private char stack[];
36 | /**
37 | * Points to the next free slot int the stack
38 | */
39 | private int stackPointer;
40 |
41 | /**
42 | * This timer is used to delay events in programs/games
43 | */
44 | private int delay_timer;
45 | /**
46 | * This timer is used to make a beeping sound
47 | */
48 | private int sound_timer;
49 |
50 | /**
51 | * This array will be our keyboard state
52 | */
53 | private byte[] keys;
54 | /**
55 | * The 64x32 pixel monochrome (black/white) display
56 | */
57 | private byte[] display;
58 |
59 | private boolean needRedraw;
60 |
61 | /**
62 | * Reset the Chip 8 memory and pointers
63 | */
64 | public void init() {
65 | memory = new char[4096];
66 | V = new char[16];
67 | I = 0x0;
68 | pc = 0x200;
69 |
70 | stack = new char[16];
71 | stackPointer = 0;
72 |
73 | delay_timer = 0;
74 | sound_timer = 0;
75 |
76 | keys = new byte[16];
77 |
78 | display = new byte[64 * 32];
79 |
80 | needRedraw = false;
81 | loadFontset();
82 | }
83 |
84 | /**
85 | * Executes a single Operation Code (Opcode)
86 | */
87 | public void run() {
88 | //fetch Opcode
89 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
90 | System.out.print(Integer.toHexString(opcode).toUpperCase() + ": ");
91 | //decode opcode
92 | switch(opcode & 0xF000) {
93 |
94 | case 0x0000: //Multi-case
95 | switch(opcode & 0x00FF) {
96 | case 0x00E0: //00E0: Clear Screen
97 | System.err.println("Unsupported Opcode!");
98 | System.exit(0);
99 | break;
100 |
101 | case 0x00EE: //00EE: Returns from subroutine
102 | stackPointer--;
103 | pc = (char)(stack[stackPointer] + 2);
104 | System.out.println("Returning to " + Integer.toHexString(pc).toUpperCase());
105 | break;
106 |
107 | default: //0NNN: Calls RCA 1802 Program at address NNN
108 | System.err.println("Unsupported Opcode!");
109 | System.exit(0);
110 | break;
111 | }
112 | break;
113 |
114 | case 0x1000: //1NNN: Jumps to address NNN
115 | int nnn = opcode & 0x0FFF;
116 | pc = (char)nnn;
117 | break;
118 |
119 | case 0x2000: //2NNN: Calls subroutine at NNN
120 | stack[stackPointer] = pc;
121 | stackPointer++;
122 | pc = (char)(opcode & 0x0FFF);
123 | System.out.println("Calling " + Integer.toHexString(pc).toUpperCase() + " from " + Integer.toHexString(stack[stackPointer - 1]).toUpperCase());
124 | break;
125 |
126 | case 0x3000: { //3XNN: Skips the next instruction if VX equals NN
127 | int x = (opcode & 0x0F00) >> 8;
128 | int nn = (opcode & 0x00FF);
129 | if(V[x] == nn) {
130 | pc += 4;
131 | System.out.println("Skipping next instruction (V[" + x +"] == " + nn + ")");
132 | } else {
133 | pc += 2;
134 | System.out.println("Not skipping next instruction (V[" + x +"] != " + nn + ")");
135 | }
136 | break;
137 | }
138 |
139 | case 0x6000: { //6XNN: Set VX to NN
140 | int x = (opcode & 0x0F00) >> 8;
141 | V[x] = (char)(opcode & 0x00FF);
142 | pc += 2;
143 | System.out.println("Setting V[" + x + "] to " + (int)V[x]);
144 | break;
145 | }
146 |
147 | case 0x7000: { //7XNN: Adds NN to VX
148 | int x = (opcode & 0x0F00) >> 8;
149 | int nn = (opcode & 0x00FF);
150 | V[x] = (char)((V[x] + nn) & 0xFF);
151 | pc += 2;
152 | System.out.println("Adding " + nn + " to V["+ x + "] = " + (int)V[x]);
153 | break;
154 | }
155 |
156 | case 0x8000: //Contains more data in last nibble
157 |
158 | switch(opcode & 0x000F) {
159 |
160 | case 0x0000: //8XY0: Sets VX to the value of VY.
161 | default:
162 | System.err.println("Unsupported Opcode!");
163 | System.exit(0);
164 | break;
165 | }
166 |
167 | break;
168 |
169 | case 0xA000: //ANNN: Set I to NNN
170 | I = (char)(opcode & 0x0FFF);
171 | pc += 2;
172 | System.out.println("Set I to " + Integer.toHexString(I).toUpperCase());
173 | break;
174 |
175 | case 0xD000: { //DXYN: Draw a sprite (X, Y) size (8, N). Sprite is located at I
176 | int x = V[(opcode & 0x0F00) >> 8];
177 | int y = V[(opcode & 0x00F0) >> 4];
178 | int height = opcode & 0x000F;
179 |
180 | V[0xF] = 0;
181 |
182 | for(int _y = 0; _y < height; _y++) {
183 | int line = memory[I + _y];
184 | for(int _x = 0; _x < 8; _x++) {
185 | int pixel = line & (0x80 >> _x);
186 | if(pixel != 0) {
187 | int totalX = x + _x;
188 | int totalY = y + _y;
189 | int index = totalY * 64 + totalX;
190 |
191 | if(display[index] == 1)
192 | V[0xF] = 1;
193 |
194 | display[index] ^= 1;
195 | }
196 | }
197 | }
198 | pc += 2;
199 | needRedraw = true;
200 | System.out.println("Drawing at V[" + ((opcode & 0x0F00) >> 8) + "] = " + x + ", V[" + ((opcode & 0x00F0) >> 4) + "] = " + y);
201 | break;
202 | }
203 |
204 | case 0xF000:
205 |
206 | switch(opcode & 0x00FF) {
207 |
208 | case 0x0029: { //Sets I to the location of the sprite for the character VX (Fontset)
209 | int x = (opcode & 0x0F00) >> 8;
210 | int character = V[x];
211 | I = (char)(0x050 + (character * 5));
212 | System.out.println("Setting I to Character V[" + x + "] = " + (int)V[x] + " Offset to 0x" + Integer.toHexString(I).toUpperCase());
213 | pc += 2;
214 | break;
215 | }
216 |
217 | case 0x0033: { //FX33 Store a binary-coded decimal value VX in I, I + 1 and I + 2
218 | int x = (opcode & 0x0F00) >> 8;
219 | int value = V[x];
220 | int hundreds = (value - (value % 100)) / 100;
221 | value -= hundreds * 100;
222 | int tens = (value - (value % 10))/ 10;
223 | value -= tens * 10;
224 | memory[I] = (char)hundreds;
225 | memory[I + 1] = (char)tens;
226 | memory[I + 2] = (char)value;
227 | System.out.println("Storing Binary-Coded Decimal V[" + x + "] = " + (int)(V[(opcode & 0x0F00) >> 8]) + " as { " + hundreds+ ", " + tens + ", " + value + "}");
228 | pc += 2;
229 | break;
230 | }
231 |
232 | case 0x0065: { //FX65 Filss V0 to VX with values from I
233 | int x = (opcode & 0x0F00) >> 8;
234 | for(int i = 0; i < x; i++) {
235 | V[i] = memory[I + i];
236 | }
237 | System.out.println("Setting V[0] to V[" + x + "] to the values of merory[0x" + Integer.toHexString(I & 0xFFFF).toUpperCase() + "]");
238 | pc += 2;
239 | break;
240 | }
241 |
242 | default:
243 | System.err.println("Unsupported Opcode!");
244 | System.exit(0);
245 | }
246 | break;
247 |
248 | default:
249 | System.err.println("Unsupported Opcode!");
250 | System.exit(0);
251 | }
252 | }
253 |
254 | /**
255 | * Returns the display data
256 | * @return
257 | * Current state of the 64x32 display
258 | */
259 | public byte[] getDisplay() {
260 | return display;
261 | }
262 |
263 | /**
264 | * Checks if there is a redraw needed
265 | * @return
266 | * If a redraw is needed
267 | */
268 | public boolean needsRedraw() {
269 | return needRedraw;
270 | }
271 |
272 | /**
273 | * Notify the chip that is has been redrawn
274 | */
275 | public void removeDrawFlag() {
276 | needRedraw = false;
277 | }
278 |
279 | /**
280 | * Loads the program into the memory
281 | * @param file
282 | * The location of the program
283 | */
284 | public void loadProgram(String file) {
285 | DataInputStream input = null;
286 | try {
287 | input = new DataInputStream(new FileInputStream(new File(file)));
288 |
289 | int offset = 0;
290 | while(input.available() > 0) {
291 | memory[0x200 + offset] = (char)(input.readByte() & 0xFF);
292 | offset++;
293 | }
294 |
295 | } catch (IOException e) {
296 | e.printStackTrace();
297 | System.exit(0);
298 | } finally {
299 | if(input != null) {
300 | try { input.close(); } catch (IOException ex) {}
301 | }
302 | }
303 | }
304 |
305 | /**
306 | * Loads the fontset into the memory
307 | */
308 | public void loadFontset() {
309 | for(int i = 0; i < ChipData.fontset.length; i++) {
310 | memory[0x50 + i] = (char)(ChipData.fontset[i] & 0xFF);
311 | }
312 | }
313 |
314 | public void setKeyBuffer(int[] keyBuffer) {
315 | for(int i = 0; i < keys.length; i++) {
316 | keys[i] = (byte)keyBuffer[i];
317 | }
318 | }
319 |
320 | }
321 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 5 - Coding the F codes/src/chip/ChipData.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class ChipData {
4 |
5 | /**
6 | * Fontset in bytes
7 | * Memory position 0x50
8 | */
9 | public static int[] fontset =
10 | {
11 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
12 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
13 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
14 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
15 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
16 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
17 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
18 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
19 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
20 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
21 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
22 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
23 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
24 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
25 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
26 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 5 - Coding the F codes/src/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 |
6 | import javax.swing.JFrame;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipFrame extends JFrame {
11 |
12 | private ChipPanel panel;
13 |
14 | public ChipFrame(Chip c) {
15 | setPreferredSize(new Dimension(640, 320));
16 | pack();
17 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
18 | panel = new ChipPanel(c);
19 | setLayout(new BorderLayout());
20 | add(panel, BorderLayout.CENTER);
21 | setDefaultCloseOperation(EXIT_ON_CLOSE);
22 | setTitle("Chip 8 Emulator");
23 | pack();
24 | setVisible(true);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 5 - Coding the F codes/src/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private Chip chip;
13 |
14 | public ChipPanel(Chip chip) {
15 | this.chip = chip;
16 | }
17 |
18 | public void paint(Graphics g) {
19 | byte[] display = chip.getDisplay();
20 | for(int i = 0; i < display.length; i++) {
21 | if(display[i] == 0)
22 | g.setColor(Color.BLACK);
23 | else
24 | g.setColor(Color.WHITE);
25 |
26 | int x = (i % 64);
27 | int y = (int)Math.floor(i / 64);
28 |
29 | g.fillRect(x * 10, y * 10, 10, 10);
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 5 - Coding the F codes/src/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main extends Thread {
6 |
7 | private Chip chip8;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | chip8 = new Chip();
12 | chip8.init();
13 | chip8.loadProgram("./pong2.c8");
14 | frame = new ChipFrame(chip8);
15 | }
16 |
17 | public void run() {
18 | //60 hz, 60 updates per second
19 | while(true) {
20 | chip8.run();
21 | if(chip8.needsRedraw()) {
22 | frame.repaint();
23 | chip8.removeDrawFlag();
24 | }
25 | try {
26 | Thread.sleep(16);
27 | } catch (InterruptedException e) {
28 | //Unthrown exception
29 | }
30 | }
31 | }
32 |
33 | public static void main(String[] args) {
34 | Main main = new Main();
35 | main.start();
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 6 - Opcodes/src/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 | import java.util.Random;
8 |
9 | public class Chip {
10 |
11 | /**
12 | * 4kB of 8-bit memory
13 | * At position 0x50: The "bios" fontset
14 | * At position 0x200: The start of every program
15 | */
16 | private char[] memory;
17 | /**
18 | * 16 8-bit registers.
19 | * They will be used to store data which is used in several operation
20 | * Register 0xF is used for Carry, Borrow and collision detection
21 | */
22 | private char[] V;
23 | /**
24 | * 16-bit (only 12 are used) to point to a specific point in the memory
25 | */
26 | private char I;
27 | /**
28 | * The 16-bit (only 12 are used) to point to the current operation
29 | */
30 | private char pc;
31 |
32 | /**
33 | * Subroutine callstack
34 | * Allows up to 16 levels of nesting
35 | */
36 | private char stack[];
37 | /**
38 | * Points to the next free slot int the stack
39 | */
40 | private int stackPointer;
41 |
42 | /**
43 | * This timer is used to delay events in programs/games
44 | */
45 | private int delay_timer;
46 | /**
47 | * This timer is used to make a beeping sound
48 | */
49 | private int sound_timer;
50 |
51 | /**
52 | * This array will be our keyboard state
53 | */
54 | private byte[] keys;
55 | /**
56 | * The 64x32 pixel monochrome (black/white) display
57 | */
58 | private byte[] display;
59 |
60 | private boolean needRedraw;
61 |
62 | /**
63 | * Reset the Chip 8 memory and pointers
64 | */
65 | public void init() {
66 | memory = new char[4096];
67 | V = new char[16];
68 | I = 0x0;
69 | pc = 0x200;
70 |
71 | stack = new char[16];
72 | stackPointer = 0;
73 |
74 | delay_timer = 0;
75 | sound_timer = 0;
76 |
77 | keys = new byte[16];
78 |
79 | display = new byte[64 * 32];
80 |
81 | needRedraw = false;
82 | loadFontset();
83 | }
84 |
85 | /**
86 | * Executes a single Operation Code (Opcode)
87 | */
88 | public void run() {
89 | //fetch Opcode
90 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
91 | System.out.print(Integer.toHexString(opcode).toUpperCase() + ": ");
92 | //decode opcode
93 | switch(opcode & 0xF000) {
94 |
95 | case 0x0000: //Multi-case
96 | switch(opcode & 0x00FF) {
97 | case 0x00E0: //00E0: Clear Screen
98 | System.err.println("Unsupported Opcode!");
99 | System.exit(0);
100 | break;
101 |
102 | case 0x00EE: //00EE: Returns from subroutine
103 | stackPointer--;
104 | pc = (char)(stack[stackPointer] + 2);
105 | System.out.println("Returning to " + Integer.toHexString(pc).toUpperCase());
106 | break;
107 |
108 | default: //0NNN: Calls RCA 1802 Program at address NNN
109 | System.err.println("Unsupported Opcode!");
110 | System.exit(0);
111 | break;
112 | }
113 | break;
114 |
115 | case 0x1000: //1NNN: Jumps to address NNN
116 | int nnn = opcode & 0x0FFF;
117 | pc = (char)nnn;
118 | System.out.println("Jumping to " + Integer.toHexString(pc).toUpperCase());
119 | break;
120 |
121 | case 0x2000: //2NNN: Calls subroutine at NNN
122 | stack[stackPointer] = pc;
123 | stackPointer++;
124 | pc = (char)(opcode & 0x0FFF);
125 | System.out.println("Calling " + Integer.toHexString(pc).toUpperCase() + " from " + Integer.toHexString(stack[stackPointer - 1]).toUpperCase());
126 | break;
127 |
128 | case 0x3000: { //3XNN: Skips the next instruction if VX equals NN
129 | int x = (opcode & 0x0F00) >> 8;
130 | int nn = (opcode & 0x00FF);
131 | if(V[x] == nn) {
132 | pc += 4;
133 | System.out.println("Skipping next instruction (V[" + x +"] == " + nn + ")");
134 | } else {
135 | pc += 2;
136 | System.out.println("Not skipping next instruction (V[" + x +"] != " + nn + ")");
137 | }
138 | break;
139 | }
140 |
141 | case 0x6000: { //6XNN: Set VX to NN
142 | int x = (opcode & 0x0F00) >> 8;
143 | V[x] = (char)(opcode & 0x00FF);
144 | pc += 2;
145 | System.out.println("Setting V[" + x + "] to " + (int)V[x]);
146 | break;
147 | }
148 |
149 | case 0x7000: { //7XNN: Adds NN to VX
150 | int x = (opcode & 0x0F00) >> 8;
151 | int nn = (opcode & 0x00FF);
152 | V[x] = (char)((V[x] + nn) & 0xFF);
153 | pc += 2;
154 | System.out.println("Adding " + nn + " to V["+ x + "] = " + (int)V[x]);
155 | break;
156 | }
157 |
158 | case 0x8000: //Contains more data in last nibble
159 |
160 | switch(opcode & 0x000F) {
161 |
162 | case 0x0000: //8XY0: Sets VX to the value of VY.
163 | default:
164 | System.err.println("Unsupported Opcode!");
165 | System.exit(0);
166 | break;
167 | }
168 |
169 | break;
170 |
171 | case 0xA000: //ANNN: Set I to NNN
172 | I = (char)(opcode & 0x0FFF);
173 | pc += 2;
174 | System.out.println("Set I to " + Integer.toHexString(I).toUpperCase());
175 | break;
176 |
177 | case 0xC000: { //CXNN: Set VX to a random number and NN
178 | int x = (opcode & 0x0F00) >> 8;
179 | int nn = (opcode & 0x00FF);
180 | int randomNumber = new Random().nextInt(256) & nn;
181 | System.out.println("V[" + x + "] has been set to (randomised) " + randomNumber);
182 | V[x] = (char)randomNumber;
183 | pc += 2;
184 | }
185 |
186 | case 0xD000: { //DXYN: Draw a sprite (X, Y) size (8, N). Sprite is located at I
187 | int x = V[(opcode & 0x0F00) >> 8];
188 | int y = V[(opcode & 0x00F0) >> 4];
189 | int height = opcode & 0x000F;
190 |
191 | V[0xF] = 0;
192 |
193 | for(int _y = 0; _y < height; _y++) {
194 | int line = memory[I + _y];
195 | for(int _x = 0; _x < 8; _x++) {
196 | int pixel = line & (0x80 >> _x);
197 | if(pixel != 0) {
198 | int totalX = x + _x;
199 | int totalY = y + _y;
200 | int index = totalY * 64 + totalX;
201 |
202 | if(display[index] == 1)
203 | V[0xF] = 1;
204 |
205 | display[index] ^= 1;
206 | }
207 | }
208 | }
209 | pc += 2;
210 | needRedraw = true;
211 | System.out.println("Drawing at V[" + ((opcode & 0x0F00) >> 8) + "] = " + x + ", V[" + ((opcode & 0x00F0) >> 4) + "] = " + y);
212 | break;
213 | }
214 |
215 | case 0xE000: {
216 | switch (opcode & 0x00FF) {
217 | case 0x009E: { //EX9E Skip the next instruction if the Key VX is pressed
218 | int key = (opcode & 0x0F00) >> 8;
219 | if(keys[key] == 1) {
220 | pc += 4;
221 | } else {
222 | pc += 2;
223 | }
224 | break;
225 | }
226 |
227 | case 0x00A1: { //EXA1 Skip the next instruction if the Key VX is NOT pressed
228 | int key = (opcode & 0x0F00) >> 8;
229 | if(keys[key] == 0) {
230 | pc += 4;
231 | } else {
232 | pc += 2;
233 | }
234 | break;
235 | }
236 |
237 | default:
238 | System.err.println("Unexisting opcode");
239 | System.exit(0);
240 | return;
241 | }
242 | break;
243 | }
244 |
245 | case 0xF000:
246 |
247 | switch(opcode & 0x00FF) {
248 |
249 | case 0x0007: { //FX07: Set VX to the value of delay_timer
250 | int x = (opcode & 0x0F00) >> 8;
251 | V[x] = (char)delay_timer;
252 | pc += 2;
253 | System.out.println("V[" + x + "] has been set to " + delay_timer);
254 | }
255 |
256 | case 0x0015: { //FX15: Set delay timer to V[x]
257 | int x = (opcode & 0x0F00) >> 8;
258 | delay_timer = V[x];
259 | pc += 2;
260 | System.out.println("Set delay_timer to V[" + x + "] = " + (int)V[x]);
261 | }
262 |
263 | case 0x0029: { //FX29: Sets I to the location of the sprite for the character VX (Fontset)
264 | int x = (opcode & 0x0F00) >> 8;
265 | int character = V[x];
266 | I = (char)(0x050 + (character * 5));
267 | System.out.println("Setting I to Character V[" + x + "] = " + (int)V[x] + " Offset to 0x" + Integer.toHexString(I).toUpperCase());
268 | pc += 2;
269 | break;
270 | }
271 |
272 | case 0x0033: { //FX33 Store a binary-coded decimal value VX in I, I + 1 and I + 2
273 | int x = (opcode & 0x0F00) >> 8;
274 | int value = V[x];
275 | int hundreds = (value - (value % 100)) / 100;
276 | value -= hundreds * 100;
277 | int tens = (value - (value % 10))/ 10;
278 | value -= tens * 10;
279 | memory[I] = (char)hundreds;
280 | memory[I + 1] = (char)tens;
281 | memory[I + 2] = (char)value;
282 | System.out.println("Storing Binary-Coded Decimal V[" + x + "] = " + (int)(V[(opcode & 0x0F00) >> 8]) + " as { " + hundreds+ ", " + tens + ", " + value + "}");
283 | pc += 2;
284 | break;
285 | }
286 |
287 | case 0x0065: { //FX65 Filss V0 to VX with values from I
288 | int x = (opcode & 0x0F00) >> 8;
289 | for(int i = 0; i < x; i++) {
290 | V[i] = memory[I + i];
291 | }
292 | System.out.println("Setting V[0] to V[" + x + "] to the values of merory[0x" + Integer.toHexString(I & 0xFFFF).toUpperCase() + "]");
293 | pc += 2;
294 | break;
295 | }
296 |
297 | default:
298 | System.err.println("Unsupported Opcode!");
299 | System.exit(0);
300 | }
301 | break;
302 |
303 | default:
304 | System.err.println("Unsupported Opcode!");
305 | System.exit(0);
306 | }
307 | }
308 |
309 | /**
310 | * Returns the display data
311 | * @return
312 | * Current state of the 64x32 display
313 | */
314 | public byte[] getDisplay() {
315 | return display;
316 | }
317 |
318 | /**
319 | * Checks if there is a redraw needed
320 | * @return
321 | * If a redraw is needed
322 | */
323 | public boolean needsRedraw() {
324 | return needRedraw;
325 | }
326 |
327 | /**
328 | * Notify the chip that is has been redrawn
329 | */
330 | public void removeDrawFlag() {
331 | needRedraw = false;
332 | }
333 |
334 | /**
335 | * Loads the program into the memory
336 | * @param file
337 | * The location of the program
338 | */
339 | public void loadProgram(String file) {
340 | DataInputStream input = null;
341 | try {
342 | input = new DataInputStream(new FileInputStream(new File(file)));
343 |
344 | int offset = 0;
345 | while(input.available() > 0) {
346 | memory[0x200 + offset] = (char)(input.readByte() & 0xFF);
347 | offset++;
348 | }
349 |
350 | } catch (IOException e) {
351 | e.printStackTrace();
352 | System.exit(0);
353 | } finally {
354 | if(input != null) {
355 | try { input.close(); } catch (IOException ex) {}
356 | }
357 | }
358 | }
359 |
360 | /**
361 | * Loads the fontset into the memory
362 | */
363 | public void loadFontset() {
364 | for(int i = 0; i < ChipData.fontset.length; i++) {
365 | memory[0x50 + i] = (char)(ChipData.fontset[i] & 0xFF);
366 | }
367 | }
368 |
369 | public void setKeyBuffer(int[] keyBuffer) {
370 | for(int i = 0; i < keys.length; i++) {
371 | keys[i] = (byte)keyBuffer[i];
372 | }
373 | }
374 |
375 | }
376 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 6 - Opcodes/src/chip/ChipData.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class ChipData {
4 |
5 | /**
6 | * Fontset in bytes
7 | * Memory position 0x50
8 | */
9 | public static int[] fontset =
10 | {
11 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
12 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
13 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
14 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
15 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
16 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
17 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
18 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
19 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
20 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
21 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
22 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
23 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
24 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
25 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
26 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 6 - Opcodes/src/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 | import java.awt.event.KeyEvent;
6 | import java.awt.event.KeyListener;
7 |
8 | import javax.swing.JFrame;
9 |
10 | import chip.Chip;
11 |
12 | public class ChipFrame extends JFrame implements KeyListener {
13 |
14 | private static final long serialVersionUID = 1L;
15 | private ChipPanel panel;
16 | private int[] keyBuffer;
17 | private int[] keyIdToKey;
18 |
19 | public ChipFrame(Chip c) {
20 | setPreferredSize(new Dimension(640, 320));
21 | pack();
22 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
23 | panel = new ChipPanel(c);
24 | setLayout(new BorderLayout());
25 | add(panel, BorderLayout.CENTER);
26 | setDefaultCloseOperation(EXIT_ON_CLOSE);
27 | setTitle("Chip 8 Emulator");
28 | pack();
29 | setVisible(true);
30 |
31 | keyIdToKey = new int[256];
32 | keyBuffer = new int[16];
33 | fillKeyIds();
34 | }
35 |
36 | private void fillKeyIds() {
37 | for(int i = 0; i < keyIdToKey.length; i++) {
38 | keyIdToKey[i] = -1;
39 | }
40 | keyIdToKey['1'] = 1;
41 | keyIdToKey['2'] = 2;
42 | keyIdToKey['3'] = 3;
43 | keyIdToKey['Q'] = 4;
44 | keyIdToKey['W'] = 5;
45 | keyIdToKey['E'] = 6;
46 | keyIdToKey['A'] = 7;
47 | keyIdToKey['S'] = 8;
48 | keyIdToKey['D'] = 9;
49 | keyIdToKey['Z'] = 0xA;
50 | keyIdToKey['X'] = 0;
51 | keyIdToKey['C'] = 0xB;
52 | keyIdToKey['4'] = 0xC;
53 | keyIdToKey['R'] = 0xD;
54 | keyIdToKey['F'] = 0xE;
55 | keyIdToKey['V'] = 0xF;
56 | }
57 |
58 | @Override
59 | public void keyPressed(KeyEvent e) {
60 | if(keyIdToKey[e.getKeyCode()] != -1) {
61 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 1;
62 | }
63 | }
64 |
65 | @Override
66 | public void keyReleased(KeyEvent e) {
67 | if(keyIdToKey[e.getKeyCode()] != -1) {
68 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 0;
69 | }
70 | }
71 |
72 | @Override
73 | public void keyTyped(KeyEvent e) {
74 | }
75 |
76 | public int[] getKeyBuffer() {
77 | return keyBuffer;
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 6 - Opcodes/src/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private static final long serialVersionUID = 1L;
13 | private Chip chip;
14 |
15 | public ChipPanel(Chip chip) {
16 | this.chip = chip;
17 | }
18 |
19 | public void paint(Graphics g) {
20 | byte[] display = chip.getDisplay();
21 | for(int i = 0; i < display.length; i++) {
22 | if(display[i] == 0)
23 | g.setColor(Color.BLACK);
24 | else
25 | g.setColor(Color.WHITE);
26 |
27 | int x = (i % 64);
28 | int y = (int)Math.floor(i / 64);
29 |
30 | g.fillRect(x * 10, y * 10, 10, 10);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 6 - Opcodes/src/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main extends Thread {
6 |
7 | private Chip chip8;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | chip8 = new Chip();
12 | chip8.init();
13 | chip8.loadProgram("./pong2.c8");
14 | frame = new ChipFrame(chip8);
15 | }
16 |
17 | public void run() {
18 | //60 hz, 60 updates per second
19 | while(true) {
20 | chip8.setKeyBuffer(frame.getKeyBuffer());
21 | chip8.run();
22 | if(chip8.needsRedraw()) {
23 | frame.repaint();
24 | chip8.removeDrawFlag();
25 | }
26 | try {
27 | Thread.sleep(16);
28 | } catch (InterruptedException e) {
29 | //Unthrown exception
30 | }
31 | }
32 | }
33 |
34 | public static void main(String[] args) {
35 | Main main = new Main();
36 | main.start();
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 8 - Debugging/src/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 | import java.util.Random;
8 |
9 | public class Chip {
10 |
11 | /**
12 | * 4kB of 8-bit memory
13 | * At position 0x50: The "bios" fontset
14 | * At position 0x200: The start of every program
15 | */
16 | private char[] memory;
17 | /**
18 | * 16 8-bit registers.
19 | * They will be used to store data which is used in several operation
20 | * Register 0xF is used for Carry, Borrow and collision detection
21 | */
22 | private char[] V;
23 | /**
24 | * 16-bit (only 12 are used) to point to a specific point in the memory
25 | */
26 | private char I;
27 | /**
28 | * The 16-bit (only 12 are used) to point to the current operation
29 | */
30 | private char pc;
31 |
32 | /**
33 | * Subroutine callstack
34 | * Allows up to 16 levels of nesting
35 | */
36 | private char stack[];
37 | /**
38 | * Points to the next free slot int the stack
39 | */
40 | private int stackPointer;
41 |
42 | /**
43 | * This timer is used to delay events in programs/games
44 | */
45 | private int delay_timer;
46 | /**
47 | * This timer is used to make a beeping sound
48 | */
49 | private int sound_timer;
50 |
51 | /**
52 | * This array will be our keyboard state
53 | */
54 | private byte[] keys;
55 | /**
56 | * The 64x32 pixel monochrome (black/white) display
57 | */
58 | private byte[] display;
59 |
60 | private boolean needRedraw;
61 |
62 | /**
63 | * Reset the Chip 8 memory and pointers
64 | */
65 | public void init() {
66 | memory = new char[4096];
67 | V = new char[16];
68 | I = 0x0;
69 | pc = 0x200;
70 |
71 | stack = new char[16];
72 | stackPointer = 0;
73 |
74 | delay_timer = 0;
75 | sound_timer = 0;
76 |
77 | keys = new byte[16];
78 |
79 | display = new byte[64 * 32];
80 |
81 | needRedraw = false;
82 | loadFontset();
83 | }
84 |
85 | /**
86 | * Executes a single Operation Code (Opcode)
87 | */
88 | public void run() {
89 | //fetch Opcode
90 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
91 | System.out.print(Integer.toHexString(opcode).toUpperCase() + ": ");
92 | //decode opcode
93 | switch(opcode & 0xF000) {
94 |
95 | case 0x0000: //Multi-case
96 | switch(opcode & 0x00FF) {
97 | case 0x00E0: //00E0: Clear Screen
98 | System.err.println("Unsupported Opcode!");
99 | System.exit(0);
100 | break;
101 |
102 | case 0x00EE: //00EE: Returns from subroutine
103 | stackPointer--;
104 | pc = (char)(stack[stackPointer] + 2);
105 | System.out.println("Returning to " + Integer.toHexString(pc).toUpperCase());
106 | break;
107 |
108 | default: //0NNN: Calls RCA 1802 Program at address NNN
109 | System.err.println("Unsupported Opcode!");
110 | System.exit(0);
111 | break;
112 | }
113 | break;
114 |
115 | case 0x1000: //1NNN: Jumps to address NNN
116 | int nnn = opcode & 0x0FFF;
117 | pc = (char)nnn;
118 | System.out.println("Jumping to " + Integer.toHexString(pc).toUpperCase());
119 | break;
120 |
121 | case 0x2000: //2NNN: Calls subroutine at NNN
122 | stack[stackPointer] = pc;
123 | stackPointer++;
124 | pc = (char)(opcode & 0x0FFF);
125 | System.out.println("Calling " + Integer.toHexString(pc).toUpperCase() + " from " + Integer.toHexString(stack[stackPointer - 1]).toUpperCase());
126 | break;
127 |
128 | case 0x3000: { //3XNN: Skips the next instruction if VX equals NN
129 | int x = (opcode & 0x0F00) >> 8;
130 | int nn = (opcode & 0x00FF);
131 | if(V[x] == nn) {
132 | pc += 4;
133 | System.out.println("Skipping next instruction (V[" + x +"] == " + nn + ")");
134 | } else {
135 | pc += 2;
136 | System.out.println("Not skipping next instruction (V[" + x +"] != " + nn + ")");
137 | }
138 | break;
139 | }
140 |
141 | case 0x4000: { //4XNN: Skip the next instruction if VX != NN
142 | int x = (opcode & 0x0F00) >> 8;
143 | int nn = opcode & 0x00FF;
144 | if(V[x] != nn) {
145 | System.out.println("Skipping next instruction V[" + x + "] = " + (int)V[x] + " != " + nn);
146 | pc += 4;
147 | } else {
148 | System.out.println("Not skipping next instruction V[" + x + "] = " + (int)V[x] + " == " + nn);
149 | pc += 2;
150 | }
151 | break;
152 | }
153 |
154 | case 0x6000: { //6XNN: Set VX to NN
155 | int x = (opcode & 0x0F00) >> 8;
156 | V[x] = (char)(opcode & 0x00FF);
157 | pc += 2;
158 | System.out.println("Setting V[" + x + "] to " + (int)V[x]);
159 | break;
160 | }
161 |
162 | case 0x7000: { //7XNN: Adds NN to VX
163 | int x = (opcode & 0x0F00) >> 8;
164 | int nn = (opcode & 0x00FF);
165 | V[x] = (char)((V[x] + nn) & 0xFF);
166 | pc += 2;
167 | System.out.println("Adding " + nn + " to V["+ x + "] = " + (int)V[x]);
168 | break;
169 | }
170 |
171 | case 0x8000: //Contains more data in last nibble
172 |
173 | switch(opcode & 0x000F) {
174 |
175 | case 0x0002: { //8XY2: Sets VX to VX AND VY
176 | int x = (opcode & 0x0F00) >> 8;
177 | int y = (opcode & 0x00F0) >> 4;
178 | System.out.println("Set V[" + x + "] to V[" + x + "] = " + (int)V[x] + " & V[" + y + "] = " + (int)V[y] + " = " + (int)(V[x] & V[y]));
179 | V[x] = (char)(V[x] & V[y]);
180 | pc += 2;
181 | break;
182 | }
183 |
184 | case 0x0004: { //Adds VY to VX. VF is set to 1 when carry applies else to 0
185 | int x = (opcode & 0x0F00) >> 8;
186 | int y = (opcode & 0x00F0) >> 4;
187 | System.out.print("Adding V[" + x + "] (" + (int)V[x] + ") to V[" + y + "] (" + (int)V[y] + ") = " + ((V[x] + V[y]) & 0xFF) + ", ");
188 | if(V[y] > 0xFF - V[x]) {
189 | V[0xF] = 1;
190 | System.out.println("Carry!");
191 | } else {
192 | V[0xF] = 0;
193 | System.out.println("No Carry");
194 | }
195 | V[x] = (char)((V[x] + V[y]) & 0xFF);
196 | pc += 2;
197 | break;
198 | }
199 |
200 | default:
201 | System.err.println("Unsupported Opcode!");
202 | System.exit(0);
203 | break;
204 | }
205 |
206 | break;
207 |
208 | case 0xA000: //ANNN: Set I to NNN
209 | I = (char)(opcode & 0x0FFF);
210 | pc += 2;
211 | System.out.println("Set I to " + Integer.toHexString(I).toUpperCase());
212 | break;
213 |
214 | case 0xC000: { //CXNN: Set VX to a random number and NN
215 | int x = (opcode & 0x0F00) >> 8;
216 | int nn = (opcode & 0x00FF);
217 | int randomNumber = new Random().nextInt(255) & nn;
218 | System.out.println("V[" + x + "] has been set to (randomised) " + randomNumber);
219 | V[x] = (char)randomNumber;
220 | pc += 2;
221 | break;
222 | }
223 |
224 | case 0xD000: { //DXYN: Draw a sprite (X, Y) size (8, N). Sprite is located at I
225 | int x = V[(opcode & 0x0F00) >> 8];
226 | int y = V[(opcode & 0x00F0) >> 4];
227 | int height = opcode & 0x000F;
228 |
229 | V[0xF] = 0;
230 |
231 | for(int _y = 0; _y < height; _y++) {
232 | int line = memory[I + _y];
233 | for(int _x = 0; _x < 8; _x++) {
234 | int pixel = line & (0x80 >> _x);
235 | if(pixel != 0) {
236 | int totalX = x + _x;
237 | int totalY = y + _y;
238 | int index = (totalY * 64) + totalX;
239 |
240 | if(display[index] == 1)
241 | V[0xF] = 1;
242 |
243 | display[index] ^= 1;
244 | }
245 | }
246 | }
247 | pc += 2;
248 | needRedraw = true;
249 | System.out.println("Drawing at V[" + ((opcode & 0x0F00) >> 8) + "] = " + x + ", V[" + ((opcode & 0x00F0) >> 4) + "] = " + y);
250 | break;
251 | }
252 |
253 | case 0xE000: {
254 | switch (opcode & 0x00FF) {
255 | case 0x009E: { //EX9E Skip the next instruction if the Key VX is pressed
256 | int x = (opcode & 0x0F00) >> 8;
257 | int key = V[x];
258 | if(keys[key] == 1) {
259 | pc += 4;
260 | } else {
261 | pc += 2;
262 | }
263 | System.out.println("Skipping next instruction if V[" + (int)V[x] + "] is pressed");
264 | break;
265 | }
266 |
267 | case 0x00A1: { //EXA1 Skip the next instruction if the Key VX is NOT pressed
268 | int x = (opcode & 0x0F00) >> 8;
269 | int key = V[x];
270 | if(keys[key] == 0) {
271 | pc += 4;
272 | } else {
273 | pc += 2;
274 | }
275 | System.out.println("Skipping next instruction if V[" + (int)V[x] + "] is NOT pressed");
276 | break;
277 | }
278 |
279 | default:
280 | System.err.println("Unexisting opcode");
281 | System.exit(0);
282 | return;
283 | }
284 | break;
285 | }
286 |
287 | case 0xF000:
288 |
289 | switch(opcode & 0x00FF) {
290 |
291 | case 0x0007: { //FX07: Set VX to the value of delay_timer
292 | int x = (opcode & 0x0F00) >> 8;
293 | V[x] = (char)delay_timer;
294 | pc += 2;
295 | System.out.println("V[" + x + "] has been set to " + delay_timer);
296 | }
297 |
298 | case 0x0015: { //FX15: Set delay timer to V[x]
299 | int x = (opcode & 0x0F00) >> 8;
300 | delay_timer = V[x];
301 | pc += 2;
302 | System.out.println("Set delay_timer to V[" + x + "] = " + (int)V[x]);
303 | }
304 |
305 | case 0x0029: { //FX29: Sets I to the location of the sprite for the character VX (Fontset)
306 | int x = (opcode & 0x0F00) >> 8;
307 | int character = V[x];
308 | I = (char)(0x050 + (character * 5));
309 | System.out.println("Setting I to Character V[" + x + "] = " + (int)V[x] + " Offset to 0x" + Integer.toHexString(I).toUpperCase());
310 | pc += 2;
311 | break;
312 | }
313 |
314 | case 0x0033: { //FX33 Store a binary-coded decimal value VX in I, I + 1 and I + 2
315 | int x = (opcode & 0x0F00) >> 8;
316 | int value = V[x];
317 | int hundreds = (value - (value % 100)) / 100;
318 | value -= hundreds * 100;
319 | int tens = (value - (value % 10))/ 10;
320 | value -= tens * 10;
321 | memory[I] = (char)hundreds;
322 | memory[I + 1] = (char)tens;
323 | memory[I + 2] = (char)value;
324 | System.out.println("Storing Binary-Coded Decimal V[" + x + "] = " + (int)(V[(opcode & 0x0F00) >> 8]) + " as { " + hundreds+ ", " + tens + ", " + value + "}");
325 | pc += 2;
326 | break;
327 | }
328 |
329 | case 0x0065: { //FX65 Filss V0 to VX with values from I
330 | int x = (opcode & 0x0F00) >> 8;
331 | for(int i = 0; i < x; i++) {
332 | V[i] = memory[I + i];
333 | }
334 | System.out.println("Setting V[0] to V[" + x + "] to the values of memory[0x" + Integer.toHexString(I & 0xFFFF).toUpperCase() + "]");
335 | I = (char)(I + x + 1);
336 | pc += 2;
337 | break;
338 | }
339 |
340 | default:
341 | System.err.println("Unsupported Opcode!");
342 | System.exit(0);
343 | }
344 | break;
345 |
346 | default:
347 | System.err.println("Unsupported Opcode!");
348 | System.exit(0);
349 | }
350 | }
351 |
352 | /**
353 | * Returns the display data
354 | * @return
355 | * Current state of the 64x32 display
356 | */
357 | public byte[] getDisplay() {
358 | return display;
359 | }
360 |
361 | /**
362 | * Checks if there is a redraw needed
363 | * @return
364 | * If a redraw is needed
365 | */
366 | public boolean needsRedraw() {
367 | return needRedraw;
368 | }
369 |
370 | /**
371 | * Notify the chip that is has been redrawn
372 | */
373 | public void removeDrawFlag() {
374 | needRedraw = false;
375 | }
376 |
377 | /**
378 | * Loads the program into the memory
379 | * @param file
380 | * The location of the program
381 | */
382 | public void loadProgram(String file) {
383 | DataInputStream input = null;
384 | try {
385 | input = new DataInputStream(new FileInputStream(new File(file)));
386 |
387 | int offset = 0;
388 | while(input.available() > 0) {
389 | memory[0x200 + offset] = (char)(input.readByte() & 0xFF);
390 | offset++;
391 | }
392 |
393 | } catch (IOException e) {
394 | e.printStackTrace();
395 | System.exit(0);
396 | } finally {
397 | if(input != null) {
398 | try { input.close(); } catch (IOException ex) {}
399 | }
400 | }
401 | }
402 |
403 | /**
404 | * Loads the fontset into the memory
405 | */
406 | public void loadFontset() {
407 | for(int i = 0; i < ChipData.fontset.length; i++) {
408 | memory[0x50 + i] = (char)(ChipData.fontset[i] & 0xFF);
409 | }
410 | }
411 |
412 | public void setKeyBuffer(int[] keyBuffer) {
413 | for(int i = 0; i < keys.length; i++) {
414 | keys[i] = (byte)keyBuffer[i];
415 | }
416 | }
417 |
418 | }
419 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 8 - Debugging/src/chip/ChipData.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class ChipData {
4 |
5 | /**
6 | * Fontset in bytes
7 | * Memory position 0x50
8 | */
9 | public static int[] fontset =
10 | {
11 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
12 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
13 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
14 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
15 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
16 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
17 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
18 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
19 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
20 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
21 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
22 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
23 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
24 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
25 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
26 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 8 - Debugging/src/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 | import java.awt.event.KeyEvent;
6 | import java.awt.event.KeyListener;
7 |
8 | import javax.swing.JFrame;
9 |
10 | import chip.Chip;
11 |
12 | public class ChipFrame extends JFrame implements KeyListener {
13 |
14 | private static final long serialVersionUID = 1L;
15 | private ChipPanel panel;
16 | private int[] keyBuffer;
17 | private int[] keyIdToKey;
18 |
19 | public ChipFrame(Chip c) {
20 | setPreferredSize(new Dimension(640, 320));
21 | pack();
22 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
23 | panel = new ChipPanel(c);
24 | setLayout(new BorderLayout());
25 | add(panel, BorderLayout.CENTER);
26 | setDefaultCloseOperation(EXIT_ON_CLOSE);
27 | setTitle("Chip 8 Emulator");
28 | pack();
29 | setVisible(true);
30 | addKeyListener(this);
31 |
32 | keyIdToKey = new int[256];
33 | keyBuffer = new int[16];
34 | fillKeyIds();
35 | }
36 |
37 | private void fillKeyIds() {
38 | for(int i = 0; i < keyIdToKey.length; i++) {
39 | keyIdToKey[i] = -1;
40 | }
41 | keyIdToKey['1'] = 1;
42 | keyIdToKey['2'] = 2;
43 | keyIdToKey['3'] = 3;
44 | keyIdToKey['Q'] = 4;
45 | keyIdToKey['W'] = 5;
46 | keyIdToKey['E'] = 6;
47 | keyIdToKey['A'] = 7;
48 | keyIdToKey['S'] = 8;
49 | keyIdToKey['D'] = 9;
50 | keyIdToKey['Z'] = 0xA;
51 | keyIdToKey['X'] = 0;
52 | keyIdToKey['C'] = 0xB;
53 | keyIdToKey['4'] = 0xC;
54 | keyIdToKey['R'] = 0xD;
55 | keyIdToKey['F'] = 0xE;
56 | keyIdToKey['V'] = 0xF;
57 | }
58 |
59 | @Override
60 | public void keyPressed(KeyEvent e) {
61 | if(keyIdToKey[e.getKeyCode()] != -1) {
62 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 1;
63 | }
64 | }
65 |
66 | @Override
67 | public void keyReleased(KeyEvent e) {
68 | if(keyIdToKey[e.getKeyCode()] != -1) {
69 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 0;
70 | }
71 | }
72 |
73 | @Override
74 | public void keyTyped(KeyEvent e) {
75 | }
76 |
77 | public int[] getKeyBuffer() {
78 | return keyBuffer;
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 8 - Debugging/src/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private static final long serialVersionUID = 1L;
13 | private Chip chip;
14 |
15 | public ChipPanel(Chip chip) {
16 | this.chip = chip;
17 | }
18 |
19 | public void paint(Graphics g) {
20 | byte[] display = chip.getDisplay();
21 | for(int i = 0; i < display.length; i++) {
22 | if(display[i] == 0)
23 | g.setColor(Color.BLACK);
24 | else
25 | g.setColor(Color.WHITE);
26 |
27 | int x = (i % 64);
28 | int y = (int)Math.floor(i / 64);
29 |
30 | g.fillRect(x * 10, y * 10, 10, 10);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 8 - Debugging/src/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main extends Thread {
6 |
7 | private Chip chip8;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | chip8 = new Chip();
12 | chip8.init();
13 | chip8.loadProgram("./pong2.c8");
14 | frame = new ChipFrame(chip8);
15 | }
16 |
17 | public void run() {
18 | //60 hz, 60 updates per second
19 | while(true) {
20 | chip8.setKeyBuffer(frame.getKeyBuffer());
21 | chip8.run();
22 | if(chip8.needsRedraw()) {
23 | frame.repaint();
24 | chip8.removeDrawFlag();
25 | }
26 | try {
27 | Thread.sleep(16);
28 | } catch (InterruptedException e) {
29 | //Unthrown exception
30 | }
31 | }
32 | }
33 |
34 | public static void main(String[] args) {
35 | Main main = new Main();
36 | main.start();
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 9 - Pong/src/chip/Chip.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 | import java.util.Random;
8 |
9 | public class Chip {
10 |
11 | /**
12 | * 4kB of 8-bit memory
13 | * At position 0x50: The "bios" fontset
14 | * At position 0x200: The start of every program
15 | */
16 | private char[] memory;
17 | /**
18 | * 16 8-bit registers.
19 | * They will be used to store data which is used in several operation
20 | * Register 0xF is used for Carry, Borrow and collision detection
21 | */
22 | private char[] V;
23 | /**
24 | * 16-bit (only 12 are used) to point to a specific point in the memory
25 | */
26 | private char I;
27 | /**
28 | * The 16-bit (only 12 are used) to point to the current operation
29 | */
30 | private char pc;
31 |
32 | /**
33 | * Subroutine callstack
34 | * Allows up to 16 levels of nesting
35 | */
36 | private char stack[];
37 | /**
38 | * Points to the next free slot int the stack
39 | */
40 | private int stackPointer;
41 |
42 | /**
43 | * This timer is used to delay events in programs/games
44 | */
45 | private int delay_timer;
46 | /**
47 | * This timer is used to make a beeping sound
48 | */
49 | private int sound_timer;
50 |
51 | /**
52 | * This array will be our keyboard state
53 | */
54 | private byte[] keys;
55 | /**
56 | * The 64x32 pixel monochrome (black/white) display
57 | */
58 | private byte[] display;
59 |
60 | private boolean needRedraw;
61 |
62 | /**
63 | * Reset the Chip 8 memory and pointers
64 | */
65 | public void init() {
66 | memory = new char[4096];
67 | V = new char[16];
68 | I = 0x0;
69 | pc = 0x200;
70 |
71 | stack = new char[16];
72 | stackPointer = 0;
73 |
74 | delay_timer = 0;
75 | sound_timer = 0;
76 |
77 | keys = new byte[16];
78 |
79 | display = new byte[64 * 32];
80 |
81 | needRedraw = false;
82 | loadFontset();
83 | }
84 |
85 | /**
86 | * Executes a single Operation Code (Opcode)
87 | */
88 | public void run() {
89 | //fetch Opcode
90 | char opcode = (char)((memory[pc] << 8) | memory[pc + 1]);
91 | System.out.print(Integer.toHexString(opcode).toUpperCase() + ": ");
92 | //decode opcode
93 | switch(opcode & 0xF000) {
94 |
95 | case 0x0000: //Multi-case
96 | switch(opcode & 0x00FF) {
97 | case 0x00E0: //00E0: Clear Screen
98 | System.err.println("Unsupported Opcode!");
99 | System.exit(0);
100 | break;
101 |
102 | case 0x00EE: //00EE: Returns from subroutine
103 | stackPointer--;
104 | pc = (char)(stack[stackPointer] + 2);
105 | System.out.println("Returning to " + Integer.toHexString(pc).toUpperCase());
106 | break;
107 |
108 | default: //0NNN: Calls RCA 1802 Program at address NNN
109 | System.err.println("Unsupported Opcode!");
110 | System.exit(0);
111 | break;
112 | }
113 | break;
114 |
115 | case 0x1000: //1NNN: Jumps to address NNN
116 | int nnn = opcode & 0x0FFF;
117 | pc = (char)nnn;
118 | System.out.println("Jumping to " + Integer.toHexString(pc).toUpperCase());
119 | break;
120 |
121 | case 0x2000: //2NNN: Calls subroutine at NNN
122 | stack[stackPointer] = pc;
123 | stackPointer++;
124 | pc = (char)(opcode & 0x0FFF);
125 | System.out.println("Calling " + Integer.toHexString(pc).toUpperCase() + " from " + Integer.toHexString(stack[stackPointer - 1]).toUpperCase());
126 | break;
127 |
128 | case 0x3000: { //3XNN: Skips the next instruction if VX equals NN
129 | int x = (opcode & 0x0F00) >> 8;
130 | int nn = (opcode & 0x00FF);
131 | if(V[x] == nn) {
132 | pc += 4;
133 | System.out.println("Skipping next instruction (V[" + x +"] == " + nn + ")");
134 | } else {
135 | pc += 2;
136 | System.out.println("Not skipping next instruction (V[" + x +"] != " + nn + ")");
137 | }
138 | break;
139 | }
140 |
141 | case 0x4000: { //4XNN: Skip the next instruction if VX != NN
142 | int x = (opcode & 0x0F00) >> 8;
143 | int nn = opcode & 0x00FF;
144 | if(V[x] != nn) {
145 | System.out.println("Skipping next instruction V[" + x + "] = " + (int)V[x] + " != " + nn);
146 | pc += 4;
147 | } else {
148 | System.out.println("Not skipping next instruction V[" + x + "] = " + (int)V[x] + " == " + nn);
149 | pc += 2;
150 | }
151 | break;
152 | }
153 |
154 | case 0x6000: { //6XNN: Set VX to NN
155 | int x = (opcode & 0x0F00) >> 8;
156 | V[x] = (char)(opcode & 0x00FF);
157 | pc += 2;
158 | System.out.println("Setting V[" + x + "] to " + (int)V[x]);
159 | break;
160 | }
161 |
162 | case 0x7000: { //7XNN: Adds NN to VX
163 | int x = (opcode & 0x0F00) >> 8;
164 | int nn = (opcode & 0x00FF);
165 | V[x] = (char)((V[x] + nn) & 0xFF);
166 | pc += 2;
167 | System.out.println("Adding " + nn + " to V["+ x + "] = " + (int)V[x]);
168 | break;
169 | }
170 |
171 | case 0x8000: //Contains more data in last nibble
172 |
173 | switch(opcode & 0x000F) {
174 |
175 | case 0x0000: { //8XY0: Sets VX to the value of VY
176 | int x = (opcode & 0x0F00) >> 8;
177 | int y = (opcode & 0x00F0) >> 4;
178 | System.out.println("Setting V[" + x + "] to " + (int)V[y]);
179 | V[x] = V[y];
180 | pc += 2;
181 | break;
182 | }
183 |
184 | case 0x0002: { //8XY2: Sets VX to VX AND VY
185 | int x = (opcode & 0x0F00) >> 8;
186 | int y = (opcode & 0x00F0) >> 4;
187 | System.out.println("Set V[" + x + "] to V[" + x + "] = " + (int)V[x] + " & V[" + y + "] = " + (int)V[y] + " = " + (int)(V[x] & V[y]));
188 | V[x] = (char)(V[x] & V[y]);
189 | pc += 2;
190 | break;
191 | }
192 |
193 | case 0x0004: { //Adds VY to VX. VF is set to 1 when carry applies else to 0
194 | int x = (opcode & 0x0F00) >> 8;
195 | int y = (opcode & 0x00F0) >> 4;
196 | System.out.print("Adding V[" + x + "] (" + (int)V[x] + ") to V[" + y + "] (" + (int)V[y] + ") = " + ((V[x] + V[y]) & 0xFF) + ", ");
197 | if(V[y] > 0xFF - V[x]) {
198 | V[0xF] = 1;
199 | System.out.println("Carry!");
200 | } else {
201 | V[0xF] = 0;
202 | System.out.println("No Carry");
203 | }
204 | V[x] = (char)((V[x] + V[y]) & 0xFF);
205 | pc += 2;
206 | break;
207 | }
208 |
209 | case 0x0005: { //VY is subtracted from VX. VF is set to 0 when there is a borrow else 1
210 | int x = (opcode & 0x0F00) >> 8;
211 | int y = (opcode & 0x00F0) >> 4;
212 | System.out.print("V[" + x + "] = " + (int)V[x] + " V[" + y + "] = " + (int)V[y] + ", ");
213 | if(V[x] > V[y]) {
214 | V[0xF] = 1;
215 | System.out.println("No Borrow");
216 | } else {
217 | V[0xF] = 0;
218 | System.out.println("Borrow");
219 | }
220 | V[x] = (char)((V[x] - V[y]) & 0xFF);
221 | pc += 2;
222 | break;
223 | }
224 |
225 | default:
226 | System.err.println("Unsupported Opcode!");
227 | System.exit(0);
228 | break;
229 | }
230 |
231 | break;
232 |
233 | case 0xA000: //ANNN: Set I to NNN
234 | I = (char)(opcode & 0x0FFF);
235 | pc += 2;
236 | System.out.println("Set I to " + Integer.toHexString(I).toUpperCase());
237 | break;
238 |
239 | case 0xC000: { //CXNN: Set VX to a random number and NN
240 | int x = (opcode & 0x0F00) >> 8;
241 | int nn = (opcode & 0x00FF);
242 | int randomNumber = new Random().nextInt(255) & nn;
243 | System.out.println("V[" + x + "] has been set to (randomised) " + randomNumber);
244 | V[x] = (char)randomNumber;
245 | pc += 2;
246 | break;
247 | }
248 |
249 | case 0xD000: { //DXYN: Draw a sprite (X, Y) size (8, N). Sprite is located at I
250 | int x = V[(opcode & 0x0F00) >> 8];
251 | int y = V[(opcode & 0x00F0) >> 4];
252 | int height = opcode & 0x000F;
253 |
254 | V[0xF] = 0;
255 |
256 | for(int _y = 0; _y < height; _y++) {
257 | int line = memory[I + _y];
258 | for(int _x = 0; _x < 8; _x++) {
259 | int pixel = line & (0x80 >> _x);
260 | if(pixel != 0) {
261 | int totalX = x + _x;
262 | int totalY = y + _y;
263 | int index = (totalY * 64) + totalX;
264 |
265 | if(display[index] == 1)
266 | V[0xF] = 1;
267 |
268 | display[index] ^= 1;
269 | }
270 | }
271 | }
272 | pc += 2;
273 | needRedraw = true;
274 | System.out.println("Drawing at V[" + ((opcode & 0x0F00) >> 8) + "] = " + x + ", V[" + ((opcode & 0x00F0) >> 4) + "] = " + y);
275 | break;
276 | }
277 |
278 | case 0xE000: {
279 | switch (opcode & 0x00FF) {
280 | case 0x009E: { //EX9E Skip the next instruction if the Key VX is pressed
281 | int x = (opcode & 0x0F00) >> 8;
282 | int key = V[x];
283 | if(keys[key] == 1) {
284 | pc += 4;
285 | } else {
286 | pc += 2;
287 | }
288 | System.out.println("Skipping next instruction if V[" + (int)V[x] + "] is pressed");
289 | break;
290 | }
291 |
292 | case 0x00A1: { //EXA1 Skip the next instruction if the Key VX is NOT pressed
293 | int x = (opcode & 0x0F00) >> 8;
294 | int key = V[x];
295 | if(keys[key] == 0) {
296 | pc += 4;
297 | } else {
298 | pc += 2;
299 | }
300 | System.out.println("Skipping next instruction if V[" + (int)V[x] + "] is NOT pressed");
301 | break;
302 | }
303 |
304 | default:
305 | System.err.println("Unexisting opcode");
306 | System.exit(0);
307 | return;
308 | }
309 | break;
310 | }
311 |
312 | case 0xF000:
313 |
314 | switch(opcode & 0x00FF) {
315 |
316 | case 0x0007: { //FX07: Set VX to the value of delay_timer
317 | int x = (opcode & 0x0F00) >> 8;
318 | V[x] = (char)delay_timer;
319 | pc += 2;
320 | System.out.println("V[" + x + "] has been set to " + delay_timer);
321 | break;
322 | }
323 |
324 | case 0x0015: { //FX15: Set delay timer to V[x]
325 | int x = (opcode & 0x0F00) >> 8;
326 | delay_timer = V[x];
327 | pc += 2;
328 | System.out.println("Set delay_timer to V[" + x + "] = " + (int)V[x]);
329 | break;
330 | }
331 |
332 | case 0x0018: { //FX18: Set the sound timer to V[x]
333 | int x = (opcode & 0x0F00) >> 8;
334 | sound_timer = V[x];
335 | pc += 2;
336 | break;
337 | }
338 |
339 | case 0x0029: { //FX29: Sets I to the location of the sprite for the character VX (Fontset)
340 | int x = (opcode & 0x0F00) >> 8;
341 | int character = V[x];
342 | I = (char)(0x050 + (character * 5));
343 | System.out.println("Setting I to Character V[" + x + "] = " + (int)V[x] + " Offset to 0x" + Integer.toHexString(I).toUpperCase());
344 | pc += 2;
345 | break;
346 | }
347 |
348 | case 0x0033: { //FX33 Store a binary-coded decimal value VX in I, I + 1 and I + 2
349 | int x = (opcode & 0x0F00) >> 8;
350 | int value = V[x];
351 | int hundreds = (value - (value % 100)) / 100;
352 | value -= hundreds * 100;
353 | int tens = (value - (value % 10))/ 10;
354 | value -= tens * 10;
355 | memory[I] = (char)hundreds;
356 | memory[I + 1] = (char)tens;
357 | memory[I + 2] = (char)value;
358 | System.out.println("Storing Binary-Coded Decimal V[" + x + "] = " + (int)(V[(opcode & 0x0F00) >> 8]) + " as { " + hundreds+ ", " + tens + ", " + value + "}");
359 | pc += 2;
360 | break;
361 | }
362 |
363 | case 0x0065: { //FX65 Filss V0 to VX with values from I
364 | int x = (opcode & 0x0F00) >> 8;
365 | for(int i = 0; i < x; i++) {
366 | V[i] = memory[I + i];
367 | }
368 | System.out.println("Setting V[0] to V[" + x + "] to the values of memory[0x" + Integer.toHexString(I & 0xFFFF).toUpperCase() + "]");
369 | I = (char)(I + x + 1);
370 | pc += 2;
371 | break;
372 | }
373 |
374 | default:
375 | System.err.println("Unsupported Opcode!");
376 | System.exit(0);
377 | }
378 | break;
379 |
380 | default:
381 | System.err.println("Unsupported Opcode!");
382 | System.exit(0);
383 | }
384 |
385 | if(sound_timer > 0)
386 | sound_timer--;
387 | if(delay_timer > 0)
388 | delay_timer--;
389 | }
390 |
391 | /**
392 | * Returns the display data
393 | * @return
394 | * Current state of the 64x32 display
395 | */
396 | public byte[] getDisplay() {
397 | return display;
398 | }
399 |
400 | /**
401 | * Checks if there is a redraw needed
402 | * @return
403 | * If a redraw is needed
404 | */
405 | public boolean needsRedraw() {
406 | return needRedraw;
407 | }
408 |
409 | /**
410 | * Notify the chip that is has been redrawn
411 | */
412 | public void removeDrawFlag() {
413 | needRedraw = false;
414 | }
415 |
416 | /**
417 | * Loads the program into the memory
418 | * @param file
419 | * The location of the program
420 | */
421 | public void loadProgram(String file) {
422 | DataInputStream input = null;
423 | try {
424 | input = new DataInputStream(new FileInputStream(new File(file)));
425 |
426 | int offset = 0;
427 | while(input.available() > 0) {
428 | memory[0x200 + offset] = (char)(input.readByte() & 0xFF);
429 | offset++;
430 | }
431 |
432 | } catch (IOException e) {
433 | e.printStackTrace();
434 | System.exit(0);
435 | } finally {
436 | if(input != null) {
437 | try { input.close(); } catch (IOException ex) {}
438 | }
439 | }
440 | }
441 |
442 | /**
443 | * Loads the fontset into the memory
444 | */
445 | public void loadFontset() {
446 | for(int i = 0; i < ChipData.fontset.length; i++) {
447 | memory[0x50 + i] = (char)(ChipData.fontset[i] & 0xFF);
448 | }
449 | }
450 |
451 | public void setKeyBuffer(int[] keyBuffer) {
452 | for(int i = 0; i < keys.length; i++) {
453 | keys[i] = (byte)keyBuffer[i];
454 | }
455 | }
456 |
457 | }
458 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 9 - Pong/src/chip/ChipData.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public class ChipData {
4 |
5 | /**
6 | * Fontset in bytes
7 | * Memory position 0x50
8 | */
9 | public static int[] fontset =
10 | {
11 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
12 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1
13 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
14 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
15 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
16 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
17 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
18 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
19 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
20 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
21 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
22 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
23 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
24 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
25 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
26 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F
27 | };
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 9 - Pong/src/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 | import java.awt.event.KeyEvent;
6 | import java.awt.event.KeyListener;
7 |
8 | import javax.swing.JFrame;
9 |
10 | import chip.Chip;
11 |
12 | public class ChipFrame extends JFrame implements KeyListener {
13 |
14 | private static final long serialVersionUID = 1L;
15 | private ChipPanel panel;
16 | private int[] keyBuffer;
17 | private int[] keyIdToKey;
18 |
19 | public ChipFrame(Chip c) {
20 | setPreferredSize(new Dimension(640, 320));
21 | pack();
22 | setPreferredSize(new Dimension(640 + getInsets().left + getInsets().right, 320 + getInsets().top + getInsets().bottom));
23 | panel = new ChipPanel(c);
24 | setLayout(new BorderLayout());
25 | add(panel, BorderLayout.CENTER);
26 | setDefaultCloseOperation(EXIT_ON_CLOSE);
27 | setTitle("Chip 8 Emulator");
28 | pack();
29 | setVisible(true);
30 | addKeyListener(this);
31 |
32 | keyIdToKey = new int[256];
33 | keyBuffer = new int[16];
34 | fillKeyIds();
35 | }
36 |
37 | private void fillKeyIds() {
38 | for(int i = 0; i < keyIdToKey.length; i++) {
39 | keyIdToKey[i] = -1;
40 | }
41 | keyIdToKey['1'] = 1;
42 | keyIdToKey['2'] = 2;
43 | keyIdToKey['3'] = 3;
44 | keyIdToKey['Q'] = 4;
45 | keyIdToKey['W'] = 5;
46 | keyIdToKey['E'] = 6;
47 | keyIdToKey['A'] = 7;
48 | keyIdToKey['S'] = 8;
49 | keyIdToKey['D'] = 9;
50 | keyIdToKey['Z'] = 0xA;
51 | keyIdToKey['X'] = 0;
52 | keyIdToKey['C'] = 0xB;
53 | keyIdToKey['4'] = 0xC;
54 | keyIdToKey['R'] = 0xD;
55 | keyIdToKey['F'] = 0xE;
56 | keyIdToKey['V'] = 0xF;
57 | }
58 |
59 | @Override
60 | public void keyPressed(KeyEvent e) {
61 | if(keyIdToKey[e.getKeyCode()] != -1) {
62 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 1;
63 | }
64 | }
65 |
66 | @Override
67 | public void keyReleased(KeyEvent e) {
68 | if(keyIdToKey[e.getKeyCode()] != -1) {
69 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 0;
70 | }
71 | }
72 |
73 | @Override
74 | public void keyTyped(KeyEvent e) {
75 | }
76 |
77 | public int[] getKeyBuffer() {
78 | return keyBuffer;
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 9 - Pong/src/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Chip;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | private static final long serialVersionUID = 1L;
13 | private Chip chip;
14 |
15 | public ChipPanel(Chip chip) {
16 | this.chip = chip;
17 | }
18 |
19 | public void paint(Graphics g) {
20 | byte[] display = chip.getDisplay();
21 | for(int i = 0; i < display.length; i++) {
22 | if(display[i] == 0)
23 | g.setColor(Color.BLACK);
24 | else
25 | g.setColor(Color.WHITE);
26 |
27 | int x = (i % 64);
28 | int y = (int)Math.floor(i / 64);
29 |
30 | g.fillRect(x * 10, y * 10, 10, 10);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/emulator_chip8/Part 9 - Pong/src/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Chip;
4 |
5 | public class Main extends Thread {
6 |
7 | private Chip chip8;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | chip8 = new Chip();
12 | chip8.init();
13 | chip8.loadProgram("./pong2.c8");
14 | frame = new ChipFrame(chip8);
15 | }
16 |
17 | public void run() {
18 | //60 hz, 60 updates per second
19 | while(true) {
20 | chip8.setKeyBuffer(frame.getKeyBuffer());
21 | chip8.run();
22 | if(chip8.needsRedraw()) {
23 | frame.repaint();
24 | chip8.removeDrawFlag();
25 | }
26 | try {
27 | Thread.sleep(8);
28 | } catch (InterruptedException e) {
29 | //Unthrown exception
30 | }
31 | }
32 | }
33 |
34 | public static void main(String[] args) {
35 | Main main = new Main();
36 | main.start();
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/emulator_chip8/links and data.txt:
--------------------------------------------------------------------------------
1 | Chip 8 Instruction Set:
2 | http://en.wikipedia.org/wiki/CHIP-8#Opcode_table
3 | http://devernay.free.fr/hacks/chip8/C8TECH10.HTM
4 |
5 | Used Libraries:
6 | None
7 |
--------------------------------------------------------------------------------
/emulator_chip8/programs/invaders.c8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Johnnei/Youtube-Tutorials/e3bbe8c4bbecfb87b218b7bbcb8745e943376d10/emulator_chip8/programs/invaders.c8
--------------------------------------------------------------------------------
/emulator_chip8/programs/pong2.c8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Johnnei/Youtube-Tutorials/e3bbe8c4bbecfb87b218b7bbcb8745e943376d10/emulator_chip8/programs/pong2.c8
--------------------------------------------------------------------------------
/emulator_chip8/programs/tetris.c8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Johnnei/Youtube-Tutorials/e3bbe8c4bbecfb87b218b7bbcb8745e943376d10/emulator_chip8/programs/tetris.c8
--------------------------------------------------------------------------------
/emulator_gameboy/GBCPUman.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Johnnei/Youtube-Tutorials/e3bbe8c4bbecfb87b218b7bbcb8745e943376d10/emulator_gameboy/GBCPUman.pdf
--------------------------------------------------------------------------------
/emulator_gameboy/chip/Condition.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public enum Condition {
4 |
5 | NZ,
6 | Z,
7 | NC,
8 | C
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/emulator_gameboy/chip/Flag.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public enum Flag {
4 |
5 | /**
6 | * The bit in {@link #REGISTER_F} that represents if the last math operation result was: 0
7 | */
8 | Zero(7),
9 | /**
10 | * The bit in {@link #REGISTER_F} that represents if the last math operation was a subtraction
11 | */
12 | Subtract(6),
13 | /**
14 | * The bit in {@link #REGISTER_F} that represents if the last math operation caused a carry on the lower nibble (result >= 16)
15 | */
16 | HalfCarry(5),
17 | /**
18 | * The bit in {@link #REGISTER_F} that represents if the last math operation caused a carry (result > 255)
19 | * This bit is also set of {@link #REGISTER_A} is the smaller value during the CP Instruction
20 | */
21 | Carry(4);
22 |
23 | public final int bit;
24 |
25 | private Flag(int bit) {
26 | this.bit = bit;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/emulator_gameboy/chip/Register.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | public enum Register {
4 | A(0),
5 | B(1),
6 | C(2),
7 | D(3),
8 | E(4),
9 | H(5),
10 | L(6),
11 | F(7);
12 |
13 | public final int index;
14 |
15 | private Register(int index) {
16 | this.index = index;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/emulator_gameboy/chip/Z80.java:
--------------------------------------------------------------------------------
1 | package chip;
2 |
3 | import java.io.DataInputStream;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 |
8 | import chip.opcode.IOpcode;
9 | import chip.opcode.Opcode00NOP;
10 | import chip.opcode.Opcode01Load;
11 | import chip.opcode.Opcode02Load;
12 | import chip.opcode.Opcode0FRotateRight;
13 | import chip.opcode.Opcode1000Stop;
14 | import chip.opcode.Opcode1FRotateRight;
15 |
16 | public class Z80 {
17 |
18 | /**
19 | * The 160x144 pixel 4 colors display
20 | */
21 | private byte[] display;
22 | /**
23 | * 64K Memory
24 | * 0000-3FFF 16KB ROM Bank 00 (in cartridge, fixed at bank 00)
25 | * 4000-7FFF 16KB ROM Bank 01..NN (in cartridge, switchable bank number)
26 | * 8000-9FFF 8KB Video RAM (VRAM) (switchable bank 0-1 in CGB Mode)
27 | * A000-BFFF 8KB External RAM (in cartridge, switchable bank, if any)
28 | * C000-CFFF 4KB Work RAM Bank 0 (WRAM)
29 | * D000-DFFF 4KB Work RAM Bank 1 (WRAM) (switchable bank 1-7 in CGB Mode)
30 | * E000-FDFF Same as C000-DDFF (ECHO) (typically not used)
31 | * FE00-FE9F Sprite Attribute Table (OAM)
32 | * FEA0-FEFF Not Usable
33 | * FF00-FF7F I/O Ports
34 | * FF80-FFFE High RAM (HRAM)
35 | * FFFF Interrupt Enable Register
36 | */
37 | private char[] memory;
38 | /**
39 | * 8 8-bit Registers
40 | * They can be paired up to 16-bit registers
41 | * Left 8-bit registers can be: A, B, D and H
42 | * Register F is the "Flag Register"
43 | * The flag register contains multiple flags
44 | * Bit 7: Zero flag (Z)
45 | * Bit 6: Subtract Flag (N)
46 | * Bit 5: Half Carry Flag (H)
47 | * Bit 4: Carry Flag (C)
48 | */
49 | private char[] register;
50 | /**
51 | * The StackPointer
52 | */
53 | private char sp;
54 | /**
55 | * The ProgramCounter
56 | * The PC is being initiated on startup to the position: 0x100
57 | * At that location is commenly a jump and a nop
58 | */
59 | private char pc;
60 |
61 | private boolean needRedraw;
62 |
63 | /**
64 | * Resets the Z80 processor
65 | */
66 | public void reset() {
67 | display = new byte[160 * 144];
68 | memory = new char[65535];
69 | register = new char[8];
70 | pc = 0x100;
71 | sp = 0xFFFE;
72 | }
73 |
74 | /**
75 | * Processes a single opcode and returns the amount of "cycles" it took to process it
76 | *
77 | * @return The amount of cycles this operation took
78 | */
79 | public int run() {
80 | IOpcode opcode = getOpcode();
81 | opcode.execute(this);
82 | return opcode.getCycleCount();
83 | }
84 |
85 | /**
86 | * A big Factory-Method to create the correct Opcode for the current byte found in the memory at the current programcounter
87 | * @return The opcode to process
88 | */
89 | private IOpcode getOpcode() {
90 | switch (memory[pc]) {
91 | case 0x00:
92 | return new Opcode00NOP();
93 | case 0x01:
94 | return new Opcode01Load();
95 | case 0x02:
96 | return new Opcode02Load();
97 | case 0x03:
98 | // TODO Implement opcode
99 | return new Opcode00NOP();
100 | case 0x04:
101 | // TODO Implement opcode
102 | return new Opcode00NOP();
103 | case 0x05:
104 | // TODO Implement opcode
105 | return new Opcode00NOP();
106 | case 0x06:
107 | // TODO Implement opcode
108 | return new Opcode00NOP();
109 | case 0x07:
110 | // TODO Implement opcode
111 | return new Opcode00NOP();
112 | case 0x08:
113 | // TODO Implement opcode
114 | return new Opcode00NOP();
115 | case 0x09:
116 | // TODO Implement opcode
117 | return new Opcode00NOP();
118 | case 0x0A:
119 | // TODO Implement opcode
120 | return new Opcode00NOP();
121 | case 0x0B:
122 | // TODO Implement opcode
123 | return new Opcode00NOP();
124 | case 0x0C:
125 | // TODO Implement opcode
126 | return new Opcode00NOP();
127 | case 0x0D:
128 | // TODO Implement opcode
129 | return new Opcode00NOP();
130 | case 0x0E:
131 | // TODO Implement opcode
132 | return new Opcode00NOP();
133 | case 0x0F:
134 | return new Opcode0FRotateRight();
135 | case 0x10:
136 | // 16 Bit Opcode
137 |
138 | switch (memory[pc + 1]) {
139 | case 0x00: // STOP
140 | return new Opcode1000Stop();
141 |
142 | default:
143 | throw new RuntimeException("Invalid ROM (0x10 Case) (0x" + Integer.toHexString(pc) + ")");
144 | }
145 | case 0x11:
146 | // TODO Implement opcode
147 | return new Opcode00NOP();
148 | case 0x12:
149 | // TODO Implement opcode
150 | return new Opcode00NOP();
151 | case 0x13:
152 | // TODO Implement opcode
153 | return new Opcode00NOP();
154 | case 0x14:
155 | // TODO Implement opcode
156 | return new Opcode00NOP();
157 | case 0x15:
158 | // TODO Implement opcode
159 | return new Opcode00NOP();
160 | case 0x16:
161 | // TODO Implement opcode
162 | return new Opcode00NOP();
163 | case 0x17:
164 | // TODO Implement opcode
165 | return new Opcode00NOP();
166 | case 0x18:
167 | // TODO Implement opcode
168 | return new Opcode00NOP();
169 | case 0x19:
170 | // TODO Implement opcode
171 | return new Opcode00NOP();
172 | case 0x1A:
173 | // TODO Implement opcode
174 | return new Opcode00NOP();
175 | case 0x1B:
176 | // TODO Implement opcode
177 | return new Opcode00NOP();
178 | case 0x1C:
179 | // TODO Implement opcode
180 | return new Opcode00NOP();
181 | case 0x1D:
182 | // TODO Implement opcode
183 | return new Opcode00NOP();
184 | case 0x1E:
185 | // TODO Implement opcode
186 | return new Opcode00NOP();
187 | case 0x1F:
188 | return new Opcode1FRotateRight();
189 | case 0x20:
190 | // TODO Implement opcode
191 | return new Opcode00NOP();
192 | case 0x21:
193 | // TODO Implement opcode
194 | return new Opcode00NOP();
195 | case 0x22:
196 | // TODO Implement opcode
197 | return new Opcode00NOP();
198 | case 0x23:
199 | // TODO Implement opcode
200 | return new Opcode00NOP();
201 | case 0x24:
202 | // TODO Implement opcode
203 | return new Opcode00NOP();
204 | case 0x25:
205 | // TODO Implement opcode
206 | return new Opcode00NOP();
207 | case 0x26:
208 | // TODO Implement opcode
209 | return new Opcode00NOP();
210 | case 0x27:
211 | // TODO Implement opcode
212 | return new Opcode00NOP();
213 | case 0x28:
214 | // TODO Implement opcode
215 | return new Opcode00NOP();
216 | case 0x29:
217 | // TODO Implement opcode
218 | return new Opcode00NOP();
219 | case 0x2A:
220 | // TODO Implement opcode
221 | return new Opcode00NOP();
222 | case 0x2B:
223 | // TODO Implement opcode
224 | return new Opcode00NOP();
225 | case 0x2C:
226 | // TODO Implement opcode
227 | return new Opcode00NOP();
228 | case 0x2D:
229 | // TODO Implement opcode
230 | return new Opcode00NOP();
231 | case 0x2E:
232 | // TODO Implement opcode
233 | return new Opcode00NOP();
234 | case 0x2F:
235 | // TODO Implement opcode
236 | return new Opcode00NOP();
237 | case 0x30:
238 | // TODO Implement opcode
239 | return new Opcode00NOP();
240 | case 0x31:
241 | // TODO Implement opcode
242 | return new Opcode00NOP();
243 | case 0x32:
244 | // TODO Implement opcode
245 | return new Opcode00NOP();
246 | case 0x33:
247 | // TODO Implement opcode
248 | return new Opcode00NOP();
249 | case 0x34:
250 | // TODO Implement opcode
251 | return new Opcode00NOP();
252 | case 0x35:
253 | // TODO Implement opcode
254 | return new Opcode00NOP();
255 | case 0x36:
256 | // TODO Implement opcode
257 | return new Opcode00NOP();
258 | case 0x37:
259 | // TODO Implement opcode
260 | return new Opcode00NOP();
261 | case 0x38:
262 | // TODO Implement opcode
263 | return new Opcode00NOP();
264 | case 0x39:
265 | // TODO Implement opcode
266 | return new Opcode00NOP();
267 | case 0x3A:
268 | // TODO Implement opcode
269 | return new Opcode00NOP();
270 | case 0x3B:
271 | // TODO Implement opcode
272 | return new Opcode00NOP();
273 | case 0x3C:
274 | // TODO Implement opcode
275 | return new Opcode00NOP();
276 | case 0x3D:
277 | // TODO Implement opcode
278 | return new Opcode00NOP();
279 | case 0x3E:
280 | // TODO Implement opcode
281 | return new Opcode00NOP();
282 | case 0x3F:
283 | // TODO Implement opcode
284 | return new Opcode00NOP();
285 | case 0x40:
286 | // TODO Implement opcode
287 | return new Opcode00NOP();
288 | case 0x41:
289 | // TODO Implement opcode
290 | return new Opcode00NOP();
291 | case 0x42:
292 | // TODO Implement opcode
293 | return new Opcode00NOP();
294 | case 0x43:
295 | // TODO Implement opcode
296 | return new Opcode00NOP();
297 | case 0x44:
298 | // TODO Implement opcode
299 | return new Opcode00NOP();
300 | case 0x45:
301 | // TODO Implement opcode
302 | return new Opcode00NOP();
303 | case 0x46:
304 | // TODO Implement opcode
305 | return new Opcode00NOP();
306 | case 0x47:
307 | // TODO Implement opcode
308 | return new Opcode00NOP();
309 | case 0x48:
310 | // TODO Implement opcode
311 | return new Opcode00NOP();
312 | case 0x49:
313 | // TODO Implement opcode
314 | return new Opcode00NOP();
315 | case 0x4A:
316 | // TODO Implement opcode
317 | return new Opcode00NOP();
318 | case 0x4B:
319 | // TODO Implement opcode
320 | return new Opcode00NOP();
321 | case 0x4C:
322 | // TODO Implement opcode
323 | return new Opcode00NOP();
324 | case 0x4D:
325 | // TODO Implement opcode
326 | return new Opcode00NOP();
327 | case 0x4E:
328 | // TODO Implement opcode
329 | return new Opcode00NOP();
330 | case 0x4F:
331 | // TODO Implement opcode
332 | return new Opcode00NOP();
333 | case 0x50:
334 | // TODO Implement opcode
335 | return new Opcode00NOP();
336 | case 0x51:
337 | // TODO Implement opcode
338 | return new Opcode00NOP();
339 | case 0x52:
340 | // TODO Implement opcode
341 | return new Opcode00NOP();
342 | case 0x53:
343 | // TODO Implement opcode
344 | return new Opcode00NOP();
345 | case 0x54:
346 | // TODO Implement opcode
347 | return new Opcode00NOP();
348 | case 0x55:
349 | // TODO Implement opcode
350 | return new Opcode00NOP();
351 | case 0x56:
352 | // TODO Implement opcode
353 | return new Opcode00NOP();
354 | case 0x57:
355 | // TODO Implement opcode
356 | return new Opcode00NOP();
357 | case 0x58:
358 | // TODO Implement opcode
359 | return new Opcode00NOP();
360 | case 0x59:
361 | // TODO Implement opcode
362 | return new Opcode00NOP();
363 | case 0x5A:
364 | // TODO Implement opcode
365 | return new Opcode00NOP();
366 | case 0x5B:
367 | // TODO Implement opcode
368 | return new Opcode00NOP();
369 | case 0x5C:
370 | // TODO Implement opcode
371 | return new Opcode00NOP();
372 | case 0x5D:
373 | // TODO Implement opcode
374 | return new Opcode00NOP();
375 | case 0x5E:
376 | // TODO Implement opcode
377 | return new Opcode00NOP();
378 | case 0x5F:
379 | // TODO Implement opcode
380 | return new Opcode00NOP();
381 | case 0x60:
382 | // TODO Implement opcode
383 | return new Opcode00NOP();
384 | case 0x61:
385 | // TODO Implement opcode
386 | return new Opcode00NOP();
387 | case 0x62:
388 | // TODO Implement opcode
389 | return new Opcode00NOP();
390 | case 0x63:
391 | // TODO Implement opcode
392 | return new Opcode00NOP();
393 | case 0x64:
394 | // TODO Implement opcode
395 | return new Opcode00NOP();
396 | case 0x65:
397 | // TODO Implement opcode
398 | return new Opcode00NOP();
399 | case 0x66:
400 | // TODO Implement opcode
401 | return new Opcode00NOP();
402 | case 0x67:
403 | // TODO Implement opcode
404 | return new Opcode00NOP();
405 | case 0x68:
406 | // TODO Implement opcode
407 | return new Opcode00NOP();
408 | case 0x69:
409 | // TODO Implement opcode
410 | return new Opcode00NOP();
411 | case 0x6A:
412 | // TODO Implement opcode
413 | return new Opcode00NOP();
414 | case 0x6B:
415 | // TODO Implement opcode
416 | return new Opcode00NOP();
417 | case 0x6C:
418 | // TODO Implement opcode
419 | return new Opcode00NOP();
420 | case 0x6D:
421 | // TODO Implement opcode
422 | return new Opcode00NOP();
423 | case 0x6E:
424 | // TODO Implement opcode
425 | return new Opcode00NOP();
426 | case 0x6F:
427 | // TODO Implement opcode
428 | return new Opcode00NOP();
429 | case 0x70:
430 | // TODO Implement opcode
431 | return new Opcode00NOP();
432 | case 0x71:
433 | // TODO Implement opcode
434 | return new Opcode00NOP();
435 | case 0x72:
436 | // TODO Implement opcode
437 | return new Opcode00NOP();
438 | case 0x73:
439 | // TODO Implement opcode
440 | return new Opcode00NOP();
441 | case 0x74:
442 | // TODO Implement opcode
443 | return new Opcode00NOP();
444 | case 0x75:
445 | // TODO Implement opcode
446 | return new Opcode00NOP();
447 | case 0x76:
448 | // TODO Implement opcode
449 | return new Opcode00NOP();
450 | case 0x77:
451 | // TODO Implement opcode
452 | return new Opcode00NOP();
453 | case 0x78:
454 | // TODO Implement opcode
455 | return new Opcode00NOP();
456 | case 0x79:
457 | // TODO Implement opcode
458 | return new Opcode00NOP();
459 | case 0x7A:
460 | // TODO Implement opcode
461 | return new Opcode00NOP();
462 | case 0x7B:
463 | // TODO Implement opcode
464 | return new Opcode00NOP();
465 | case 0x7C:
466 | // TODO Implement opcode
467 | return new Opcode00NOP();
468 | case 0x7D:
469 | // TODO Implement opcode
470 | return new Opcode00NOP();
471 | case 0x7E:
472 | // TODO Implement opcode
473 | return new Opcode00NOP();
474 | case 0x7F:
475 | // TODO Implement opcode
476 | return new Opcode00NOP();
477 | case 0x80:
478 | // TODO Implement opcode
479 | return new Opcode00NOP();
480 | case 0x81:
481 | // TODO Implement opcode
482 | return new Opcode00NOP();
483 | case 0x82:
484 | // TODO Implement opcode
485 | return new Opcode00NOP();
486 | case 0x83:
487 | // TODO Implement opcode
488 | return new Opcode00NOP();
489 | case 0x84:
490 | // TODO Implement opcode
491 | return new Opcode00NOP();
492 | case 0x85:
493 | // TODO Implement opcode
494 | return new Opcode00NOP();
495 | case 0x86:
496 | // TODO Implement opcode
497 | return new Opcode00NOP();
498 | case 0x87:
499 | // TODO Implement opcode
500 | return new Opcode00NOP();
501 | case 0x88:
502 | // TODO Implement opcode
503 | return new Opcode00NOP();
504 | case 0x89:
505 | // TODO Implement opcode
506 | return new Opcode00NOP();
507 | case 0x8A:
508 | // TODO Implement opcode
509 | return new Opcode00NOP();
510 | case 0x8B:
511 | // TODO Implement opcode
512 | return new Opcode00NOP();
513 | case 0x8C:
514 | // TODO Implement opcode
515 | return new Opcode00NOP();
516 | case 0x8D:
517 | // TODO Implement opcode
518 | return new Opcode00NOP();
519 | case 0x8E:
520 | // TODO Implement opcode
521 | return new Opcode00NOP();
522 | case 0x8F:
523 | // TODO Implement opcode
524 | return new Opcode00NOP();
525 | case 0x90:
526 | // TODO Implement opcode
527 | return new Opcode00NOP();
528 | case 0x91:
529 | // TODO Implement opcode
530 | return new Opcode00NOP();
531 | case 0x92:
532 | // TODO Implement opcode
533 | return new Opcode00NOP();
534 | case 0x93:
535 | // TODO Implement opcode
536 | return new Opcode00NOP();
537 | case 0x94:
538 | // TODO Implement opcode
539 | return new Opcode00NOP();
540 | case 0x95:
541 | // TODO Implement opcode
542 | return new Opcode00NOP();
543 | case 0x96:
544 | // TODO Implement opcode
545 | return new Opcode00NOP();
546 | case 0x97:
547 | // TODO Implement opcode
548 | return new Opcode00NOP();
549 | case 0x98:
550 | // TODO Implement opcode
551 | return new Opcode00NOP();
552 | case 0x99:
553 | // TODO Implement opcode
554 | return new Opcode00NOP();
555 | case 0x9A:
556 | // TODO Implement opcode
557 | return new Opcode00NOP();
558 | case 0x9B:
559 | // TODO Implement opcode
560 | return new Opcode00NOP();
561 | case 0x9C:
562 | // TODO Implement opcode
563 | return new Opcode00NOP();
564 | case 0x9D:
565 | // TODO Implement opcode
566 | return new Opcode00NOP();
567 | case 0x9E:
568 | // TODO Implement opcode
569 | return new Opcode00NOP();
570 | case 0x9F:
571 | // TODO Implement opcode
572 | return new Opcode00NOP();
573 | case 0xA0:
574 | // TODO Implement opcode
575 | return new Opcode00NOP();
576 | case 0xA1:
577 | // TODO Implement opcode
578 | return new Opcode00NOP();
579 | case 0xA2:
580 | // TODO Implement opcode
581 | return new Opcode00NOP();
582 | case 0xA3:
583 | // TODO Implement opcode
584 | return new Opcode00NOP();
585 | case 0xA4:
586 | // TODO Implement opcode
587 | return new Opcode00NOP();
588 | case 0xA5:
589 | // TODO Implement opcode
590 | return new Opcode00NOP();
591 | case 0xA6:
592 | // TODO Implement opcode
593 | return new Opcode00NOP();
594 | case 0xA7:
595 | // TODO Implement opcode
596 | return new Opcode00NOP();
597 | case 0xA8:
598 | // TODO Implement opcode
599 | return new Opcode00NOP();
600 | case 0xA9:
601 | // TODO Implement opcode
602 | return new Opcode00NOP();
603 | case 0xAA:
604 | // TODO Implement opcode
605 | return new Opcode00NOP();
606 | case 0xAB:
607 | // TODO Implement opcode
608 | return new Opcode00NOP();
609 | case 0xAC:
610 | // TODO Implement opcode
611 | return new Opcode00NOP();
612 | case 0xAD:
613 | // TODO Implement opcode
614 | return new Opcode00NOP();
615 | case 0xAE:
616 | // TODO Implement opcode
617 | return new Opcode00NOP();
618 | case 0xAF:
619 | // TODO Implement opcode
620 | return new Opcode00NOP();
621 | case 0xB0:
622 | // TODO Implement opcode
623 | return new Opcode00NOP();
624 | case 0xB1:
625 | // TODO Implement opcode
626 | return new Opcode00NOP();
627 | case 0xB2:
628 | // TODO Implement opcode
629 | return new Opcode00NOP();
630 | case 0xB3:
631 | // TODO Implement opcode
632 | return new Opcode00NOP();
633 | case 0xB4:
634 | // TODO Implement opcode
635 | return new Opcode00NOP();
636 | case 0xB5:
637 | // TODO Implement opcode
638 | return new Opcode00NOP();
639 | case 0xB6:
640 | // TODO Implement opcode
641 | return new Opcode00NOP();
642 | case 0xB7:
643 | // TODO Implement opcode
644 | return new Opcode00NOP();
645 | case 0xB8:
646 | // TODO Implement opcode
647 | return new Opcode00NOP();
648 | case 0xB9:
649 | // TODO Implement opcode
650 | return new Opcode00NOP();
651 | case 0xBA:
652 | // TODO Implement opcode
653 | return new Opcode00NOP();
654 | case 0xBB:
655 | // TODO Implement opcode
656 | return new Opcode00NOP();
657 | case 0xBC:
658 | // TODO Implement opcode
659 | return new Opcode00NOP();
660 | case 0xBD:
661 | // TODO Implement opcode
662 | return new Opcode00NOP();
663 | case 0xBE:
664 | // TODO Implement opcode
665 | return new Opcode00NOP();
666 | case 0xBF:
667 | // TODO Implement opcode
668 | return new Opcode00NOP();
669 | case 0xC0:
670 | // TODO Implement opcode
671 | return new Opcode00NOP();
672 | case 0xC1:
673 | // TODO Implement opcode
674 | return new Opcode00NOP();
675 | case 0xC2:
676 | // TODO Implement opcode
677 | return new Opcode00NOP();
678 | case 0xC3:
679 | // TODO Implement opcode
680 | return new Opcode00NOP();
681 | case 0xC4:
682 | // TODO Implement opcode
683 | return new Opcode00NOP();
684 | case 0xC5:
685 | // TODO Implement opcode
686 | return new Opcode00NOP();
687 | case 0xC6:
688 | // TODO Implement opcode
689 | return new Opcode00NOP();
690 | case 0xC7:
691 | // TODO Implement opcode
692 | return new Opcode00NOP();
693 | case 0xC8:
694 | // TODO Implement opcode
695 | return new Opcode00NOP();
696 | case 0xC9:
697 | // TODO Implement opcode
698 | return new Opcode00NOP();
699 | case 0xCA:
700 | // TODO Implement opcode
701 | return new Opcode00NOP();
702 | case 0xCB:
703 | // 16 Bit Opcode
704 |
705 | switch (memory[pc + 1]) {
706 | // TODO Implement 0xCB Opcodes
707 | }
708 |
709 | throw new RuntimeException("Invalid ROM (0xCB Case) (0x" + Integer.toHexString(pc) + ")");
710 | case 0xCC:
711 | // TODO Implement opcode
712 | return new Opcode00NOP();
713 | case 0xCD:
714 | // TODO Implement opcode
715 | return new Opcode00NOP();
716 | case 0xCE:
717 | // TODO Implement opcode
718 | return new Opcode00NOP();
719 | case 0xCF:
720 | // TODO Implement opcode
721 | return new Opcode00NOP();
722 | case 0xD0:
723 | // TODO Implement opcode
724 | return new Opcode00NOP();
725 | case 0xD1:
726 | // TODO Implement opcode
727 | return new Opcode00NOP();
728 | case 0xD2:
729 | // TODO Implement opcode
730 | return new Opcode00NOP();
731 | case 0xD4:
732 | // TODO Implement opcode
733 | return new Opcode00NOP();
734 | case 0xD5:
735 | // TODO Implement opcode
736 | return new Opcode00NOP();
737 | case 0xD6:
738 | // TODO Implement opcode
739 | return new Opcode00NOP();
740 | case 0xD7:
741 | // TODO Implement opcode
742 | return new Opcode00NOP();
743 | case 0xD8:
744 | // TODO Implement opcode
745 | return new Opcode00NOP();
746 | case 0xD9:
747 | // TODO Implement opcode
748 | return new Opcode00NOP();
749 | case 0xDA:
750 | // TODO Implement opcode
751 | return new Opcode00NOP();
752 | case 0xDC:
753 | // TODO Implement opcode
754 | return new Opcode00NOP();
755 | case 0xDF:
756 | // TODO Implement opcode
757 | return new Opcode00NOP();
758 | case 0xE0:
759 | // TODO Implement opcode
760 | return new Opcode00NOP();
761 | case 0xE1:
762 | // TODO Implement opcode
763 | return new Opcode00NOP();
764 | case 0xE2:
765 | // TODO Implement opcode
766 | return new Opcode00NOP();
767 | case 0xE5:
768 | // TODO Implement opcode
769 | return new Opcode00NOP();
770 | case 0xE6:
771 | // TODO Implement opcode
772 | return new Opcode00NOP();
773 | case 0xE7:
774 | // TODO Implement opcode
775 | return new Opcode00NOP();
776 | case 0xE8:
777 | // TODO Implement opcode
778 | return new Opcode00NOP();
779 | case 0xE9:
780 | // TODO Implement opcode
781 | return new Opcode00NOP();
782 | case 0xEA:
783 | // TODO Implement opcode
784 | return new Opcode00NOP();
785 | case 0xEE:
786 | // TODO Implement opcode
787 | return new Opcode00NOP();
788 | case 0xEF:
789 | // TODO Implement opcode
790 | return new Opcode00NOP();
791 | case 0xF0:
792 | // TODO Implement opcode
793 | return new Opcode00NOP();
794 | case 0xF1:
795 | // TODO Implement opcode
796 | return new Opcode00NOP();
797 | case 0xF2:
798 | // TODO Implement opcode
799 | return new Opcode00NOP();
800 | case 0xF3:
801 | // TODO Implement opcode
802 | return new Opcode00NOP();
803 | case 0xF5:
804 | // TODO Implement opcode
805 | return new Opcode00NOP();
806 | case 0xF6:
807 | // TODO Implement opcode
808 | return new Opcode00NOP();
809 | case 0xF7:
810 | // TODO Implement opcode
811 | return new Opcode00NOP();
812 | case 0xF8:
813 | // TODO Implement opcode
814 | return new Opcode00NOP();
815 | case 0xF9:
816 | // TODO Implement opcode
817 | return new Opcode00NOP();
818 | case 0xFA:
819 | // TODO Implement opcode
820 | return new Opcode00NOP();
821 | case 0xFB:
822 | // TODO Implement opcode
823 | return new Opcode00NOP();
824 | case 0xFE:
825 | // TODO Implement opcode
826 | return new Opcode00NOP();
827 | case 0xFF:
828 | // TODO Implement opcode
829 | return new Opcode00NOP();
830 |
831 | default:
832 | System.out.println("Unprocessed opcode 0x" + Integer.toHexString(memory[pc]).toUpperCase() + ", Processing as NOP");
833 | return new Opcode00NOP();
834 | }
835 | }
836 |
837 | /**
838 | * Merges two registers into the 16-bit value
839 | *
840 | * @param leftRegister The index of the left byte (upper 8 bits) register (A, B, D or H)
841 | * @param rightRegister The index of the right byte (lower 8 bits) register
842 | * @return The 16 bit register value
843 | */
844 | public int getRegister(Register leftRegister, Register rightRegister) {
845 | return (register[leftRegister.index] << 8) | register[rightRegister.index];
846 | }
847 |
848 | /**
849 | * Gets the data from an eight bit register
850 | *
851 | * @param register
852 | * @return
853 | */
854 | public int getRegister(Register register) {
855 | return this.register[register.index];
856 | }
857 |
858 | /**
859 | * The possible conditions:
860 | * cc = NZ, if Z flag is reset.
861 | * cc = Z, if Z flag is set.
862 | * cc = NC, if C flag is reset.
863 | * cc = C, if C flag is set.
864 | *
865 | * @param cc
866 | * @return
867 | */
868 | public boolean passesCondition(Condition cc) {
869 | switch (cc) {
870 | case NZ:
871 | return (register[Register.F.index] >>> Flag.Zero.bit) == 0;
872 | case Z:
873 | return (register[Register.F.index] >>> Flag.Zero.bit) == 1;
874 | case NC:
875 | return (register[Register.F.index] >>> Flag.Carry.bit) == 0;
876 | case C:
877 | return (register[Register.F.index] >>> Flag.Carry.bit) == 1;
878 |
879 | default:
880 | throw new RuntimeException("This Exception won't be thrown as all Condition are being checked");
881 | }
882 | }
883 |
884 | /**
885 | * Gets the stack pointer
886 | *
887 | * @return
888 | */
889 | public int getStackPointer() {
890 | return sp & 0xFFFF;
891 | }
892 |
893 | /**
894 | * Gets the program counter
895 | *
896 | * @return
897 | */
898 | public int getProgramCounter() {
899 | return pc & 0xFFFF;
900 | }
901 |
902 | /**
903 | * Read data from the memory
904 | *
905 | * @param address
906 | * @return
907 | */
908 | public int readMemory(int address) {
909 | return memory[address] & 0xFFFF;
910 | }
911 |
912 | /**
913 | * Read 16bit data from the memory
914 | * @param address
915 | * @return
916 | */
917 | public int readMemory16bit(int address) {
918 | return ((memory[address] & 0xFF) << 8) + (memory[address] & 0xFF);
919 | }
920 |
921 | /**
922 | * Writes an eight bit value into the register
923 | * @param register
924 | * @param value
925 | */
926 | public void setRegister(Register register, int value) {
927 | this.register[register.index] = (char)(value & 0xFF);
928 | }
929 |
930 | /**
931 | * Writes an sixteen bit value into two registers
932 | * @param leftRegister The 8 MSB register
933 | * @param rightRegister The 8 LSB register
934 | * @param value
935 | */
936 | public void setRegister(Register leftRegister, Register rightRegister, int value) {
937 | register[leftRegister.index] = (char)((value >>> 8) & 0xFF);
938 | register[leftRegister.index] = (char)(value & 0xFF);
939 | }
940 |
941 | /**
942 | * Set the stackpointer
943 | *
944 | * @param sp
945 | */
946 | public void setStackPointer(int sp) {
947 | this.sp = (char)(sp & 0xFFFF);
948 | }
949 |
950 | /**
951 | * Set the program counter
952 | *
953 | * @param pc
954 | */
955 | public void setProgramCounter(int pc) {
956 | this.pc = (char)(pc & 0xFFFF);
957 | }
958 |
959 | /**
960 | * Turns a single bit on or off in the flag register
961 | * @param flag
962 | * @param set true: set flag, false: reset flag
963 | */
964 | public void setFlagRegister(Flag flag, boolean set) {
965 | if(set) {
966 | //Turn flag on
967 | register[Register.F.index] |= 1 << flag.bit;
968 | } else {
969 | //Turn flag off
970 | register[Register.F.index] &= ~(1 << flag.bit);
971 | }
972 | }
973 |
974 | /**
975 | * Reset Flag Register
976 | */
977 | public void resetFlagRegister() {
978 | register[Register.F.index] = 0;
979 | }
980 |
981 | /**
982 | * Write data to the memory
983 | *
984 | * @param address
985 | * @param value
986 | */
987 | public void writeMemory(int address, int value) {
988 | memory[address] = (char)(value & 0xFF);
989 | }
990 |
991 | /**
992 | * Increments the stackpointer by 1
993 | */
994 | public void incrementStackPointer() {
995 | ++sp;
996 | }
997 |
998 | /**
999 | * Decrements the stack pointer by 1
1000 | */
1001 | public void decrementStackPointer() {
1002 | --sp;
1003 | }
1004 |
1005 | /**
1006 | * Increments the program counter by 1
1007 | */
1008 | public void incrementProgramCounter() {
1009 | ++pc;
1010 | }
1011 |
1012 | /**
1013 | * Returns the display data
1014 | *
1015 | * @return Current state of the 160x144 display
1016 | */
1017 | public byte[] getDisplay() {
1018 | return display;
1019 | }
1020 |
1021 | /**
1022 | * Checks if there is a redraw needed
1023 | *
1024 | * @return If a redraw is needed
1025 | */
1026 | public boolean needsRedraw() {
1027 | return needRedraw;
1028 | }
1029 |
1030 | /**
1031 | * Notify the chip that is has been redrawn
1032 | */
1033 | public void removeDrawFlag() {
1034 | needRedraw = false;
1035 | }
1036 |
1037 | /**
1038 | * Sets the key data to the memory
1039 | * The keys are stored in a single byte as followed:
1040 | * Bit 7 - Not used
1041 | * Bit 6 - Not used
1042 | * Bit 5 - P15 Select Button Keys (0=Select)
1043 | * Bit 4 - P14 Select Direction Keys (0=Select)
1044 | * Bit 3 - P13 Input Down or Start (0=Pressed) (Read Only)
1045 | * Bit 2 - P12 Input Up or Select (0=Pressed) (Read Only)
1046 | * Bit 1 - P11 Input Left or Button B (0=Pressed) (Read Only)
1047 | * Bit 0 - P10 Input Right or Button A (0=Pressed) (Read Only)
1048 | *
1049 | * @param b The key states
1050 | */
1051 | public void setKeys(byte b) {
1052 | memory[0xFF00] = (char) b;
1053 | }
1054 |
1055 | /**
1056 | * Loads the cartridge into the memory
1057 | *
1058 | * @param file The location of the cartrigde
1059 | */
1060 | public void loadCartridge(String file) {
1061 | DataInputStream input = null;
1062 | try {
1063 | input = new DataInputStream(new FileInputStream(new File(file)));
1064 |
1065 | int offset = 0;
1066 | while (input.available() > 0) {
1067 | memory[offset] = (char) (input.readByte() & 0xFF);
1068 | offset++;
1069 | }
1070 |
1071 | } catch (IOException e) {
1072 | e.printStackTrace();
1073 | System.exit(0);
1074 | } finally {
1075 | if (input != null) {
1076 | try {
1077 | input.close();
1078 | } catch (IOException ex) {
1079 | }
1080 | }
1081 | }
1082 | }
1083 | }
1084 |
--------------------------------------------------------------------------------
/emulator_gameboy/chip/opcode/IOpcode.java:
--------------------------------------------------------------------------------
1 | package chip.opcode;
2 |
3 | import chip.Z80;
4 |
5 | public interface IOpcode {
6 |
7 | public void execute(Z80 gba);
8 |
9 | public int getCycleCount();
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/emulator_gameboy/chip/opcode/Opcode00NOP.java:
--------------------------------------------------------------------------------
1 | package chip.opcode;
2 |
3 | import chip.Z80;
4 |
5 | public class Opcode00NOP implements IOpcode {
6 |
7 | @Override
8 | public void execute(Z80 gba) {
9 | gba.incrementProgramCounter();
10 | }
11 |
12 | @Override
13 | public int getCycleCount() {
14 | return 4;
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/emulator_gameboy/chip/opcode/Opcode01Load.java:
--------------------------------------------------------------------------------
1 | package chip.opcode;
2 |
3 | import chip.Register;
4 | import chip.Z80;
5 |
6 | public class Opcode01Load implements IOpcode {
7 |
8 | @Override
9 | public void execute(Z80 gba) {
10 | gba.setRegister(Register.B, Register.C, gba.readMemory16bit(gba.getProgramCounter() + 1));
11 | gba.setProgramCounter(gba.getProgramCounter() + 3); //3 -> 2 read bytes + 1 to advance
12 | }
13 |
14 | @Override
15 | public int getCycleCount() {
16 | return 12;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/emulator_gameboy/chip/opcode/Opcode02Load.java:
--------------------------------------------------------------------------------
1 | package chip.opcode;
2 |
3 | import chip.Register;
4 | import chip.Z80;
5 |
6 | public class Opcode02Load implements IOpcode {
7 |
8 | @Override
9 | public void execute(Z80 gba) {
10 | gba.setRegister(Register.B, 0);
11 | gba.setRegister(Register.C, gba.getRegister(Register.A));
12 | gba.incrementProgramCounter();
13 | }
14 |
15 | @Override
16 | public int getCycleCount() {
17 | return 8;
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/emulator_gameboy/chip/opcode/Opcode0FRotateRight.java:
--------------------------------------------------------------------------------
1 | package chip.opcode;
2 |
3 | import chip.Flag;
4 | import chip.Register;
5 | import chip.Z80;
6 |
7 | public class Opcode0FRotateRight implements IOpcode {
8 |
9 | @Override
10 | public void execute(Z80 gba) {
11 | int flagA = gba.getRegister(Register.A);
12 | int bitZero = flagA & 0x1;
13 | gba.resetFlagRegister();
14 | gba.setFlagRegister(Flag.Carry, bitZero == 1);
15 | flagA = flagA >>> 1;
16 | flagA |= bitZero << 7;
17 | gba.setFlagRegister(Flag.Zero, flagA == 0);
18 | gba.setRegister(Register.A, flagA);
19 | }
20 |
21 | @Override
22 | public int getCycleCount() {
23 | return 4;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/emulator_gameboy/chip/opcode/Opcode1000Stop.java:
--------------------------------------------------------------------------------
1 | package chip.opcode;
2 |
3 | import chip.Z80;
4 |
5 | //TODO Implement
6 | public class Opcode1000Stop implements IOpcode {
7 |
8 | @Override
9 | public void execute(Z80 gba) {
10 | // TODO Auto-generated method stub
11 |
12 | }
13 |
14 | @Override
15 | public int getCycleCount() {
16 | // TODO Auto-generated method stub
17 | return 0;
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/emulator_gameboy/chip/opcode/Opcode1FRotateRight.java:
--------------------------------------------------------------------------------
1 | package chip.opcode;
2 |
3 | import chip.Flag;
4 | import chip.Register;
5 | import chip.Z80;
6 |
7 | public class Opcode1FRotateRight implements IOpcode {
8 |
9 | @Override
10 | public void execute(Z80 gba) {
11 | int flagA = gba.getRegister(Register.A);
12 | int bitZero = flagA & 0x1;
13 | gba.resetFlagRegister();
14 | flagA = flagA >>> 1;
15 | flagA |= (gba.getRegister(Register.A) & 0x1) << 7;
16 | gba.setFlagRegister(Flag.Carry, bitZero == 1);
17 | gba.setFlagRegister(Flag.Zero, flagA == 0);
18 | gba.setRegister(Register.A, flagA);
19 | }
20 |
21 | @Override
22 | public int getCycleCount() {
23 | return 4;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/emulator_gameboy/command_map.txt:
--------------------------------------------------------------------------------
1 | 0x00 NOP
2 | 0x01 LD BC,nn
3 | 0x02 LD (BC),A
4 | 0x03 INC BC
5 | 0x04 INC B
6 | 0x05 DEC B
7 | 0x06 LD B,n
8 | 0x07 RLCA
9 | 0x08 LD (nn),SP
10 | 0x09 ADD HL,BC
11 | 0x0A LD A,(BC)
12 | 0x0B DEC BC
13 | 0x0C INC C
14 | 0x0D DEC C
15 | 0x0E LD C,n
16 | 0x0F RRCA
17 | 0x10 Indicates 16 Bit Opcode
18 | 0x10 00 STOP
19 | 0x11 LD DE,nn
20 | 0x12 LD (DE),A
21 | 0x13 INC DE
22 | 0x14 INC D
23 | 0x15 DEC D
24 | 0x16 LD D,n
25 | 0x17 RLA
26 | 0x18 JR N
27 | 0x19 ADD HL,DE
28 | 0x1A LD A,(DE)
29 | 0x1B DEC DE
30 | 0x1C INC E
31 | 0x1D DEC E
32 | 0x1E LD E,n
33 | 0x1F RRA
34 | 0x20 JR NZ,*
35 | 0x21 LD HL,nn
36 | 0x22 LDI (HL),A LD (HLI),A LD A,(HL+) (No Problem)
37 | 0x23 INC HL
38 | 0x24 INC H
39 | 0x25 DEC H
40 | 0x26 LD H,n
41 | 0x27 DAA
42 | 0x28 JR Z,*
43 | 0x29 ADD HL,HL
44 | 0x2A LDI A,(HL) LD A,(HLI) LD A,(HL+) (No Problem)
45 | 0x2B DEC HL
46 | 0x2C INC L
47 | 0x2D DEC L
48 | 0x2E LD L,n
49 | 0x2F CPL
50 | 0x30 JR NC,*
51 | 0x31 LD SP,nn
52 | 0x32 LDD (HL),A LD (HLD),A LD (HL-),A (No Problem)
53 | 0x33 INC SP
54 | 0x34 INC (HL)
55 | 0x35 DEC (HL)
56 | 0x36 LD (HL),n
57 | 0x37 SCF
58 | 0x38 JR C,*
59 | 0x39 ADD HL,SP
60 | 0x3A LDD A,(HL) LD A,(HLD) LD A,(HL-) (No Problem)
61 | 0x3B DEC SP
62 | 0x3C INC A
63 | 0x3D DEC A
64 | 0x3E LD A,#
65 | 0x3F CCF
66 | 0x40 LD B,B
67 | 0x41 LD B,C
68 | 0x42 LD B,D
69 | 0x43 LD B,E
70 | 0x44 LD B,H
71 | 0x45 LD B,L
72 | 0x46 LD B,(HL)
73 | 0x47 LD B,A
74 | 0x48 LD C,B
75 | 0x49 LD C,C
76 | 0x4A LD C,D
77 | 0x4B LD C,E
78 | 0x4C LD C,H
79 | 0x4D LD C,L
80 | 0x4E LD C,(HL)
81 | 0x4F LD C,A
82 | 0x50 LD D,B
83 | 0x51 LD D,C
84 | 0x52 LD D,D
85 | 0x53 LD D,E
86 | 0x54 LD D,H
87 | 0x55 LD D,L
88 | 0x56 LD D,(HL)
89 | 0x57 LD D,A
90 | 0x58 LD E,B
91 | 0x59 LD E,C
92 | 0x5A LD E,D
93 | 0x5B LD E,E
94 | 0x5C LD E,H
95 | 0x5D LD E,L
96 | 0x5E LD E,(HL)
97 | 0x5F LD E,A
98 | 0x60 LD H,B
99 | 0x61 LD H,C
100 | 0x62 LD H,D
101 | 0x63 LD H,E
102 | 0x64 LD H,H
103 | 0x65 LD H,L
104 | 0x66 LD H,(HL)
105 | 0x67 LD H,A
106 | 0x68 LD L,B
107 | 0x69 LD L,C
108 | 0x6A LD L,D
109 | 0x6B LD L,E
110 | 0x6C LD L,H
111 | 0x6D LD L,L
112 | 0x6E LD L,(HL)
113 | 0x6F LD L,A
114 | 0x70 LD (HL),B
115 | 0x71 LD (HL),C
116 | 0x72 LD (HL),D
117 | 0x73 LD (HL),E
118 | 0x74 LD (HL),H
119 | 0x75 LD (HL),L
120 | 0x76 HALT
121 | 0x77 LD (HL),A
122 | 0x78 LD A,B LD A,B (No Problem)
123 | 0x79 LD A,C LD A,C (No Problem)
124 | 0x7A LD A,D LD A,D (No Problem)
125 | 0x7B LD A,E LD A,E (No Problem)
126 | 0x7C LD A,H LD A,H (No Problem)
127 | 0x7D LD A,L LD A,L (No Problem)
128 | 0x7E LD A,(HL) LD A,(HL) (No Problem)
129 | 0x7F LD A,A LD A,A (No Problem) LD A,A (No Problem)
130 | 0x80 ADD A,B
131 | 0x81 ADD A,C
132 | 0x82 ADD A,D
133 | 0x83 ADD A,E
134 | 0x84 ADD A,H
135 | 0x85 ADD A,L
136 | 0x86 ADD A,(HL)
137 | 0x87 ADD A,A
138 | 0x88 ADC A,B
139 | 0x89 ADC A,C
140 | 0x8A ADC A,D
141 | 0x8B ADC A,E
142 | 0x8C ADC A,H
143 | 0x8D ADC A,L
144 | 0x8E ADC A,(HL)
145 | 0x8F ADC A,A
146 | 0x90 SUB B
147 | 0x91 SUB C
148 | 0x92 SUB D
149 | 0x93 SUB E
150 | 0x94 SUB H
151 | 0x95 SUB L
152 | 0x96 SUB (HL)
153 | 0x97 SUB A,n
154 | 0x98 SBC A,B
155 | 0x99 SBC A,C
156 | 0x9A SBC A,D
157 | 0x9B SBC A,E
158 | 0x9C SBC A,H
159 | 0x9D SBC A,L
160 | 0x9E SBC A,(HL)
161 | 0x9F SBC A,A
162 | 0xA0 AND B
163 | 0xA1 AND C
164 | 0xA2 AND D
165 | 0xA3 AND E
166 | 0xA4 AND H
167 | 0xA5 AND L
168 | 0xA6 AND (HL)
169 | 0xA7 AND A
170 | 0xA8 XOR B
171 | 0xA9 XOR C
172 | 0xAA XOR D
173 | 0xAB XOR E
174 | 0xAC XOR H
175 | 0xAD XOR L
176 | 0xAE XOR (HL)
177 | 0xAF XOR A
178 | 0xB0 OR B
179 | 0xB1 OR C
180 | 0xB2 OR D
181 | 0xB3 OR E
182 | 0xB4 OR H
183 | 0xB5 OR L
184 | 0xB6 OR (HL)
185 | 0xB7 OR A
186 | 0xB8 CP B
187 | 0xB9 CP C
188 | 0xBA CP D
189 | 0xBB CP E
190 | 0xBC CP H
191 | 0xBD CP L
192 | 0xBE CP (HL)
193 | 0xBF CP A
194 | 0xC0 RET NZ
195 | 0xC1 POP BC
196 | 0xC2 JP NZ,nn
197 | 0xC3 JP nn
198 | 0xC4 CALL NZ,nn
199 | 0xC5 PUSH BC
200 | 0xC6 ADD A,#
201 | 0xC7 RST 0x00
202 | 0xC8 RET Z
203 | 0xC9 RET
204 | 0xCA JP Z,nn
205 | 0xCB Indicates 16 Bit Opcode
206 | 0xCB 00 RLC B
207 | 0xCB 01 RLC C
208 | 0xCB 02 RLC D
209 | 0xCB 03 RLC E
210 | 0xCB 04 RLC H
211 | 0xCB 05 RLC L
212 | 0xCB 06 RLC (HL)
213 | 0xCB 07 RLC A
214 | 0xCB 08 RRC B
215 | 0xCB 09 RRC C
216 | 0xCB 0A RRC D
217 | 0xCB 0B RRC E
218 | 0xCB 0C RRC H
219 | 0xCB 0D RRC L
220 | 0xCB 0E RRC (HL)
221 | 0xCB 0F RRC A
222 | 0xCB 10 RL B
223 | 0xCB 11 RL C
224 | 0xCB 12 RL D
225 | 0xCB 13 RL E
226 | 0xCB 14 RL H
227 | 0xCB 15 RL L
228 | 0xCB 16 RL (HL)
229 | 0xCB 17 RL A
230 | 0xCB 18 RR B
231 | 0xCB 19 RR C
232 | 0xCB 1A RR D
233 | 0xCB 1B RR E
234 | 0xCB 1C RR H
235 | 0xCB 1D RR L
236 | 0xCB 1E RR (HL)
237 | 0xCB 1F RR A
238 | 0xCB 20 SLA B
239 | 0xCB 21 SLA C
240 | 0xCB 22 SLA D
241 | 0xCB 23 SLA E
242 | 0xCB 24 SLA H
243 | 0xCB 25 SLA L
244 | 0xCB 26 SLA (HL)
245 | 0xCB 27 SLA A
246 | 0xCB 28 SRA B
247 | 0xCB 29 SRA C
248 | 0xCB 2A SRA D
249 | 0xCB 2B SRA E
250 | 0xCB 2C SRA H
251 | 0xCB 2D SRA L
252 | 0xCB 2E SRA (HL)
253 | 0xCB 2F SRA A
254 | 0xCB 30 SWAP B
255 | 0xCB 31 SWAP C
256 | 0xCB 32 SWAP D
257 | 0xCB 33 SWAP E
258 | 0xCB 34 SWAP H
259 | 0xCB 35 SWAP L
260 | 0xCB 36 SWAP (HL)
261 | 0xCB 37 SWAP A
262 | 0xCB 38 SRL B
263 | 0xCB 39 SRL C
264 | 0xCB 3A SRL D
265 | 0xCB 3B SRL E
266 | 0xCB 3C SRL H
267 | 0xCB 3D SRL L
268 | 0xCB 3E SRL (HL)
269 | 0xCB 3F SRL A
270 | 0xCB 40 BIT b,B
271 | 0xCB 41 BIT b,C
272 | 0xCB 42 BIT b,D
273 | 0xCB 43 BIT b,E
274 | 0xCB 44 BIT b,H
275 | 0xCB 45 BIT b,L
276 | 0xCB 46 BIT b,(HL)
277 | 0xCB 47 BIT b,A
278 | 0xCB 80 RES b,B
279 | 0xCB 81 RES b,C
280 | 0xCB 82 RES b,D
281 | 0xCB 83 RES b,E
282 | 0xCB 84 RES b,H
283 | 0xCB 85 RES b,L
284 | 0xCB 86 RES b,(HL)
285 | 0xCB 87 RES b,A
286 | 0xCB C0 SET b,B
287 | 0xCB C1 SET b,C
288 | 0xCB C2 SET b,D
289 | 0xCB C3 SET b,E
290 | 0xCB C4 SET b,H
291 | 0xCB C5 SET b,L
292 | 0xCB C6 SET b,(HL)
293 | 0xCB C7 SET b,A
294 | 0xCC CALL Z,nn
295 | 0xCD CALL nn
296 | 0xCE ADC A,#
297 | 0xCF RST 0x08
298 | 0xD0 RET NC
299 | 0xD1 POP DE
300 | 0xD2 JP NC,nn
301 | 0xD3
302 | 0xD4 CALL NC,nn
303 | 0xD5 PUSH DE
304 | 0xD6 SUB #
305 | 0xD7 RST 0x10
306 | 0xD8 RET C
307 | 0xD9 RETI
308 | 0xDA JP C,nn
309 | 0xDB
310 | 0xDC CALL C,nn
311 | 0xDD
312 | 0xDE
313 | 0xDF RST 0x18
314 | 0xE0 LD (0xFF00+n),A
315 | 0xE1 POP HL
316 | 0xE2 LD (0xFF00+C),A
317 | 0xE3
318 | 0xE4
319 | 0xE5 PUSH HL
320 | 0xE6 AND #
321 | 0xE7 RST 0x20
322 | 0xE8 ADD SP,#
323 | 0xE9 JP (HL)
324 | 0xEA LD (nn),A
325 | 0xEB
326 | 0xEC
327 | 0xED
328 | 0xEE XOR * (I suppose this is a typo and should be #)
329 | 0xEF RST 0x28
330 | 0xF0 LD A,(0xFF00+n)
331 | 0xF1 POP AF
332 | 0xF2 LD A,(C)
333 | 0xF3 DI
334 | 0xF4
335 | 0xF5 PUSH AF
336 | 0xF6 OR #
337 | 0xF7 RST 0x30
338 | 0xF8 LDHL SP,n
339 | 0xF9 LD SP,HL
340 | 0xFA LD A,(nn)
341 | 0xFB EI
342 | 0xFC
343 | 0xFD
344 | 0xFE CP #
345 | 0xFF RST 0x38
346 | 0x?? SBC A,# (Defined in spec but doesn't give the Opcode)
347 |
348 | Notes:
349 | # = one byte signed immediate value, Page 91.
--------------------------------------------------------------------------------
/emulator_gameboy/emu/ChipFrame.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Dimension;
5 | import java.awt.event.KeyEvent;
6 | import java.awt.event.KeyListener;
7 |
8 | import javax.swing.JFrame;
9 |
10 | import chip.Z80;
11 |
12 | public class ChipFrame extends JFrame implements KeyListener {
13 |
14 | private static final int KEY_LEFT = 37;
15 | private static final int KEY_UP = 38;
16 | private static final int KEY_RIGHT = 39;
17 | private static final int KEY_DOWN = 40;
18 |
19 | public static final int WIDTH = 160;
20 | public static final int HEIGHT = 144;
21 |
22 | private static final long serialVersionUID = 1L;
23 | private ChipPanel panel;
24 | private int[] keyBuffer;
25 | private int[] keyIdToKey;
26 |
27 | public ChipFrame(Z80 c) {
28 | setPreferredSize(new Dimension(WIDTH * ChipPanel.SCALE, HEIGHT * ChipPanel.SCALE));
29 | setResizable(false);
30 | pack();
31 | setPreferredSize(new Dimension(WIDTH * ChipPanel.SCALE + getInsets().left + getInsets().right, HEIGHT * ChipPanel.SCALE + getInsets().top + getInsets().bottom));
32 | panel = new ChipPanel(c);
33 | setLayout(new BorderLayout());
34 | add(panel, BorderLayout.CENTER);
35 | setDefaultCloseOperation(EXIT_ON_CLOSE);
36 | setTitle("Gameboy Emulator");
37 | pack();
38 | setLocationRelativeTo(null);
39 | setVisible(true);
40 | addKeyListener(this);
41 |
42 | keyIdToKey = new int[256];
43 | keyBuffer = new int[16];
44 | fillKeyIds();
45 | }
46 |
47 | private void fillKeyIds() {
48 | for(int i = 0; i < keyIdToKey.length; i++) {
49 | keyIdToKey[i] = -1;
50 | }
51 | }
52 |
53 | @Override
54 | public void keyPressed(KeyEvent e) {
55 | System.out.println("KeyPressed: " + e.getKeyCode());
56 | if(keyIdToKey[e.getKeyCode()] != -1) {
57 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 1;
58 | }
59 | }
60 |
61 | @Override
62 | public void keyReleased(KeyEvent e) {
63 | if(keyIdToKey[e.getKeyCode()] != -1) {
64 | keyBuffer[keyIdToKey[e.getKeyCode()]] = 0;
65 | }
66 | }
67 |
68 | @Override
69 | public void keyTyped(KeyEvent e) {
70 | }
71 |
72 | public int[] getKeyBuffer() {
73 | return keyBuffer;
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/emulator_gameboy/emu/ChipPanel.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import java.awt.Color;
4 | import java.awt.Graphics;
5 |
6 | import javax.swing.JPanel;
7 |
8 | import chip.Z80;
9 |
10 | public class ChipPanel extends JPanel {
11 |
12 | public static final int SCALE = 2;
13 |
14 | private static final long serialVersionUID = 1L;
15 | private Z80 chip;
16 |
17 | public ChipPanel(Z80 chip) {
18 | this.chip = chip;
19 | }
20 |
21 | public void paint(Graphics g) {
22 | final Color[] COLORS = { new Color(0x00, 0x00, 0x00), new Color(0x66, 0x66, 0x66), new Color(0xBB, 0xBB, 0xBB), new Color(0xFF, 0xFF, 0xFF) };
23 | byte[] display = chip.getDisplay();
24 | for(int i = 0; i < display.length; i++) {
25 | g.setColor(COLORS[display[i]]);
26 |
27 | int x = (i % ChipFrame.WIDTH);
28 | int y = (int)Math.floor(i / ChipFrame.WIDTH);
29 |
30 | g.fillRect(x * SCALE, y * SCALE, SCALE, SCALE);
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/emulator_gameboy/emu/Main.java:
--------------------------------------------------------------------------------
1 | package emu;
2 |
3 | import chip.Z80;
4 |
5 | public class Main extends Thread {
6 |
7 | private Z80 z80;
8 | private ChipFrame frame;
9 |
10 | public Main() {
11 | z80 = new Z80();
12 | z80.reset();
13 | z80.loadCartridge("./tetris.gb");
14 | frame = new ChipFrame(z80);
15 | }
16 |
17 | public void run() {
18 | //4194304 Hz (roughly 4.2 mhz), 1 hz is about 23.84 nanoseconds worth of sleep
19 | while(true) {
20 | long startTime = System.nanoTime();
21 | int cyclesUsed = z80.run();
22 | long usedTime = System.nanoTime() - startTime;
23 | if(z80.needsRedraw()) {
24 | frame.repaint();
25 | z80.removeDrawFlag();
26 | }
27 | try {
28 | double nanosSleep = (cyclesUsed * 23.84D) - usedTime;
29 | if(nanosSleep > 0D) {
30 | Thread.sleep(0, (int)nanosSleep);
31 | }
32 | } catch (InterruptedException e) {
33 | //Unthrown exception
34 | }
35 | }
36 | }
37 |
38 | public static void main(String[] args) {
39 | Main main = new Main();
40 | main.start();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------