├── .gitignore
├── src
├── test
│ ├── resources
│ │ ├── E03TestRom.ch8
│ │ ├── E05SoundLoop.ch8
│ │ ├── E05TimerLoop.ch8
│ │ ├── E06KeypadLoop.ch8
│ │ └── E07GraphicsRom.ch8
│ └── java
│ │ └── net
│ │ └── saga
│ │ └── console
│ │ └── chip8
│ │ └── test
│ │ ├── E03ClockExecutionAndMemoryTest.java
│ │ ├── E05TimersTest.java
│ │ ├── E06KeypadInputTest.java
│ │ ├── E08UtilitiesTest.java
│ │ ├── E04FlowControlTest.java
│ │ ├── E02BitwiseOpCodeTest.java
│ │ ├── E01ArithmeticOpCodeTest.java
│ │ └── E07GraphicsTest.java
└── main
│ ├── resources
│ ├── about.png
│ ├── input.png
│ ├── open.png
│ ├── pause.png
│ ├── play.png
│ └── skip.png
│ └── java
│ └── net
│ └── saga
│ └── console
│ └── chip8
│ ├── util
│ ├── Audio.java
│ ├── Input.java
│ ├── Chip8DisplayPanel.java
│ └── Chip8Utils.java
│ ├── Chip8Model.java
│ ├── Chip8.java
│ ├── SwingMain.form
│ ├── SwingMain.java
│ ├── InputMapping.form
│ └── InputMapping.java
├── pom.xml
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | target/**
2 | nb-**
3 | **/keycloak.json
4 | /nbproject/
--------------------------------------------------------------------------------
/src/test/resources/E03TestRom.ch8:
--------------------------------------------------------------------------------
1 | `a b%c0
--------------------------------------------------------------------------------
/src/main/resources/about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secondsun/chip8/HEAD/src/main/resources/about.png
--------------------------------------------------------------------------------
/src/main/resources/input.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secondsun/chip8/HEAD/src/main/resources/input.png
--------------------------------------------------------------------------------
/src/main/resources/open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secondsun/chip8/HEAD/src/main/resources/open.png
--------------------------------------------------------------------------------
/src/main/resources/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secondsun/chip8/HEAD/src/main/resources/pause.png
--------------------------------------------------------------------------------
/src/main/resources/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secondsun/chip8/HEAD/src/main/resources/play.png
--------------------------------------------------------------------------------
/src/main/resources/skip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secondsun/chip8/HEAD/src/main/resources/skip.png
--------------------------------------------------------------------------------
/src/test/resources/E05SoundLoop.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secondsun/chip8/HEAD/src/test/resources/E05SoundLoop.ch8
--------------------------------------------------------------------------------
/src/test/resources/E05TimerLoop.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secondsun/chip8/HEAD/src/test/resources/E05TimerLoop.ch8
--------------------------------------------------------------------------------
/src/test/resources/E06KeypadLoop.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secondsun/chip8/HEAD/src/test/resources/E06KeypadLoop.ch8
--------------------------------------------------------------------------------
/src/test/resources/E07GraphicsRom.ch8:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/secondsun/chip8/HEAD/src/test/resources/E07GraphicsRom.ch8
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | net.saga.console
5 | chip8
6 | 0.1.0-SNAPSHOT
7 | jar
8 |
9 |
10 | junit
11 | junit
12 | 4.12
13 | test
14 |
15 |
16 |
17 | UTF-8
18 | 1.8
19 | 1.8
20 |
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 summers pittma
2 |
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9 |
--------------------------------------------------------------------------------
/src/main/java/net/saga/console/chip8/util/Audio.java:
--------------------------------------------------------------------------------
1 | package net.saga.console.chip8.util;
2 |
3 | import java.util.function.IntSupplier;
4 | import javax.sound.sampled.AudioFormat;
5 | import javax.sound.sampled.AudioSystem;
6 | import javax.sound.sampled.LineUnavailableException;
7 | import javax.sound.sampled.SourceDataLine;
8 |
9 | /**
10 | * This class is a helper class for emitting, playing, and viewing audio data.
11 | *
12 | * @author summers
13 | */
14 | public final class Audio {
15 |
16 | private static final AudioFormat AF = new AudioFormat((float) 44100, 8, 1, true, false);
17 | private static SourceDataLine SDL;
18 | private static byte[] BUF = new byte[1];
19 |
20 | static {
21 | try {
22 | SDL = AudioSystem.getSourceDataLine(AF);
23 | SDL.open();
24 |
25 | } catch (LineUnavailableException ex) {
26 | SDL = null;
27 | }
28 | }
29 |
30 | private static final IntSupplier STREAM = (new IntSupplier() {
31 |
32 | int i = 0;
33 |
34 | @Override
35 | public int getAsInt() {
36 | double angle = i / ((float) 44100 / 880) * 2.0 * Math.PI;
37 | i++;
38 | return (byte) (Math.sin(angle) * 20);
39 |
40 | }
41 | });
42 |
43 | private static boolean PLAYING = false;
44 |
45 | public static void play() {
46 | if (!PLAYING) {
47 | SDL.start();
48 | }
49 |
50 | for (int i = 0; i < (44100 / 60); i++) {
51 | int number = STREAM.getAsInt();
52 | BUF[0] = (byte) number;
53 | SDL.write(BUF, 0, 1);
54 | }
55 |
56 | }
57 |
58 | public static void stop() {
59 | if (PLAYING) {
60 | SDL.stop();
61 | PLAYING = false;
62 | }
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/net/saga/console/chip8/util/Input.java:
--------------------------------------------------------------------------------
1 | package net.saga.console.chip8.util;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | public final class Input {
7 |
8 | private final static Map keyMap = new HashMap<>();
9 |
10 | static {
11 | keyMap.put("0", 0x0);
12 | keyMap.put("1", 0x1);
13 | keyMap.put("2", 0x2);
14 | keyMap.put("3", 0x3);
15 | keyMap.put("4", 0x4);
16 | keyMap.put("5", 0x5);
17 | keyMap.put("6", 0x6);
18 | keyMap.put("7", 0x7);
19 | keyMap.put("8", 0x8);
20 | keyMap.put("9", 0x9);
21 | keyMap.put("a", 0xa);
22 | keyMap.put("b", 0xb);
23 | keyMap.put("c", 0xc);
24 | keyMap.put("d", 0xd);
25 | keyMap.put("e", 0xe);
26 | keyMap.put("f", 0xf);
27 |
28 | }
29 |
30 | public static void map(int i, String text) {
31 | if (text != null && !text.isEmpty()) {
32 | keyMap.put(text.toLowerCase(), i);
33 | }
34 | }
35 |
36 | private Input() {
37 | }
38 |
39 | private static int KEYS = -1;
40 |
41 | public static void press(int i) {
42 | KEYS = 0x0000000F & i;
43 | }
44 |
45 | public static void press(String input) {
46 | input = input.toLowerCase();
47 | if (keyMap.containsKey(input)) {
48 | KEYS = 0x0000000F & keyMap.get(input);
49 | }
50 |
51 | }
52 |
53 | public static void unpress(String input) {
54 | input = input.toLowerCase();
55 |
56 | if (keyMap.containsKey(input)) {
57 | KEYS = -1;
58 | }
59 | }
60 |
61 | public static void unpress() {
62 | KEYS = -1;
63 | }
64 |
65 | /**
66 | * returns the currently pressed keys
67 | *
68 | * @return the int value of the key pressed
69 | */
70 | public static int read() {
71 | return KEYS;
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/src/test/java/net/saga/console/chip8/test/E03ClockExecutionAndMemoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * To change this license header, choose License Headers in Project Properties.
3 | * To change this template file, choose Tools | Templates
4 | * and open the template in the editor.
5 | */
6 | package net.saga.console.chip8.test;
7 |
8 | import java.io.IOException;
9 | import net.saga.console.chip8.Chip8;
10 | import net.saga.console.chip8.util.Chip8Utils;
11 | import org.junit.Assert;
12 | import org.junit.Before;
13 | import org.junit.Test;
14 |
15 | /**
16 | *
17 | * We will take our chip 8 processor and have it begin executing instructions
18 | * from memory.
19 | *
20 | * @author summers
21 | */
22 | public class E03ClockExecutionAndMemoryTest {
23 |
24 | private Chip8 chip8;
25 |
26 | /**
27 | * Until now we have only executed instructions manually. Eventually for a
28 | * working virtual machine we will need to load a program from an external
29 | * source.
30 | *
31 | * This provided Chip8Utils class has utility methods for creating a Chip8
32 | * system with memory loaded from a file.
33 | *
34 | *
35 | * @throws java.io.IOException
36 | */
37 | @Before
38 | public void loadMemory() throws IOException {
39 | this.chip8 = Chip8Utils.createFromRom(E03ClockExecutionAndMemoryTest.class.getResource("/E03TestRom.ch8"));
40 | }
41 |
42 | /**
43 | * A cycle loop of the chip8 processor should
44 | *
45 | * fetch the instruction pointed at by the PC
46 | * increment the PC
47 | * decode the instruction
48 | * execute the instruction
49 | * repeat
50 | *
51 | * This will be modified slightly in E05Timers to update timers as well.
52 | *
53 | * Some notes, an instruction is two bytes, however the memory is addressed
54 | * as bytes. This means you will need to load two bytes and combine them
55 | * into a single instruction.
56 | *
57 | */
58 | @Test
59 | public void testCycle() {
60 | chip8.cycle();
61 | chip8.cycle();
62 | chip8.cycle();
63 | chip8.cycle();
64 | Assert.assertEquals(0x15, chip8.getV0());
65 | Assert.assertEquals(0x20, chip8.getV1());
66 | Assert.assertEquals(0x25, chip8.getV2());
67 | Assert.assertEquals(0x30, chip8.getV3());
68 | Assert.assertEquals(0x208, chip8.getPC());
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/net/saga/console/chip8/util/Chip8DisplayPanel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2016 summers.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package net.saga.console.chip8.util;
25 |
26 | import java.awt.Color;
27 | import java.awt.Graphics;
28 | import javax.swing.JPanel;
29 | import net.saga.console.chip8.Chip8;
30 |
31 | /**
32 | *
33 | * @author summers
34 | */
35 | public class Chip8DisplayPanel extends JPanel {
36 |
37 | private static final long serialVersionUID = 0x203920L;
38 |
39 | private Chip8 chip8 = new Chip8();
40 |
41 | @Override
42 | public void paint(Graphics g) {
43 | int width = super.getWidth();
44 | int height = super.getHeight();
45 | int pixelWidth = width / 64;
46 | int pixelHeight = height / 32;
47 | byte[] video = chip8.getScreen();
48 | for (int x = 0; x < 64; x++) {
49 | for (int y = 0; y < 32; y++) {
50 | if (video[y * 64 + x] == 0) {
51 | g.setColor(Color.BLACK);
52 | } else {
53 | g.setColor(Color.WHITE);
54 | }
55 | g.fillRect(x * pixelWidth, y*pixelHeight , pixelWidth, pixelHeight);
56 | }
57 | }
58 | }
59 |
60 | public void setChip8(Chip8 chip8) {
61 | this.chip8 = chip8;
62 | }
63 |
64 |
65 |
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/net/saga/console/chip8/util/Chip8Utils.java:
--------------------------------------------------------------------------------
1 | package net.saga.console.chip8.util;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URISyntaxException;
6 | import java.net.URL;
7 | import java.nio.file.Files;
8 | import java.nio.file.Path;
9 | import java.nio.file.Paths;
10 | import java.util.logging.Level;
11 | import java.util.logging.Logger;
12 | import net.saga.console.chip8.Chip8;
13 |
14 | public final class Chip8Utils {
15 |
16 | private Chip8Utils() {
17 | }
18 |
19 | public static Chip8 createFromRom(URL rom) throws IOException {
20 | try {
21 | return createFromRom(Paths.get(rom.toURI()));
22 | } catch (URISyntaxException ex) {
23 | Logger.getLogger(Chip8Utils.class.getName()).log(Level.SEVERE, null, ex);
24 | throw new IOException(ex);
25 | }
26 | }
27 |
28 | public static Chip8 createFromRom(String rom) throws IOException {
29 | return createFromRom(Paths.get(rom));
30 | }
31 |
32 | public static Chip8 createFromRom(File rom) throws IOException {
33 | return createFromRom(rom.toPath());
34 | }
35 |
36 | public static Chip8 createFromRom(Path rom) throws IOException {
37 |
38 | byte[] reader = Files.readAllBytes(rom);
39 | int index = 0x200;
40 | byte[] memory = new byte[4096];//4k memory
41 | try {
42 | for (byte read : reader) {
43 | memory[index] = (byte) (read);
44 | index += 1;
45 | }
46 | } catch (ArrayIndexOutOfBoundsException ignore) {
47 | throw new IOException("File is too big to fit in memory", ignore);
48 | }
49 |
50 | Chip8 chip8 = new Chip8(memory);
51 | return chip8;
52 |
53 | }
54 |
55 | /**
56 | *
57 | * Packs a graphics row (8 pixels of the sprite) into a btye
58 | *
59 | * @param x
60 | * @param y
61 | * @param video
62 | * @return
63 | */
64 | public static byte getSpriteRow(int x, int y, byte[] video) {
65 | x = x % 64;
66 | y = y % 32;
67 | byte byte1 = video[x + y * 64];
68 | byte byte2 = video[x + 1 + y * 64];
69 | byte byte3 = video[x + 2 + y * 64];
70 | byte byte4 = video[x + 3 + y * 64];
71 | byte byte5 = video[x + 4 + y * 64];
72 | byte byte6 = video[x + 5 + y * 64];
73 | byte byte7 = video[x + 6 + y * 64];
74 | byte byte8 = video[x + 7 + y * 64];
75 |
76 | return (byte) ((byte1 << 7)
77 | | (byte2 << 6)
78 | | (byte3 << 5)
79 | | (byte4 << 4)
80 | | (byte5 << 3)
81 | | (byte6 << 2)
82 | | (byte7 << 1)
83 | | (byte8));
84 |
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/src/test/java/net/saga/console/chip8/test/E05TimersTest.java:
--------------------------------------------------------------------------------
1 | package net.saga.console.chip8.test;
2 |
3 | import java.io.IOException;
4 | import net.saga.console.chip8.Chip8;
5 | import net.saga.console.chip8.util.Chip8Utils;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 | import static org.junit.Assert.*;
9 | import org.junit.Ignore;
10 |
11 | /**
12 | *
13 | * @author summers
14 | */
15 | public class E05TimersTest {
16 |
17 | private Chip8 chip8;
18 |
19 | @Before
20 | public void setUp() throws IOException {
21 | this.chip8 = Chip8Utils.createFromRom(getClass().getResource("/E05TimerLoop.ch8"));
22 | this.chip8.execute(0x6064);
23 | this.chip8.execute(0x6127);
24 | this.chip8.execute(0x6212);
25 | this.chip8.execute(0x63AE);
26 | this.chip8.execute(0x64FF);
27 | this.chip8.execute(0x65B4);
28 | this.chip8.execute(0x6642);
29 | this.chip8.execute(0x6F25);
30 | }
31 |
32 | /**
33 | * As documented in E03, we will modify the cycle method to count down the
34 | * delay timer.
35 | *
36 | * There are also timer instructions to set the timer to begin a countdown.
37 | *
38 | * FX15 Set the timer to VX FX07 Store the value of the timer to VX
39 | *
40 | * The timer works on a 60 hertz cycle. IE if you set the timer to 60 it
41 | * will countdown to 0 over the course of a second.
42 | *
43 | */
44 | @Test
45 | public void testDelayTimerOpcodes() {
46 | chip8.execute(0xF015); //Set timer to 0x64
47 | chip8.execute(0xF107); //Read timer into V1
48 | assertEquals(0x64, chip8.getV1());
49 |
50 | }
51 |
52 | @Test(timeout = 500l)
53 | public void testDelayTimerCountdown() {
54 | while (chip8.getV5() != 255) {
55 | chip8.cycle();
56 | }
57 | }
58 |
59 | /**
60 | * There is a second timer, the sound timer. The sound timer will emit a
61 | * tone until it reaches 0. It operates on the same cycle as the delay
62 | * timer.
63 | *
64 | * Opcodes : 0xFX18 Set the sound timer to the value in VX
65 | *
66 | */
67 | @Test
68 | public void testSoundTimer() {
69 | chip8.execute(0xF018); //Set timer to 0x64
70 | chip8.cycle();
71 | }
72 |
73 | /**
74 | * This test will test that sound is actually emitted.
75 | *
76 | * It is disabled by default because it could be annoying.
77 | *
78 | * @throws java.io.IOException loading the program may throw an ioexception.
79 | */
80 | @Ignore
81 | @Test(timeout = 110000l)
82 | public void testEmitSoundTimer() throws IOException {
83 |
84 | Chip8 soundChip = Chip8Utils.createFromRom(getClass().getResource("/E05SoundLoop.ch8"));
85 | while (soundChip.getV5() != 255) {
86 | soundChip.cycle();
87 | }
88 |
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | CHIP 8 by secondsun
2 | ===================
3 |
4 | A TDD Chip-8 implementation designed to teach how to write an "emulator" from scratch.
5 |
6 | [](https://www.youtube.com/watch?v=X8fVRDHrw18)
7 |
8 | ## Welcome
9 |
10 | Chip-8 is a was created in the 1970s by Joseph Weisbecker to run games on the 8-bit computers of the day. Chip-8 includes an assembly language, an interpreter, and the runtime. There is a community around the platform which has created several games, and it has also expanded the platform to include newer features. However, this project focuses on just the "vanilla" Chip-8 from the 1970s.
11 |
12 | This project focuses on implementing the Chip8 interpreter in a test driven manner. You don't need any experience in writing emulators, but understanding Hexedecimal notation and binary operators are important.
13 |
14 | The master branch of this project is a stubbed out TDD project. The [working](https://github.com/secondsun/chip8/tree/working) branch has my finished project.
15 |
16 | ## Prerequisites
17 |
18 | You need to have [Java 8](http://java.oracle.com) and [Maven](http://maven.apache.org) installed. You can confirm it is working by running :
19 |
20 | ```bash
21 | mvn clean install
22 | ```
23 |
24 | You should maven attempt to run the project and fail because the tests do not pass. Implementing these tests and thus Chip8 is part of th exercise.
25 |
26 | ## Code overview
27 |
28 | Our tests are in the [test](src/test/java/net/saga/console/chip8/test) directory. They are grouped into logical categories to to work through. Feel free to add more tests if you don't feel comfortable with your code or want to see more how it works.
29 |
30 | There are a few helper classes for outputting audio, loading roms, displaying graphics, and handling input.
31 |
32 | Finally, the [Chip8](src/main/java/net/saga/console/chip8/Chip8.java) class has a few parameters initialized but is otherwise empty. This is the class file you will implement.
33 |
34 | ## Let's Code
35 |
36 | The project is broken up into eight sections :
37 | - 01 [Arithmetic](src/test/java/net/saga/console/chip8/test/E01ArithmeticOpCodeTest.java)
38 | - 02 [Bitwise operators](src/test/java/net/saga/console/chip8/test/E02BitwiseOpCodeTest.java)
39 | - 03 [Execution](src/test/java/net/saga/console/chip8/test/E03ClockExecutionAndMemoryTest.java)
40 | - 04 [Flow Control](src/test/java/net/saga/console/chip8/test/E04FlowControlTest.java)
41 | - 05 [Timers](src/test/java/net/saga/console/chip8/test/E05TimersTest.java)
42 | - 06 [Keypad Input](src/test/java/net/saga/console/chip8/test/E06KeypadInputTest.java)
43 | - 07 [Graphics](src/test/java/net/saga/console/chip8/test/E07GraphicsTest.java)
44 | - 08 [Utilities](src/test/java/net/saga/console/chip8/test/E08UtilitiesTest.java)
45 |
46 | Additionally you may want to review other documents on CHIP8 from the web. My reference for writing this project was [Mastering CHIP-8 by Matthew Mikolay](http://mattmik.com/files/chip8/mastering/chip8.html) and the [CHIP-8 Wikipedia article](https://en.wikipedia.org/wiki/CHIP-8).
47 |
48 | # Exec
49 |
50 | When you are finished you can run Chip8 in a Swing window
51 |
52 | ```bash
53 | mvn exec:java -Dexec.mainClass="net.saga.console.chip8.SwingMain"
54 | ```
55 |
56 | Find some roms online and run them in your emulator.
--------------------------------------------------------------------------------
/src/main/java/net/saga/console/chip8/Chip8Model.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2016 summers.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software")), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package net.saga.console.chip8;
25 |
26 | import static java.lang.Integer.toHexString;
27 | import javax.swing.table.DefaultTableModel;
28 |
29 | /**
30 | *
31 | * @author summers
32 | */
33 | public class Chip8Model extends DefaultTableModel {
34 |
35 | private static final long serialVersionUID = 1L;
36 |
37 | public Chip8Model() {
38 | super(20, 2);
39 | super.setColumnIdentifiers(new String[]{"", "V"});
40 | super.setValueAt("V0", 0, 0);
41 | super.setValueAt("V1", 1, 0);
42 | super.setValueAt("V2", 2, 0);
43 | super.setValueAt("V3", 3, 0);
44 | super.setValueAt("V4", 4, 0);
45 | super.setValueAt("V5", 5, 0);
46 | super.setValueAt("V6", 6, 0);
47 | super.setValueAt("V7", 7, 0);
48 | super.setValueAt("V8", 8, 0);
49 | super.setValueAt("V9", 9, 0);
50 | super.setValueAt("VA", 10, 0);
51 | super.setValueAt("VB", 11, 0);
52 | super.setValueAt("VC", 12, 0);
53 | super.setValueAt("VD", 13, 0);
54 | super.setValueAt("VE", 14, 0);
55 | super.setValueAt("VF", 15, 0);
56 | super.setValueAt("", 16, 0);
57 | super.setValueAt("I", 17, 0);
58 | super.setValueAt("PC", 18, 0);
59 | super.setValueAt("SP", 19, 0);
60 | }
61 |
62 | public void update(Chip8 chip8) {
63 | setValueAt(toHexString(chip8.getV0()), 0, 1);
64 | setValueAt(toHexString(chip8.getV1()), 1, 1);
65 | setValueAt(toHexString(chip8.getV2()), 2, 1);
66 | setValueAt(toHexString(chip8.getV3()), 3, 1);
67 | setValueAt(toHexString(chip8.getV4()), 4, 1);
68 | setValueAt(toHexString(chip8.getV5()), 5, 1);
69 | setValueAt(toHexString(chip8.getV6()), 6, 1);
70 | setValueAt(toHexString(chip8.getV7()), 7, 1);
71 | setValueAt(toHexString(chip8.getV8()), 8, 1);
72 | setValueAt(toHexString(chip8.getV9()), 9, 1);
73 | setValueAt(toHexString(chip8.getVA()), 10, 1);
74 | setValueAt(toHexString(chip8.getVB()), 11, 1);
75 | setValueAt(toHexString(chip8.getVC()), 12, 1);
76 | setValueAt(toHexString(chip8.getVD()), 13, 1);
77 | setValueAt(toHexString(chip8.getVE()), 14, 1);
78 | setValueAt(toHexString(chip8.getVF()), 15, 1);
79 | setValueAt("", 16, 1);
80 | setValueAt(toHexString(chip8.getiRegister()), 17, 1);
81 | setValueAt(toHexString(chip8.getPC()), 18, 1);
82 | setValueAt(toHexString(chip8.getSP()), 19, 1);
83 | }
84 |
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/test/java/net/saga/console/chip8/test/E06KeypadInputTest.java:
--------------------------------------------------------------------------------
1 | package net.saga.console.chip8.test;
2 |
3 | import java.io.IOException;
4 | import net.saga.console.chip8.Chip8;
5 | import net.saga.console.chip8.util.Chip8Utils;
6 | import net.saga.console.chip8.util.Input;
7 | import org.junit.After;
8 | import static org.junit.Assert.assertEquals;
9 | import org.junit.Before;
10 | import org.junit.Test;
11 |
12 | /**
13 | *
14 | * Chip-8 has support for a 16 button keypad input. The keys are assigned values
15 | * from 0x0 - 0xF. The mapping from your keyboard/input device to chip8
16 | * shouldn't matter for these tests.
17 | */
18 | public class E06KeypadInputTest {
19 |
20 | private Chip8 chip8;
21 |
22 | @Before
23 | public void setUp() throws IOException {
24 | this.chip8 = Chip8Utils.createFromRom(getClass().getResource("/E06KeypadLoop.ch8"));
25 | this.chip8.execute(0x6064);
26 | this.chip8.execute(0x6127);
27 | this.chip8.execute(0x6212);
28 | this.chip8.execute(0x63AE);
29 | this.chip8.execute(0x64FF);
30 | this.chip8.execute(0x65B4);
31 | this.chip8.execute(0x6642);
32 | this.chip8.execute(0x6F25);
33 | }
34 |
35 | @After
36 | public void unpress() {
37 | Input.unpress();
38 | }
39 |
40 | /**
41 | * The opcode FX0A will wait for a key press and store its value into
42 | * register VX.
43 | *
44 | * This test will execute the opcode and then call cycle several times. As
45 | * long as the program counter does not increment we will assume that chip8
46 | * is not executing.
47 | *
48 | */
49 | @Test
50 | public void testChip8WaitsForKeyboardInput() {
51 | int pc = chip8.getPC();
52 | chip8.cycle();
53 | chip8.cycle();
54 | chip8.cycle();
55 | chip8.cycle();
56 | assertEquals(pc, chip8.getPC());
57 | }
58 |
59 | @Test
60 | public void testChip8ContinuesAfterKeyboardInput() {
61 | int pc = chip8.getPC();
62 | chip8.cycle();
63 | chip8.cycle();
64 | assertEquals(pc, chip8.getPC());
65 |
66 | Input.press(0xA);
67 | chip8.cycle();
68 | chip8.cycle();
69 | assertEquals(0xA, chip8.getV6());
70 |
71 | }
72 |
73 | /**
74 | * The next opcode will skip the next instruction until the keypress matches
75 | * a value in a certain register.
76 | *
77 | * EX9E : Skip the next instruction of the key corresponding to the value in
78 | * VX is pressed.
79 | *
80 | */
81 | @Test
82 | public void skipIfPressed() {
83 | Input.press(0x1);
84 | chip8.execute(0x6002);//Store 0x02 into V0
85 | chip8.execute(0xE09E);//Skip if 0x02 is pressed (it isn't)
86 | assertEquals(0x200, chip8.getPC());
87 |
88 | Input.press(0x2);
89 | chip8.execute(0x6002);//Store 0x02 into V0
90 | chip8.execute(0xE09E);//Skip if 0x02 is pressed (it is)
91 | assertEquals(0x202, chip8.getPC());
92 |
93 | }
94 |
95 | /**
96 | * The next opcode will not skip the next instruction if the keypress
97 | * matches a value in a certain register.
98 | *
99 | * EXA1 : Skip the next instruction of the key corresponding to the value in
100 | * VX is not pressed.
101 | *
102 | */
103 | @Test
104 | public void skipIfNotPressed() {
105 | Input.press(0x1);
106 | chip8.execute(0x6002);//Store 0x02 into V0
107 | chip8.execute(0xE0A1);//Skip if 0x02 is not pressed (it isn't)
108 | assertEquals(0x202, chip8.getPC());
109 |
110 | Input.press(0x2);
111 | chip8.execute(0x6002);//Store 0x02 into V0
112 | chip8.execute(0xE0A1);//Skip if 0x02 is pressed (it is)
113 | assertEquals(0x202, chip8.getPC());
114 | }
115 |
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/net/saga/console/chip8/Chip8.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2016 summers.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package net.saga.console.chip8;
25 |
26 | import net.saga.console.chip8.util.Input;
27 | import net.saga.console.chip8.util.Audio;
28 | import java.util.Arrays;
29 | import java.util.Random;
30 |
31 | /**
32 | * This class is the main chip8 system
33 | */
34 | public class Chip8 {
35 |
36 | private int pc = 0x200;
37 | private int iRegister = 0;
38 | private final int[] registers = new int[0x10];
39 | private final Random random = new Random();
40 | private int sp = 0;
41 | private int delayTimer = 0;
42 | private int soundTimer = 0;
43 | private final int[] stack = new int[16];
44 | private final byte[] memory;
45 | private byte[] video = new byte[64 * 32];
46 | private long nextTimer = 0;
47 |
48 | public Chip8() {
49 | this.memory = new byte[4096];
50 | }
51 |
52 | public Chip8(byte[] memory) {
53 | if (memory.length > 4096) {
54 | throw new IllegalArgumentException("Memory may not be greater than 4096 bytes");
55 | }
56 | this.memory = memory;
57 | }
58 |
59 | public int getPC() {
60 | return pc;
61 | }
62 |
63 | private void setVX(int value, int register) {
64 | registers[register] = (0x000000FF & value);
65 | }
66 |
67 | private int getVX(int x) {
68 | return 0x000000FF & registers[x];
69 | }
70 |
71 | public int getV0() {
72 | return registers[0] & 0xFF;
73 | }
74 |
75 | public int getV1() {
76 | return registers[1] & 0xFF;
77 | }
78 |
79 | public int getV2() {
80 | return registers[0x2] & 0xFF;
81 | }
82 |
83 | public int getV3() {
84 | return registers[0x3] & 0xFF;
85 | }
86 |
87 | public int getV4() {
88 | return registers[0x4] & 0xFF;
89 | }
90 |
91 | public int getV5() {
92 | return registers[0x5] & 0xFF;
93 | }
94 |
95 | public int getV6() {
96 | return registers[0x6] & 0xFF;
97 | }
98 |
99 | public int getV7() {
100 | return registers[0x7] & 0xFF;
101 | }
102 |
103 | public int getV8() {
104 | return registers[0x8] & 0xFF;
105 | }
106 |
107 | public int getV9() {
108 | return registers[0x9] & 0xFF;
109 | }
110 |
111 | public int getVA() {
112 | return registers[0xa] & 0xFF;
113 | }
114 |
115 | public int getVB() {
116 | return registers[0xb] & 0xFF;
117 | }
118 |
119 | public int getVC() {
120 | return registers[0xc] & 0xFF;
121 | }
122 |
123 | public int getVD() {
124 | return registers[0xd] & 0xFF;
125 | }
126 |
127 | public int getVE() {
128 | return registers[0xe] & 0xFF;
129 | }
130 |
131 | public int getVF() {
132 | return registers[0xf] & 0xFF;
133 | }
134 |
135 | public void execute(int instruction) {
136 | throw new UnsupportedOperationException("Unsupported opcode:" + Integer.toHexString(instruction));
137 | }
138 |
139 | public void cycle() {
140 |
141 | }
142 |
143 | public int getiRegister() {
144 | return iRegister;
145 | }
146 |
147 | public byte[] getScreen() {
148 | return Arrays.copyOf(video, video.length);
149 | }
150 |
151 | public byte[] getMemory() {
152 | return Arrays.copyOf(memory, memory.length);
153 | }
154 |
155 | public int getSP() {
156 | return sp;
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/src/test/java/net/saga/console/chip8/test/E08UtilitiesTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2016 summers.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package net.saga.console.chip8.test;
25 |
26 | import net.saga.console.chip8.Chip8;
27 | import static org.junit.Assert.assertEquals;
28 | import org.junit.Before;
29 | import org.junit.Test;
30 |
31 | /**
32 | *
33 | * Chip 8 has a few opcodes which are convenient utility methods.
34 | */
35 | public class E08UtilitiesTest {
36 |
37 | private Chip8 chip8;
38 |
39 | @Before
40 | public void setup() {
41 | this.chip8 = new Chip8();
42 | this.chip8.execute(0x6064);
43 | this.chip8.execute(0x6127);
44 | this.chip8.execute(0x6212);
45 | this.chip8.execute(0x63AE);
46 | this.chip8.execute(0x64FF);
47 | this.chip8.execute(0x65B4);
48 | this.chip8.execute(0x6642);
49 | this.chip8.execute(0x6F25);
50 | }
51 |
52 |
53 | /**
54 | * FX33 : Convert the value in VX to decimal and store each digit in I, I+1,
55 | * and I+2 along with any leading 0s.
56 | */
57 | @Test
58 | public void testBCD() {
59 | chip8.execute(0xA200);
60 |
61 | chip8.execute(0xF033);
62 | byte[] memory = chip8.getMemory();
63 | assertEquals(1, memory[0x200]);
64 | assertEquals(0, memory[0x201]);
65 | assertEquals(0, memory[0x202]);
66 |
67 |
68 | chip8.execute(0xF133);
69 | memory = chip8.getMemory();
70 | assertEquals(0, memory[0x200]);
71 | assertEquals(3, memory[0x201]);
72 | assertEquals(9, memory[0x202]);
73 |
74 | chip8.execute(0xF433);
75 | memory = chip8.getMemory();
76 | assertEquals(2, memory[0x200]);
77 | assertEquals(5, memory[0x201]);
78 | assertEquals(5, memory[0x202]);
79 |
80 | }
81 |
82 | /**
83 | * Chip8 has the ability to copy a range of registers to memory.
84 | * FX55 : Store the values of registers V0 -> VX to memory addresses I -> I + X.
85 | * Set I to I + X + 1 afterward.
86 | */
87 | @Test
88 | public void copyToMemory() {
89 | chip8.execute(0xA200);
90 | chip8.execute(0xF355);
91 |
92 | byte[] memory = chip8.getMemory();
93 |
94 | assertEquals((byte)0x64, memory[0x200]);
95 | assertEquals((byte)0x27, memory[0x201]);
96 | assertEquals((byte)0x12, memory[0x202]);
97 | assertEquals((byte)0xAE, memory[0x203]);
98 | assertEquals(0x204, chip8.getiRegister());
99 |
100 | }
101 |
102 | /**
103 | * Chip8 has the ability to copy a range of memory to registers.
104 | * FX65 : Store the values of memory addresses I -> I + X to registers V0 -> VX.
105 | * Set I to I + X + 1 afterward.
106 | */
107 | @Test
108 | public void copyFromMemory() {
109 | chip8.execute(0xA200);
110 | chip8.execute(0xF355);
111 |
112 | byte[] memory = chip8.getMemory();
113 |
114 | assertEquals((byte)0x64, memory[0x200]);
115 | assertEquals((byte)0x27, memory[0x201]);
116 | assertEquals((byte)0x12, memory[0x202]);
117 | assertEquals((byte)0xAE, memory[0x203]);
118 | assertEquals(0x204, chip8.getiRegister());
119 |
120 | chip8.execute(0xA200);
121 | this.chip8.execute(0x6000);
122 | this.chip8.execute(0x6100);
123 | this.chip8.execute(0x6200);
124 | this.chip8.execute(0x6300);
125 | chip8.execute(0xF365);
126 |
127 | assertEquals((byte)0x64, chip8.getV0());
128 | assertEquals((byte)0x27, chip8.getV1());
129 | assertEquals((byte)0x12, chip8.getV2());
130 | assertEquals((byte)0xAE, (byte)chip8.getV3());
131 | assertEquals(0x204, chip8.getiRegister());
132 |
133 |
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/src/test/java/net/saga/console/chip8/test/E04FlowControlTest.java:
--------------------------------------------------------------------------------
1 | package net.saga.console.chip8.test;
2 |
3 | import net.saga.console.chip8.Chip8;
4 | import org.junit.After;
5 | import org.junit.AfterClass;
6 | import org.junit.Before;
7 | import org.junit.BeforeClass;
8 | import org.junit.Test;
9 | import static org.junit.Assert.assertEquals;
10 |
11 | /**
12 | * This test suite will work with flow control op codes. They will adjust the
13 | * program counter to change the next command which will be executed.
14 | *
15 | * @author summers
16 | */
17 | public class E04FlowControlTest {
18 |
19 | private Chip8 chip8;
20 |
21 | public E04FlowControlTest() {
22 | }
23 |
24 | @BeforeClass
25 | public static void setUpClass() {
26 | }
27 |
28 | @AfterClass
29 | public static void tearDownClass() {
30 | }
31 |
32 | @Before
33 | public void setUp() {
34 | this.chip8 = new Chip8();
35 | this.chip8.execute(0x6064);
36 | this.chip8.execute(0x6127);
37 | this.chip8.execute(0x6212);
38 | this.chip8.execute(0x63AE);
39 | this.chip8.execute(0x64FF);
40 | this.chip8.execute(0x65B4);
41 | this.chip8.execute(0x6642);
42 | this.chip8.execute(0x6F25);
43 | }
44 |
45 | @After
46 | public void tearDown() {
47 | }
48 |
49 | /**
50 | * Jump instructions are the most basic flow control. They simply tell the
51 | * processor to start executing programs from a different area of memory.
52 | *
53 | * Chip-8 has two jump instructions : 1NNN This instruction sets the program
54 | * counter to execute from memory address NNN BNNN This instruction sets the
55 | * program counter to execute from memory address (NNN + V0)
56 | */
57 | @Test
58 | public void testJump() {
59 | chip8.execute(0x1DAE);
60 | assertEquals(0xDAE, chip8.getPC());
61 |
62 | chip8.execute(0xB432);
63 | assertEquals(1174, chip8.getPC());
64 |
65 | }
66 |
67 | /**
68 | * Chip8 has build in support for subroutines.
69 | *
70 | * There are two subroutine opcodes : 2NNN start executing subroutine at NNN
71 | * 00EE return from the subroutine
72 | *
73 | * For this we will have to do manipulations to the stack. 2NNN should write
74 | * the current instruction address to the stack and increment the stack
75 | * pointer. Most chip8 systems support 16 entries in the stack pointer.
76 | *
77 | */
78 | @Test
79 | public void testSubroutines() {
80 | chip8.execute(0x2DAE);
81 | assertEquals(0xDAE, chip8.getPC());
82 |
83 | chip8.execute(0x00EE);
84 | assertEquals(0x200, chip8.getPC());
85 |
86 | }
87 |
88 | /**
89 | * We will now implement conditional skipping. Conditional skips are
90 | * basically jumps if some condition is met like a register having a value.
91 | *
92 | * Opcodes :
93 | * 3XNN : Skip the next instruction if the value in Vx equals NN
94 | * 5XY0 : Skip the next instruction if the value in Vx equals Vy
95 | * 4XNN : Skip the next instruction if the value in Vx does not equal NN
96 | * 9XY0 : Skip the next instruction if the value in Vx does not equal Vy
97 | *
98 | * This test will be running the program counter forward so it will also test that
99 | * we aren't doing anything too wrong with it.
100 | *
101 | * Also, don't forget we have the follow constants loaded into registers already :
102 | * V0 = 0x64
103 | * V1 = 0x27
104 | * V2 = 0x12
105 | * V3 = 0xAE
106 | * V4 = 0xFF
107 | * V5 = 0xB4
108 | * V6 = 0x42
109 | * VF = 0x25
110 | *
111 | */
112 | @Test
113 | public void testEqualJumps() {
114 | chip8.execute(0x3064); // Skip if V0 == 0x64
115 | assertEquals(0x202, chip8.getPC());//Increment the PC by 2
116 |
117 | chip8.execute(0x3164); // Skip if V1 == 0x64, doesn't skip because V1 == 0x27
118 | assertEquals(0x202, chip8.getPC());//Do not increment the PC
119 |
120 | chip8.execute(0x6764); // Set V7 to 64
121 | chip8.execute(0x5070); // Skip if V0 == V7
122 | assertEquals(0x204, chip8.getPC());//Increment the PC by 2
123 |
124 | chip8.execute(0x5170); // Skip if V1 == V7 (It doesn't)
125 | assertEquals(0x204, chip8.getPC());//Increment the PC by 2
126 | }
127 |
128 | @Test
129 | public void testNonEqualJumps() {
130 | chip8.execute(0x4064); // Skip if V0 != 0x64 (it won't skip)
131 | assertEquals(0x200, chip8.getPC());//Increment the PC by 2
132 |
133 | chip8.execute(0x4164); // Skip if V1 == 0x64, skips because V1 == 0x27
134 | assertEquals(0x202, chip8.getPC());//Do not increment the PC
135 |
136 | chip8.execute(0x6764); // Set V7 to 64
137 | chip8.execute(0x9070); // Skip if V0 != V7(It won't skip)
138 | assertEquals(0x202, chip8.getPC());//Increment the PC by 2
139 |
140 | chip8.execute(0x9170); // Skip if V1 != V7
141 | assertEquals(0x204, chip8.getPC());//Increment the PC by 2
142 |
143 |
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/test/java/net/saga/console/chip8/test/E02BitwiseOpCodeTest.java:
--------------------------------------------------------------------------------
1 | package net.saga.console.chip8.test;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.Random;
5 | import java.util.logging.Level;
6 | import java.util.logging.Logger;
7 | import net.saga.console.chip8.Chip8;
8 | import org.junit.After;
9 | import org.junit.AfterClass;
10 | import org.junit.Before;
11 | import org.junit.BeforeClass;
12 | import org.junit.Test;
13 | import static org.junit.Assert.assertEquals;
14 | import static org.junit.Assert.assertEquals;
15 |
16 | /**
17 | *
18 | * In E01 We set up the Chip-8 processor and implemented the basic arithmetic
19 | * opcodes. In this section we will implement bitwise opcodes.
20 | *
21 | * These include AND, OR, XOR, and shifts.
22 | *
23 | * In this tests we will initialize the registers to the following values : V0 =
24 | * 0x64 V1 = 0x27 V2 = 0x12 V3 = 0xAE V4 = 0xFF V5 = 0xB4 V6 = 0x42 VF = 0x25
25 | *
26 | * All other registers will be 0.
27 | *
28 | * Additionally we will be using reflection to control the random values
29 | * generated by java.
30 | *
31 | */
32 | public class E02BitwiseOpCodeTest {
33 |
34 | private Chip8 chip8;
35 |
36 | public E02BitwiseOpCodeTest() {
37 | }
38 |
39 | @BeforeClass
40 | public static void setUpClass() {
41 | }
42 |
43 | @AfterClass
44 | public static void tearDownClass() {
45 | }
46 |
47 | @Before
48 | public void setUp() {
49 | this.chip8 = new Chip8();
50 | controlRandom();
51 | this.chip8.execute(0x6064);
52 | this.chip8.execute(0x6127);
53 | this.chip8.execute(0x6212);
54 | this.chip8.execute(0x63AE);
55 | this.chip8.execute(0x64FF);
56 | this.chip8.execute(0x65B4);
57 | this.chip8.execute(0x6642);
58 | this.chip8.execute(0x6F25);
59 | }
60 |
61 | @After
62 | public void tearDown() {
63 | }
64 |
65 | /**
66 | * 8XY2 Set VX to VX & VY
67 | */
68 | @Test
69 | public void testAndOpCodes() {
70 | chip8.execute(0x8012); // v0 = 0x64 & 0x27
71 | assertEquals(36, chip8.getV0());
72 | assertEquals(0x27, chip8.getV1());
73 |
74 | chip8.execute(0x8232); // v2 = 0x12 & 0xAE
75 | assertEquals(2, chip8.getV2());
76 | assertEquals(0xAE, chip8.getV3());
77 |
78 | chip8.execute(0x8FE2); // 0x25 & 0x0
79 | assertEquals(0, chip8.getVF());
80 |
81 | }
82 |
83 | /**
84 | * 8XY1 Set VX to VX | VY
85 | */
86 | @Test
87 | public void testOROpCodes() {
88 | chip8.execute(0x8011); // v0 = 0x64 | 0x27
89 | assertEquals(103, chip8.getV0());
90 | assertEquals(0x27, chip8.getV1());
91 |
92 | chip8.execute(0x8231); // v2 = 0x12 | 0xAE
93 | assertEquals(190, chip8.getV2());
94 | assertEquals(0xAE, chip8.getV3());
95 |
96 | chip8.execute(0x8FE1); // 0x25 | 0x0
97 | assertEquals(0x25, chip8.getVF());
98 |
99 | }
100 |
101 | /**
102 | * 8XY3 Set VX to VX ^ VY
103 | */
104 | @Test
105 | public void testXOROpCodes() {
106 | chip8.execute(0x8013); // v0 = 0x64 ^ 0x27
107 | assertEquals(67, chip8.getV0());
108 | assertEquals(0x27, chip8.getV1());
109 |
110 | chip8.execute(0x8233); // v2 = 0x12 ^ 0xAE
111 | assertEquals(188, chip8.getV2());
112 | assertEquals(0xAE, chip8.getV3());
113 |
114 | chip8.execute(0x8FE3); // 0x25 ^ 0x0
115 | assertEquals(0x25, chip8.getVF());
116 |
117 | }
118 |
119 | /**
120 | * 8XY6 Shift VX Right one place and store the result in VX. Also store the
121 | * lest significant bit in VF
122 | */
123 | @Test
124 | public void testShiftRight() {
125 | chip8.execute(0x8016); // v0 = 0x27 >> 1; xF = 0x1
126 | assertEquals(0x32, chip8.getV0());
127 | assertEquals(0x0, chip8.getVF());
128 |
129 | chip8.execute(0x8236); // v2 = 0xAE >> 1; VF = 0x0
130 | assertEquals(0x09, chip8.getV2());
131 | assertEquals(0x0, chip8.getVF());
132 |
133 | chip8.execute(0x8446); // V4 = 0xFF >> 1; VF = 0x1;
134 | assertEquals(127, chip8.getV4());
135 | assertEquals(0x1, chip8.getVF());
136 |
137 | }
138 |
139 | /**
140 | * 8XYE Shift VX left one place and store the result in VX. Also store the
141 | * most significant bit in VF
142 | */
143 | @Test
144 | public void testShiftLeft() {
145 | chip8.execute(0x801E); // v0 = 0x27 << 1; xF = 0x1
146 | assertEquals(200, chip8.getV0());
147 | assertEquals(0x0, chip8.getVF());
148 |
149 | chip8.execute(0x823E); // v2 = 0xAE << 1; VF = 0x0
150 | assertEquals(36, chip8.getV2());
151 | assertEquals(0x0, chip8.getVF());
152 |
153 | chip8.execute(0x844E); // V4 = 0xFF << 1; VF = 0x1;
154 | assertEquals(254, chip8.getV4());
155 | assertEquals(0x1, chip8.getVF());
156 |
157 | }
158 |
159 | /**
160 | * CXNN Generate a Random number, mask it with NN and store the results in
161 | * Vx.
162 | */
163 | @Test
164 | public void testRandom() {
165 | //230, 198,153, 29
166 | chip8.execute(0xC1FF); // V1 = 230 & 0xFF
167 | assertEquals(230, chip8.getV1());
168 |
169 | chip8.execute(0xC23E); // v2 = 198 & 0x3E
170 | assertEquals(6, chip8.getV2());
171 |
172 | chip8.execute(0xC44E); // V4 = 153 & 0x4E
173 | assertEquals(8, chip8.getV4());
174 |
175 |
176 | }
177 |
178 | private void controlRandom() {
179 | try {
180 | Field field = chip8.getClass().getDeclaredField("random");
181 | field.setAccessible(true);
182 | field.set(chip8, new Random(42));
183 | } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ex) {
184 | throw new RuntimeException(ex);
185 | }
186 | }
187 |
188 | }
189 |
--------------------------------------------------------------------------------
/src/test/java/net/saga/console/chip8/test/E01ArithmeticOpCodeTest.java:
--------------------------------------------------------------------------------
1 | package net.saga.console.chip8.test;
2 |
3 | import net.saga.console.chip8.Chip8;
4 | import org.junit.After;
5 | import org.junit.AfterClass;
6 | import org.junit.Assert;
7 | import org.junit.Before;
8 | import org.junit.BeforeClass;
9 | import org.junit.Test;
10 |
11 | /**
12 | *
13 | * The first part of writing a Chip-8 emulator is setting up the main memory
14 | * correctly.
15 | *
16 | * Specifically there are several constants which are found below 0x0200 and
17 | * programs typically start at 0x0200
18 | */
19 | public class E01ArithmeticOpCodeTest {
20 |
21 | private Chip8 chip8;
22 |
23 | public E01ArithmeticOpCodeTest() {
24 | }
25 |
26 | @BeforeClass
27 | public static void setUpClass() {
28 | }
29 |
30 | @AfterClass
31 | public static void tearDownClass() {
32 | }
33 |
34 | @Before
35 | public void setUp() {
36 | this.chip8 = new Chip8();
37 | }
38 |
39 | @After
40 | public void tearDown() {
41 | }
42 |
43 | /**
44 | * This chip 8 program counter starts at 0x200. This seems like an obvious
45 | * first test.
46 | */
47 | @Test
48 | public void testPCInitializedto0x200() {
49 | Assert.assertEquals(0x0200, chip8.getPC());
50 | }
51 |
52 | /**
53 | * CHIP-8 has 16 8 bit data registers numbered V0 - VF.
54 | *
55 | * The next few tests test the initial values of the registers and several
56 | * simple opcodes.
57 | *
58 | */
59 | @Test
60 | public void testDataRegistersInitializedTo0() {
61 | Assert.assertEquals(0x0, chip8.getV0());//Test initialized to 0
62 | Assert.assertEquals(0x0, chip8.getV1());//Test initialized to 0
63 | Assert.assertEquals(0x0, chip8.getV2());//Test initialized to 0
64 | Assert.assertEquals(0x0, chip8.getV3());//Test initialized to 0
65 | Assert.assertEquals(0x0, chip8.getV4());//Test initialized to 0
66 | Assert.assertEquals(0x0, chip8.getV5());//Test initialized to 0
67 | Assert.assertEquals(0x0, chip8.getV6());//Test initialized to 0
68 | Assert.assertEquals(0x0, chip8.getV7());//Test initialized to 0
69 | Assert.assertEquals(0x0, chip8.getV8());//Test initialized to 0
70 | Assert.assertEquals(0x0, chip8.getV9());//Test initialized to 0
71 | Assert.assertEquals(0x0, chip8.getVA());//Test initialized to 0
72 | Assert.assertEquals(0x0, chip8.getVB());//Test initialized to 0
73 | Assert.assertEquals(0x0, chip8.getVC());//Test initialized to 0
74 | Assert.assertEquals(0x0, chip8.getVD());//Test initialized to 0
75 | Assert.assertEquals(0x0, chip8.getVE());//Test initialized to 0
76 | Assert.assertEquals(0x0, chip8.getVF());//Test initialized to 0
77 | }
78 |
79 | /**
80 | * The opcode 0x6XNN stores the constant NN into register VX.
81 | *
82 | * IE 0x6A42 would store 42 into register VA. It should be noted at this
83 | * point that CHIP-8 is big-endian.
84 | *
85 | *
86 | *
87 | */
88 | @Test
89 | public void testLoadConstant() {
90 | chip8.execute(0x6015);
91 | Assert.assertEquals(0x15, chip8.getV0());//Test loads 15 to V0
92 |
93 | chip8.execute(0x6120);
94 | Assert.assertEquals(0x20, chip8.getV1());//etc
95 |
96 | chip8.execute(0x6225);
97 | Assert.assertEquals(0x25, chip8.getV2());
98 |
99 | chip8.execute(0x6330);
100 | Assert.assertEquals(0x30, chip8.getV3());
101 |
102 | chip8.execute(0x6435);
103 | Assert.assertEquals(0x35, chip8.getV4());
104 |
105 | chip8.execute(0x6540);
106 | Assert.assertEquals(0x40, chip8.getV5());
107 |
108 | chip8.execute(0x6645);
109 | Assert.assertEquals(0x45, chip8.getV6());
110 |
111 | chip8.execute(0x6750);
112 | Assert.assertEquals(0x50, chip8.getV7());
113 |
114 | chip8.execute(0x6855);
115 | Assert.assertEquals(0x55, chip8.getV8());
116 |
117 | chip8.execute(0x6960);
118 | Assert.assertEquals(0x60, chip8.getV9());
119 |
120 | chip8.execute(0x6A65);
121 | Assert.assertEquals(0x65, chip8.getVA());
122 |
123 | chip8.execute(0x6B70);
124 | Assert.assertEquals(0x70, chip8.getVB());
125 |
126 | chip8.execute(0x6C75);
127 | Assert.assertEquals(0x75, chip8.getVC());
128 |
129 | chip8.execute(0x6D80);
130 | Assert.assertEquals(0x80, chip8.getVD());
131 |
132 | chip8.execute(0x6E85);
133 | Assert.assertEquals(0x85, chip8.getVE());
134 |
135 | chip8.execute(0x6F90);
136 | Assert.assertEquals(0x90, chip8.getVF());
137 | }
138 |
139 | /**
140 | * The opcode 0x7XNN adds the constant NN into the value of register VX.
141 | *
142 | * IE 0x6A42 0x7A42
143 | *
144 | * would store 42 into register VA and then add 42 to get 84.
145 | *
146 | * A special note, registers are 8 bit and should overflow appropriately.
147 | *
148 | * We will use fewer tests now, but feel free to add more to test edge cases
149 | * and other questions you might have about your code.
150 | *
151 | */
152 | @Test
153 | public void testAddConstant() {
154 | chip8.execute(0x6015);
155 | chip8.execute(0x7015);
156 | Assert.assertEquals(0x2A, chip8.getV0());
157 |
158 | chip8.execute(0x6A42);
159 | chip8.execute(0x7A42);
160 | Assert.assertEquals(0x84, chip8.getVA());//Test loads 15 to V0
161 |
162 | chip8.execute(0x6EFF);
163 | chip8.execute(0x7E01);
164 | Assert.assertEquals(0x0, chip8.getVE());//Test Overflow
165 |
166 | }
167 |
168 |
169 |
170 | /**
171 | * The opcode 8XY0 stores the value of register VY into VX
172 | *
173 | * IE 0x6A42 0x8EA0
174 | *
175 | * would store 42 into register VA and then copy it to register VE.
176 | *
177 | *
178 | */
179 | @Test
180 | public void testCopyRegister() {
181 | chip8.execute(0x6A42);
182 | chip8.execute(0x8EA0);
183 | Assert.assertEquals(0x42, chip8.getVA());
184 | Assert.assertEquals(0x42, chip8.getVE());
185 |
186 | chip8.execute(0x6ADE);
187 | chip8.execute(0x8FA0);
188 | Assert.assertEquals(0x42, chip8.getVE());
189 | Assert.assertEquals(0xDE, chip8.getVF());
190 |
191 | }
192 |
193 | /**
194 | * The opcode 8XY4 adds the value of register VY to VX
195 | *
196 | * IE 0x6A42 0x6E42 0x8EA4
197 | *
198 | * would store 42 into register VA and VE and then add VA into register VE.
199 | * This instruction will modify register VF. If there is an overflow then VF
200 | * will be set to 01. If there is not an overflow it will be set to 00.
201 | *
202 | * This means that VF is always modified.
203 | *
204 | *
205 | */
206 | @Test
207 | public void testAddRegister() {
208 | chip8.execute(0x6A42);
209 | chip8.execute(0x6E42);
210 | chip8.execute(0x8FA0);
211 | chip8.execute(0x8EA4);
212 | Assert.assertEquals(0x42, chip8.getVA());
213 | Assert.assertEquals(0x84, chip8.getVE());
214 | Assert.assertEquals(0x00, chip8.getVF());
215 |
216 | chip8.execute(0x6AF0);
217 | chip8.execute(0x6E42);
218 | chip8.execute(0x8FA0);
219 | chip8.execute(0x8EA4);
220 | Assert.assertEquals(0xF0, chip8.getVA());
221 | Assert.assertEquals(0x32, chip8.getVE());
222 | Assert.assertEquals(0x01, chip8.getVF());
223 |
224 | }
225 |
226 |
227 | /**
228 | * The opcodes 8XY5 and 8XY7 perform subtraction operations and set VF to
229 | * indicate a borrow.
230 | *
231 | * 8XY5 sets VX to VX - VY 8XY7 sets VX to VY - VX
232 | *
233 | * IE 0x6B84 0x6D25 0x8DB5
234 | *
235 | * would store 84 into register VB and 25 into VD. Then VD would be set to
236 | * (25 - 84) which would underflow to xxx. Additionally VF would be set to
237 | * 0x00 to indicate the borrow.
238 | *
239 | * IE 0x6B84 0x6D25 0x8DB7
240 | *
241 | * would store 84 into register VB and 25 into VD. Then VD would be set to
242 | * (84 - 25) which would be 59. Additionally VF would be set to 0x01 to
243 | * indicate there was no borrow.
244 | *
245 | */
246 | @Test
247 | public void testSubtractRegister() {
248 | chip8.execute(0x6B84); // Set Register B to 0x84
249 | chip8.execute(0x6F84); // Set Register F to 0x84 (this tests carry)
250 | chip8.execute(0x6D25); // Set Register D to 0x25
251 | chip8.execute(0x8DB5); // Set Register D to 0x25 - 0x84. This underflows and forces a carry
252 |
253 | Assert.assertEquals(0x84, chip8.getVB());
254 | Assert.assertEquals(161, chip8.getVD());
255 | Assert.assertEquals(0x00, chip8.getVF());
256 |
257 | chip8.execute(0x6B84);
258 | chip8.execute(0x6F84);
259 | chip8.execute(0x6D25);
260 | chip8.execute(0x8DB7);
261 |
262 | Assert.assertEquals(0x84, chip8.getVB());
263 | Assert.assertEquals(95, chip8.getVD());
264 | Assert.assertEquals(0x01, chip8.getVF());
265 |
266 | }
267 |
268 | }
269 |
--------------------------------------------------------------------------------
/src/main/java/net/saga/console/chip8/SwingMain.form:
--------------------------------------------------------------------------------
1 |
2 |
3 |
246 |
--------------------------------------------------------------------------------
/src/test/java/net/saga/console/chip8/test/E07GraphicsTest.java:
--------------------------------------------------------------------------------
1 | package net.saga.console.chip8.test;
2 |
3 | import java.io.IOException;
4 | import net.saga.console.chip8.Chip8;
5 | import net.saga.console.chip8.util.Chip8Utils;
6 | import static org.junit.Assert.assertEquals;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 |
10 | /**
11 | * This last test class covers graphics. There are three main parts of graphics
12 | * : The I Register, Fonts, and Sprites.
13 | */
14 | public class E07GraphicsTest {
15 |
16 | private Chip8 chip8;
17 |
18 | @Before
19 | public void setUp() throws IOException {
20 | //Graphics ROM has the sprite FF3C stored starting at 202
21 | // This is the following two row sprite
22 | //
23 | // ********
24 | // ****
25 | //
26 | //Executed the program just infinitely loops
27 | this.chip8 = Chip8Utils.createFromRom(getClass().getResource("/E07GraphicsRom.ch8"));
28 | this.chip8.execute(0x6064);
29 | this.chip8.execute(0x6127);
30 | this.chip8.execute(0x6212);
31 | this.chip8.execute(0x63AE);
32 | this.chip8.execute(0x64FF);
33 | this.chip8.execute(0x65B4);
34 | this.chip8.execute(0x6642);
35 | this.chip8.execute(0x673F);
36 | this.chip8.execute(0x681F);
37 | this.chip8.execute(0x6F25);
38 | }
39 |
40 | /**
41 | * The I Register holds memory addresses. A program can not read the value
42 | * currently set in the I register but may only set it.
43 | *
44 | * There are two opcodes for the IRegister : ANNN : Stores in the I register
45 | * the address 0xNNN FX1E : Adds to the value in the I-Register the value of
46 | * VX.
47 | */
48 | @Test
49 | public void testIRegister() {
50 | this.chip8.execute(0xA123);
51 | assertEquals(0x123, chip8.getiRegister());
52 |
53 | this.chip8.execute(0xF11E);
54 | assertEquals(0x123 + 0x27, chip8.getiRegister());
55 |
56 | }
57 |
58 | /**
59 | *
60 | * Sprints in chip8 are represented in memory as columns of 8 bits with a
61 | * variable number of rows. The 8 bits each correspond to a pixel with 1
62 | * being toggled and 0 being transparent. The sprites are accessed starting
63 | * at the memory address pointed to by the I register.
64 | *
65 | * Sprites are XOR drawn. This means that a 1 will flip that particular
66 | * pixel. If a pixel is unset by this operation then the register VF is set
67 | * to 1. Otherwise it is 0.
68 | *
69 | * DXYN : Draw a Sprite of N rows at position VX,VY with the data pointed to
70 | * by the I register.
71 | *
72 | *
73 | */
74 | @Test
75 | public void drawSprite() {
76 | chip8.execute(0xA202);
77 | chip8.execute(0xD122); // Draw Sprite at 39, 18
78 | assertEquals(0, chip8.getVF());
79 |
80 | byte[] video = chip8.getScreen();
81 | assertEquals(1, video[39 + 64 * 18]);
82 | assertEquals(1, video[40 + 64 * 18]);
83 | assertEquals(1, video[41 + 64 * 18]);
84 | assertEquals(1, video[42 + 64 * 18]);
85 | assertEquals(1, video[43 + 64 * 18]);
86 | assertEquals(1, video[44 + 64 * 18]);
87 | assertEquals(1, video[45 + 64 * 18]);
88 | assertEquals(1, video[46 + 64 * 18]);
89 |
90 | assertEquals(0, video[39 + 64 * 19]);
91 | assertEquals(0, video[40 + 64 * 19]);
92 | assertEquals(1, video[41 + 64 * 19]);
93 | assertEquals(1, video[42 + 64 * 19]);
94 | assertEquals(1, video[43 + 64 * 19]);
95 | assertEquals(1, video[44 + 64 * 19]);
96 | assertEquals(0, video[45 + 64 * 19]);
97 | assertEquals(0, video[46 + 64 * 19]);
98 |
99 | chip8.execute(0xD122); // Draw Sprite at 39,18
100 | assertEquals(1, chip8.getVF());
101 |
102 | video = chip8.getScreen();
103 | assertEquals(0, video[39 + 64 * 18]);
104 | assertEquals(0, video[40 + 64 * 18]);
105 | assertEquals(0, video[41 + 64 * 18]);
106 | assertEquals(0, video[42 + 64 * 18]);
107 | assertEquals(0, video[43 + 64 * 18]);
108 | assertEquals(0, video[44 + 64 * 18]);
109 | assertEquals(0, video[45 + 64 * 18]);
110 | assertEquals(0, video[46 + 64 * 18]);
111 |
112 | assertEquals(0, video[39 + 64 * 19]);
113 | assertEquals(0, video[40 + 64 * 19]);
114 | assertEquals(0, video[41 + 64 * 19]);
115 | assertEquals(0, video[42 + 64 * 19]);
116 | assertEquals(0, video[43 + 64 * 19]);
117 | assertEquals(0, video[44 + 64 * 19]);
118 | assertEquals(0, video[45 + 64 * 19]);
119 | assertEquals(0, video[46 + 64 * 19]);
120 |
121 | }
122 |
123 | /**
124 | * Sprites wrap around on their axis. IE If you draw to X 65 it will wrap to
125 | * position 1.
126 | */
127 | @Test
128 | public void drawSpriteWrap() {
129 | chip8.execute(0xA202);
130 | chip8.execute(0xD212); // Draw Sprite at 18, 7 (39 wraps to 7)
131 | assertEquals(0, chip8.getVF());
132 |
133 | byte[] video = chip8.getScreen();
134 | assertEquals(1, video[18 + 64 * 7]);
135 | assertEquals(1, video[19 + 64 * 7]);
136 | assertEquals(1, video[20 + 64 * 7]);
137 | assertEquals(1, video[21 + 64 * 7]);
138 | assertEquals(1, video[22 + 64 * 7]);
139 | assertEquals(1, video[23 + 64 * 7]);
140 | assertEquals(1, video[24 + 64 * 7]);
141 | assertEquals(1, video[25 + 64 * 7]);
142 |
143 | assertEquals(0, video[18 + 64 * 8]);
144 | assertEquals(0, video[19 + 64 * 8]);
145 | assertEquals(1, video[20 + 64 * 8]);
146 | assertEquals(1, video[21 + 64 * 8]);
147 | assertEquals(1, video[22 + 64 * 8]);
148 | assertEquals(1, video[23 + 64 * 8]);
149 | assertEquals(0, video[24 + 64 * 8]);
150 | assertEquals(0, video[25 + 64 * 8]);
151 |
152 | }
153 |
154 | /**
155 | * Sprites wrap around on their axis. IE If you draw to X 65 it will wrap to
156 | * position 1.
157 | */
158 | @Test
159 | public void drawSpriteBottomRightEdgeTests() {
160 | chip8.execute(0xA202);
161 | chip8.execute(0xD781);
162 | chip8.execute(0xD871);
163 | chip8.execute(0xD781);
164 | chip8.execute(0xD871);
165 | }
166 |
167 | /**
168 | * The opcode 00E0 clears the screen.
169 | *
170 | */
171 | @Test
172 | public void testClearVideo() {
173 | chip8.execute(0xA202);
174 | chip8.execute(0xD212); // Draw Sprite at 18, 7 (39 wraps to 7)
175 | chip8.execute(0x00E0);
176 |
177 | byte[] video = chip8.getScreen();
178 |
179 | assertEquals(0, video[18 + 64 * 7]);
180 | assertEquals(0, video[19 + 64 * 7]);
181 | assertEquals(0, video[20 + 64 * 7]);
182 | assertEquals(0, video[21 + 64 * 7]);
183 | assertEquals(0, video[22 + 64 * 7]);
184 | assertEquals(0, video[23 + 64 * 7]);
185 | assertEquals(0, video[24 + 64 * 7]);
186 | assertEquals(0, video[25 + 64 * 7]);
187 |
188 | }
189 |
190 | /**
191 | * Chip 8 has build in hexedecimal fonts. These fonts are stored in the 200
192 | * bytes of reserved data and accessed using a special opcode.
193 | *
194 | * FX29 : Set the I register to the address of the sprite corresponding to
195 | * the hex digit stored in VX.
196 | */
197 | @Test
198 | public void testHexFonts() {
199 |
200 | chip8.execute(0x6100); //Store 00 in V1
201 | chip8.execute(0x6200); //Store 00 in V2
202 |
203 | for (int i = 0; i <= 0xf; i++) {
204 | chip8.execute(0x6000 + i); //Store 00 in V0
205 | chip8.execute(0xF029); // Set I to the memory address of the font sprite in V0
206 | chip8.execute(0xD125); // Draw all 5 lines of the sprite at 0, 0.
207 |
208 | checkGraphics(i);//Test for being drawn.
209 | chip8.execute(0x00E0); // Clear Screen
210 |
211 | }
212 |
213 | }
214 |
215 | private void checkGraphics(int i) {
216 | byte[] video = chip8.getScreen();
217 | switch (i) {
218 | case 0:
219 | assertEquals((byte)0xF0, (byte)Chip8Utils.getSpriteRow(0, 0, video));
220 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 1, video));
221 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 2, video));
222 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 3, video));
223 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 4, video));
224 | break;
225 | case 1:
226 | assertEquals((byte)0x20, Chip8Utils.getSpriteRow(0, 0, video));
227 | assertEquals((byte)0x60, Chip8Utils.getSpriteRow(0, 1, video));
228 | assertEquals((byte)0x20, Chip8Utils.getSpriteRow(0, 2, video));
229 | assertEquals((byte)0x20, Chip8Utils.getSpriteRow(0, 3, video));
230 | assertEquals((byte)0x70, Chip8Utils.getSpriteRow(0, 4, video));
231 | break;
232 | case 2:
233 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
234 | assertEquals((byte)0x10, Chip8Utils.getSpriteRow(0, 1, video));
235 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 2, video));
236 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 3, video));
237 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 4, video));
238 | break;
239 | case 3:
240 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
241 | assertEquals((byte)0x10, Chip8Utils.getSpriteRow(0, 1, video));
242 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 2, video));
243 | assertEquals((byte)0x10, Chip8Utils.getSpriteRow(0, 3, video));
244 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 4, video));
245 | break;
246 | case 4:
247 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 0, video));
248 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 1, video));
249 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 2, video));
250 | assertEquals((byte)0x10, Chip8Utils.getSpriteRow(0, 3, video));
251 | assertEquals((byte)0x10, Chip8Utils.getSpriteRow(0, 4, video));
252 | break;
253 | case 5:
254 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
255 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 1, video));
256 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 2, video));
257 | assertEquals((byte)0x10, Chip8Utils.getSpriteRow(0, 3, video));
258 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 4, video));
259 | break;
260 | case 6:
261 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
262 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 1, video));
263 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 2, video));
264 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 3, video));
265 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 4, video));
266 | break;
267 | case 7:
268 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
269 | assertEquals((byte)0x10, Chip8Utils.getSpriteRow(0, 1, video));
270 | assertEquals((byte)0x20, Chip8Utils.getSpriteRow(0, 2, video));
271 | assertEquals((byte)0x40, Chip8Utils.getSpriteRow(0, 3, video));
272 | assertEquals((byte)0x40, Chip8Utils.getSpriteRow(0, 4, video));
273 | break;
274 | case 8:
275 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
276 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 1, video));
277 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 2, video));
278 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 3, video));
279 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 4, video));
280 | break;
281 | case 9:
282 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
283 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 1, video));
284 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 2, video));
285 | assertEquals((byte)0x10, Chip8Utils.getSpriteRow(0, 3, video));
286 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 4, video));
287 | break;
288 | case 0xA:
289 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
290 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 1, video));
291 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 2, video));
292 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 3, video));
293 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 4, video));
294 | break;
295 | case 0xB:
296 | assertEquals((byte)0xE0, Chip8Utils.getSpriteRow(0, 0, video));
297 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 1, video));
298 | assertEquals((byte)0xE0, Chip8Utils.getSpriteRow(0, 2, video));
299 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 3, video));
300 | assertEquals((byte)0xE0, Chip8Utils.getSpriteRow(0, 4, video));
301 | break;
302 | case 0xC:
303 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
304 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 1, video));
305 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 2, video));
306 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 3, video));
307 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 4, video));
308 | break;
309 | case 0xD:
310 | assertEquals((byte)0xE0, Chip8Utils.getSpriteRow(0, 0, video));
311 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 1, video));
312 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 2, video));
313 | assertEquals((byte)0x90, Chip8Utils.getSpriteRow(0, 3, video));
314 | assertEquals((byte)0xE0, Chip8Utils.getSpriteRow(0, 4, video));
315 | break;
316 | case 0xE:
317 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
318 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 1, video));
319 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 2, video));
320 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 3, video));
321 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 4, video));
322 | break;
323 | case 0xF:
324 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 0, video));
325 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 1, video));
326 | assertEquals((byte)0xF0, Chip8Utils.getSpriteRow(0, 2, video));
327 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 3, video));
328 | assertEquals((byte)0x80, Chip8Utils.getSpriteRow(0, 4, video));
329 |
330 | break;
331 | }
332 | }
333 |
334 | }
335 |
--------------------------------------------------------------------------------
/src/main/java/net/saga/console/chip8/SwingMain.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2016 summers.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package net.saga.console.chip8;
25 |
26 | import net.saga.console.chip8.util.Chip8DisplayPanel;
27 | import net.saga.console.chip8.util.Chip8Utils;
28 | import java.awt.KeyEventDispatcher;
29 | import java.awt.KeyboardFocusManager;
30 | import java.awt.event.KeyEvent;
31 | import java.awt.event.KeyListener;
32 | import java.io.File;
33 | import java.io.IOException;
34 | import static java.lang.Thread.sleep;
35 | import java.util.logging.Level;
36 | import java.util.logging.Logger;
37 | import javax.swing.JFileChooser;
38 | import javax.swing.JOptionPane;
39 | import net.saga.console.chip8.util.Input;
40 |
41 | /**
42 | *
43 | * @author summers
44 | */
45 | public class SwingMain extends javax.swing.JFrame implements KeyListener {
46 |
47 | private static final long serialVersionUID = 1L;
48 |
49 | final JFileChooser fc = new JFileChooser();
50 | private Chip8 chip8;
51 | private boolean running = false;
52 | private boolean pause = false;
53 | private boolean step = false;
54 |
55 | /**
56 | * Creates new form SwingMain
57 | */
58 | public SwingMain() {
59 | this.chip8Runner = new Thread(() -> {
60 | while (true) {
61 | try {
62 | if (running) {
63 | if (pause == false || step) {
64 | step = false;
65 | chip8.cycle();
66 | ((Chip8Model) jTable1.getModel()).update(chip8);
67 | }
68 | outputPanel.repaint();
69 | }
70 | sleep(1);
71 | } catch (InterruptedException ex) {
72 | Logger.getLogger(SwingMain.class.getName()).log(Level.SEVERE, null, ex);
73 | }
74 | }
75 | });
76 | initComponents();
77 | KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
78 | manager.addKeyEventDispatcher(new KeyEventDispatcher() {
79 | @Override
80 | public boolean dispatchKeyEvent(KeyEvent e) {
81 | switch (e.getID()) {
82 | case KeyEvent.KEY_PRESSED:
83 | Input.press(e.getKeyChar() + "");
84 | break;
85 | case KeyEvent.KEY_RELEASED:
86 | Input.unpress();
87 | break;
88 | default:
89 | break;
90 | }
91 | return false;
92 | }
93 | });
94 | }
95 |
96 | /**
97 | * This method is called from within the constructor to initialize the form.
98 | * WARNING: Do NOT modify this code. The content of this method is always
99 | * regenerated by the Form Editor.
100 | */
101 | @SuppressWarnings("unchecked")
102 | // //GEN-BEGIN:initComponents
103 | private void initComponents() {
104 |
105 | jPanel1 = new javax.swing.JPanel();
106 | openButton = new javax.swing.JButton();
107 | aboutButton = new javax.swing.JButton();
108 | inputMapButton = new javax.swing.JButton();
109 | outputPanel = new Chip8DisplayPanel();
110 | jScrollPane1 = new javax.swing.JScrollPane();
111 | jTable1 = new javax.swing.JTable();
112 | jButton1 = new javax.swing.JButton();
113 | jButton2 = new javax.swing.JButton();
114 | jButton3 = new javax.swing.JButton();
115 |
116 | setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
117 | setBackground(java.awt.Color.white);
118 | setMinimumSize(new java.awt.Dimension(286, 175));
119 | addKeyListener(new java.awt.event.KeyAdapter() {
120 | public void keyPressed(java.awt.event.KeyEvent evt) {
121 | formKeyPressed(evt);
122 | }
123 | public void keyReleased(java.awt.event.KeyEvent evt) {
124 | formKeyReleased(evt);
125 | }
126 | });
127 |
128 | jPanel1.setBackground(new java.awt.Color(51, 51, 255));
129 | outputPanel.addKeyListener(SwingMain.this);
130 |
131 | openButton.setBackground(new java.awt.Color(102, 102, 255));
132 | openButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/open.png"))); // NOI18N
133 | openButton.setToolTipText("Open Binary");
134 | openButton.addActionListener(new java.awt.event.ActionListener() {
135 | public void actionPerformed(java.awt.event.ActionEvent evt) {
136 | openButtonActionPerformed(evt);
137 | }
138 | });
139 |
140 | aboutButton.setBackground(new java.awt.Color(102, 102, 255));
141 | aboutButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/about.png"))); // NOI18N
142 | aboutButton.setToolTipText("About");
143 | aboutButton.addActionListener(new java.awt.event.ActionListener() {
144 | public void actionPerformed(java.awt.event.ActionEvent evt) {
145 | aboutButtonActionPerformed(evt);
146 | }
147 | });
148 |
149 | inputMapButton.setBackground(new java.awt.Color(102, 102, 255));
150 | inputMapButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/input.png"))); // NOI18N
151 | inputMapButton.addActionListener(new java.awt.event.ActionListener() {
152 | public void actionPerformed(java.awt.event.ActionEvent evt) {
153 | inputMapButtonActionPerformed(evt);
154 | }
155 | });
156 |
157 | javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
158 | jPanel1.setLayout(jPanel1Layout);
159 | jPanel1Layout.setHorizontalGroup(
160 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
161 | .addGroup(jPanel1Layout.createSequentialGroup()
162 | .addContainerGap()
163 | .addComponent(openButton, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
164 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
165 | .addComponent(aboutButton, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
166 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
167 | .addComponent(inputMapButton, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
168 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
169 | );
170 | jPanel1Layout.setVerticalGroup(
171 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
172 | .addGroup(jPanel1Layout.createSequentialGroup()
173 | .addContainerGap()
174 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
175 | .addComponent(inputMapButton, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
176 | .addComponent(aboutButton, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
177 | .addComponent(openButton, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE))
178 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
179 | );
180 |
181 | outputPanel.setMinimumSize(new java.awt.Dimension(64, 32));
182 | outputPanel.addKeyListener(SwingMain.this);
183 |
184 | javax.swing.GroupLayout outputPanelLayout = new javax.swing.GroupLayout(outputPanel);
185 | outputPanel.setLayout(outputPanelLayout);
186 | outputPanelLayout.setHorizontalGroup(
187 | outputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
188 | .addGap(0, 697, Short.MAX_VALUE)
189 | );
190 | outputPanelLayout.setVerticalGroup(
191 | outputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
192 | .addGap(0, 0, Short.MAX_VALUE)
193 | );
194 |
195 | jTable1.setModel(new Chip8Model());
196 | jScrollPane1.setViewportView(jTable1);
197 |
198 | jButton1.setBackground(new java.awt.Color(102, 102, 255));
199 | jButton1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/play.png"))); // NOI18N
200 | jButton1.addActionListener(new java.awt.event.ActionListener() {
201 | public void actionPerformed(java.awt.event.ActionEvent evt) {
202 | jButton1ActionPerformed(evt);
203 | }
204 | });
205 |
206 | jButton2.setBackground(new java.awt.Color(102, 102, 255));
207 | jButton2.setIcon(new javax.swing.ImageIcon(getClass().getResource("/pause.png"))); // NOI18N
208 | jButton2.addActionListener(new java.awt.event.ActionListener() {
209 | public void actionPerformed(java.awt.event.ActionEvent evt) {
210 | jButton2ActionPerformed(evt);
211 | }
212 | });
213 |
214 | jButton3.setBackground(new java.awt.Color(102, 102, 255));
215 | jButton3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/skip.png"))); // NOI18N
216 | jButton3.addActionListener(new java.awt.event.ActionListener() {
217 | public void actionPerformed(java.awt.event.ActionEvent evt) {
218 | jButton3ActionPerformed(evt);
219 | }
220 | });
221 |
222 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
223 | getContentPane().setLayout(layout);
224 | layout.setHorizontalGroup(
225 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
226 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
227 | .addContainerGap()
228 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
229 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
230 | .addGroup(layout.createSequentialGroup()
231 | .addComponent(outputPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
232 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
233 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
234 | .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 186, javax.swing.GroupLayout.PREFERRED_SIZE)
235 | .addGroup(layout.createSequentialGroup()
236 | .addGap(2, 2, 2)
237 | .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
238 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
239 | .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
240 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
241 | .addComponent(jButton3, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)))))
242 | .addContainerGap())
243 | );
244 | layout.setVerticalGroup(
245 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
246 | .addGroup(layout.createSequentialGroup()
247 | .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
248 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
249 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
250 | .addComponent(outputPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
251 | .addGroup(layout.createSequentialGroup()
252 | .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 456, Short.MAX_VALUE)
253 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
254 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
255 | .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
256 | .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
257 | .addComponent(jButton3, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE))))
258 | .addContainerGap())
259 | );
260 |
261 | pack();
262 | }// //GEN-END:initComponents
263 |
264 | private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openButtonActionPerformed
265 | if (evt.getSource() == openButton) {
266 | int returnVal = fc.showOpenDialog(SwingMain.this);
267 |
268 | if (returnVal == JFileChooser.APPROVE_OPTION) {
269 | try {
270 | running = false;
271 | File file = fc.getSelectedFile();
272 | SwingMain.this.chip8 = Chip8Utils.createFromRom(file);
273 | restartEmulator();
274 | } catch (IOException ex) {
275 | Logger.getLogger(SwingMain.class.getName()).log(Level.SEVERE, null, ex);
276 | JOptionPane.showMessageDialog(null, ex, "Error", JOptionPane.ERROR_MESSAGE);
277 | }
278 | }
279 | }
280 | }//GEN-LAST:event_openButtonActionPerformed
281 |
282 | private void aboutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aboutButtonActionPerformed
283 | JOptionPane.showMessageDialog(null, "Chip-8 in Java by secondsun. \n github.com/secondsun/chip8");
284 | }//GEN-LAST:event_aboutButtonActionPerformed
285 |
286 | private void inputMapButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_inputMapButtonActionPerformed
287 | new InputMapping().setVisible(true);
288 | }//GEN-LAST:event_inputMapButtonActionPerformed
289 |
290 | private void formKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_formKeyPressed
291 | Input.press(evt.getKeyChar());
292 | }//GEN-LAST:event_formKeyPressed
293 |
294 | private void formKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_formKeyReleased
295 | Input.unpress();
296 | }//GEN-LAST:event_formKeyReleased
297 |
298 | private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
299 | this.pause = true;
300 | }//GEN-LAST:event_jButton2ActionPerformed
301 |
302 | private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
303 | this.pause = false;
304 | }//GEN-LAST:event_jButton1ActionPerformed
305 |
306 | private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed
307 | this.step = true;
308 | }//GEN-LAST:event_jButton3ActionPerformed
309 |
310 | /**
311 | * @param args the command line arguments
312 | */
313 | public static void main(String args[]) {
314 | /* Set the Nimbus look and feel */
315 | //
316 | /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
317 | * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
318 | */
319 | try {
320 | for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
321 | if ("Nimbus".equals(info.getName())) {
322 | javax.swing.UIManager.setLookAndFeel(info.getClassName());
323 | break;
324 | }
325 | }
326 | } catch (ClassNotFoundException ex) {
327 | java.util.logging.Logger.getLogger(SwingMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
328 | } catch (InstantiationException ex) {
329 | java.util.logging.Logger.getLogger(SwingMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
330 | } catch (IllegalAccessException ex) {
331 | java.util.logging.Logger.getLogger(SwingMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
332 | } catch (javax.swing.UnsupportedLookAndFeelException ex) {
333 | java.util.logging.Logger.getLogger(SwingMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
334 | }
335 | //
336 |
337 | /* Create and display the form */
338 | java.awt.EventQueue.invokeLater(new Runnable() {
339 | public void run() {
340 | new SwingMain().setVisible(true);
341 | }
342 | });
343 | }
344 |
345 | // Variables declaration - do not modify//GEN-BEGIN:variables
346 | private javax.swing.JButton aboutButton;
347 | private javax.swing.JButton inputMapButton;
348 | private javax.swing.JButton jButton1;
349 | private javax.swing.JButton jButton2;
350 | private javax.swing.JButton jButton3;
351 | private javax.swing.JPanel jPanel1;
352 | private javax.swing.JScrollPane jScrollPane1;
353 | private javax.swing.JTable jTable1;
354 | private javax.swing.JButton openButton;
355 | private javax.swing.JPanel outputPanel;
356 | // End of variables declaration//GEN-END:variables
357 |
358 | private void restartEmulator() {
359 | Chip8DisplayPanel panel = (Chip8DisplayPanel) outputPanel;
360 | panel.setChip8(chip8);
361 | if (!chip8Runner.isAlive()) {
362 | this.chip8Runner = new Thread(() -> {
363 | while (true) {
364 | try {
365 | if (running) {
366 | if (pause == false || step) {
367 | step = false;
368 | chip8.cycle();
369 | ((Chip8Model) jTable1.getModel()).update(chip8);
370 | }
371 | outputPanel.repaint();
372 | }
373 | sleep(1);
374 | } catch (InterruptedException ex) {
375 | Logger.getLogger(SwingMain.class.getName()).log(Level.SEVERE, null, ex);
376 | }
377 | }
378 | });
379 | chip8Runner.start();
380 | }
381 | running = true;
382 |
383 | }
384 | private Thread chip8Runner;
385 |
386 | @Override
387 | public void keyTyped(KeyEvent e) {
388 | }
389 |
390 | @Override
391 | public void keyPressed(KeyEvent e) {
392 | Input.press(e.getKeyChar());
393 | }
394 |
395 | @Override
396 | public void keyReleased(KeyEvent e) {
397 | Input.unpress();
398 | }
399 |
400 | }
401 |
--------------------------------------------------------------------------------
/src/main/java/net/saga/console/chip8/InputMapping.form:
--------------------------------------------------------------------------------
1 |
2 |
3 |
443 |
--------------------------------------------------------------------------------
/src/main/java/net/saga/console/chip8/InputMapping.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2016 summers.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package net.saga.console.chip8;
25 |
26 | import javax.swing.text.AttributeSet;
27 | import javax.swing.text.BadLocationException;
28 | import javax.swing.text.PlainDocument;
29 | import net.saga.console.chip8.util.Input;
30 |
31 | /**
32 | *
33 | * @author summers
34 | */
35 | public class InputMapping extends javax.swing.JFrame {
36 |
37 | /**
38 | * Creates new form InputMapping
39 | */
40 | public InputMapping() {
41 | initComponents();
42 | }
43 |
44 | /**
45 | * This method is called from within the constructor to initialize the form.
46 | * WARNING: Do NOT modify this code. The content of this method is always
47 | * regenerated by the Form Editor.
48 | */
49 | @SuppressWarnings("unchecked")
50 | // //GEN-BEGIN:initComponents
51 | private void initComponents() {
52 |
53 | jLabel1 = new javax.swing.JLabel();
54 | jLabel2 = new javax.swing.JLabel();
55 | jLabel3 = new javax.swing.JLabel();
56 | jLabel4 = new javax.swing.JLabel();
57 | jLabel5 = new javax.swing.JLabel();
58 | jLabel6 = new javax.swing.JLabel();
59 | jLabel7 = new javax.swing.JLabel();
60 | jLabel8 = new javax.swing.JLabel();
61 | jLabel9 = new javax.swing.JLabel();
62 | jLabel10 = new javax.swing.JLabel();
63 | jLabel11 = new javax.swing.JLabel();
64 | jLabel12 = new javax.swing.JLabel();
65 | jLabel13 = new javax.swing.JLabel();
66 | jLabel14 = new javax.swing.JLabel();
67 | jLabel15 = new javax.swing.JLabel();
68 | jLabel16 = new javax.swing.JLabel();
69 | zeroMapping = new javax.swing.JTextField();
70 | oneMapping = new javax.swing.JTextField();
71 | twoMapping = new javax.swing.JTextField();
72 | threeMapping = new javax.swing.JTextField();
73 | fourMapping = new javax.swing.JTextField();
74 | fiveMapping = new javax.swing.JTextField();
75 | sixMapping = new javax.swing.JTextField();
76 | sevenMapping = new javax.swing.JTextField();
77 | eightMapping = new javax.swing.JTextField();
78 | nineMapping = new javax.swing.JTextField();
79 | aMapping = new javax.swing.JTextField();
80 | bMapping = new javax.swing.JTextField();
81 | cMapping = new javax.swing.JTextField();
82 | dMapping = new javax.swing.JTextField();
83 | eMapping = new javax.swing.JTextField();
84 | fMapping = new javax.swing.JTextField();
85 | jButton1 = new javax.swing.JButton();
86 | jButton2 = new javax.swing.JButton();
87 |
88 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
89 |
90 | jLabel1.setText("0x0");
91 |
92 | jLabel2.setText("0x1");
93 |
94 | jLabel3.setText("0x2");
95 |
96 | jLabel4.setText("0x3");
97 |
98 | jLabel5.setText("0x4");
99 |
100 | jLabel6.setText("0x5");
101 |
102 | jLabel7.setText("0x6");
103 |
104 | jLabel8.setText("0x7");
105 |
106 | jLabel9.setText("0x8");
107 |
108 | jLabel10.setText("0x9");
109 |
110 | jLabel11.setText("0xA");
111 |
112 | jLabel12.setText("0xB");
113 |
114 | jLabel13.setText("0xC");
115 |
116 | jLabel14.setText("0xD");
117 |
118 | jLabel15.setText("0xE");
119 |
120 | jLabel16.setText("0xF");
121 |
122 | zeroMapping.setDocument(new SingleCharacterDocument());
123 | zeroMapping.addActionListener(new java.awt.event.ActionListener() {
124 | public void actionPerformed(java.awt.event.ActionEvent evt) {
125 | zeroMappingActionPerformed(evt);
126 | }
127 | });
128 |
129 | oneMapping.setDocument(new SingleCharacterDocument());
130 |
131 | twoMapping.setDocument(new SingleCharacterDocument());
132 |
133 | threeMapping.setDocument(new SingleCharacterDocument());
134 |
135 | fourMapping.setDocument(new SingleCharacterDocument());
136 | fourMapping.addActionListener(new java.awt.event.ActionListener() {
137 | public void actionPerformed(java.awt.event.ActionEvent evt) {
138 | fourMappingActionPerformed(evt);
139 | }
140 | });
141 |
142 | fiveMapping.setDocument(new SingleCharacterDocument());
143 |
144 | sixMapping.setDocument(new SingleCharacterDocument());
145 |
146 | sevenMapping.setDocument(new SingleCharacterDocument());
147 |
148 | eightMapping.setDocument(new SingleCharacterDocument());
149 |
150 | nineMapping.setDocument(new SingleCharacterDocument());
151 |
152 | aMapping.setDocument(new SingleCharacterDocument());
153 |
154 | bMapping.setDocument(new SingleCharacterDocument());
155 |
156 | cMapping.setDocument(new SingleCharacterDocument());
157 |
158 | dMapping.setDocument(new SingleCharacterDocument());
159 |
160 | eMapping.setDocument(new SingleCharacterDocument());
161 |
162 | fMapping.setDocument(new SingleCharacterDocument());
163 |
164 | jButton1.setText("Save");
165 | jButton1.addActionListener(new java.awt.event.ActionListener() {
166 | public void actionPerformed(java.awt.event.ActionEvent evt) {
167 | jButton1ActionPerformed(evt);
168 | }
169 | });
170 |
171 | jButton2.setText("Cancel");
172 | jButton2.addActionListener(new java.awt.event.ActionListener() {
173 | public void actionPerformed(java.awt.event.ActionEvent evt) {
174 | jButton2ActionPerformed(evt);
175 | }
176 | });
177 |
178 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
179 | getContentPane().setLayout(layout);
180 | layout.setHorizontalGroup(
181 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
182 | .addGroup(layout.createSequentialGroup()
183 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
184 | .addGroup(layout.createSequentialGroup()
185 | .addContainerGap()
186 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
187 | .addGroup(layout.createSequentialGroup()
188 | .addComponent(jLabel1)
189 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
190 | .addComponent(zeroMapping))
191 | .addGroup(layout.createSequentialGroup()
192 | .addComponent(jLabel2)
193 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
194 | .addComponent(oneMapping))
195 | .addGroup(layout.createSequentialGroup()
196 | .addComponent(jLabel3)
197 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
198 | .addComponent(twoMapping))
199 | .addGroup(layout.createSequentialGroup()
200 | .addComponent(jLabel4)
201 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
202 | .addComponent(threeMapping))
203 | .addGroup(layout.createSequentialGroup()
204 | .addComponent(jLabel5)
205 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
206 | .addComponent(fourMapping))
207 | .addGroup(layout.createSequentialGroup()
208 | .addComponent(jLabel6)
209 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
210 | .addComponent(fiveMapping))
211 | .addGroup(layout.createSequentialGroup()
212 | .addComponent(jLabel8)
213 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
214 | .addComponent(sevenMapping))
215 | .addGroup(layout.createSequentialGroup()
216 | .addComponent(jLabel7)
217 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
218 | .addComponent(sixMapping)))
219 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
220 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
221 | .addGroup(layout.createSequentialGroup()
222 | .addComponent(jLabel9)
223 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
224 | .addComponent(eightMapping))
225 | .addGroup(layout.createSequentialGroup()
226 | .addComponent(jLabel10)
227 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
228 | .addComponent(nineMapping))
229 | .addGroup(layout.createSequentialGroup()
230 | .addComponent(jLabel11)
231 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
232 | .addComponent(aMapping))
233 | .addGroup(layout.createSequentialGroup()
234 | .addComponent(jLabel12)
235 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
236 | .addComponent(bMapping))
237 | .addGroup(layout.createSequentialGroup()
238 | .addComponent(jLabel13)
239 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
240 | .addComponent(cMapping))
241 | .addGroup(layout.createSequentialGroup()
242 | .addComponent(jLabel14)
243 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
244 | .addComponent(dMapping))
245 | .addGroup(layout.createSequentialGroup()
246 | .addComponent(jLabel15)
247 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
248 | .addComponent(eMapping))
249 | .addGroup(layout.createSequentialGroup()
250 | .addComponent(jLabel16)
251 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
252 | .addComponent(fMapping))))
253 | .addGroup(layout.createSequentialGroup()
254 | .addGap(60, 60, 60)
255 | .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
256 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
257 | .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
258 | .addGap(0, 0, Short.MAX_VALUE)))
259 | .addContainerGap())
260 | );
261 | layout.setVerticalGroup(
262 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
263 | .addGroup(layout.createSequentialGroup()
264 | .addContainerGap()
265 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
266 | .addGroup(layout.createSequentialGroup()
267 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
268 | .addComponent(jLabel1)
269 | .addComponent(zeroMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
270 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
271 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
272 | .addComponent(jLabel2)
273 | .addComponent(oneMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
274 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
275 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
276 | .addComponent(jLabel3)
277 | .addComponent(twoMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
278 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
279 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
280 | .addComponent(jLabel4)
281 | .addComponent(threeMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
282 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
283 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
284 | .addComponent(jLabel5)
285 | .addComponent(fourMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
286 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
287 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
288 | .addComponent(jLabel6)
289 | .addComponent(fiveMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
290 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
291 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
292 | .addComponent(jLabel7)
293 | .addComponent(sixMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
294 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
295 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
296 | .addComponent(jLabel8)
297 | .addComponent(sevenMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
298 | .addGroup(layout.createSequentialGroup()
299 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
300 | .addComponent(jLabel9)
301 | .addComponent(eightMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
302 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
303 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
304 | .addComponent(jLabel10)
305 | .addComponent(nineMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
306 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
307 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
308 | .addComponent(jLabel11)
309 | .addComponent(aMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
310 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
311 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
312 | .addComponent(jLabel12)
313 | .addComponent(bMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
314 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
315 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
316 | .addComponent(jLabel13)
317 | .addComponent(cMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
318 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
319 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
320 | .addComponent(jLabel14)
321 | .addComponent(dMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
322 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
323 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
324 | .addComponent(jLabel15)
325 | .addComponent(eMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
326 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
327 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
328 | .addComponent(jLabel16)
329 | .addComponent(fMapping, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
330 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
331 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
332 | .addComponent(jButton2)
333 | .addComponent(jButton1))
334 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
335 | );
336 |
337 | pack();
338 | }// //GEN-END:initComponents
339 |
340 | private void zeroMappingActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zeroMappingActionPerformed
341 | // TODO add your handling code here:
342 | }//GEN-LAST:event_zeroMappingActionPerformed
343 |
344 | private void fourMappingActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fourMappingActionPerformed
345 | // TODO add your handling code here:
346 | }//GEN-LAST:event_fourMappingActionPerformed
347 |
348 | private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
349 | InputMapping.this.setVisible(false);
350 | this.dispose();
351 | }//GEN-LAST:event_jButton2ActionPerformed
352 |
353 | private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
354 | Input.map(0x0, zeroMapping.getText());
355 | Input.map(0x1, oneMapping.getText());
356 | Input.map(0x2, twoMapping.getText());
357 | Input.map(0x3, threeMapping.getText());
358 | Input.map(0x4, fourMapping.getText());
359 | Input.map(0x5, fiveMapping.getText());
360 | Input.map(0x6, sixMapping.getText());
361 | Input.map(0x7, sevenMapping.getText());
362 | Input.map(0x8, eightMapping.getText());
363 | Input.map(0x9, nineMapping.getText());
364 | Input.map(0xa, aMapping.getText());
365 | Input.map(0xb, bMapping.getText());
366 | Input.map(0xc, cMapping.getText());
367 | Input.map(0xd, dMapping.getText());
368 | Input.map(0xe, eMapping.getText());
369 | Input.map(0xf, fMapping.getText());
370 | this.setVisible(false);
371 | this.dispose();
372 | }//GEN-LAST:event_jButton1ActionPerformed
373 |
374 | /**
375 | * @param args the command line arguments
376 | */
377 | public static void main(String args[]) {
378 | /* Set the Nimbus look and feel */
379 | //
380 | /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
381 | * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
382 | */
383 | try {
384 | for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
385 | if ("Nimbus".equals(info.getName())) {
386 | javax.swing.UIManager.setLookAndFeel(info.getClassName());
387 | break;
388 | }
389 | }
390 | } catch (ClassNotFoundException ex) {
391 | java.util.logging.Logger.getLogger(InputMapping.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
392 | } catch (InstantiationException ex) {
393 | java.util.logging.Logger.getLogger(InputMapping.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
394 | } catch (IllegalAccessException ex) {
395 | java.util.logging.Logger.getLogger(InputMapping.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
396 | } catch (javax.swing.UnsupportedLookAndFeelException ex) {
397 | java.util.logging.Logger.getLogger(InputMapping.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
398 | }
399 | //
400 |
401 | /* Create and display the form */
402 | java.awt.EventQueue.invokeLater(new Runnable() {
403 | public void run() {
404 | new InputMapping().setVisible(true);
405 | }
406 | });
407 | }
408 |
409 | private static class SingleCharacterDocument extends PlainDocument {
410 |
411 | private static final long serialVersionUID = 1L;
412 |
413 | @Override
414 | public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
415 | if ((getLength() + str.length()) <= 1) {
416 | super.insertString(offs, str, a);
417 | }
418 | }
419 |
420 | }
421 |
422 | // Variables declaration - do not modify//GEN-BEGIN:variables
423 | private javax.swing.JTextField aMapping;
424 | private javax.swing.JTextField bMapping;
425 | private javax.swing.JTextField cMapping;
426 | private javax.swing.JTextField dMapping;
427 | private javax.swing.JTextField eMapping;
428 | private javax.swing.JTextField eightMapping;
429 | private javax.swing.JTextField fMapping;
430 | private javax.swing.JTextField fiveMapping;
431 | private javax.swing.JTextField fourMapping;
432 | private javax.swing.JButton jButton1;
433 | private javax.swing.JButton jButton2;
434 | private javax.swing.JLabel jLabel1;
435 | private javax.swing.JLabel jLabel10;
436 | private javax.swing.JLabel jLabel11;
437 | private javax.swing.JLabel jLabel12;
438 | private javax.swing.JLabel jLabel13;
439 | private javax.swing.JLabel jLabel14;
440 | private javax.swing.JLabel jLabel15;
441 | private javax.swing.JLabel jLabel16;
442 | private javax.swing.JLabel jLabel2;
443 | private javax.swing.JLabel jLabel3;
444 | private javax.swing.JLabel jLabel4;
445 | private javax.swing.JLabel jLabel5;
446 | private javax.swing.JLabel jLabel6;
447 | private javax.swing.JLabel jLabel7;
448 | private javax.swing.JLabel jLabel8;
449 | private javax.swing.JLabel jLabel9;
450 | private javax.swing.JTextField nineMapping;
451 | private javax.swing.JTextField oneMapping;
452 | private javax.swing.JTextField sevenMapping;
453 | private javax.swing.JTextField sixMapping;
454 | private javax.swing.JTextField threeMapping;
455 | private javax.swing.JTextField twoMapping;
456 | private javax.swing.JTextField zeroMapping;
457 | // End of variables declaration//GEN-END:variables
458 | }
459 |
--------------------------------------------------------------------------------