├── .gitignore
├── .gradle
├── 7.2
│ ├── dependencies-accessors
│ │ ├── dependencies-accessors.lock
│ │ └── gc.properties
│ ├── executionHistory
│ │ ├── executionHistory.bin
│ │ └── executionHistory.lock
│ ├── fileChanges
│ │ └── last-build.bin
│ ├── fileHashes
│ │ ├── fileHashes.bin
│ │ └── fileHashes.lock
│ └── gc.properties
├── 8.0.2
│ ├── checksums
│ │ └── checksums.lock
│ ├── dependencies-accessors
│ │ ├── dependencies-accessors.lock
│ │ └── gc.properties
│ ├── fileChanges
│ │ └── last-build.bin
│ ├── fileHashes
│ │ └── fileHashes.lock
│ └── gc.properties
├── buildOutputCleanup
│ ├── buildOutputCleanup.lock
│ ├── cache.properties
│ └── outputFiles.bin
├── checksums
│ └── checksums.lock
└── vcs-1
│ └── gc.properties
├── .idea
├── .gitignore
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── encodings.xml
├── jarRepositories.xml
├── kotlinc.xml
├── libraries
│ ├── KotlinJavaRuntime__2_.xml
│ └── javassist.xml
├── misc.xml
├── uiDesigner.xml
├── vcs.xml
└── workspace.xml
├── GBemu.iml
├── README.md
├── pom.xml
├── src
├── main
│ ├── java
│ │ ├── gbc
│ │ │ ├── controller
│ │ │ │ ├── ConfigManager.java
│ │ │ │ ├── EmulatorController.java
│ │ │ │ └── FileLoader.java
│ │ │ ├── model
│ │ │ │ ├── GameBoyColor.java
│ │ │ │ ├── cartridge
│ │ │ │ │ ├── Cartridge.java
│ │ │ │ │ ├── CartridgeFactory.java
│ │ │ │ │ ├── MBC1.java
│ │ │ │ │ ├── MBC2.java
│ │ │ │ │ ├── MBC3.java
│ │ │ │ │ ├── MBC5.java
│ │ │ │ │ └── ROM.java
│ │ │ │ ├── controller
│ │ │ │ │ └── Controller.java
│ │ │ │ ├── cpu
│ │ │ │ │ ├── CPU.java
│ │ │ │ │ ├── Instructions.java
│ │ │ │ │ ├── Interruptions.java
│ │ │ │ │ ├── Operation.java
│ │ │ │ │ ├── OperationExecutor.java
│ │ │ │ │ ├── OperationsLoader.java
│ │ │ │ │ └── Registers.java
│ │ │ │ ├── graphics
│ │ │ │ │ ├── GPU.java
│ │ │ │ │ └── Screen.java
│ │ │ │ ├── memory
│ │ │ │ │ └── Memory.java
│ │ │ │ └── sound
│ │ │ │ │ ├── SoundChannel1.java
│ │ │ │ │ └── SoundChannel2.java
│ │ │ └── view
│ │ │ │ ├── DebugView.java
│ │ │ │ ├── EmulatorView.java
│ │ │ │ ├── EmulatorWindow.java
│ │ │ │ └── MenuBar.java
│ │ └── main.java
│ └── resources
│ │ ├── OperationCodes.json
│ │ └── opcodes.xml
└── test
│ └── java
│ └── gbc
│ ├── MemoryTest.java
│ └── RegistersTest.java
└── target
├── classes
├── gbc
│ ├── controller
│ │ ├── EmulatorController.class
│ │ └── FileLoader.class
│ ├── model
│ │ ├── GameBoyColor.class
│ │ ├── cartridge
│ │ │ ├── Cartridge.class
│ │ │ ├── CartridgeFactory.class
│ │ │ ├── MBC1.class
│ │ │ ├── MBC2.class
│ │ │ ├── MBC3.class
│ │ │ ├── MBC5.class
│ │ │ └── ROM.class
│ │ ├── controller
│ │ │ └── Controller.class
│ │ ├── cpu
│ │ │ ├── CPU.class
│ │ │ ├── Instructions.class
│ │ │ ├── Interruptions.class
│ │ │ ├── Operation.class
│ │ │ └── Registers.class
│ │ ├── graphics
│ │ │ ├── GPU.class
│ │ │ └── Screen.class
│ │ └── memory
│ │ │ └── Memory.class
│ └── view
│ │ ├── DebugView.class
│ │ ├── EmulatorView.class
│ │ └── MenuBar.class
└── main.class
└── test-classes
└── gbc
└── MemoryTest.class
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gb
2 | *.gbc
3 | .idea/
4 | .gradle/
5 | target/
6 |
--------------------------------------------------------------------------------
/.gradle/7.2/dependencies-accessors/dependencies-accessors.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/7.2/dependencies-accessors/dependencies-accessors.lock
--------------------------------------------------------------------------------
/.gradle/7.2/dependencies-accessors/gc.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/7.2/dependencies-accessors/gc.properties
--------------------------------------------------------------------------------
/.gradle/7.2/executionHistory/executionHistory.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/7.2/executionHistory/executionHistory.bin
--------------------------------------------------------------------------------
/.gradle/7.2/executionHistory/executionHistory.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/7.2/executionHistory/executionHistory.lock
--------------------------------------------------------------------------------
/.gradle/7.2/fileChanges/last-build.bin:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gradle/7.2/fileHashes/fileHashes.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/7.2/fileHashes/fileHashes.bin
--------------------------------------------------------------------------------
/.gradle/7.2/fileHashes/fileHashes.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/7.2/fileHashes/fileHashes.lock
--------------------------------------------------------------------------------
/.gradle/7.2/gc.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/7.2/gc.properties
--------------------------------------------------------------------------------
/.gradle/8.0.2/checksums/checksums.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/8.0.2/checksums/checksums.lock
--------------------------------------------------------------------------------
/.gradle/8.0.2/dependencies-accessors/dependencies-accessors.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/8.0.2/dependencies-accessors/dependencies-accessors.lock
--------------------------------------------------------------------------------
/.gradle/8.0.2/dependencies-accessors/gc.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/8.0.2/dependencies-accessors/gc.properties
--------------------------------------------------------------------------------
/.gradle/8.0.2/fileChanges/last-build.bin:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gradle/8.0.2/fileHashes/fileHashes.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/8.0.2/fileHashes/fileHashes.lock
--------------------------------------------------------------------------------
/.gradle/8.0.2/gc.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/8.0.2/gc.properties
--------------------------------------------------------------------------------
/.gradle/buildOutputCleanup/buildOutputCleanup.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/buildOutputCleanup/buildOutputCleanup.lock
--------------------------------------------------------------------------------
/.gradle/buildOutputCleanup/cache.properties:
--------------------------------------------------------------------------------
1 | #Sun Nov 19 12:15:19 CET 2023
2 | gradle.version=7.2
3 |
--------------------------------------------------------------------------------
/.gradle/buildOutputCleanup/outputFiles.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/buildOutputCleanup/outputFiles.bin
--------------------------------------------------------------------------------
/.gradle/checksums/checksums.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/checksums/checksums.lock
--------------------------------------------------------------------------------
/.gradle/vcs-1/gc.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/.gradle/vcs-1/gc.properties
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/libraries/KotlinJavaRuntime__2_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/libraries/javassist.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | {
71 | "associatedIndex": 1
72 | }
73 |
74 |
75 |
76 |
77 |
78 |
79 | {
80 | "keyToString": {
81 | "Application.main.executor": "Run",
82 | "RunOnceActivity.OpenProjectViewOnStart": "true",
83 | "RunOnceActivity.ShowReadmeOnStart": "true",
84 | "WebServerToolWindowFactoryState": "false",
85 | "git-widget-placeholder": "master",
86 | "kotlin-language-version-configured": "true",
87 | "last_opened_file_path": "/home/pablo/ProyectosPropios/GBJava",
88 | "node.js.detected.package.eslint": "true",
89 | "node.js.detected.package.tslint": "true",
90 | "node.js.selected.package.eslint": "(autodetect)",
91 | "node.js.selected.package.tslint": "(autodetect)",
92 | "nodejs_package_manager_path": "npm",
93 | "onboarding.tips.debug.path": "/home/pablo/ProyectosPropios/GBJava/untitled/src/main/java/org/example/Main.java",
94 | "project.structure.last.edited": "Modules",
95 | "project.structure.proportion": "0.0",
96 | "project.structure.side.proportion": "0.0",
97 | "settings.editor.selected.configurable": "reference.settings.project.maven.repository.indices",
98 | "vue.rearranger.settings.migration": "true"
99 | }
100 | }
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 | 1700442285478
151 |
152 |
153 | 1700442285478
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 | file://$PROJECT_DIR$/src/main/java/org/example/Main.java
187 | 15
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
--------------------------------------------------------------------------------
/GBemu.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gbc
2 | A WIP GB emulator made from scratch in java.
3 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.example
8 | GBJava
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 19
13 | 19
14 | UTF-8
15 |
16 |
17 |
18 | junit
19 | junit
20 | RELEASE
21 | test
22 |
23 |
24 | org.junit.jupiter
25 | junit-jupiter
26 | RELEASE
27 | test
28 |
29 |
30 | com.google.code.gson
31 | gson
32 | 2.10.1
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/main/java/gbc/controller/ConfigManager.java:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/murapadev/GBJava/ae578ecaa476b27bb0eb89754ab4e6bbed312663/src/main/java/gbc/controller/ConfigManager.java
--------------------------------------------------------------------------------
/src/main/java/gbc/controller/EmulatorController.java:
--------------------------------------------------------------------------------
1 | package gbc.controller;
2 |
3 | import gbc.model.GameBoyColor;
4 | import gbc.view.EmulatorWindow;
5 |
6 | public class EmulatorController {
7 | private GameBoyColor gbc;
8 | private EmulatorWindow view;
9 |
10 |
11 | public EmulatorController(GameBoyColor gbc, EmulatorWindow view) {
12 | this.gbc = gbc;
13 | this.view = view;
14 | }
15 |
16 | public EmulatorController() {
17 | this.gbc = new GameBoyColor();
18 | this.view = new EmulatorWindow(this.gbc);
19 |
20 | }
21 |
22 |
23 | public void start() {
24 | // Init view
25 | view.update();
26 |
27 |
28 | // Main loop
29 | while (true) {
30 | // Handle input
31 | handleInput();
32 | // Update model
33 | gbc.executeCycle();
34 | // Update view
35 | view.update();
36 |
37 | // Update debug view
38 |
39 | }
40 |
41 | }
42 |
43 | public void handleInput() {
44 | // Procesar las entradas del usuario y actualizar el Modelo y la Vista
45 | }
46 |
47 | public void loadRom(String path) {
48 | // Cargar el archivo ROM en el Modelo
49 | gbc.insertCartridge(path);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/gbc/controller/FileLoader.java:
--------------------------------------------------------------------------------
1 | package gbc.controller;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.nio.file.Files;
6 |
7 | public class FileLoader {
8 | public static byte[] loadFile(String path) {
9 | File file = new File(path);
10 | try {
11 | return Files.readAllBytes(file.toPath());
12 | } catch (IOException e) {
13 | e.printStackTrace();
14 | return null;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/main/java/gbc/model/GameBoyColor.java:
--------------------------------------------------------------------------------
1 | package gbc.model;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Files;
5 | import java.nio.file.Paths;
6 |
7 | import gbc.model.cartridge.*;
8 | import gbc.model.controller.Controller;
9 | import gbc.model.cpu.*;
10 | import gbc.model.memory.*;
11 | import gbc.view.EmulatorView;
12 | import gbc.model.graphics.*;
13 |
14 | public class GameBoyColor {
15 | private final CPU cpu;
16 | private final Memory memory;
17 | private GPU gpu;
18 |
19 | private Screen screen;
20 |
21 | private Controller input;
22 |
23 | public GameBoyColor() {
24 | this.memory = new Memory();
25 | this.cpu = new CPU(this.memory);
26 | this.screen = new Screen();
27 | this.gpu = new GPU(this.memory, this.screen);
28 | this.input = new Controller();
29 |
30 | }
31 |
32 | public void executeCycle() {
33 |
34 | input.handleInput();
35 | cpu.executeCycle();
36 | gpu.step(cpu.getCycles());
37 | cpu.setCycles(0);
38 | }
39 |
40 | public void insertCartridge(String path) {
41 | byte[] data;
42 | try {
43 | data = Files.readAllBytes(Paths.get(path));
44 | Cartridge cartridge = CartridgeFactory.create(data);
45 | memory.loadCartridge(cartridge);
46 | } catch (IOException e) {
47 | e.printStackTrace();
48 | }
49 |
50 | }
51 |
52 | public void reset() {
53 | cpu.reset();
54 | memory.reset();
55 | this.screen = new Screen();
56 | gpu = new GPU(this.memory, this.screen);
57 |
58 | }
59 |
60 | public Controller getController() {
61 | return this.input;
62 | }
63 |
64 | public CPU getCpu() {
65 | return this.cpu;
66 | }
67 |
68 | public Memory getMemory() {
69 | return this.memory;
70 | }
71 |
72 |
73 | public GPU getGpu() {
74 | return this.gpu;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cartridge/Cartridge.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cartridge;
2 |
3 | public abstract class Cartridge {
4 | protected byte[] data;
5 |
6 | public Cartridge(byte[] data) {
7 | this.data = data;
8 | }
9 |
10 | // Use 'int' for address to accommodate the full range of Game Boy addresses
11 | public abstract byte read(int address);
12 | public abstract void write(int address, byte value);
13 |
14 | public byte[] getData() {
15 | return data;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cartridge/CartridgeFactory.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cartridge;
2 |
3 | public class CartridgeFactory {
4 |
5 | public static Cartridge create(byte[] data) {
6 | byte value = data[0x147];
7 | System.out.println("CartridgeFactory: " + value);
8 | return switch (value) {
9 | case 0x00 -> new ROM(data);
10 | case 0x01, 0x02, 0x03 -> new MBC1(data);
11 | case 0x05, 0x06 -> new MBC2(data);
12 | case 0x0F, 0x10, 0x11, 0x12, 0x13 -> new MBC3(data);
13 | case 0x19, 0x1A, 0x1B, 0x1C, 0x1D -> new MBC5(data);
14 | default -> null;
15 | };
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cartridge/MBC1.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cartridge;
2 |
3 | public class MBC1 extends Cartridge {
4 | private int romBankNumber = 1; // Default ROM bank
5 | private int ramBankNumber = 0; // Default RAM bank
6 | private boolean ramEnabled = false;
7 | private boolean bankingMode = false; // false: ROM banking mode, true: RAM banking mode
8 |
9 | public MBC1(byte[] data) {
10 | super(data);
11 | }
12 |
13 | @Override
14 | public byte read(int address) {
15 | if (address >= 0x0000 && address < 0x4000) {
16 | // ROM bank 0
17 | return data[address];
18 | } else if (address >= 0x4000 && address < 0x8000) {
19 | // Switchable ROM bank
20 | int bankOffset = romBankNumber * 0x4000;
21 | int index = bankOffset + (address - 0x4000);
22 | return data[index];
23 | } else if (address >= 0xA000 && address < 0xC000 && ramEnabled) {
24 | // Switchable RAM bank
25 | int bankOffset = ramBankNumber * 0x2000;
26 | int index = bankOffset + (address - 0xA000);
27 | return data[index];
28 | }
29 | return 0;
30 | }
31 |
32 | @Override
33 | public void write(int address, byte value) {
34 | if (address >= 0x0000 && address < 0x2000) {
35 | // RAM enable/disable
36 | ramEnabled = ((value & 0x0F) == 0x0A);
37 | } else if (address >= 0x2000 && address < 0x4000) {
38 | // ROM bank number
39 | romBankNumber = (value & 0x1F);
40 | if (romBankNumber == 0) romBankNumber = 1;
41 | } else if (address >= 0x4000 && address < 0x6000) {
42 | // RAM bank number or upper bits of ROM bank number
43 | if (bankingMode) {
44 | ramBankNumber = (value & 0x03);
45 | } else {
46 | romBankNumber = (romBankNumber & 0x1F) | ((value & 0x03) << 5);
47 | }
48 | } else if (address >= 0x6000 && address < 0x8000) {
49 | // Banking mode select
50 | bankingMode = ((value & 0x01) == 0x01);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cartridge/MBC2.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cartridge;
2 |
3 | public class MBC2 extends Cartridge {
4 | private int romBankNumber = 1; // Default ROM bank
5 | private boolean ramEnabled = false;
6 |
7 | public MBC2(byte[] data) {
8 | super(data);
9 | }
10 |
11 | @Override
12 | public byte read(int address) {
13 | if (address >= 0x0000 && address < 0x4000) {
14 | // ROM bank 0
15 | return data[address];
16 | } else if (address >= 0x4000 && address < 0x8000) {
17 | // Switchable ROM bank
18 | int bankOffset = romBankNumber * 0x4000;
19 | int index = bankOffset + (address - 0x4000);
20 | return data[index];
21 | } else if (address >= 0xA000 && address < 0xA200 && ramEnabled) {
22 | // MBC2 has 512x4 bits built-in RAM
23 | int index = address - 0xA000;
24 | return (byte) (data[index] & 0x0F); // Only lower 4 bits are used
25 | }
26 | return 0;
27 | }
28 |
29 | @Override
30 | public void write(int address, byte value) {
31 | if (address >= 0x0000 && address < 0x4000) {
32 | if ((address & 0x0100) == 0) {
33 | // RAM enable/disable
34 | ramEnabled = ((value & 0x0F) == 0x0A);
35 | } else {
36 | // ROM bank number
37 | romBankNumber = (value & 0x0F);
38 | if (romBankNumber == 0) romBankNumber = 1;
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cartridge/MBC3.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cartridge;
2 |
3 | //Imports for Real Time Clock
4 | import java.util.Calendar;
5 | import java.util.Date;
6 | import java.util.GregorianCalendar;
7 |
8 |
9 | public class MBC3 extends Cartridge {
10 | private int romBankNumber = 1; // Default ROM bank
11 | private int ramBankNumber = 0; // Default RAM bank
12 | private boolean ramEnabled = false;
13 |
14 | public MBC3(byte[] data) {
15 | super(data);
16 | }
17 |
18 | public byte[] rtc() {
19 | //Logic of Real Time Clock
20 | Calendar calendar = new GregorianCalendar();
21 | Date trialTime = new Date();
22 | calendar.setTime(trialTime);
23 | int hour = calendar.get(Calendar.HOUR);
24 | int minute = calendar.get(Calendar.MINUTE);
25 | int second = calendar.get(Calendar.SECOND);
26 | int day = calendar.get(Calendar.DAY_OF_MONTH);
27 | int month = calendar.get(Calendar.MONTH);
28 | int year = calendar.get(Calendar.YEAR);
29 | byte[] rtc = new byte[7];
30 | rtc[0] = (byte) second;
31 | rtc[1] = (byte) minute;
32 | rtc[2] = (byte) hour;
33 | rtc[3] = (byte) day;
34 | rtc[4] = (byte) month;
35 | rtc[5] = (byte) year;
36 | rtc[6] = (byte) 0;
37 | return rtc;
38 |
39 | }
40 |
41 | @Override
42 | public byte read(int address) {
43 | if (address >= 0x0000 && address < 0x4000) {
44 | // ROM bank 0
45 | return data[address];
46 | } else if (address >= 0x4000 && address < 0x8000) {
47 | // Switchable ROM bank
48 | int bankOffset = romBankNumber * 0x4000;
49 | int index = bankOffset + (address - 0x4000);
50 | return data[index];
51 | } else if (address >= 0xA000 && address < 0xC000 && ramEnabled) {
52 | // Switchable RAM bank
53 | int bankOffset = ramBankNumber * 0x2000;
54 | int index = bankOffset + (address - 0xA000);
55 | return data[index];
56 | }
57 | return 0;
58 | }
59 |
60 | @Override
61 | public void write(int address, byte value) {
62 | if (address >= 0x0000 && address < 0x2000) {
63 | // RAM enable/disable
64 | ramEnabled = ((value & 0x0F) == 0x0A);
65 | } else if (address >= 0x2000 && address < 0x4000) {
66 | // ROM bank number
67 | romBankNumber = (value & 0x7F);
68 | if (romBankNumber == 0) romBankNumber = 1;
69 | } else if (address >= 0x4000 && address < 0x6000) {
70 | // RAM bank number
71 | ramBankNumber = (value & 0x03);
72 | }
73 |
74 |
75 |
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cartridge/MBC5.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cartridge;
2 |
3 | public class MBC5 extends Cartridge {
4 | private static final int ROM_BANK_SIZE = 0x4000; // Size of a ROM bank
5 | private static final int RAM_BANK_SIZE = 0x2000; // Size of a RAM bank
6 |
7 | private int romBankNumber;
8 | private int ramBankNumber;
9 | private boolean ramEnabled;
10 |
11 | public MBC5(byte[] data) {
12 | super(data);
13 | this.romBankNumber = 1; // Default to bank 1, as bank 0 is always mapped to 0x0000-0x3FFF
14 | this.ramBankNumber = 0;
15 | this.ramEnabled = false;
16 | }
17 |
18 | @Override
19 | public byte read(int address) {
20 | if (address >= 0x0000 && address < 0x4000) {
21 | // Always read from ROM bank 0
22 | return data[address];
23 | } else if (address >= 0x4000 && address < 0x8000) {
24 | // Read from the selected ROM bank
25 | int bankOffset = romBankNumber * ROM_BANK_SIZE;
26 | int index = bankOffset + (address - 0x4000);
27 | return data[index];
28 | } else if (address >= 0xA000 && address < 0xC000 && ramEnabled) {
29 | // Read from the selected RAM bank, if enabled
30 | int bankOffset = ramBankNumber * RAM_BANK_SIZE;
31 | int index = bankOffset + (address - 0xA000);
32 | return data[index];
33 | }
34 | return 0; // Return 0 for invalid or disabled addresses
35 | }
36 |
37 | @Override
38 | public void write(int address, byte value) {
39 | if (address >= 0x0000 && address < 0x2000) {
40 | // Enable or disable RAM
41 | ramEnabled = ((value & 0x0A) != 0);
42 | } else if (address >= 0x2000 && address < 0x3000) {
43 | // Write lower 8 bits of ROM bank number
44 | romBankNumber = (romBankNumber & 0x100) | (value & 0xFF);
45 | } else if (address >= 0x3000 && address < 0x4000) {
46 | // Write 9th bit of ROM bank number
47 | romBankNumber = (romBankNumber & 0xFF) | ((value & 0x01) << 8);
48 | } else if (address >= 0x4000 && address < 0x6000) {
49 | // Select RAM bank
50 | ramBankNumber = value & 0x0F;
51 | } else if (address >= 0xA000 && address < 0xC000 && ramEnabled) {
52 | // Write to the selected RAM bank, if enabled
53 | int bankOffset = ramBankNumber * RAM_BANK_SIZE;
54 | int index = bankOffset + (address - 0xA000);
55 | data[index] = value;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cartridge/ROM.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cartridge;
2 |
3 | public class ROM extends Cartridge {
4 |
5 | public ROM(byte[] data) {
6 | super(data);
7 | }
8 |
9 | @Override
10 | public byte read(int address) {
11 | // Simple direct mapping for ROM-only cartridges
12 | if (address >= 0 && address < data.length) {
13 | return data[address];
14 | }
15 | return 0;
16 | }
17 |
18 | @Override
19 | public void write(int address, byte value) {
20 | // ROM is read-only; writes have no effect
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/gbc/model/controller/Controller.java:
--------------------------------------------------------------------------------
1 | package gbc.model.controller;
2 |
3 | public class Controller {
4 | private boolean[] keys;
5 | private boolean[] keysPressed;
6 | private boolean[] keysReleased;
7 |
8 | public Controller() {
9 | keys = new boolean[8];
10 | keysPressed = new boolean[8];
11 | keysReleased = new boolean[8];
12 | }
13 |
14 | public void setKey(int key, boolean pressed) {
15 | keys[key] = pressed;
16 | if (pressed) {
17 | keysPressed[key] = true;
18 | } else {
19 | keysReleased[key] = true;
20 | }
21 | }
22 |
23 | public void handleInput() {
24 | resetKeys();
25 | }
26 |
27 | public boolean isKeyPressed(int key) {
28 | return keysPressed[key];
29 | }
30 |
31 | public boolean isKeyReleased(int key) {
32 | return keysReleased[key];
33 | }
34 |
35 | public void resetKeys() {
36 | keysPressed = new boolean[8];
37 | keysReleased = new boolean[8];
38 | }
39 |
40 | public boolean isKeyPressed() {
41 | for (int i = 0; i < keysPressed.length; i++) {
42 | if (keysPressed[i]) {
43 | return true;
44 | }
45 | }
46 | return false;
47 | }
48 |
49 | public boolean isKeyReleased() {
50 | for (int i = 0; i < keysReleased.length; i++) {
51 | if (keysReleased[i]) {
52 | return true;
53 | }
54 | }
55 | return false;
56 | }
57 |
58 | public boolean isKey(int key) {
59 | return keys[key];
60 | }
61 | }
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cpu/CPU.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cpu;
2 |
3 | import gbc.model.memory.Memory;
4 |
5 | public class CPU {
6 | private Registers registers;
7 | private Memory memory;
8 | private Interruptions interrupts;
9 | private OperationsLoader operationsLoader;
10 |
11 | private int cycles;
12 | private StringBuilder opcodeLog;
13 |
14 | public CPU(Memory memory) {
15 | this.memory = memory;
16 | this.registers = new Registers();
17 | this.interrupts = new Interruptions(memory, registers);
18 | this.opcodeLog = new StringBuilder();
19 | this.operationsLoader = new OperationsLoader();
20 | reset();
21 | }
22 |
23 | public void reset() {
24 | registers.reset();
25 | memory.reset();
26 | interrupts.reset();
27 | cycles = 0;
28 | }
29 |
30 | public void executeCycle() {
31 | handleInterrupts();
32 | int opcode = fetchByte();
33 | executeInstruction(opcode);
34 | updateCycles(opcode);
35 | }
36 |
37 | private void handleInterrupts() {
38 | interrupts.handleInterrupts();
39 | }
40 |
41 | private void executeInstruction(int opcode) {
42 | Operation operation = operationsLoader.getOperation(opcode);
43 | if (operation != null) {
44 | operation.perform(registers, memory);
45 | opcodeLog.append(String.format("PC: 0x%04X, Opcode: 0x%02X\n", (byte)registers.getPC(), opcode));
46 | }
47 | }
48 |
49 | private void updateCycles(int opcode) {
50 | Operation operation = operationsLoader.getOperation(opcode);
51 | if (operation != null) {
52 | cycles += operation.getCycles().get(0); // assuming the first cycle count is the base cycle count
53 | }
54 | }
55 |
56 | private int fetchByte() {
57 | int value = memory.readByte(registers.getPC()) & 0xFF;
58 | registers.incrementPC();
59 | return value;
60 | }
61 |
62 | public int getCycles() {
63 | return cycles;
64 | }
65 |
66 | public void setCycles(int cycles) {
67 | this.cycles = cycles;
68 | }
69 |
70 | public String getOpcodeLog() {
71 | return opcodeLog.toString();
72 | }
73 |
74 | public Registers getRegisters() {
75 | return registers;
76 | }
77 |
78 |
79 | // Other methods...
80 | }
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cpu/Instructions.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cpu;
2 |
3 | import gbc.model.memory.Memory;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 |
7 | public class Instructions {
8 |
9 | private Registers r;
10 | private Memory m;
11 |
12 |
13 | Instructions(Registers r, Memory m) {
14 | this.r = r;
15 | this.m = m;
16 | }
17 |
18 | public void execute(byte opcode) {
19 |
20 | }
21 |
22 |
23 |
24 |
25 |
26 | // ... (other instruction methods)
27 |
28 | public void setRegisters(Registers r) {
29 | this.r = r;
30 | }
31 |
32 | public void setMemory(Memory m) {
33 | this.m = m;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cpu/Interruptions.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cpu;
2 | import gbc.model.memory.Memory;
3 |
4 | public class Interruptions {
5 | private boolean masterEnabled;
6 | private Memory memory;
7 | private Registers registers;
8 |
9 | private static final int ADDR_INTERRUPT_FLAG = 0xFF0F;
10 | private static final int ADDR_INTERRUPT_ENABLE = 0xFFFF;
11 |
12 | private static final byte INTERRUPT_VBLANK = 0x01;
13 | private static final byte INTERRUPT_LCDSTAT = 0x02;
14 | private static final byte INTERRUPT_TIMER = 0x04;
15 | private static final byte INTERRUPT_SERIAL = 0x08;
16 | private static final byte INTERRUPT_JOYPAD = 0x10;
17 |
18 | public Interruptions(Memory memory, Registers registers) {
19 | this.memory = memory;
20 | this.registers = registers;
21 | this.masterEnabled = true;
22 | }
23 |
24 | public void setMasterEnabled(boolean enabled) {
25 | this.masterEnabled = enabled;
26 | }
27 |
28 | public boolean isMasterEnabled() {
29 | return masterEnabled;
30 | }
31 |
32 | public void requestInterrupt(byte interrupt) {
33 | byte currentFlags = (byte) memory.readByte(ADDR_INTERRUPT_FLAG);
34 | currentFlags |= interrupt;
35 | memory.writeByte(ADDR_INTERRUPT_FLAG, currentFlags);
36 | }
37 |
38 | public void handleInterrupts() {
39 | if (!masterEnabled) {
40 | return;
41 | }
42 |
43 | byte interruptFlag = (byte) memory.readByte(ADDR_INTERRUPT_FLAG);
44 | byte interruptEnable = (byte) memory.readByte(ADDR_INTERRUPT_ENABLE);
45 |
46 | byte interruptFired = (byte) (interruptFlag & interruptEnable);
47 | if (interruptFired == 0) {
48 | return;
49 | }
50 |
51 | // Service the first interrupt that's fired
52 | if ((interruptFired & INTERRUPT_VBLANK) != 0) {
53 | serviceInterrupt(INTERRUPT_VBLANK, 0x0040);
54 | } else if ((interruptFired & INTERRUPT_LCDSTAT) != 0) {
55 | serviceInterrupt(INTERRUPT_LCDSTAT, 0x0048);
56 | } else if ((interruptFired & INTERRUPT_TIMER) != 0) {
57 | serviceInterrupt(INTERRUPT_TIMER, 0x0050);
58 | } else if ((interruptFired & INTERRUPT_SERIAL) != 0) {
59 | serviceInterrupt(INTERRUPT_SERIAL, 0x0058);
60 | } else if ((interruptFired & INTERRUPT_JOYPAD) != 0) {
61 | serviceInterrupt(INTERRUPT_JOYPAD, 0x0060);
62 | }
63 | }
64 |
65 | private void serviceInterrupt(byte interrupt, int handlerAddress) {
66 | // Push current PC on stack
67 | pushPC();
68 |
69 | // Jump to interrupt handler
70 | registers.setPC((char) handlerAddress);
71 |
72 | // Disable further interrupts
73 | setMasterEnabled(false);
74 |
75 | // Reset the interrupt flag
76 | byte currentFlags = (byte) memory.readByte(ADDR_INTERRUPT_FLAG);
77 | currentFlags &= ~interrupt;
78 | memory.writeByte(ADDR_INTERRUPT_FLAG, currentFlags);
79 | }
80 |
81 | private void pushPC() {
82 | char sp = registers.getSP();
83 | memory.writeChar((char) (sp - 2), registers.getPC());
84 | registers.setSP((char) (sp - 2));
85 | }
86 |
87 | public void reset() {
88 | masterEnabled = true;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/gbc/model/cpu/Operation.java:
--------------------------------------------------------------------------------
1 | package gbc.model.cpu;
2 |
3 | import gbc.model.memory.Memory;
4 |
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | public class Operation {
9 | private OperationExecutor executor;
10 | private String mnemonic;
11 | private int bytes;
12 | private List cycles;
13 | private boolean immediate;
14 | private Map flags;
15 | private List