├── .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 | --------------------------------------------------------------------------------