From 7c9a9bb5ca9f07da642ff19d2f70c3079b3e3678 Mon Sep 17 00:00:00 2001
From: yangkui <752544765@qq.com>
Date: Fri, 2 Dec 2022 15:19:50 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93?=
=?UTF-8?q?=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/cn/navclub/nes4j/app/CHRView.java | 151 ----------------
.../nes4j/app/{NES4J.java => Nes4j.java} | 6 +-
.../nes4j/app/{ => assets}/FXResource.java | 2 +-
.../nes4j/app/control/NesGameItem.java | 3 +-
.../cn/navclub/nes4j/app/dialog/DHandle.java | 6 +-
.../cn/navclub/nes4j/app/dialog/DPalette.java | 6 +-
.../navclub/nes4j/app/view/DebuggerView.java | 6 +-
.../cn/navclub/nes4j/app/view/GameWorld.java | 23 ++-
app/src/main/java/module-info.java | 1 +
.../nes4j/app/{ => assets}/css/DHandle.css | 0
.../app/{ => assets}/css/DebuggerView.css | 0
.../nes4j/app/{ => assets}/css/Nes4j.css | 0
.../app/{ => assets}/css/SystemPalette.css | 0
.../nes4j/app/{ => assets}/css/common.css | 0
.../nes4j/app/{ => assets}/img/bin.png | Bin
.../nes4j/app/{ => assets}/img/delete.png | Bin
.../nes4j/app/{ => assets}/img/game.png | Bin
.../app/{ => assets}/img/handler/down.png | Bin
.../app/{ => assets}/img/handler/left.png | Bin
.../app/{ => assets}/img/handler/right.png | Bin
.../nes4j/app/{ => assets}/img/handler/up.png | Bin
.../nes4j/app/{ => assets}/img/icon.png | Bin
.../nes4j/app/{ => assets}/img/launcher.png | Bin
.../nes4j/app/{ => assets}/img/rrun.png | Bin
.../nes4j/app/{ => assets}/img/run.png | Bin
.../nes4j/app/{ => assets}/img/stepinto.png | Bin
.../nes4j/app/{ => assets}/img/stepout.png | Bin
.../{ => assets}/language/nes4j.properties | 0
.../language/nes4j_zh_CN.properties | 0
.../cn/navclub/nes4j/bin/core/Cartridge.java | 20 ++-
.../java/cn/navclub/nes4j/bin/core/PPU.java | 5 +-
.../nes4j/bin/core/impl/CTRegister.java | 11 +-
.../navclub/nes4j/bin/enums/NameMirror.java | 2 +
.../navclub/nes4j/bin/enums/NameTMirror.java | 15 ++
.../cn/navclub/nes4j/bin/screen/Camera.java | 15 ++
.../cn/navclub/nes4j/bin/screen/Render.java | 40 ++---
.../cn/navclub/nes4j/bin/util/PPUUtil.java | 162 ++++++++++++++++++
37 files changed, 257 insertions(+), 217 deletions(-)
delete mode 100644 app/src/main/java/cn/navclub/nes4j/app/CHRView.java
rename app/src/main/java/cn/navclub/nes4j/app/{NES4J.java => Nes4j.java} (97%)
rename app/src/main/java/cn/navclub/nes4j/app/{ => assets}/FXResource.java (94%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/css/DHandle.css (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/css/DebuggerView.css (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/css/Nes4j.css (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/css/SystemPalette.css (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/css/common.css (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/bin.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/delete.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/game.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/handler/down.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/handler/left.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/handler/right.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/handler/up.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/icon.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/launcher.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/rrun.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/run.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/stepinto.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/img/stepout.png (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/language/nes4j.properties (100%)
rename app/src/main/resources/cn/navclub/nes4j/app/{ => assets}/language/nes4j_zh_CN.properties (100%)
create mode 100644 bin/src/main/java/cn/navclub/nes4j/bin/enums/NameTMirror.java
create mode 100644 bin/src/main/java/cn/navclub/nes4j/bin/util/PPUUtil.java
diff --git a/app/src/main/java/cn/navclub/nes4j/app/CHRView.java b/app/src/main/java/cn/navclub/nes4j/app/CHRView.java
deleted file mode 100644
index 563b679..0000000
--- a/app/src/main/java/cn/navclub/nes4j/app/CHRView.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package cn.navclub.nes4j.app;
-
-import cn.navclub.nes4j.app.control.Tile;
-import cn.navclub.nes4j.app.util.OSUtil;
-import cn.navclub.nes4j.bin.core.Cartridge;
-import cn.navclub.nes4j.bin.util.PatternTableUtil;
-import javafx.application.Application;
-import javafx.application.Platform;
-import javafx.geometry.Insets;
-import javafx.geometry.Pos;
-import javafx.scene.Scene;
-import javafx.scene.control.*;
-import javafx.scene.image.PixelFormat;
-import javafx.scene.image.WritableImage;
-import javafx.scene.input.TransferMode;
-import javafx.scene.layout.BorderPane;
-import javafx.scene.layout.FlowPane;
-import javafx.scene.paint.Color;
-import javafx.stage.Stage;
-
-import java.io.File;
-import java.nio.ByteBuffer;
-import java.util.concurrent.CompletableFuture;
-
-/**
- * ch-rom数据可视化查看程序
- */
-public class CHRView extends Application {
- private static final Color TRANSPARENT = new Color(102 / 255.0, 102 / 255.0, 102 / 255.0, .5);
-
- private FlowPane flowPane;
-
- @Override
- public void start(Stage stage) {
-
- this.flowPane = new FlowPane();
-
- this.flowPane.setHgap(10);
- this.flowPane.setVgap(10);
- this.flowPane.setAlignment(Pos.CENTER);
-
-
- var scrollPane = new ScrollPane();
- scrollPane.setFitToWidth(true);
- scrollPane.setFitToHeight(true);
- scrollPane.setContent(this.flowPane);
- scrollPane.setPadding(new Insets(10));
-
- this.flowPane.prefWidthProperty().bind(scrollPane.widthProperty());
-
- var menuBar = new MenuBar();
-
- var menu = new Menu("File");
- var open = new MenuItem("Open");
- var setting = new MenuItem("Setting");
- open.setOnAction(e -> {
- var optional = OSUtil.chooseFile(stage, "NES rom file", "*.nes", "*.NES");
- if (optional.isEmpty()) {
- return;
- }
- this.loadNESFile(optional.get());
- });
- menu.getItems().addAll(open, setting);
- menuBar.getMenus().add(menu);
-
- var root = new BorderPane();
-
- root.setTop(menuBar);
- root.setCenter(scrollPane);
-
- var scene = new Scene(root);
- scene.setOnDragOver(event -> {
- event.acceptTransferModes(TransferMode.ANY);
- event.consume();
- });
- scene.setOnDragDropped(event -> {
- var board = event.getDragboard();
- var list = board
- .getFiles()
- .stream()
- .filter(it -> it.getName().endsWith(".nes"))
- .toList();
- if (list.isEmpty()) {
- return;
- }
- this.loadNESFile(list.get(0));
- event.consume();
- });
- stage.setWidth(600);
- stage.setHeight(800);
- stage.setScene(scene);
- stage.setTitle("CHView");
- stage.getIcons().add(FXResource.loadImage("bin.png"));
- scene.getStylesheets().add(FXResource.loadStyleSheet("common.css"));
-
- stage.show();
- }
-
- public void loadNESFile(File file) {
- //Clear already exist tiles
- this.flowPane.getChildren().clear();
- var future = CompletableFuture.supplyAsync(() -> new Cartridge(file));
- future.whenComplete((nesFile, t) -> {
- if (t != null) {
- t.printStackTrace();
- return;
- }
- var ch = nesFile.getChrom();
- var len = ch.length / 16;
- for (int i = 0; i < len; i++) {
- var k = i * 16;
- var arr = new byte[0x10];
- System.arraycopy(ch, k, arr, 0, 0x10);
- this.renderTile(PatternTableUtil.tiles(arr), i);
- }
- });
- }
-
- public void renderTile(byte[][] tile, int index) {
- var w = 15;
- var h = 15;
- var image = new WritableImage(w * 8, h * 8);
- var writer = image.getPixelWriter();
- var pixelFormat = PixelFormat.getByteBgraInstance();
- for (int i = 0; i < tile.length; i++) {
- for (int j = 0; j < tile[i].length; j++) {
- var value = tile[i][j];
- var color = switch (value) {
- case 1 -> Color.RED;
- case 2 -> Color.GREEN;
- case 3 -> Color.BLUE;
- default -> TRANSPARENT;
- };
- var arr = new byte[w * h];
- for (int k = 0; k < (w * h); k++) {
- arr[k] = (byte) Math.round(color.getBlue() * 0xff);
- arr[k + 1] = (byte) Math.round(color.getGreen() * 0xff);
- arr[k + 2] = (byte) Math.round(color.getRed() * 0xff);
- arr[k + 3] = (byte) Math.round(color.getOpacity() * 0xff);
- k += 4;
- }
- writer.setPixels(j * w, i * h, w, h, pixelFormat, ByteBuffer.wrap(arr), 0);
- }
- }
- Platform.runLater(() -> this.flowPane.getChildren().add(new Tile(image, index)));
- }
-
- public static void main(String[] args) {
- launch(args);
- }
-}
diff --git a/app/src/main/java/cn/navclub/nes4j/app/NES4J.java b/app/src/main/java/cn/navclub/nes4j/app/Nes4j.java
similarity index 97%
rename from app/src/main/java/cn/navclub/nes4j/app/NES4J.java
rename to app/src/main/java/cn/navclub/nes4j/app/Nes4j.java
index e4700aa..bac7f49 100644
--- a/app/src/main/java/cn/navclub/nes4j/app/NES4J.java
+++ b/app/src/main/java/cn/navclub/nes4j/app/Nes4j.java
@@ -1,11 +1,11 @@
package cn.navclub.nes4j.app;
+import cn.navclub.nes4j.app.assets.FXResource;
import cn.navclub.nes4j.app.config.NESConfig;
import cn.navclub.nes4j.app.control.NesGameItem;
import cn.navclub.nes4j.app.dialog.DHandle;
import cn.navclub.nes4j.app.util.JsonUtil;
import cn.navclub.nes4j.app.util.StrUtil;
-import cn.navclub.nes4j.bin.Player;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
@@ -23,7 +23,7 @@
import java.util.ResourceBundle;
@Slf4j
-public class NES4J extends Application {
+public class Nes4j extends Application {
public static final ResourceBundle RESOURCE_BUNDLE;
private static final String DEFAULT_CONFIG_PATH = "config/config.json";
@@ -33,7 +33,7 @@ public class NES4J extends Application {
static {
System.loadLibrary("nes4j");
System.setProperty("java.util.PropertyResourceBundle.encoding", "UTF-8");
- RESOURCE_BUNDLE = ResourceBundle.getBundle("cn.navclub.nes4j.app.language.nes4j");
+ RESOURCE_BUNDLE = ResourceBundle.getBundle("cn.navclub.nes4j.app.assets.language.nes4j");
}
private ListView listView;
diff --git a/app/src/main/java/cn/navclub/nes4j/app/FXResource.java b/app/src/main/java/cn/navclub/nes4j/app/assets/FXResource.java
similarity index 94%
rename from app/src/main/java/cn/navclub/nes4j/app/FXResource.java
rename to app/src/main/java/cn/navclub/nes4j/app/assets/FXResource.java
index e96fa20..16a025a 100644
--- a/app/src/main/java/cn/navclub/nes4j/app/FXResource.java
+++ b/app/src/main/java/cn/navclub/nes4j/app/assets/FXResource.java
@@ -1,4 +1,4 @@
-package cn.navclub.nes4j.app;
+package cn.navclub.nes4j.app.assets;
import javafx.scene.image.Image;
diff --git a/app/src/main/java/cn/navclub/nes4j/app/control/NesGameItem.java b/app/src/main/java/cn/navclub/nes4j/app/control/NesGameItem.java
index ed1cf03..5a2ecb9 100644
--- a/app/src/main/java/cn/navclub/nes4j/app/control/NesGameItem.java
+++ b/app/src/main/java/cn/navclub/nes4j/app/control/NesGameItem.java
@@ -1,9 +1,8 @@
package cn.navclub.nes4j.app.control;
-import cn.navclub.nes4j.app.FXResource;
+import cn.navclub.nes4j.app.assets.FXResource;
import cn.navclub.nes4j.app.util.StrUtil;
import cn.navclub.nes4j.app.view.GameWorld;
-import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
diff --git a/app/src/main/java/cn/navclub/nes4j/app/dialog/DHandle.java b/app/src/main/java/cn/navclub/nes4j/app/dialog/DHandle.java
index 65ea3c7..d62639f 100644
--- a/app/src/main/java/cn/navclub/nes4j/app/dialog/DHandle.java
+++ b/app/src/main/java/cn/navclub/nes4j/app/dialog/DHandle.java
@@ -1,7 +1,7 @@
package cn.navclub.nes4j.app.dialog;
-import cn.navclub.nes4j.app.FXResource;
-import cn.navclub.nes4j.app.NES4J;
+import cn.navclub.nes4j.app.assets.FXResource;
+import cn.navclub.nes4j.app.Nes4j;
import cn.navclub.nes4j.app.model.KeyMapper;
import javafx.geometry.Pos;
import javafx.scene.control.*;
@@ -114,7 +114,7 @@ public DHandle(final KeyMapper[] mappers) {
this.initTableView();
- this.setTitle(NES4J.localeValue("nes4j.handle", true));
+ this.setTitle(Nes4j.localeValue("nes4j.handle", true));
}
private void initTableView() {
diff --git a/app/src/main/java/cn/navclub/nes4j/app/dialog/DPalette.java b/app/src/main/java/cn/navclub/nes4j/app/dialog/DPalette.java
index 8ef9f7b..83b0b19 100644
--- a/app/src/main/java/cn/navclub/nes4j/app/dialog/DPalette.java
+++ b/app/src/main/java/cn/navclub/nes4j/app/dialog/DPalette.java
@@ -1,8 +1,8 @@
package cn.navclub.nes4j.app.dialog;
-import cn.navclub.nes4j.app.FXResource;
+import cn.navclub.nes4j.app.assets.FXResource;
-import cn.navclub.nes4j.app.NES4J;
+import cn.navclub.nes4j.app.Nes4j;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
@@ -50,7 +50,7 @@ public DPalette(int[][] palette) {
this.getDialogPane().getStylesheets().add(STYLE_SHEET);
this.getDialogPane().getButtonTypes().addAll(ButtonType.APPLY, ButtonType.CANCEL);
- this.setTitle(NES4J.localeValue("nes4j.palette", true));
+ this.setTitle(Nes4j.localeValue("nes4j.palette", true));
}
private void initPalette() {
diff --git a/app/src/main/java/cn/navclub/nes4j/app/view/DebuggerView.java b/app/src/main/java/cn/navclub/nes4j/app/view/DebuggerView.java
index 99a3457..dc7421c 100644
--- a/app/src/main/java/cn/navclub/nes4j/app/view/DebuggerView.java
+++ b/app/src/main/java/cn/navclub/nes4j/app/view/DebuggerView.java
@@ -1,7 +1,7 @@
package cn.navclub.nes4j.app.view;
-import cn.navclub.nes4j.app.FXResource;
-import cn.navclub.nes4j.app.NES4J;
+import cn.navclub.nes4j.app.assets.FXResource;
+import cn.navclub.nes4j.app.Nes4j;
import cn.navclub.nes4j.app.control.BreakLine;
import cn.navclub.nes4j.app.control.CPUControlPane;
import cn.navclub.nes4j.app.control.PPUControlPane;
@@ -53,7 +53,7 @@ public DebuggerView(final Window owner) {
rrun.setTooltip(new Tooltip("re-run"));
stepOut.setTooltip(new Tooltip("step out"));
stepInto.setTooltip(new Tooltip("step into"));
- run.setTooltip(new Tooltip(NES4J.localeValue("nes4j.run")));
+ run.setTooltip(new Tooltip(Nes4j.localeValue("nes4j.run")));
run.setGraphic(new ImageView(FXResource.loadImage("run.png")));
rrun.setGraphic(new ImageView(FXResource.loadImage("rrun.png")));
diff --git a/app/src/main/java/cn/navclub/nes4j/app/view/GameWorld.java b/app/src/main/java/cn/navclub/nes4j/app/view/GameWorld.java
index c68a668..99d56ab 100644
--- a/app/src/main/java/cn/navclub/nes4j/app/view/GameWorld.java
+++ b/app/src/main/java/cn/navclub/nes4j/app/view/GameWorld.java
@@ -1,7 +1,7 @@
package cn.navclub.nes4j.app.view;
-import cn.navclub.nes4j.app.FXResource;
-import cn.navclub.nes4j.app.NES4J;
+import cn.navclub.nes4j.app.assets.FXResource;
+import cn.navclub.nes4j.app.Nes4j;
import cn.navclub.nes4j.app.audio.NativePlayer;
import cn.navclub.nes4j.app.dialog.DPalette;
import cn.navclub.nes4j.app.event.GameEventWrap;
@@ -33,7 +33,6 @@
import java.io.File;
import java.nio.ByteBuffer;
import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingDeque;
public class GameWorld extends Stage {
@@ -65,14 +64,14 @@ public GameWorld(final File file) {
this.eventQueue = new LinkedBlockingDeque<>();
this.debuggerView = new DebuggerView(this);
- var view = new Menu(NES4J.localeValue("nes4j.view"));
- var tool = new Menu(NES4J.localeValue("nes4j.tool"));
- var emulator = new Menu(NES4J.localeValue("nes4j.emulator"));
+ var view = new Menu(Nes4j.localeValue("nes4j.view"));
+ var tool = new Menu(Nes4j.localeValue("nes4j.tool"));
+ var emulator = new Menu(Nes4j.localeValue("nes4j.emulator"));
- var debug = new MenuItem(NES4J.localeValue("nes4j.debug"));
- var softRest = new MenuItem(NES4J.localeValue("nes4j.reset"));
- var pausePlay = new MenuItem(NES4J.localeValue("nes4j.pplay"));
- var palette = new MenuItem(NES4J.localeValue("nes4j.palette"));
+ var debug = new MenuItem(Nes4j.localeValue("nes4j.debug"));
+ var softRest = new MenuItem(Nes4j.localeValue("nes4j.reset"));
+ var pausePlay = new MenuItem(Nes4j.localeValue("nes4j.pplay"));
+ var palette = new MenuItem(Nes4j.localeValue("nes4j.palette"));
palette.setOnAction(this::systemPalette);
@@ -124,7 +123,7 @@ public GameWorld(final File file) {
if (!(eventType == KeyEvent.KEY_PRESSED || eventType == KeyEvent.KEY_RELEASED)) {
return;
}
- for (KeyMapper keyMapper : NES4J.config.getMapper()) {
+ for (KeyMapper keyMapper : Nes4j.config.getMapper()) {
if (keyMapper.getKeyCode() == code) {
try {
this.eventQueue.put(new GameEventWrap(eventType, keyMapper.getButton()));
@@ -187,7 +186,7 @@ private void dispose(Throwable t) {
}
if (t != null) {
var dialog = new ExceptionDialog(t);
- dialog.setHeaderText(NES4J.localeValue("nes4j.game.error"));
+ dialog.setHeaderText(Nes4j.localeValue("nes4j.game.error"));
dialog.showAndWait();
// this.close();
}
diff --git a/app/src/main/java/module-info.java b/app/src/main/java/module-info.java
index 2a074bf..227ac36 100644
--- a/app/src/main/java/module-info.java
+++ b/app/src/main/java/module-info.java
@@ -21,6 +21,7 @@
opens cn.navclub.nes4j.app.config to com.fasterxml.jackson.databind;
opens cn.navclub.nes4j.app.model to javafx.base, com.fasterxml.jackson.databind;
+ opens cn.navclub.nes4j.app.assets;
uses cn.navclub.nes4j.bin.Player;
}
\ No newline at end of file
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/css/DHandle.css b/app/src/main/resources/cn/navclub/nes4j/app/assets/css/DHandle.css
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/css/DHandle.css
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/css/DHandle.css
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/css/DebuggerView.css b/app/src/main/resources/cn/navclub/nes4j/app/assets/css/DebuggerView.css
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/css/DebuggerView.css
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/css/DebuggerView.css
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/css/Nes4j.css b/app/src/main/resources/cn/navclub/nes4j/app/assets/css/Nes4j.css
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/css/Nes4j.css
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/css/Nes4j.css
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/css/SystemPalette.css b/app/src/main/resources/cn/navclub/nes4j/app/assets/css/SystemPalette.css
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/css/SystemPalette.css
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/css/SystemPalette.css
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/css/common.css b/app/src/main/resources/cn/navclub/nes4j/app/assets/css/common.css
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/css/common.css
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/css/common.css
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/bin.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/bin.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/bin.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/bin.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/delete.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/delete.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/delete.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/delete.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/game.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/game.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/game.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/game.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/handler/down.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/handler/down.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/handler/down.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/handler/down.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/handler/left.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/handler/left.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/handler/left.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/handler/left.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/handler/right.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/handler/right.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/handler/right.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/handler/right.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/handler/up.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/handler/up.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/handler/up.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/handler/up.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/icon.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/icon.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/icon.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/icon.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/launcher.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/launcher.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/launcher.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/launcher.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/rrun.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/rrun.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/rrun.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/rrun.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/run.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/run.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/run.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/run.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/stepinto.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/stepinto.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/stepinto.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/stepinto.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/img/stepout.png b/app/src/main/resources/cn/navclub/nes4j/app/assets/img/stepout.png
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/img/stepout.png
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/img/stepout.png
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/language/nes4j.properties b/app/src/main/resources/cn/navclub/nes4j/app/assets/language/nes4j.properties
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/language/nes4j.properties
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/language/nes4j.properties
diff --git a/app/src/main/resources/cn/navclub/nes4j/app/language/nes4j_zh_CN.properties b/app/src/main/resources/cn/navclub/nes4j/app/assets/language/nes4j_zh_CN.properties
similarity index 100%
rename from app/src/main/resources/cn/navclub/nes4j/app/language/nes4j_zh_CN.properties
rename to app/src/main/resources/cn/navclub/nes4j/app/assets/language/nes4j_zh_CN.properties
diff --git a/bin/src/main/java/cn/navclub/nes4j/bin/core/Cartridge.java b/bin/src/main/java/cn/navclub/nes4j/bin/core/Cartridge.java
index 5d64c9a..be68621 100644
--- a/bin/src/main/java/cn/navclub/nes4j/bin/core/Cartridge.java
+++ b/bin/src/main/java/cn/navclub/nes4j/bin/core/Cartridge.java
@@ -4,6 +4,7 @@
import cn.navclub.nes4j.bin.enums.NESFormat;
import cn.navclub.nes4j.bin.enums.NMapper;
import cn.navclub.nes4j.bin.enums.NameMirror;
+import cn.navclub.nes4j.bin.enums.NameTMirror;
import cn.navclub.nes4j.bin.util.ByteUtil;
import cn.navclub.nes4j.bin.util.IOUtil;
@@ -180,10 +181,7 @@ public Cartridge(byte[] buffer) {
var flag7 = headers[7] & 0xff;
var flag8 = headers[8] & 0xff;
-
- this.mirrors = NameMirror.values()[flag6 & 1];
-
- var mapper = (flag7 & 0b1111_0000) | ((flag6 & 0b1111_0000) >> 4);
+ var mapper = (flag7 & 0xf0) | ((flag6 & 0xf0) >> 4);
//NES2.0包含12位
if (this.format == NESFormat.NES_20) {
@@ -196,6 +194,20 @@ public Cartridge(byte[] buffer) {
this.mapper = NMapper.values()[mapper];
}
+ NameMirror mirrors;
+ if (((flag6 >> 3) & 0x01) == 1) {
+ mirrors = NameMirror.FOUR_SCREEN;
+ } else {
+ mirrors = NameMirror.values()[flag6 & 1];
+ }
+
+ //UNROM 512 uses %....1..0 to indicate a 1-screen board, and %....1..1 to indicate a 4-screen board.
+ if (this.mapper == NMapper.UX_ROM && mirrors == NameMirror.FOUR_SCREEN && (flag6 & 0x01) == 0) {
+ mirrors = NameMirror.SINGLE_SCREEN;
+ }
+
+ this.mirrors = mirrors;
+
var trainSize = this.trainAreaSize(flag6);
chrom = new byte[chSize];
diff --git a/bin/src/main/java/cn/navclub/nes4j/bin/core/PPU.java b/bin/src/main/java/cn/navclub/nes4j/bin/core/PPU.java
index 80f97e4..e3a32f9 100644
--- a/bin/src/main/java/cn/navclub/nes4j/bin/core/PPU.java
+++ b/bin/src/main/java/cn/navclub/nes4j/bin/core/PPU.java
@@ -8,12 +8,11 @@
import cn.navclub.nes4j.bin.enums.MaskFlag;
import cn.navclub.nes4j.bin.enums.NameMirror;
import cn.navclub.nes4j.bin.enums.PStatus;
+import cn.navclub.nes4j.bin.screen.Render;
import cn.navclub.nes4j.bin.util.MathUtil;
import lombok.Getter;
import lombok.Setter;
-import java.security.PublicKey;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* PPU document
@@ -108,6 +107,7 @@ public void tick() {
if (this.scanLine >= 262) {
this.nmi = false;
this.scanLine = 0;
+ //Sprite 0 notify cpu VBL already end.
this.status.clear(PStatus.V_BLANK_OCCUR, PStatus.SPRITE_ZERO_HIT);
}
}
@@ -191,6 +191,7 @@ public void write(int address, byte b) {
public byte readStatus() {
var b = this.status.getBits();
+ //Due to every read ppu status clear VBL so can't judge VBL whether end need use sprite zero
this.status.clear(PStatus.V_BLANK_OCCUR);
this.addr.reset();
this.scroll.reset();
diff --git a/bin/src/main/java/cn/navclub/nes4j/bin/core/impl/CTRegister.java b/bin/src/main/java/cn/navclub/nes4j/bin/core/impl/CTRegister.java
index 3e1c3db..4f5286f 100644
--- a/bin/src/main/java/cn/navclub/nes4j/bin/core/impl/CTRegister.java
+++ b/bin/src/main/java/cn/navclub/nes4j/bin/core/impl/CTRegister.java
@@ -1,6 +1,7 @@
package cn.navclub.nes4j.bin.core.impl;
import cn.navclub.nes4j.bin.core.SRegister;
+import cn.navclub.nes4j.bin.enums.NameTMirror;
import cn.navclub.nes4j.bin.enums.PControl;
import lombok.extern.slf4j.Slf4j;
@@ -36,14 +37,8 @@ public CTRegister() {
/**
* Get current name table address
*/
- public int nameTableAddr() {
- return switch (this.bits & 0x03) {
- case 0 -> 0x2000;
- case 1 -> 0x2400;
- case 2 -> 0x2800;
- case 3 -> 0x2c00;
- default -> 0;
- };
+ public NameTMirror nameTableAddr() {
+ return NameTMirror.values()[this.bits & 0x03];
}
public int VRamIncrement() {
diff --git a/bin/src/main/java/cn/navclub/nes4j/bin/enums/NameMirror.java b/bin/src/main/java/cn/navclub/nes4j/bin/enums/NameMirror.java
index 86b2420..eaa9eef 100644
--- a/bin/src/main/java/cn/navclub/nes4j/bin/enums/NameMirror.java
+++ b/bin/src/main/java/cn/navclub/nes4j/bin/enums/NameMirror.java
@@ -3,4 +3,6 @@
public enum NameMirror {
HORIZONTAL,
VERTICAL,
+ SINGLE_SCREEN,
+ FOUR_SCREEN
}
diff --git a/bin/src/main/java/cn/navclub/nes4j/bin/enums/NameTMirror.java b/bin/src/main/java/cn/navclub/nes4j/bin/enums/NameTMirror.java
new file mode 100644
index 0000000..5ab2478
--- /dev/null
+++ b/bin/src/main/java/cn/navclub/nes4j/bin/enums/NameTMirror.java
@@ -0,0 +1,15 @@
+package cn.navclub.nes4j.bin.enums;
+
+
+public enum NameTMirror {
+ L1(0x2000),
+ L2(0x2400),
+ L3(0x2800),
+ L4(0x2c00);
+
+ public final int address;
+
+ NameTMirror(int address) {
+ this.address = address;
+ }
+}
diff --git a/bin/src/main/java/cn/navclub/nes4j/bin/screen/Camera.java b/bin/src/main/java/cn/navclub/nes4j/bin/screen/Camera.java
index e7ddcfe..a92c5aa 100644
--- a/bin/src/main/java/cn/navclub/nes4j/bin/screen/Camera.java
+++ b/bin/src/main/java/cn/navclub/nes4j/bin/screen/Camera.java
@@ -1,5 +1,20 @@
package cn.navclub.nes4j.bin.screen;
+/**
+ * Current camera visible range
+ *
+ * +++++++++++++++++++++++++++++++++++++++(x1/y1)
+ * + +
+ * + +
+ * + +
+ * + visible area +
+ * + +
+ * + +
+ * + +
+ * + +
+ * (x0/y0)+++++++++++++++++++++++++++++++++++++++
+ *
+ */
public record Camera(int x0, int y0, int x1, int y1) {
}
diff --git a/bin/src/main/java/cn/navclub/nes4j/bin/screen/Render.java b/bin/src/main/java/cn/navclub/nes4j/bin/screen/Render.java
index c332e54..b90923c 100644
--- a/bin/src/main/java/cn/navclub/nes4j/bin/screen/Render.java
+++ b/bin/src/main/java/cn/navclub/nes4j/bin/screen/Render.java
@@ -4,6 +4,7 @@
import cn.navclub.nes4j.bin.core.impl.CTRegister;
import cn.navclub.nes4j.bin.enums.MaskFlag;
import cn.navclub.nes4j.bin.enums.NameMirror;
+import cn.navclub.nes4j.bin.util.PPUUtil;
import lombok.Getter;
public class Render {
@@ -71,11 +72,6 @@ public void render(PPU ppu, Frame frame) {
var background = mask.contain(MaskFlag.SHOW_BACKGROUND);
//Render background
if (background) {
- var vram = ppu.getVram();
- var mirror = ppu.getMirrors();
-
- var ctr = ppu.getControl();
- var nameTable = ctr.nameTableAddr();
var scrollX = ppu.getScroll().getX();
var scrollY = ppu.getScroll().getY();
@@ -83,17 +79,8 @@ public void render(PPU ppu, Frame frame) {
var firstNameTable = new byte[0x400];
var secondNameTable = new byte[0x400];
- if ((mirror == NameMirror.VERTICAL && (nameTable == 0x2000 || nameTable == 0x2800))
- || (mirror == NameMirror.HORIZONTAL && (nameTable == 0x2000 || nameTable == 0x2400))) {
- System.arraycopy(vram, 0, firstNameTable, 0, 0x400);
- System.arraycopy(vram, 0x400, secondNameTable, 0, 0x400);
- } else if ((mirror == NameMirror.VERTICAL && (nameTable == 0x2400 || nameTable == 0x2c00))
- || (mirror == NameMirror.HORIZONTAL && (nameTable == 0x2800 || nameTable == 0x2c00))) {
- System.arraycopy(vram, 0x400, firstNameTable, 0, 0x400);
- System.arraycopy(vram, 0, secondNameTable, 0, 0x400);
- } else {
- throw new RuntimeException("Not support mirror type:" + mirror);
- }
+ //Fill fist and second name table
+ PPUUtil.fillNameTable(ppu, firstNameTable, secondNameTable);
//Render first screen background
@@ -144,7 +131,7 @@ public void render(PPU ppu, Frame frame) {
var ctrl = ppu.getControl();
var size = ctrl.spriteSize();
- var tile = new byte[size + 8];
+ var tile = new byte[16];
var bank = size == 0x08 ? ctrl.spritePattern8() : ctrl.spritePattern16(idx);
@@ -218,22 +205,23 @@ private void renderNameTable(PPU ppu, Frame frame, byte[] nameTable, Camera came
var bank = ppu.getControl().bkNamePatternTable();
- //渲染背景960个tile
+ var tile = new byte[16];
+
+ //渲染背景32*30=960个tile
for (int i = 0; i < 0x3c0; i++) {
var row = i / 32;
var column = i % 32;
var idx = nameTable[i] & 0xff;
- var tile = new byte[16];
var offset = bank + idx * 16;
System.arraycopy(ppu.getCh(), offset, tile, 0, 16);
var palette = bgPalette(ppu, attrTable, column, row);
for (int y = 0; y < 8; y++) {
- var upper = Byte.toUnsignedInt(tile[y]);
- var lower = Byte.toUnsignedInt(tile[y + 8]);
+ var left = tile[y] & 0xff;
+ var right = tile[y + 8] & 0xff;
for (int x = 7; x >= 0; x--) {
- var value = ((1 & lower) << 1) | (1 & upper);
- upper >>= 1;
- lower >>= 1;
+ var value = ((1 & right) << 1) | (1 & left);
+ left >>= 1;
+ right >>= 1;
var rgb = switch (value) {
case 0 -> sysPalette[ppu.getPaletteTable()[0]];
case 1 -> sysPalette[palette[1]];
@@ -246,7 +234,7 @@ private void renderNameTable(PPU ppu, Frame frame, byte[] nameTable, Camera came
var py = row * 8 + y;
var px = column * 8 + x;
- //判断是否显示范围
+ //判断是否相机显示范围
if (px >= camera.x0() && px < camera.x1() && py >= camera.y0() && py < camera.y1()) {
frame.updatePixel(sx + px, sy + py, rgb);
}
@@ -254,4 +242,6 @@ private void renderNameTable(PPU ppu, Frame frame, byte[] nameTable, Camera came
}
}
}
+
+
}
diff --git a/bin/src/main/java/cn/navclub/nes4j/bin/util/PPUUtil.java b/bin/src/main/java/cn/navclub/nes4j/bin/util/PPUUtil.java
new file mode 100644
index 0000000..e81ef09
--- /dev/null
+++ b/bin/src/main/java/cn/navclub/nes4j/bin/util/PPUUtil.java
@@ -0,0 +1,162 @@
+package cn.navclub.nes4j.bin.util;
+
+import cn.navclub.nes4j.bin.core.PPU;
+import cn.navclub.nes4j.bin.enums.MaskFlag;
+import cn.navclub.nes4j.bin.enums.NameMirror;
+import cn.navclub.nes4j.bin.enums.NameTMirror;
+
+public class PPUUtil {
+ /**
+ *
+ *
+ * The NES only has 2 KB to store name tables and attribute tables, allowing it to store two of
+ * each. However it can address up to four of each. Mirroring is used to allow it to do this. There
+ * are four types of mirroring which are described below, using abbreviations for logical name
+ * tables (those that can be addressed), L1 at $2000, L2 at $2400, L3 at $2800 and L4 at
+ * $2C00:
+ *
+ * Horizontal mirroring maps L1 and L2 to the first physical name table and L3 and L4 to the
+ * second as shown in figure 3-4.
+ *
+ *
+ *
+ * Name table 1 |
+ * Name table 1 |
+ *
+ *
+ * Name table 2 |
+ * Name table 2 |
+ *
+ *
+ * Figure 3-4. Horizontal mirroring.
+ *
+ * Vertical mirroring maps L1 and L3 to the first physical name table and L2 and L4 to the
+ * second as shown in figure 3-5.
+ *
+ *
+ *
+ * Name table 1 |
+ * Name table 2 |
+ *
+ *
+ * Name table 1 |
+ * Name table 2 |
+ *
+ *
+ * Figure 3-5. Vertical mirroring.
+ *
+ * @param ppu PPU instance
+ * @param t1 First name table
+ * @param t2 Second name table
+ */
+ public static void fillNameTable(PPU ppu, byte[] t1, byte[] t2) {
+ var vram = ppu.getVram();
+ var ctr = ppu.getControl();
+ var nameTable = ctr.nameTableAddr();
+ var mirror = ppu.getMirrors();
+
+ if (nameTable == NameTMirror.L1
+ || ((mirror == NameMirror.HORIZONTAL && nameTable == NameTMirror.L2)
+ || (mirror == NameMirror.VERTICAL && nameTable == NameTMirror.L3))) {
+ System.arraycopy(vram, 0, t1, 0, 0x400);
+ System.arraycopy(vram, 0x400, t2, 0, 0x400);
+ } else if (nameTable == NameTMirror.L4
+ || ((mirror == NameMirror.VERTICAL && nameTable == NameTMirror.L2)
+ || (mirror == NameMirror.HORIZONTAL && nameTable == NameTMirror.L3))) {
+ System.arraycopy(vram, 0, t2, 0, 0x400);
+ System.arraycopy(vram, 0x400, t1, 0, 0x400);
+ } else {
+ throw new RuntimeException("Not satisfy name table fill condition.");
+ }
+ }
+
+ //
+ //
+ // Sprite zero hits
+ // Sprites are conventionally numbered 0 to 63. Sprite 0 is the sprite controlled by OAM addresses $00-$03, sprite 1 is controlled by $04-$07, ..., and sprite 63 is controlled by $FC-$FF.
+ //
+ // While the PPU is drawing the picture, when an opaque pixel of sprite 0 overlaps an opaque pixel of the background, this is a sprite zero hit. The PPU detects this condition and sets bit 6 of PPUSTATUS ($2002) to 1 starting at this pixel, letting the CPU know how far along the PPU is in drawing the picture.
+ //
+ // Sprite 0 hit does not happen:
+ //
+ // If background or sprite rendering is disabled in PPUMASK ($2001)
+ // At x=0 to x=7 if the left-side clipping window is enabled (if bit 2 or bit 1 of PPUMASK is 0).
+ // At x=255, for an obscure reason related to the pixel pipeline.
+ // At any pixel where the background or sprite pixel is transparent (2-bit color index from the CHR pattern is %00).
+ // If sprite 0 hit has already occurred this frame. Bit 6 of PPUSTATUS ($2002) is cleared to 0 at dot 1 of the pre-render line. This means only the first sprite 0 hit in a frame can be detected.
+ // Sprite 0 hit happens regardless of the following:
+ //
+ // Sprite priority. Sprite 0 can still hit the background from behind.
+ // The pixel colors. Only the CHR pattern bits are relevant, not the actual rendered colors, and any CHR color index except %00 is considered opaque.
+ // The palette. The contents of the palette are irrelevant to sprite 0 hits. For example: a black ($0F) sprite pixel can hit a black ($0F) background as long as neither is the transparent color index %00.
+ // The PAL PPU blanking on the left and right edges at x=0, x=1, and x=254 (see Overscan).
+ //
+ //
+ public static boolean checkSpriteZeroHit(PPU ppu) {
+ var mask = ppu.getMask();
+ //
+ // Set when a nonzero pixel of sprite 0 overlaps a nonzero background pixel;
+ // Sprite 0 hit does not trigger in any area where the background or sprites are hidden.
+ //
+ if (!mask.contain(MaskFlag.SHOW_SPRITES) || !mask.contain(MaskFlag.SHOW_BACKGROUND)) {
+ return false;
+ }
+
+ var oam = ppu.getOam();
+ var tx = oam[3] & 0xff;
+ var ty = oam[0] & 0xff;
+ var idx = oam[1] & 0xff;
+ var attr = oam[2] & 0xff;
+
+ var vf = (attr >> 7 & 1) == 1;
+ var hf = (attr >> 6 & 1) == 1;
+ var ctrl = ppu.getControl();
+ var scroll = ppu.getScroll();
+
+ var size = ctrl.spriteSize();
+ var bank = size == 0x08 ? ctrl.spritePattern8() : ctrl.spritePattern16(idx);
+
+ if (size == 0x10) {
+ idx = idx & 0xfe;
+ }
+ var tile = new byte[16];
+ boolean hit = false;
+ for (int k = 8; k <= size; k += 8) {
+ System.arraycopy(ppu.getCh(), bank + idx * 16, tile, 0, 16);
+ for (int y = 0; y < 8; y++) {
+ var l = tile[y] & 0xff;
+ var r = tile[y + 8];
+ for (int x = 7; x >= 0; x--) {
+ var value = ((r & 0x01) << 1) | l & 0x01;
+
+ l >>= 1;
+ r >>= 1;
+
+ //Detect sprite zero opaque pixels
+ if (value == 0) {
+ continue;
+ }
+ final int x0;
+ final int y0;
+ if (!hf && !vf) {
+ x0 = tx + x;
+ y0 = ty + y;
+ } else if (hf && !vf) {
+ x0 = tx + 7 - x;
+ y0 = ty + y;
+ } else if (!hf && vf) {
+ x0 = tx + x;
+ y0 = ty + 7 - y;
+ } else {
+ x0 = tx + 7 - x;
+ y0 = ty + 7 - y;
+ }
+ //Check x0 and y0 position background
+
+ }
+ }
+ ty += 8;
+ }
+ return hit;
+ }
+}