diff --git a/.gitignore b/.gitignore index bfd9e992..ec5693fa 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,11 @@ error_stack.txt # Ignore special links Martin uses /Platform/Apple/virtual/genBuild /Platform/Apple/virtual/mapBuild -/Platform/Apple/virtual/plasBuild \ No newline at end of file +/Platform/Apple/virtual/plasBuild + +# Hey, don't include the commercial game data you ninny! +/Platform/Apple/tools/jace/src/main/resources/jace/data/game.2mg + +# Ignore NetBeans project files +Platform/Apple/tools/jace/nb-configuration.xml +Platform/Apple/tools/jace/nbactions.xml diff --git a/.java-version b/.java-version new file mode 100644 index 00000000..2f32724e --- /dev/null +++ b/.java-version @@ -0,0 +1 @@ +graalvm64-17.0.6 diff --git a/OutlawEditor/.java-version b/OutlawEditor/.java-version new file mode 100644 index 00000000..62593409 --- /dev/null +++ b/OutlawEditor/.java-version @@ -0,0 +1 @@ +1.8 diff --git a/Platform/Apple/tools/jace/.java-version b/Platform/Apple/tools/jace/.java-version index 62593409..2f32724e 100644 --- a/Platform/Apple/tools/jace/.java-version +++ b/Platform/Apple/tools/jace/.java-version @@ -1 +1 @@ -1.8 +graalvm64-17.0.6 diff --git a/Platform/Apple/tools/jace/nb-configuration.xml b/Platform/Apple/tools/jace/nb-configuration.xml index 4a4218e2..b60ff425 100644 --- a/Platform/Apple/tools/jace/nb-configuration.xml +++ b/Platform/Apple/tools/jace/nb-configuration.xml @@ -14,6 +14,6 @@ That way multiple projects can share the same settings (useful for formatting ru Any value defined here will override the pom.xml file value but is only applicable to the current project. --> HTML5 - JDK_1.8 + GraalVM_JDK_17 diff --git a/Platform/Apple/tools/jace/nbactions.xml b/Platform/Apple/tools/jace/nbactions.xml index e228a7ad..763c4850 100644 --- a/Platform/Apple/tools/jace/nbactions.xml +++ b/Platform/Apple/tools/jace/nbactions.xml @@ -6,15 +6,11 @@ jar - -X - -e - process-classes - org.codehaus.mojo:exec-maven-plugin:1.2.1:exec + clean + javafx:run + + - - -classpath %classpath jace.LawlessLegends - java - profile @@ -22,12 +18,13 @@ jar + clean process-classes - org.codehaus.mojo:exec-maven-plugin:1.2.1:exec + javafx:run@ide-debug - -classpath %classpath jace.LawlessLegends - java + + true @@ -37,12 +34,12 @@ jar - process-classes - org.codehaus.mojo:exec-maven-plugin:1.2.1:exec + clean + javafx:run@ide-debug - -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath jace.LawlessLegends - java + + true diff --git a/Platform/Apple/tools/jace/pom.xml b/Platform/Apple/tools/jace/pom.xml index 469845fa..1cee8b3b 100644 --- a/Platform/Apple/tools/jace/pom.xml +++ b/Platform/Apple/tools/jace/pom.xml @@ -25,86 +25,168 @@ org.apache.maven.plugins - maven-dependency-plugin - 3.1.2 - + maven-compiler-plugin + + 17 + 17 + + 3.11.0 + + + com.gluonhq + gluonfx-maven-plugin + 1.0.18 + + jace.LawlessLegends + + + + org.openjfx + javafx-maven-plugin + 0.0.8 + + lawlesslegends/jace.LawlessLegends + - unpack-dependencies - package - - unpack-dependencies - + + + default-cli + + + + + debug - **/*.SF,**/*.DSA,**/*.RSA - system - junit,org.mockito,org.hamcrest - ${project.build.directory}/classes + + + + + + + + ide-debug + + + + + + + + + ide-profile + + + + + + + + + - org.codehaus.mojo - exec-maven-plugin - 3.0.0 + org.moditect + moditect-maven-plugin + 1.0.0.Final - unpack-dependencies - package + add-module-infos + generate-resources - exec + add-module-info - ${java.home}/../bin/javapackager - - -createjar - -nocss2bin - -appclass - ${mainClass} - -srcdir - ${project.build.directory}/classes - -outdir - ${project.build.directory} - -outfile - ${project.build.finalName}.jar - + + + + org.xerial.thirdparty + nestedvm + 1.0 + + + module nestedvm { + exports org.ibex.nestedvm; + exports org.ibex.nestedvm.util; + } + + + + + org.reflections + reflections + 0.10.2 + + + module reflections { + exports org.reflections; + } + + + + true - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - - 3.8.1 + - - org.reflections - reflections - 0.9.12 - junit junit 4.13.2 test - - - org.eclipse.collections - eclipse-collections - 10.4.0 org.xerial.thirdparty nestedvm 1.0 + + org.openjfx + javafx-base + 20 + jar + + + org.openjfx + javafx-fxml + 20 + jar + + + org.openjfx + javafx-web + 20 + jar + + + org.openjfx + javafx-media + 20 + jar + + + org.openjfx + javafx-graphics + 20 + jar + + + org.openjfx + javafx-swing + 18 + jar + + + org.reflections + reflections + 0.10.2 + jar + diff --git a/Platform/Apple/tools/jace/src/main/java/jace/Emulator.java b/Platform/Apple/tools/jace/src/main/java/jace/Emulator.java index 534822a4..a3100f85 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/Emulator.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/Emulator.java @@ -18,11 +18,15 @@ */ package jace; -import jace.config.Configuration; -import jace.lawless.LawlessComputer; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; + +import jace.config.Configuration; +import jace.core.RAM; +import jace.lawless.LawlessComputer; /** * Created on January 15, 2007, 10:10 PM @@ -47,6 +51,16 @@ public class Emulator { return i; } + public static void abort() { + if (instance != null) { + if (instance.computer != null) { + instance.computer.getMotherboard().suspend(); + instance.computer.getMotherboard().detach(); + } + } + instance = null; + } + public static Emulator getInstance() { if (instance == null) { instance = new Emulator(); @@ -54,10 +68,55 @@ public class Emulator { return instance; } - public static LawlessComputer getComputer() { + private static LawlessComputer getComputer() { return getInstance().computer; } + public static void withComputer(Consumer c) { + LawlessComputer computer = getComputer(); + if (computer != null) { + c.accept(computer); + } else { + System.err.println("No computer available!"); + Thread.dumpStack(); + } + } + + public static T withComputer(Function f, T defaultValue) { + LawlessComputer computer = getComputer(); + if (computer != null) { + return f.apply(computer); + } else { + System.err.println("No computer available!"); + Thread.dumpStack(); + return defaultValue; + } + } + + public static void withMemory(Consumer m) { + withComputer(c->{ + RAM memory = c.getMemory(); + if (memory != null) { + m.accept(memory); + } else { + System.err.println("No memory available!"); + Thread.dumpStack(); + } + }); + } + + public static void withVideo(Consumer v) { + withComputer(c->{ + jace.core.Video video = c.getVideo(); + if (video != null) { + v.accept(video); + } else { + System.err.println("No video available!"); + Thread.dumpStack(); + } + }); + } + /** * Creates a new instance of Emulator */ @@ -65,6 +124,7 @@ public class Emulator { instance = this; computer = new LawlessComputer(); Configuration.buildTree(); + computer.getMotherboard().suspend(); Configuration.loadSettings(); mainThread = Thread.currentThread(); // EmulatorUILogic.registerDebugger(); diff --git a/Platform/Apple/tools/jace/src/main/java/jace/EmulatorUILogic.java b/Platform/Apple/tools/jace/src/main/java/jace/EmulatorUILogic.java index 15c5b3a3..18e7c278 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/EmulatorUILogic.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/EmulatorUILogic.java @@ -18,20 +18,6 @@ */ package jace; -import jace.apple2e.MOS65C02; -import jace.apple2e.RAM128k; -import jace.apple2e.SoftSwitches; -import jace.config.ConfigurableField; -import jace.config.ConfigurationUIController; -import jace.config.InvokableAction; -import jace.config.Reconfigurable; -import jace.core.CPU; -import jace.core.Computer; -import jace.core.Debugger; -import jace.core.RAM; -import jace.core.RAMListener; -import static jace.core.Utility.*; -import jace.ide.IdeController; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -43,15 +29,28 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; + +import jace.apple2e.MOS65C02; +import jace.apple2e.RAM128k; +import jace.apple2e.SoftSwitches; +import jace.config.ConfigurableField; +import jace.config.ConfigurationUIController; +import jace.config.InvokableAction; +import jace.config.Reconfigurable; +import jace.core.Debugger; +import jace.core.RAM; +import jace.core.RAMListener; +import static jace.core.Utility.gripe; +import jace.ide.IdeController; import javafx.application.Platform; import javafx.event.EventHandler; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.scene.control.Label; -import javafx.scene.image.Image; import javafx.scene.input.KeyCombination; import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; @@ -75,7 +74,7 @@ public class EmulatorUILogic implements Reconfigurable { @Override public void updateStatus() { enableDebug(true); - MOS65C02 cpu = (MOS65C02) Emulator.getComputer().getCpu(); + MOS65C02 cpu = (MOS65C02) Emulator.withComputer(c->c.getCpu(), null); updateCPURegisters(cpu); } }; @@ -113,7 +112,7 @@ public class EmulatorUILogic implements Reconfigurable { } public static void enableTrace(boolean b) { - Emulator.getComputer().getCpu().setTraceEnabled(b); + Emulator.withComputer(c->c.getCpu().setTraceEnabled(b)); } public static void stepForward() { @@ -121,7 +120,7 @@ public class EmulatorUILogic implements Reconfigurable { } static void registerDebugger() { - Emulator.getComputer().getCpu().setDebug(debugger); + Emulator.withComputer(c->c.getCpu().setDebug(debugger)); } public static Integer getValidAddress(String s) { @@ -210,14 +209,14 @@ public class EmulatorUILogic implements Reconfigurable { alternatives = "Execute program;Load binary;Load program;Load rom;Play single-load game", defaultKeyMapping = "ctrl+shift+b") public static void runFile() { - Emulator.getComputer().pause(); - FileChooser select = new FileChooser(); - File binary = select.showOpenDialog(LawlessLegends.getApplication().primaryStage); - if (binary == null) { - Emulator.getComputer().resume(); - return; - } - runFileNamed(binary); + Emulator.withComputer(c-> + c.getMotherboard().whileSuspended(()->{ + FileChooser select = new FileChooser(); + File binary = select.showOpenDialog(LawlessLegends.getApplication().primaryStage); + if (binary != null) { + runFileNamed(binary); + } + })); } public static void runFileNamed(File binary) { @@ -232,7 +231,6 @@ public class EmulatorUILogic implements Reconfigurable { } } catch (NumberFormatException | IOException ex) { } - Emulator.getComputer().getCpu().resume(); } public static void brun(File binary, int address) throws IOException { @@ -240,16 +238,19 @@ public class EmulatorUILogic implements Reconfigurable { // If it was not yet halted, then it is the case that the CPU is processing another opcode // So if that is the case, the program counter will need to be decremented here to compensate // TODO: Find a better mousetrap for this one -- it's an ugly hack - Emulator.getComputer().pause(); - FileInputStream in = new FileInputStream(binary); - byte[] data = new byte[in.available()]; - in.read(data); - RAM ram = Emulator.getComputer().getMemory(); - for (int i = 0; i < data.length; i++) { - ram.write(address + i, data[i], false, true); + byte[] data; + try (FileInputStream in = new FileInputStream(binary)) { + data = new byte[in.available()]; + in.read(data); } - Emulator.getComputer().getCpu().setProgramCounter(address); - Emulator.getComputer().resume(); + + Emulator.withComputer(c->c.getMotherboard().whileSuspended(()->{ + RAM ram = c.getMemory(); + for (int i = 0; i < data.length; i++) { + ram.write(address + i, data[i], false, true); + } + c.getCpu().setProgramCounter(address); + })); } @InvokableAction( @@ -293,7 +294,7 @@ public class EmulatorUILogic implements Reconfigurable { SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss"); String timestamp = df.format(new Date()); String type; - int start = Emulator.getComputer().getVideo().getCurrentWriter().actualWriter().getYOffset(0); + int start = Emulator.withComputer(c->c.getVideo().getCurrentWriter().actualWriter().getYOffset(0), 0); int len; if (start < 0x02000) { // Lo-res or double-lores @@ -310,16 +311,23 @@ public class EmulatorUILogic implements Reconfigurable { } File outFile = new File("screen_" + type + "_a" + Integer.toHexString(start) + "_" + timestamp); try (FileOutputStream out = new FileOutputStream(outFile)) { - RAM128k ram = (RAM128k) Emulator.getComputer().memory; - Emulator.getComputer().pause(); - if (dres) { - for (int i = 0; i < len; i++) { - out.write(ram.getAuxVideoMemory().readByte(start + i)); - } - } - for (int i = 0; i < len; i++) { - out.write(ram.getMainMemory().readByte(start + i)); - } + Emulator.withComputer(c -> { + RAM128k ram = (RAM128k) c.getMemory(); + c.getMotherboard().whileSuspended(() -> { + try { + if (dres) { + for (int i = 0; i < len; i++) { + out.write(ram.getAuxVideoMemory().readByte(start + i)); + } + } + for (int i = 0; i < len; i++) { + out.write(ram.getMainMemory().readByte(start + i)); + } + } catch (IOException e) { + Logger.getLogger(EmulatorUILogic.class.getName()).log(Level.SEVERE, "Error writing screenshot", e); + } + }); + }); } System.out.println("Wrote screenshot to " + outFile.getAbsolutePath()); } @@ -332,8 +340,7 @@ public class EmulatorUILogic implements Reconfigurable { defaultKeyMapping = "ctrl+shift+s") public static void saveScreenshot() throws IOException { FileChooser select = new FileChooser(); - Emulator.getComputer().pause(); - Image i = Emulator.getComputer().getVideo().getFrameBuffer(); + // Image i = Emulator.getComputer().getVideo().getFrameBuffer(); // BufferedImage bufImageARGB = SwingFXUtils.fromFXImage(i, null); File targetFile = select.showSaveDialog(LawlessLegends.getApplication().primaryStage); if (targetFile == null) { @@ -421,25 +428,31 @@ public class EmulatorUILogic implements Reconfigurable { } else { int width, height; switch (size) { - case 0: // 1x + case 0 -> { + // 1x width = 560; height = 384; - break; - case 1: // 1.5x + } + case 1 -> { + // 1.5x width = 840; height = 576; - break; - case 2: // 2x + } + case 2 -> { + // 2x width = 560 * 2; height = 384 * 2; - break; - case 3: // 3x (retina) 2880x1800 + } + case 3 -> { + // 3x (retina) 2880x1800 width = 560 * 3; height = 384 * 3; - break; - default: // 2x + } + default -> { + // 2x width = 560 * 2; height = 384 * 2; + } } double vgap = stage.getScene().getY(); double hgap = stage.getScene().getX(); @@ -527,16 +540,17 @@ public class EmulatorUILogic implements Reconfigurable { } public static void simulateCtrlAppleReset() { - Computer computer = LawlessLegends.singleton.controller.computer; - computer.keyboard.openApple(true); - computer.warmStart(); - Platform.runLater(() -> { - try { - Thread.sleep(500); - } catch (InterruptedException ex) { - Logger.getLogger(EmulatorUILogic.class.getName()).log(Level.SEVERE, null, ex); - } - computer.keyboard.openApple(false); + Emulator.withComputer(c -> { + c.getKeyboard().openApple(true); + c.warmStart(); + Platform.runLater(() -> { + try { + Thread.sleep(500); + } catch (InterruptedException ex) { + Logger.getLogger(EmulatorUILogic.class.getName()).log(Level.SEVERE, null, ex); + } + c.getKeyboard().openApple(false); + }); }); } @@ -558,6 +572,8 @@ public class EmulatorUILogic implements Reconfigurable { @Override public void reconfigure() { - LawlessLegends.getApplication().controller.setSpeed(speedSetting); + // Null-safe so there are no errors in unit tests + Optional.ofNullable(LawlessLegends.getApplication()) + .ifPresent(app->app.controller.setSpeed(speedSetting)); } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/JaceUIController.java b/Platform/Apple/tools/jace/src/main/java/jace/JaceUIController.java index 455c79ff..9359c049 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/JaceUIController.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/JaceUIController.java @@ -1,15 +1,26 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package jace; -import com.sun.glass.ui.Application; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + import jace.core.Card; -import jace.core.Computer; import jace.core.Motherboard; import jace.core.Utility; +import jace.core.Video; import jace.lawless.LawlessComputer; import jace.lawless.LawlessHacks; import jace.library.MediaCache; @@ -39,21 +50,18 @@ import javafx.scene.input.DragEvent; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; -import javafx.scene.layout.*; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.CornerRadii; +import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.util.Duration; import javafx.util.StringConverter; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URL; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.Level; -import java.util.logging.Logger; - /** * * @author blurry @@ -90,8 +98,6 @@ public class JaceUIController { @FXML private ComboBox musicSelection; - Computer computer; - private final BooleanProperty aspectRatioCorrectionEnabled = new SimpleBooleanProperty(false); @FXML @@ -117,12 +123,14 @@ public class JaceUIController { rootPane.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null))); rootPane.setOnMouseMoved(this::showMenuButton); rootPane.setOnMouseExited(this::hideControlOverlay); + rootPane.setOnMouseClicked((evt)->rootPane.requestFocus()); menuButton.setOnMouseClicked(this::showControlOverlay); controlOverlay.setOnMouseClicked(this::hideControlOverlay); delayTimer.getKeyFrames().add(new KeyFrame(Duration.millis(3000), evt -> { hideControlOverlay(null); rootPane.requestFocus(); })); + rootPane.requestFocus(); } private void showMenuButton(MouseEvent evt) { @@ -198,15 +206,24 @@ public class JaceUIController { } } - private void connectControls(Stage primaryStage) { + Stage primaryStage; + + public void reconnectKeyboard() { + Emulator.withComputer(computer -> { + if (computer.getKeyboard() != null) { + EventHandler keyboardHandler = computer.getKeyboard().getListener(); + primaryStage.setOnShowing(evt -> computer.getKeyboard().resetState()); + rootPane.setOnKeyPressed(keyboardHandler); + rootPane.setOnKeyReleased(keyboardHandler); + rootPane.setFocusTraversable(true); + } + }); + } + + private void connectControls(Stage ps) { + primaryStage = ps; + connectButtons(controlOverlay); - if (computer.getKeyboard() != null) { - EventHandler keyboardHandler = computer.getKeyboard().getListener(); - primaryStage.setOnShowing(evt -> computer.getKeyboard().resetState()); - rootPane.setOnKeyPressed(keyboardHandler); - rootPane.setOnKeyReleased(keyboardHandler); - rootPane.setFocusTraversable(true); - } speedSlider.setMinorTickCount(0); speedSlider.setMajorTickUnit(1); speedSlider.setLabelFormatter(new StringConverter() { @@ -237,29 +254,33 @@ public class JaceUIController { setSpeed(Emulator.logic.speedSetting); }); musicSelection.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> + Emulator.withComputer(computer -> ((LawlessHacks) ((LawlessComputer) computer).activeCheatEngine).changeMusicScore(String.valueOf(newValue)) + ) ); + reconnectKeyboard(); } private void connectButtons(Node n) { - if (n instanceof Button) { - Button button = (Button) n; + if (n instanceof Button button) { Runnable action = Utility.getNamedInvokableAction(button.getText()); button.setOnMouseClicked(evt -> action.run()); - } else if (n instanceof Parent) { - ((Parent) n).getChildrenUnmodifiable().forEach(child -> connectButtons(child)); + } else if (n instanceof Parent parent) { + parent.getChildrenUnmodifiable().forEach(child -> connectButtons(child)); } } - protected void setSpeed(double speed) { + public void setSpeed(double speed) { Emulator.logic.speedSetting = (int) speed; double speedRatio = convertSpeedToRatio(speed); if (speedSlider.getValue() != speed) { Platform.runLater(()->speedSlider.setValue(speed)); } if (speedRatio >= 100.0) { - Emulator.getComputer().getMotherboard().setMaxSpeed(true); - Emulator.getComputer().getMotherboard().setSpeedInPercentage(20000); + Emulator.withComputer(c -> { + c.getMotherboard().setSpeedInPercentage(20000); + c.getMotherboard().setMaxSpeed(true); + }); // Motherboard.cpuPerClock = 10; } else { if (speedRatio > 1000) { @@ -267,10 +288,12 @@ public class JaceUIController { } else { Motherboard.cpuPerClock = 1; } - Emulator.getComputer().getMotherboard().setMaxSpeed(false); - Emulator.getComputer().getMotherboard().setSpeedInPercentage((int) (speedRatio * 100)); + Emulator.withComputer(c -> { + c.getMotherboard().setMaxSpeed(false); + c.getMotherboard().setSpeedInPercentage((int) (speedRatio * 100)); + }); } - Emulator.getComputer().getMotherboard().reconfigure(); + Emulator.withComputer(c -> c.getMotherboard().reconfigure()); } public void toggleAspectRatio() { @@ -281,19 +304,23 @@ public class JaceUIController { aspectRatioCorrectionEnabled.set(enabled); } - public void connectComputer(Computer computer, Stage primaryStage) { - if (computer == null) { - return; - } - this.computer = computer; + public void connectComputer(Stage primaryStage) { Platform.runLater(() -> { connectControls(primaryStage); - appleScreen.setImage(computer.getVideo().getFrameBuffer()); + Emulator.withVideo(this::connectVideo); appleScreen.setVisible(true); rootPane.requestFocus(); }); } + public void connectVideo(Video video) { + if (video != null) { + appleScreen.setImage(video.getFrameBuffer()); + } else { + appleScreen.setImage(null); + } + } + private void processDragEnteredEvent(DragEvent evt) { MediaEntry media = null; if (evt.getDragboard().hasFiles()) { @@ -355,15 +382,17 @@ public class JaceUIController { }); icon.setOnDragDropped(event -> { System.out.println("Dropping media on " + icon.getText()); - try { - computer.pause(); - consumer.insertMedia(media, media.files.get(0)); - computer.resume(); + Emulator.withComputer(c -> { + c.getMotherboard().whileSuspended(() -> { + try { + consumer.insertMedia(media, media.files.get(0)); + } catch (IOException ex) { + Logger.getLogger(JaceUIController.class.getName()).log(Level.SEVERE, null, ex); + } + }); + }); event.setDropCompleted(true); event.consume(); - } catch (IOException ex) { - Logger.getLogger(JaceUIController.class.getName()).log(Level.SEVERE, null, ex); - } endDragEvent(); }); }); @@ -379,13 +408,15 @@ public class JaceUIController { private List getMediaConsumers() { List consumers = new ArrayList<>(); - consumers.add(Emulator.getComputer().getUpgradeHandler()); + Emulator.withComputer(c -> consumers.add(((LawlessComputer) c).getUpgradeHandler())); if (Emulator.logic.showDrives) { - for (Optional card : computer.memory.getAllCards()) { - card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent -> + Emulator.withMemory(m -> { + for (Optional card : m.getAllCards()) { + card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent -> consumers.addAll(Arrays.asList(((MediaConsumerParent) parent).getConsumers())) - ); - } + ); + } + }); } return consumers; } @@ -398,7 +429,7 @@ public class JaceUIController { public void addIndicator(Label icon, long TTL) { if (!iconTTL.containsKey(icon)) { - Application.invokeLater(() -> { + Platform.runLater(() -> { if (!notificationBox.getChildren().contains(icon)) { notificationBox.getChildren().add(icon); } @@ -408,7 +439,7 @@ public class JaceUIController { } public void removeIndicator(Label icon) { - Application.invokeLater(() -> { + Platform.runLater(() -> { notificationBox.getChildren().remove(icon); iconTTL.remove(icon); }); @@ -453,13 +484,13 @@ public class JaceUIController { notification.setEffect(new DropShadow(2.0, Color.BLACK)); notification.setTextFill(Color.WHITE); notification.setBackground(new Background(new BackgroundFill(Color.rgb(0, 0, 80, 0.7), new CornerRadii(5.0), new Insets(-5.0)))); - Application.invokeLater(() -> { + Platform.runLater(() -> { stackPane.getChildren().remove(oldNotification); stackPane.getChildren().add(notification); }); notificationExecutor.schedule( - () -> Application.invokeLater(() -> stackPane.getChildren().remove(notification)), + () -> Platform.runLater(() -> stackPane.getChildren().remove(notification)), 4, TimeUnit.SECONDS); } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/LawlessLegends.java b/Platform/Apple/tools/jace/src/main/java/jace/LawlessLegends.java index f02ed123..00ae4542 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/LawlessLegends.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/LawlessLegends.java @@ -1,8 +1,16 @@ package jace; +import java.awt.Taskbar; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +import jace.apple2e.Apple2e; import jace.apple2e.SoftSwitches; import jace.apple2e.VideoNTSC; import jace.config.Configuration; +import jace.core.Computer; import jace.core.RAMEvent; import jace.core.RAMListener; import jace.core.Utility; @@ -10,12 +18,14 @@ import jace.hardware.CardDiskII; import jace.hardware.CardRamFactor; import jace.hardware.CardRamworks; import jace.hardware.massStorage.CardMassStorage; +import jace.lawless.LawlessComputer; import jace.lawless.LawlessHacks; import jace.lawless.LawlessImageTool; import jace.lawless.LawlessVideo; import jace.ui.MetacheatUI; import javafx.application.Application; import javafx.application.Platform; +import javafx.embed.swing.SwingFXUtils; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.scene.layout.AnchorPane; @@ -24,10 +34,6 @@ import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.stage.StageStyle; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - /** * * @author blurry @@ -54,8 +60,18 @@ public class LawlessLegends extends Application { Scene s = new Scene(node); s.setFill(Color.BLACK); primaryStage.setScene(s); - primaryStage.setTitle("Lawless Legends"); - Utility.loadIcon("game_icon.png").ifPresent(primaryStage.getIcons()::add); + primaryStage.titleProperty().set("Lawless Legends"); + Utility.loadIcon("game_icon.png").ifPresent(icon -> { + primaryStage.getIcons().add(icon); + try { + //set icon for mac os (and other systems which do support this method) + Taskbar.getTaskbar().setIconImage(SwingFXUtils.fromFXImage(icon, null)); + } catch (final UnsupportedOperationException e) { + System.out.println("The os does not support: 'taskbar.setIconImage'"); + } catch (final SecurityException e) { + System.out.println("There was a security exception for: 'taskbar.setIconImage'"); + } + }); } catch (IOException exception) { throw new RuntimeException(exception); } @@ -63,23 +79,30 @@ public class LawlessLegends extends Application { primaryStage.show(); Platform.runLater(() -> new Thread(() -> { Emulator.getInstance(getParameters().getRaw()); + Emulator.withComputer(c->((LawlessComputer)c).initLawlessLegendsConfiguration()); configureEmulatorForGame(); reconnectUIHooks(); EmulatorUILogic.scaleIntegerRatio(); - while (Emulator.getComputer().getVideo() == null || Emulator.getComputer().getVideo().getFrameBuffer() == null) { - Thread.yield(); + AtomicBoolean waitingForVideo = new AtomicBoolean(true); + while (waitingForVideo.get()) { + Emulator.withVideo(v -> { + if (v.getFrameBuffer() != null) { + waitingForVideo.set(false); + } + }); + Thread.onSpinWait(); } bootWatchdog(); }).start()); primaryStage.setOnCloseRequest(event -> { - Emulator.getComputer().deactivate(); + Emulator.withComputer(Computer::deactivate); Platform.exit(); System.exit(0); }); } public void reconnectUIHooks() { - controller.connectComputer(Emulator.getComputer(), primaryStage); + controller.connectComputer(primaryStage); } public static LawlessLegends getApplication() { @@ -123,51 +146,76 @@ public class LawlessLegends extends Application { */ private void bootWatchdog() { romStarted = false; - if (Emulator.getComputer().PRODUCTION_MODE) { - RAMListener startListener = Emulator.getComputer().getMemory(). - observe(RAMEvent.TYPE.EXECUTE, 0x0c700, (e) -> romStarted = true); - Emulator.getComputer().invokeColdStart(); - try { - Thread.sleep(7500); - if (!romStarted) { - Logger.getLogger(getClass().getName()).log(Level.WARNING, "Boot not detected, performing a cold start"); - Emulator.getComputer().invokeColdStart(); - } - } catch (InterruptedException ex) { - Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex); + Emulator.withComputer(c -> { + if (c.PRODUCTION_MODE) { + new Thread(()->{ + Logger.getLogger(getClass().getName()).log(Level.WARNING, "Booting with watchdog"); + RAMListener startListener = c.getMemory(). + observe(RAMEvent.TYPE.EXECUTE, 0x2000, (e) -> { + Logger.getLogger(getClass().getName()).log(Level.WARNING, "Boot was detected, watchdog terminated."); + romStarted = true; + }); + c.invokeColdStart(); + try { + Thread.sleep(6500); + if (!romStarted) { + Logger.getLogger(getClass().getName()).log(Level.WARNING, "Boot not detected, performing a cold start"); + resetEmulator(); + configureEmulatorForGame(); + bootWatchdog(); + // Emulator.getComputer().getCpu().trace=true; + } else { + c.getMemory().removeListener(startListener); + } + } catch (InterruptedException ex) { + Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex); + } + }).start(); + } else { + romStarted = true; + c.invokeColdStart(); } - Emulator.getComputer().getMemory().removeListener(startListener); - } else { - romStarted = true; - Emulator.getComputer().invokeColdStart(); - } + }); + } + + public void resetEmulator() { + // Reset the emulator memory and reconfigure + Emulator.withComputer(c -> { + c.pause(); + c.getMemory().resetState(); + c.reconfigure(); + c.resume(); + }); } private void configureEmulatorForGame() { - Emulator.getComputer().enableHints = false; - Emulator.getComputer().clockEnabled = true; - Emulator.getComputer().joy1enabled = false; - Emulator.getComputer().joy2enabled = false; - Emulator.getComputer().enableStateManager = false; - Emulator.getComputer().ramCard.setValue(CardRamworks.class); - Emulator.getComputer().videoRenderer.setValue(LawlessVideo.class); - if (Emulator.getComputer().PRODUCTION_MODE) { - Emulator.getComputer().card7.setValue(CardMassStorage.class); - Emulator.getComputer().card6.setValue(CardDiskII.class); - Emulator.getComputer().card5.setValue(CardRamFactor.class); - Emulator.getComputer().card4.setValue(null); - Emulator.getComputer().card2.setValue(null); - } - Emulator.getComputer().cheatEngine.setValue(LawlessHacks.class); - Configuration.buildTree(); - Emulator.getComputer().reconfigure(); - VideoNTSC.setVideoMode(VideoNTSC.VideoMode.TextFriendly, false); - if (Emulator.getComputer().PRODUCTION_MODE) { - ((LawlessImageTool) Emulator.getComputer().getUpgradeHandler()).loadGame(); - } else { - for (SoftSwitches s : SoftSwitches.values()) { - s.getSwitch().reset(); + Emulator.withComputer(c -> { + c.enableHints = false; + c.clockEnabled = true; + c.joy1enabled = false; + c.joy2enabled = false; + c.enableStateManager = false; + c.ramCard.setValue(CardRamworks.class); + c.videoRenderer.setValue(LawlessVideo.class); + if (c.PRODUCTION_MODE) { + c.card7.setValue(CardMassStorage.class); + c.card6.setValue(CardDiskII.class); + c.card5.setValue(CardRamFactor.class); + c.card4.setValue(null); + c.card2.setValue(null); + c.getMemory().writeWord(0x03f0, 0x0c700, false, false); } - } + c.cheatEngine.setValue(LawlessHacks.class); + Configuration.buildTree(); + c.reconfigure(); + VideoNTSC.setVideoMode(VideoNTSC.VideoMode.TextFriendly, false); + if (c.PRODUCTION_MODE) { + ((LawlessImageTool) c.getUpgradeHandler()).loadGame(); + } else { + for (SoftSwitches s : SoftSwitches.values()) { + s.getSwitch().reset(); + } + } + }); } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Apple2e.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Apple2e.java index 368f0fc2..4d237945 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Apple2e.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Apple2e.java @@ -18,21 +18,13 @@ */ package jace.apple2e; -import jace.LawlessLegends; -import jace.apple2e.softswitch.VideoSoftSwitch; -import jace.cheat.Cheats; -import jace.config.ClassSelection; -import jace.config.ConfigurableField; -import jace.core.*; -import jace.hardware.*; -import jace.hardware.massStorage.CardMassStorage; -import jace.lawless.FPSMonitorDevice; -import jace.lawless.LawlessVideo; -import jace.state.Stateful; - import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -40,6 +32,32 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import jace.LawlessLegends; +import jace.apple2e.softswitch.VideoSoftSwitch; +import jace.cheat.Cheats; +import jace.config.ClassSelection; +import jace.config.ConfigurableField; +import jace.core.Card; +import jace.core.Computer; +import jace.core.Device; +import jace.core.Motherboard; +import jace.core.RAM; +import jace.core.RAMEvent; +import jace.core.RAMListener; +import jace.core.Utility; +import jace.core.Video; +import jace.hardware.CardDiskII; +import jace.hardware.CardExt80Col; +import jace.hardware.CardRamworks; +import jace.hardware.ConsoleProbe; +import jace.hardware.Joystick; +import jace.hardware.NoSlotClock; +import jace.hardware.ZipWarpAccelerator; +import jace.hardware.massStorage.CardMassStorage; +import jace.lawless.FPSMonitorDevice; +import jace.lawless.LawlessVideo; +import jace.state.Stateful; + /** * Apple2e is a computer with a 65c02 CPU, 128k of bankswitched ram, * double-hires graphics, and up to seven peripheral I/O cards installed. Pause @@ -173,7 +191,7 @@ public class Apple2e extends Computer { if (getMemory().getCard(slot).isPresent()) { if (getMemory().getCard(slot).get().getClass().equals(type)) { return; - } + } getMemory().removeCard(slot); } if (type != null) { @@ -214,13 +232,12 @@ public class Apple2e extends Computer { return; } motherboard.whileSuspended(()-> { - if (getMemory() != null) { - for (SoftSwitches s : SoftSwitches.values()) { - s.getSwitch().unregister(); - } - } if (!isMemoryConfigurationCorrect()) { try { + if (getVideo() != null) { + getVideo().suspend(); + } + setVideo(null); System.out.println("Creating new ram using " + getDesiredMemoryConfiguration().getName()); RAM128k newMemory = getDesiredMemoryConfiguration().getConstructor(Computer.class).newInstance(this); @@ -232,15 +249,12 @@ public class Apple2e extends Computer { Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex); } } - for (SoftSwitches s : SoftSwitches.values()) { - s.getSwitch().register(this); - } try { if (useDebugRom) { - loadRom("jace/data/apple2e_debug.rom"); + loadRom("/jace/data/apple2e_debug.rom"); } else { - loadRom("jace/data/apple2e.rom"); + loadRom("/jace/data/apple2e.rom"); } } catch (IOException ex) { Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex); @@ -331,15 +345,18 @@ public class Apple2e extends Computer { if (cheatEngine.getValue() == null) { if (activeCheatEngine != null) { activeCheatEngine.detach(); - newDeviceSet.add(activeCheatEngine); + activeCheatEngine.suspend(); + activeCheatEngine = null; } - activeCheatEngine = null; } else { boolean startCheats = true; if (activeCheatEngine != null) { if (activeCheatEngine.getClass().equals(cheatEngine.getValue())) { startCheats = false; + newDeviceSet.add(activeCheatEngine); } else { + activeCheatEngine.detach(); + activeCheatEngine.suspend(); activeCheatEngine = null; } } @@ -354,7 +371,7 @@ public class Apple2e extends Computer { } newDeviceSet.add(cpu); - newDeviceSet.add(video); + newDeviceSet.add(getVideo()); for (Optional c : getMemory().getAllCards()) { c.ifPresent(newDeviceSet::add); } @@ -481,6 +498,7 @@ public class Apple2e extends Computer { private void disableHints() { hints.forEach((hint) -> getMemory().removeListener(hint)); + hints.clear(); } @Override diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/MOS65C02.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/MOS65C02.java index 8a1acf10..a088baa4 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/MOS65C02.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/MOS65C02.java @@ -1075,33 +1075,31 @@ public class MOS65C02 extends CPU { int bytes; int n = op & 0x0f; switch (n) { - case 2: + case 2 -> { bytes = 2; wait = 2; - break; - case 3: - case 7: - case 0x0b: - case 0x0f: + } + case 3, 7, 0x0b, 0x0f -> { wait = 1; bytes = 1; - break; - case 4: + } + case 4 -> { bytes = 2; if ((op & 0x0f0) == 0x040) { wait = 3; } else { wait = 4; - } break; - case 0x0c: + } + } + case 0x0c -> { bytes = 3; if ((op & 0x0f0) == 0x050) { wait = 8; } else { wait = 4; - } break; - default: - bytes = 2; + } + } + default -> bytes = 2; } incrementProgramCounter(bytes); addWaitCycles(wait); @@ -1230,10 +1228,8 @@ public class MOS65C02 extends CPU { // N = true; // V = true; // Z = true; - int newPC = getMemory().readWord(RESET_VECTOR, TYPE.READ_DATA, true, false); - if (Emulator.getComputer().PRODUCTION_MODE) { - newPC = 0x0C700; - } + int resetVector = getMemory().readWord(RESET_VECTOR, TYPE.READ_DATA, true, false); + int newPC = Emulator.withComputer(c->c.PRODUCTION_MODE ? 0x0C700 : resetVector, resetVector); LOG.log(Level.WARNING, "Reset called, setting PC to ({0}) = {1}", new Object[]{Integer.toString(RESET_VECTOR, 16), Integer.toString(newPC, 16)}); setProgramCounter(newPC); } @@ -1347,16 +1343,13 @@ public class MOS65C02 extends CPU { case 0x65: // CPU functions switch (param2 & 0x0ff) { - case 0x00: - // Turn off tracing + case 0x00 -> // Turn off tracing trace = false; - break; - case 0x01: - // Turn on tracing + case 0x01 -> // Turn on tracing trace = true; - break; } break; + case 0x64: // Memory functions getMemory().performExtendedCommand(param2 & 0x0ff); diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/RAM128k.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/RAM128k.java index e04fee54..18817d79 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/RAM128k.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/RAM128k.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.Semaphore; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -157,8 +156,6 @@ abstract public class RAM128k extends RAM { } } - private final Semaphore configurationSemaphore = new Semaphore(1, true); - public String getReadConfiguration() { String rstate = ""; if (SoftSwitches.RAMRD.getState()) { @@ -374,8 +371,7 @@ abstract public class RAM128k extends RAM { * */ @Override - public void configureActiveMemory() { - + public synchronized void configureActiveMemory() { String auxZpConfiguration = getAuxZPConfiguration(); String readConfiguration = getReadConfiguration() + auxZpConfiguration; String writeConfiguration = getWriteConfiguration() + auxZpConfiguration; @@ -385,27 +381,20 @@ abstract public class RAM128k extends RAM { } state = newState; - try { - log("MMU Switches"); - configurationSemaphore.acquire(); + log("MMU Switches"); - if (memoryConfigurations.containsKey(readConfiguration)) { - activeRead = memoryConfigurations.get(readConfiguration); - } else { - activeRead = buildReadConfiguration(); - memoryConfigurations.put(readConfiguration, activeRead); - } + if (memoryConfigurations.containsKey(readConfiguration)) { + activeRead = memoryConfigurations.get(readConfiguration); + } else { + activeRead = buildReadConfiguration(); + memoryConfigurations.put(readConfiguration, activeRead); + } - if (memoryConfigurations.containsKey(writeConfiguration)) { - activeWrite = memoryConfigurations.get(writeConfiguration); - } else { - activeWrite = buildWriteConfiguration(); - memoryConfigurations.put(writeConfiguration, activeWrite); - } - - configurationSemaphore.release(); - } catch (InterruptedException ex) { - Logger.getLogger(RAM128k.class.getName()).log(Level.SEVERE, null, ex); + if (memoryConfigurations.containsKey(writeConfiguration)) { + activeWrite = memoryConfigurations.get(writeConfiguration); + } else { + activeWrite = buildWriteConfiguration(); + memoryConfigurations.put(writeConfiguration, activeWrite); } } @@ -443,11 +432,13 @@ abstract public class RAM128k extends RAM { activeWrite.setBanks(0, cPageRom.getMemory().length, 0x011, cPageRom); activeWrite.setBanks(0, rom.getMemory().length, 0x020, rom); //---------------------- - InputStream inputRom = getClass().getClassLoader().getResourceAsStream(path); + InputStream inputRom = getClass().getResourceAsStream(path); if (inputRom == null) { - LOG.log(Level.SEVERE, "Rom not found: " + path); + LOG.log(Level.SEVERE, "Rom not found: {0}", path); return; } + // Clear cached configurations as we might have outdated references now + memoryConfigurations.clear(); int read = 0; int addr = 0; byte[] in = new byte[1024]; @@ -458,11 +449,9 @@ abstract public class RAM128k extends RAM { } // System.out.println("Finished reading rom with " + inputRom.available() + " bytes left unread!"); //dump(); - // Clear cached configurations as we might have outdated references now for (int i = 0; i < 17; i++) { activeWrite.set(i, restore[i]); } - memoryConfigurations.clear(); configureActiveMemory(); } @@ -523,4 +512,9 @@ abstract public class RAM128k extends RAM { // Clear cached configurations as we might have outdated references now memoryConfigurations.clear(); } + + @Override + public void resetState() { + memoryConfigurations.clear(); + } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/SoftSwitches.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/SoftSwitches.java index 1b63f99f..97baa17d 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/SoftSwitches.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/SoftSwitches.java @@ -18,6 +18,7 @@ */ package jace.apple2e; +import jace.Emulator; import jace.apple2e.softswitch.IntC8SoftSwitch; import jace.apple2e.softswitch.KeyboardSoftSwitch; import jace.apple2e.softswitch.Memory2SoftSwitch; @@ -83,9 +84,9 @@ public enum SoftSwitches { // PAGE2 is a hybrid switch; 80STORE ? memory : video if (_80STORE.isOn()) { - computer.getMemory().configureActiveMemory(); + Emulator.withMemory(m->m.configureActiveMemory()); } else { - computer.getVideo().configureVideoMode(); + Emulator.withVideo(v->v.configureVideoMode()); } } }), @@ -102,7 +103,7 @@ public enum SoftSwitches { @Override protected byte readSwitch() { setState(true); - return computer.getVideo().getFloatingBus(); + return Emulator.withComputer(c->c.getVideo().getFloatingBus(), (byte) 0); } @Override @@ -129,7 +130,7 @@ public enum SoftSwitches { KEYBOARD_STROBE_READ(new SoftSwitch("KeyStrobe_Read", 0x0c010, -1, -1, RAMEvent.TYPE.READ, false) { @Override protected byte readSwitch() { - return computer.getVideo().getFloatingBus(); + return Emulator.withComputer(c->c.getVideo().getFloatingBus(), (byte) 0); } @Override @@ -142,10 +143,7 @@ public enum SoftSwitches { FLOATING_BUS(new SoftSwitch("FloatingBus", null, null, new int[]{0x0C050, 0x0C051, 0x0C052, 0x0C053, 0x0C054}, RAMEvent.TYPE.READ, null) { @Override protected byte readSwitch() { - if (computer.getVideo() == null) { - return 0; - } - return computer.getVideo().getFloatingBus(); + return Emulator.withComputer(c->c.getVideo().getFloatingBus(), (byte) 0); } @Override diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java index a82ad212..23b96631 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java @@ -18,19 +18,29 @@ */ package jace.apple2e; -import jace.LawlessLegends; -import jace.config.ConfigurableField; -import jace.core.*; -import javafx.stage.FileChooser; - -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.SourceDataLine; -import java.io.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.util.Timer; -import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; +import javax.sound.sampled.SourceDataLine; + +import jace.Emulator; +import jace.LawlessLegends; +import jace.config.ConfigurableField; +import jace.config.InvokableAction; +import jace.core.Computer; +import jace.core.Motherboard; +import jace.core.RAMEvent; +import jace.core.RAMListener; +import jace.core.SoundGeneratorDevice; +import jace.core.SoundMixer; +import javafx.stage.FileChooser; + /** * Apple // Speaker Emulation Created on May 9, 2007, 9:55 PM * @@ -40,7 +50,11 @@ public class Speaker extends SoundGeneratorDevice { static boolean fileOutputActive = false; static OutputStream out; + + @ConfigurableField(category = "sound", name = "1mhz timing", description = "Force speaker output to 1mhz?") + public static boolean force1mhz = true; + @InvokableAction(category = "sound", name = "Record sound", description="Toggles recording (saving) sound output to a file", defaultKeyMapping = "ctrl+shift+w") public static void toggleFileOutput() { if (fileOutputActive) { try { @@ -56,12 +70,6 @@ public class Speaker extends SoundGeneratorDevice { if (f == null) { return; } -// if (f.exists()) { -// int i = JOptionPane.showConfirmDialog(null, "Overwrite existing file?"); -// if (i != JOptionPane.OK_OPTION && i != JOptionPane.YES_OPTION) { -// return; -// } -// } try { out = new FileOutputStream(f); fileOutputActive = true; @@ -70,6 +78,7 @@ public class Speaker extends SoundGeneratorDevice { } } } + /** * Counter tracks the number of cycles between sampling */ @@ -87,9 +96,6 @@ public class Speaker extends SoundGeneratorDevice { * Number of samples in buffer */ static int BUFFER_SIZE = (int) (SoundMixer.RATE * 0.4); - // Number of samples available in output stream before playback happens (avoid extra blocking) -// static int MIN_PLAYBACK_BUFFER = BUFFER_SIZE / 2; - static int MIN_PLAYBACK_BUFFER = 64; /** * Playback volume (should be < 1423) */ @@ -122,8 +128,8 @@ public class Speaker extends SoundGeneratorDevice { private byte[] secondaryBuffer; private int bufferPos = 0; private Timer playbackTimer; - private final double TICKS_PER_SAMPLE = ((double) Motherboard.SPEED) / SoundMixer.RATE; - private final double TICKS_PER_SAMPLE_FLOOR = Math.floor(TICKS_PER_SAMPLE); + private double TICKS_PER_SAMPLE = ((double) Motherboard.DEFAULT_SPEED) / SoundMixer.RATE; + private double TICKS_PER_SAMPLE_FLOOR = Math.floor(TICKS_PER_SAMPLE); private RAMListener listener = null; /** @@ -145,13 +151,14 @@ public class Speaker extends SoundGeneratorDevice { boolean result = super.suspend(); if (playbackTimer != null) { playbackTimer.cancel(); + playbackTimer = null; } speakerBit = false; - sdl = null; - if (computer.getMotherboard() != null) { - computer.getMotherboard().cancelSpeedRequest(this); - computer.mixer.returnLine(this); + if (sdl != null && sdl.isOpen()) { + sdl.stop(); + sdl.close(); } + sdl = null; return result; } @@ -161,29 +168,29 @@ public class Speaker extends SoundGeneratorDevice { */ @Override public void resume() { - if (sdl != null && isRunning()) { - return; - } - try { - if (sdl == null || !sdl.isOpen()) { - sdl = computer.mixer.getLine(this); + if (sdl == null || !sdl.isOpen()) { + sdl = computer.mixer.getLine(); + if (sdl != null) { + sdl.start(); + counter = 0; + idleCycles = 0; + level = 0; + bufferPos = 0; + } else { + Logger.getLogger(getClass().getName()).severe("Unable to get audio line for speaker!"); + detach(); + return; } - sdl.start(); - setRun(true); - counter = 0; - idleCycles = 0; - level = 0; - bufferPos = 0; - playbackTimer = new Timer(); - playbackTimer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - playCurrentBuffer(); - } - }, 10, 30); - } catch (LineUnavailableException ex) { - Logger.getLogger(getClass().getName()).log(Level.SEVERE, "ERROR: Could not output sound", ex); } + + if (force1mhz) { + TICKS_PER_SAMPLE = ((double) Motherboard.DEFAULT_SPEED) / SoundMixer.RATE; + } else { + TICKS_PER_SAMPLE = Emulator.withComputer(c-> ((double) c.getMotherboard().getSpeedInHz()) / SoundMixer.RATE, 0.0); + } + TICKS_PER_SAMPLE_FLOOR = Math.floor(TICKS_PER_SAMPLE); + + setRun(true); } public void playCurrentBuffer() { @@ -196,7 +203,7 @@ public class Speaker extends SoundGeneratorDevice { bufferPos = 0; } secondaryBuffer = buffer; - if (sdl != null && buffer != null) { + if (sdl != null && len > 0) { sdl.write(buffer, 0, len); } } @@ -218,9 +225,6 @@ public class Speaker extends SoundGeneratorDevice { */ @Override public void tick() { - if (!isRunning() || sdl == null) { - return; - } if (idleCycles++ >= MAX_IDLE_CYCLES) { suspend(); } @@ -229,22 +233,7 @@ public class Speaker extends SoundGeneratorDevice { } counter += 1.0d; if (counter >= TICKS_PER_SAMPLE) { - int sample = level * VOLUME; - int bytes = SoundMixer.BITS >> 3; - int shift = SoundMixer.BITS; - - while (bufferPos >= primaryBuffer.length) { - Thread.yield(); - } - synchronized (bufferLock) { - int index = bufferPos; - for (int i = 0; i < SoundMixer.BITS; i += 8, index++) { - shift -= 8; - primaryBuffer[index] = primaryBuffer[index + bytes] = (byte) ((sample >> shift) & 0x0ff); - } - - bufferPos += bytes * 2; - } + playSample(level * VOLUME); // Set level back to 0 level = 0; @@ -256,11 +245,33 @@ public class Speaker extends SoundGeneratorDevice { private void toggleSpeaker(RAMEvent e) { if (e.getType() == RAMEvent.TYPE.WRITE) { level += 2; - } else { - speakerBit = !speakerBit; } + speakerBit = !speakerBit; resetIdle(); } + + private void playSample(int sample) { + if (sdl == null || !sdl.isOpen()) { + resume(); + } + int bytes = SoundMixer.BITS >> 3; + + // Prepare sound output in little endian format + for (int i = 0; i < bytes; i++) { + primaryBuffer[i] = primaryBuffer[i+bytes] = (byte) (sample & 0x0ff); + sample >>= 8; + } + sdl.write(primaryBuffer, 0, bytes*2); + if (fileOutputActive) { + try { + out.write(primaryBuffer, 0, bytes*2); + } catch (IOException ex) { + Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Error recording sound", ex); + toggleFileOutput(); + } + } + + } /** * Add a memory event listener for C03x for capturing speaker events diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java index ce1c6464..9a2bb0bf 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java @@ -18,13 +18,18 @@ */ package jace.apple2e; -import jace.core.*; +import java.util.logging.Logger; + +import jace.core.Computer; +import jace.core.Font; +import jace.core.Palette; +import jace.core.RAMEvent; +import jace.core.Video; +import jace.core.VideoWriter; import javafx.scene.image.PixelWriter; import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; -import java.util.logging.Logger; - /** * This is the primary video rendering class, which provides all necessary video * writers for every display mode as well as managing the display mode (via @@ -430,7 +435,7 @@ public class VideoDHGR extends Video { int b4 = ((RAM128k) computer.getMemory()).getMainMemory() .readByte(rowAddress + xOffset + 1); int useColOffset = xOffset << 1; // This shouldn't be necessary but prevents an index bounds exception when graphics modes are flipped (Race condition?) - if (useColOffset >= 77) { + if (useColOffset >= 77 || useColOffset < 0) { useColOffset = 76; } useColor[useColOffset ] = (b1 & 0x80) != 0; diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java index 3289cc2e..a1c38d78 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java @@ -18,6 +18,10 @@ */ package jace.apple2e; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + import jace.Emulator; import jace.EmulatorUILogic; import jace.config.ConfigurableField; @@ -30,10 +34,6 @@ import javafx.scene.image.PixelWriter; import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - /** * Provides a clean color monitor simulation, complete with text-friendly * palette and mixed color/bw (mode 7) rendering. This class extends the @@ -94,51 +94,51 @@ public class VideoNTSC extends VideoDHGR { defaultKeyMapping = {"ctrl+shift+g"}) public static void changeVideoMode() { currentMode = (currentMode + 1) % VideoMode.values().length; - ((VideoNTSC) Emulator.getComputer().getVideo())._setVideoMode(VideoMode.values()[currentMode], true); + Emulator.withVideo(v->((VideoNTSC) v)._setVideoMode(VideoMode.values()[currentMode], true)); } public static void setVideoMode(VideoMode newMode, boolean showNotification) { - ((VideoNTSC) Emulator.getComputer().getVideo())._setVideoMode(newMode, showNotification); + Emulator.withVideo(v->((VideoNTSC) v)._setVideoMode(newMode, showNotification)); } private void _setVideoMode(VideoMode newMode, boolean showNotification) { - VideoNTSC thiss = (VideoNTSC) Emulator.getComputer().video; - thiss.monochomeMode = false; - WHITE = Color.WHITE; - switch (newMode) { - case Amber: - thiss.monochomeMode = true; - WHITE = Color.web("ff8000"); - break; - case Greenscreen: - thiss.monochomeMode = true; - WHITE = Color.web("0ccc68"); - break; - case Monochrome: - thiss.monochomeMode = true; - break; - case Color: - thiss.useTextPalette = false; - thiss.enableVideo7 = false; - break; - case Mode7: - thiss.useTextPalette = false; - thiss.enableVideo7 = true; - break; - case Mode7TextFriendly: - thiss.useTextPalette = true; - thiss.enableVideo7 = true; - break; - case TextFriendly: - thiss.useTextPalette = true; - thiss.enableVideo7 = false; - break; - } - thiss.activePalette = thiss.useTextPalette ? TEXT_PALETTE : SOLID_PALETTE; - if (showNotification) { - EmulatorUILogic.notify("Video mode: " + newMode.name); - } - forceRefresh(); + Emulator.withVideo(v-> { + VideoNTSC thiss = (VideoNTSC) v; + thiss.monochomeMode = false; + WHITE = Color.WHITE; + switch (newMode) { + case Amber -> { + thiss.monochomeMode = true; + WHITE = Color.web("ff8000"); + } + case Greenscreen -> { + thiss.monochomeMode = true; + WHITE = Color.web("0ccc68"); + } + case Monochrome -> thiss.monochomeMode = true; + case Color -> { + thiss.useTextPalette = false; + thiss.enableVideo7 = false; + } + case Mode7 -> { + thiss.useTextPalette = false; + thiss.enableVideo7 = true; + } + case Mode7TextFriendly -> { + thiss.useTextPalette = true; + thiss.enableVideo7 = true; + } + case TextFriendly -> { + thiss.useTextPalette = true; + thiss.enableVideo7 = false; + } + } + thiss.activePalette = thiss.useTextPalette ? TEXT_PALETTE : SOLID_PALETTE; + if (showNotification) { + EmulatorUILogic.notify("Video mode: " + newMode.name); + } + forceRefresh(); + }); } @Override diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/IntC8SoftSwitch.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/IntC8SoftSwitch.java index 385ce419..edf81787 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/IntC8SoftSwitch.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/IntC8SoftSwitch.java @@ -18,6 +18,7 @@ */ package jace.apple2e.softswitch; +import jace.Emulator; import jace.apple2e.SoftSwitches; import jace.core.RAMEvent; import jace.core.RAMListener; @@ -76,8 +77,6 @@ public class IntC8SoftSwitch extends SoftSwitch { @Override public void stateChanged() { - if (computer.getMemory() != null) { - computer.getMemory().configureActiveMemory(); - } + Emulator.withMemory(m->m.configureActiveMemory()); } } \ No newline at end of file diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/MemorySoftSwitch.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/MemorySoftSwitch.java index 811970e2..ba3bf8d6 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/MemorySoftSwitch.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/MemorySoftSwitch.java @@ -18,6 +18,7 @@ */ package jace.apple2e.softswitch; +import jace.Emulator; import jace.core.RAMEvent; import jace.core.SoftSwitch; @@ -40,15 +41,13 @@ public class MemorySoftSwitch extends SoftSwitch { @Override public void stateChanged() { // System.out.println(getName()+ " was switched to "+getState()); - if (computer.getMemory() != null) { - computer.getMemory().configureActiveMemory(); - } + Emulator.withMemory(m->m.configureActiveMemory()); } // Todo: Implement floating bus, maybe? @Override protected byte readSwitch() { - byte value = computer.getVideo().getFloatingBus(); + byte value = Emulator.withComputer(c->c.getVideo().getFloatingBus(), (byte) 0); if (getState()) { return (byte) (value | 0x080); } else { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/VideoSoftSwitch.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/VideoSoftSwitch.java index 7413cb70..dfc219d6 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/VideoSoftSwitch.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/VideoSoftSwitch.java @@ -18,6 +18,7 @@ */ package jace.apple2e.softswitch; +import jace.Emulator; import jace.core.RAMEvent; import jace.core.SoftSwitch; @@ -40,9 +41,7 @@ public class VideoSoftSwitch extends SoftSwitch { @Override public void stateChanged() { // System.out.println("Set "+getName()+" -> "+getState()); - if (computer.getVideo() != null) { - computer.getVideo().configureVideoMode(); - } + Emulator.withVideo(video -> video.configureVideoMode()); } @Override diff --git a/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftHandler.java b/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftHandler.java index b8d10fcc..f962fa64 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftHandler.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftHandler.java @@ -17,7 +17,7 @@ public class ApplesoftHandler implements LanguageHandler { @Override public String getNewDocumentContent() { - return ApplesoftProgram.fromMemory(Emulator.getComputer().getMemory()).toString(); + return Emulator.withComputer(c->ApplesoftProgram.fromMemory(c.getMemory()).toString(), ""); } @Override diff --git a/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftProgram.java b/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftProgram.java index 90888482..ed421b99 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftProgram.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftProgram.java @@ -18,10 +18,6 @@ */ package jace.applesoft; -import jace.Emulator; -import jace.core.RAM; -import jace.core.RAMEvent; -import jace.core.RAMListener; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -33,6 +29,11 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import jace.Emulator; +import jace.core.RAM; +import jace.core.RAMEvent; +import jace.core.RAMListener; + /** * Decode an applesoft program into a list of program lines Right now this is an * example/test program but it successfully tokenized the source of Lemonade @@ -149,70 +150,70 @@ public class ApplesoftProgram { } public void run() { - RAM memory = Emulator.getComputer().memory; - Emulator.getComputer().pause(); - int programStart = memory.readWordRaw(START_OF_PROG_POINTER); - int programEnd = programStart + getProgramSize(); - if (isProgramRunning()) { - whenReady(()->{ - relocateVariables(programEnd); + Emulator.withComputer(c->c.getMotherboard().whileSuspended(()->{ + int programStart = c.getMemory().readWordRaw(START_OF_PROG_POINTER); + int programEnd = programStart + getProgramSize(); + if (isProgramRunning()) { + whenReady(()->{ + relocateVariables(programEnd); + injectProgram(); + }); + } else { injectProgram(); - }); - } else { - injectProgram(); - clearVariables(programEnd); - } - Emulator.getComputer().resume(); + clearVariables(programEnd); + } + })); } private void injectProgram() { - RAM memory = Emulator.getComputer().memory; - int pos = memory.readWordRaw(START_OF_PROG_POINTER); - for (Line line : lines) { - int nextPos = pos + line.getLength(); - memory.writeWord(pos, nextPos, false, true); - pos += 2; - memory.writeWord(pos, line.getNumber(), false, true); - pos += 2; - boolean isFirst = true; - for (Command command : line.getCommands()) { - if (!isFirst) { - memory.write(pos++, (byte) ':', false, true); - } - isFirst = false; - for (Command.ByteOrToken part : command.parts) { - memory.write(pos++, part.getByte(), false, true); + Emulator.withMemory(memory->{ + int pos = memory.readWordRaw(START_OF_PROG_POINTER); + for (Line line : lines) { + int nextPos = pos + line.getLength(); + memory.writeWord(pos, nextPos, false, true); + pos += 2; + memory.writeWord(pos, line.getNumber(), false, true); + pos += 2; + boolean isFirst = true; + for (Command command : line.getCommands()) { + if (!isFirst) { + memory.write(pos++, (byte) ':', false, true); + } + isFirst = false; + for (Command.ByteOrToken part : command.parts) { + memory.write(pos++, part.getByte(), false, true); + } } + memory.write(pos++, (byte) 0, false, true); } memory.write(pos++, (byte) 0, false, true); - } - memory.write(pos++, (byte) 0, false, true); - memory.write(pos++, (byte) 0, false, true); - memory.write(pos++, (byte) 0, false, true); - memory.write(pos++, (byte) 0, false, true); + memory.write(pos++, (byte) 0, false, true); + memory.write(pos++, (byte) 0, false, true); + memory.write(pos++, (byte) 0, false, true); + }); } private boolean isProgramRunning() { - RAM memory = Emulator.getComputer().memory; - return (memory.readRaw(RUNNING_FLAG) & 0x0FF) != NOT_RUNNING; + return Emulator.withComputer(c->(c.getMemory().readRaw(RUNNING_FLAG) & 0x0FF) != NOT_RUNNING, false); } /** * If the program is running, wait until it advances to the next line */ private void whenReady(Runnable r) { - RAM memory = Emulator.getComputer().memory; - memory.addListener(new RAMListener(RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) { - @Override - protected void doConfig() { - setScopeStart(GOTO_CMD); - } + Emulator.withMemory(memory->{ + memory.addListener(new RAMListener(RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) { + @Override + protected void doConfig() { + setScopeStart(GOTO_CMD); + } - @Override - protected void doEvent(RAMEvent e) { - r.run(); - memory.removeListener(this); - } + @Override + protected void doEvent(RAMEvent e) { + r.run(); + memory.removeListener(this); + } + }); }); } @@ -222,11 +223,12 @@ public class ApplesoftProgram { * @param programEnd Program ending address */ private void clearVariables(int programEnd) { - RAM memory = Emulator.getComputer().memory; - memory.writeWord(ARRAY_TABLE, programEnd, false, true); - memory.writeWord(VARIABLE_TABLE, programEnd, false, true); - memory.writeWord(VARIABLE_TABLE_END, programEnd, false, true); - memory.writeWord(END_OF_PROG_POINTER, programEnd, false, true); + Emulator.withMemory(memory->{ + memory.writeWord(ARRAY_TABLE, programEnd, false, true); + memory.writeWord(VARIABLE_TABLE, programEnd, false, true); + memory.writeWord(VARIABLE_TABLE_END, programEnd, false, true); + memory.writeWord(END_OF_PROG_POINTER, programEnd, false, true); + }); } /** @@ -234,20 +236,21 @@ public class ApplesoftProgram { * @param programEnd Program ending address */ private void relocateVariables(int programEnd) { - RAM memory = Emulator.getComputer().memory; - int currentEnd = memory.readWordRaw(END_OF_PROG_POINTER); - memory.writeWord(END_OF_PROG_POINTER, programEnd, false, true); - if (programEnd > currentEnd) { - int diff = programEnd - currentEnd; - int himem = memory.readWordRaw(HIMEM); - for (int i=himem - diff; i >= programEnd; i--) { - memory.write(i+diff, memory.readRaw(i), false, true); + Emulator.withMemory(memory->{ + int currentEnd = memory.readWordRaw(END_OF_PROG_POINTER); + memory.writeWord(END_OF_PROG_POINTER, programEnd, false, true); + if (programEnd > currentEnd) { + int diff = programEnd - currentEnd; + int himem = memory.readWordRaw(HIMEM); + for (int i=himem - diff; i >= programEnd; i--) { + memory.write(i+diff, memory.readRaw(i), false, true); + } + memory.writeWord(VARIABLE_TABLE, memory.readWordRaw(VARIABLE_TABLE) + diff, false, true); + memory.writeWord(ARRAY_TABLE, memory.readWordRaw(ARRAY_TABLE) + diff, false, true); + memory.writeWord(VARIABLE_TABLE_END, memory.readWordRaw(VARIABLE_TABLE_END) + diff, false, true); + memory.writeWord(STRING_TABLE, memory.readWordRaw(STRING_TABLE) + diff, false, true); } - memory.writeWord(VARIABLE_TABLE, memory.readWordRaw(VARIABLE_TABLE) + diff, false, true); - memory.writeWord(ARRAY_TABLE, memory.readWordRaw(ARRAY_TABLE) + diff, false, true); - memory.writeWord(VARIABLE_TABLE_END, memory.readWordRaw(VARIABLE_TABLE_END) + diff, false, true); - memory.writeWord(STRING_TABLE, memory.readWordRaw(STRING_TABLE) + diff, false, true); - } + }); } private int getProgramSize() { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/assembly/AcmeCrossAssembler.java b/Platform/Apple/tools/jace/src/main/java/jace/assembly/AcmeCrossAssembler.java index 12642236..c599d43e 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/assembly/AcmeCrossAssembler.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/assembly/AcmeCrossAssembler.java @@ -91861,283 +91861,283 @@ public final class AcmeCrossAssembler extends UnixRuntime { private static final java.util.Hashtable symbols = new java.util.Hashtable(); static { - symbols.put("setgrent",new Integer(0x53c00)); - symbols.put("chroot",new Integer(0x4cc00)); - symbols.put("strcpy",new Integer(0x42e00)); - symbols.put("cliargs_get_rest",new Integer(0x16e00)); - symbols.put("flow_store_doloop_condition",new Integer(0x19600)); - symbols.put("fabs",new Integer(0x32a00)); - symbols.put("waitpid",new Integer(0x4ae00)); - symbols.put("vcpu_set_pc",new Integer(0x27e00)); - symbols.put("flow_parse_block_else_block",new Integer(0x19a00)); - symbols.put("Output_passinit",new Integer(0x27600)); - symbols.put("Input_until_terminator",new Integer(0x1d800)); - symbols.put("GetQuotedByte",new Integer(0x1c600)); - symbols.put("getgid",new Integer(0x496c4)); - symbols.put("sysconf",new Integer(0x4a400)); - symbols.put("printf",new Integer(0x3fc00)); - symbols.put("vsprintf",new Integer(0x65200)); - symbols.put("utime",new Integer(0x4a800)); - symbols.put("Input_skip_or_store_block",new Integer(0x1d400)); - symbols.put("getlogin",new Integer(0x54e00)); - symbols.put("recv",new Integer(0x4fe00)); - symbols.put("Input_skip_remainder",new Integer(0x1c800)); - symbols.put("connect",new Integer(0x4e200)); - symbols.put("ungetc",new Integer(0x6a800)); - symbols.put("GetByte",new Integer(0x1c000)); - symbols.put("safe_malloc",new Integer(0x1b400)); - symbols.put("geteuid",new Integer(0x496b4)); - symbols.put("symbol_fix_forward_anon_name",new Integer(0x30800)); - symbols.put("memmove",new Integer(0x5b600)); - symbols.put("keyword_is_65816mnemo",new Integer(0x24e00)); - symbols.put("snprintf",new Integer(0x41c00)); - symbols.put("pathconf",new Integer(0x4c800)); - symbols.put("Tree_easy_scan",new Integer(0x31000)); - symbols.put("mknod",new Integer(0x4ce00)); - symbols.put("getgrgid",new Integer(0x53800)); - symbols.put("dump_tree",new Integer(0x31400)); - symbols.put("getenv",new Integer(0x3e000)); - symbols.put("fchmod",new Integer(0x4c000)); - symbols.put("floor",new Integer(0x37600)); - symbols.put("getegid",new Integer(0x496d4)); - symbols.put("setpriority",new Integer(0x4e000)); - symbols.put("getpriority",new Integer(0x4de00)); - symbols.put("usleep",new Integer(0x4d200)); - symbols.put("typesystem_want_imm",new Integer(0x32000)); - symbols.put("getpagesize",new Integer(0x48b80)); - symbols.put("fchown",new Integer(0x4ba00)); - symbols.put("fgets",new Integer(0x59400)); - symbols.put("dirname",new Integer(0x55000)); - symbols.put("Section_finalize",new Integer(0x2e600)); - symbols.put("Throw_warning",new Integer(0x1ac00)); - symbols.put("memcpy",new Integer(0x48fdc)); - symbols.put("DynaBuf_to_lower",new Integer(0x18000)); - symbols.put("Throw_error",new Integer(0x1b000)); - symbols.put("ALU_any_result",new Integer(0x16400)); - symbols.put("readlink",new Integer(0x4b600)); - symbols.put("puts",new Integer(0x40400)); - symbols.put("dup2",new Integer(0x4ac00)); - symbols.put("mbrtowc",new Integer(0x66200)); - symbols.put("getpass",new Integer(0x54a00)); - symbols.put("getuid",new Integer(0x496a4)); - symbols.put("malloc",new Integer(0x3e800)); - symbols.put("isatty",new Integer(0x5a200)); - symbols.put("symbol_define",new Integer(0x30200)); - symbols.put("iswspace",new Integer(0x65e00)); - symbols.put("endpwent",new Integer(0x54800)); - symbols.put("sleep",new Integer(0x48af0)); - symbols.put("vsnprintf",new Integer(0x64e00)); - symbols.put("recvfrom",new Integer(0x4f800)); - symbols.put("strtoul",new Integer(0x5fc00)); - symbols.put("ACME_finalize",new Integer(0x11800)); - symbols.put("typesystem_force_address_statement",new Integer(0x31e00)); - symbols.put("gethostbyaddr",new Integer(0x53200)); - symbols.put("rmdir",new Integer(0x4a200)); - symbols.put("Input_append_keyword_to_global_dynabuf",new Integer(0x1dc00)); - symbols.put("socket",new Integer(0x4e400)); - symbols.put("select",new Integer(0x4fa00)); - symbols.put("readdir",new Integer(0x52600)); - symbols.put("lchown",new Integer(0x4bc00)); - symbols.put("setgroups",new Integer(0x50e00)); - symbols.put("fflush",new Integer(0x39a00)); - symbols.put("cliargs_safe_get_next",new Integer(0x16a00)); - symbols.put("ftruncate",new Integer(0x4d000)); - symbols.put("realpath",new Integer(0x4d800)); - symbols.put("keyword_is_6510mnemo",new Integer(0x23000)); - symbols.put("chown",new Integer(0x4b800)); - symbols.put("send",new Integer(0x4fc00)); - symbols.put("vcpu_check_and_set_reg_length",new Integer(0x17200)); - symbols.put("Output_init",new Integer(0x26e00)); - symbols.put("chmod",new Integer(0x4be00)); - symbols.put("alarm",new Integer(0x49a64)); - symbols.put("Parse_optional_block",new Integer(0x1b600)); - symbols.put("strtol",new Integer(0x43600)); - symbols.put("pipe",new Integer(0x4aa00)); - symbols.put("encoding_passinit",new Integer(0x18e00)); - symbols.put("uname",new Integer(0x55400)); - symbols.put("symbol_parse_definition",new Integer(0x2fc00)); - symbols.put("Output_start_segment",new Integer(0x27a00)); - symbols.put("accept",new Integer(0x4ea00)); - symbols.put("rint",new Integer(0x38000)); - symbols.put("basename",new Integer(0x55200)); - symbols.put("ALU_optional_defined_int",new Integer(0x15a00)); - symbols.put("output_set_output_filename",new Integer(0x26c00)); - symbols.put("DynaBuf_get_copy",new Integer(0x17a00)); - symbols.put("output_set_output_format",new Integer(0x26800)); - symbols.put("strtod",new Integer(0x68800)); - symbols.put("Throw_first_pass_warning",new Integer(0x1ae00)); - symbols.put("Parse_until_eob_or_eof",new Integer(0x1a400)); - symbols.put("sysctl",new Integer(0x4dc00)); - symbols.put("fstat",new Integer(0x62e00)); - symbols.put("fprintf",new Integer(0x3bc00)); - symbols.put("cputype_passinit",new Integer(0x17400)); - symbols.put("setpwent",new Integer(0x54600)); - symbols.put("bind",new Integer(0x4f000)); - symbols.put("inet_addr",new Integer(0x56200)); - symbols.put("mkfifo",new Integer(0x4d400)); - symbols.put("chdir",new Integer(0x4a600)); - symbols.put("Input_read_keyword",new Integer(0x1ec00)); - symbols.put("cputype_find",new Integer(0x17000)); - symbols.put("initgroups",new Integer(0x56600)); - symbols.put("Macro_parse_definition",new Integer(0x20600)); - symbols.put("endgrent",new Integer(0x53e00)); - symbols.put("setsockopt",new Integer(0x4ee00)); - symbols.put("fseek",new Integer(0x3d400)); - symbols.put("cos",new Integer(0x32800)); - symbols.put("memchr",new Integer(0x5b400)); - symbols.put("output_le16",new Integer(0x26000)); - symbols.put("umask",new Integer(0x49694)); - symbols.put("symbol_set_value",new Integer(0x2f600)); - symbols.put("symbol_find",new Integer(0x2f400)); - symbols.put("lstat",new Integer(0x4c200)); - symbols.put("sin",new Integer(0x32c00)); - symbols.put("setgid",new Integer(0x50a00)); - symbols.put("signal",new Integer(0x5ea00)); - symbols.put("output_8",new Integer(0x25e00)); - symbols.put("strncmp",new Integer(0x43000)); - symbols.put("DynaBuf_create",new Integer(0x17600)); - symbols.put("pow",new Integer(0x33400)); - symbols.put("keyword_is_6502mnemo",new Integer(0x22600)); - symbols.put("strncpy",new Integer(0x5f400)); - symbols.put("DynaBuf_add_string",new Integer(0x17e00)); - symbols.put("Input_read_filename",new Integer(0x1fc00)); - symbols.put("sync",new Integer(0x51e00)); - symbols.put("ALU_int_result",new Integer(0x15c00)); - symbols.put("sendto",new Integer(0x4f600)); - symbols.put("realloc",new Integer(0x40600)); - symbols.put("vcpu_get_statement_size",new Integer(0x28200)); - symbols.put("Section_passinit",new Integer(0x2e800)); - symbols.put("listen",new Integer(0x4f200)); - symbols.put("fork",new Integer(0x62c00)); - symbols.put("sscanf",new Integer(0x5f000)); - symbols.put("getgrent",new Integer(0x53a00)); - symbols.put("sigaction",new Integer(0x52200)); - symbols.put("fread",new Integer(0x3c600)); - symbols.put("inet_aton",new Integer(0x55e00)); - symbols.put("Input_read_and_lower_keyword",new Integer(0x1f400)); - symbols.put("symlink",new Integer(0x4b400)); - symbols.put("encoding_find",new Integer(0x19200)); - symbols.put("Tree_add_table",new Integer(0x30e00)); - symbols.put("DynaBuf_init",new Integer(0x18200)); - symbols.put("fopen",new Integer(0x3b800)); - symbols.put("memset",new Integer(0x48fec)); - symbols.put("main",new Integer(0x11c00)); - symbols.put("typesystem_want_addr",new Integer(0x32200)); - symbols.put("Output_end_segment",new Integer(0x27800)); - symbols.put("Output_fake",new Integer(0x25c00)); - symbols.put("add_node_to_tree",new Integer(0x30c00)); - symbols.put("pseudoopcodes_init",new Integer(0x2e000)); - symbols.put("DynaBuf_append",new Integer(0x17c00)); - symbols.put("matherr",new Integer(0x37c00)); - symbols.put("fclose",new Integer(0x39200)); - symbols.put("inet_ntoa",new Integer(0x52a00)); - symbols.put("getppid",new Integer(0x491bc)); - symbols.put("opendir",new Integer(0x52400)); - symbols.put("getgroups",new Integer(0x50000)); - symbols.put("keyword_is_c64dtv2mnemo",new Integer(0x23a00)); - symbols.put("getgrouplist",new Integer(0x56400)); - symbols.put("seteuid",new Integer(0x50800)); - symbols.put("keyword_is_65c02mnemo",new Integer(0x24400)); - symbols.put("putc",new Integer(0x40000)); - symbols.put("output_initmem",new Integer(0x26600)); - symbols.put("finite",new Integer(0x37400)); - symbols.put("herror",new Integer(0x53000)); - symbols.put("strcmp",new Integer(0x42c00)); - symbols.put("flow_forloop",new Integer(0x19400)); - symbols.put("shutdown",new Integer(0x4f400)); - symbols.put("tan",new Integer(0x32e00)); - symbols.put("ttyname",new Integer(0x52000)); - symbols.put("vfiprintf",new Integer(0x64a00)); - symbols.put("getpwuid",new Integer(0x54200)); - symbols.put("Input_read_zone_and_keyword",new Integer(0x1e400)); - symbols.put("dup",new Integer(0x4c600)); - symbols.put("copysign",new Integer(0x38800)); - symbols.put("getcwd",new Integer(0x4b000)); - symbols.put("Section_new_zone",new Integer(0x2e400)); - symbols.put("encoding_load",new Integer(0x19000)); - symbols.put("gethostbyname",new Integer(0x53400)); - symbols.put("getpwnam",new Integer(0x54000)); - symbols.put("getservbyname",new Integer(0x52c00)); - symbols.put("atan",new Integer(0x32400)); - symbols.put("gethostname",new Integer(0x55a00)); - symbols.put("sprintf",new Integer(0x42000)); - symbols.put("pseudoopcode_parse",new Integer(0x2e200)); - symbols.put("Tree_hard_scan",new Integer(0x31200)); - symbols.put("strtof",new Integer(0x68a00)); - symbols.put("atexit",new Integer(0x38a00)); - symbols.put("cliargs_get_next",new Integer(0x16600)); - symbols.put("output_le32",new Integer(0x26400)); - symbols.put("asin",new Integer(0x33200)); - symbols.put("setlocale",new Integer(0x5b000)); - symbols.put("getpeername",new Integer(0x50400)); - symbols.put("Throw_serious_error",new Integer(0x1b200)); - symbols.put("nanf",new Integer(0x66a00)); - symbols.put("cliargs_init",new Integer(0x16c00)); - symbols.put("scalbn",new Integer(0x38400)); - symbols.put("vfork",new Integer(0x4ca00)); - symbols.put("ALU_liberal_int",new Integer(0x16200)); - symbols.put("symbols_vicelabels",new Integer(0x30600)); - symbols.put("fsync",new Integer(0x51200)); - symbols.put("Mnemo_init",new Integer(0x22400)); - symbols.put("fputc",new Integer(0x3c000)); - symbols.put("symbol_set_label",new Integer(0x2f800)); - symbols.put("DynaBuf_enlarge",new Integer(0x17800)); - symbols.put("getsockopt",new Integer(0x4ec00)); - symbols.put("typesystem_force_address_block",new Integer(0x31c00)); - symbols.put("hstrerror",new Integer(0x52e00)); - symbols.put("vcpu_end_statement",new Integer(0x28400)); - symbols.put("nan",new Integer(0x37e00)); - symbols.put("Macro_parse_call",new Integer(0x20a00)); - symbols.put("encoding_encode_char",new Integer(0x18c00)); - symbols.put("Input_new_file",new Integer(0x1be00)); - symbols.put("localeconv",new Integer(0x5b200)); - symbols.put("Tree_dump_forest",new Integer(0x31800)); - symbols.put("Bug_found",new Integer(0x1b800)); - symbols.put("Output_save_file",new Integer(0x27000)); - symbols.put("fwrite",new Integer(0x3dc00)); - symbols.put("vcpu_read_pc",new Integer(0x28000)); - symbols.put("access",new Integer(0x4a000)); - symbols.put("ALU_any_int",new Integer(0x15e00)); - symbols.put("notreallypo_setpc",new Integer(0x2de00)); - symbols.put("getdents",new Integer(0x4c400)); - symbols.put("Input_accept_comma",new Integer(0x20000)); - symbols.put("exit",new Integer(0x38e00)); - symbols.put("klogctl",new Integer(0x4d600)); - symbols.put("typesystem_says_address",new Integer(0x31a00)); - symbols.put("getgrnam",new Integer(0x53600)); - symbols.put("make_hash",new Integer(0x30a00)); - symbols.put("output_prefer_cbm_file_format",new Integer(0x26a00)); - symbols.put("AnyOS_entry",new Integer(0x28600)); - symbols.put("Macro_init",new Integer(0x20400)); - symbols.put("ALU_init",new Integer(0x15800)); - symbols.put("getpwent",new Integer(0x54400)); - symbols.put("daemon",new Integer(0x55c00)); - symbols.put("strlen",new Integer(0x10268)); - symbols.put("open",new Integer(0x63000)); - symbols.put("Input_get_force_bit",new Integer(0x20200)); - symbols.put("fputs",new Integer(0x59a00)); - symbols.put("setsid",new Integer(0x51000)); - symbols.put("setegid",new Integer(0x50c00)); - symbols.put("closedir",new Integer(0x52800)); - symbols.put("cliargs_handle_options",new Integer(0x16800)); - symbols.put("acos",new Integer(0x33000)); - symbols.put("output_le24",new Integer(0x26200)); - symbols.put("vasprintf",new Integer(0x54c00)); - symbols.put("Input_ensure_EOS",new Integer(0x1ce00)); - symbols.put("setuid",new Integer(0x50600)); - symbols.put("mkdir",new Integer(0x49e00)); - symbols.put("ALU_defined_int",new Integer(0x16000)); - symbols.put("wcrtomb",new Integer(0x6ac00)); - symbols.put("close",new Integer(0x62a00)); - symbols.put("symbols_list",new Integer(0x30400)); - symbols.put("flow_parse_and_close_file",new Integer(0x19c00)); - symbols.put("flow_doloop",new Integer(0x19800)); - symbols.put("_call_helper",new Integer(0x10284)); - symbols.put("vfprintf",new Integer(0x48000)); - symbols.put("fputwc",new Integer(0x65c00)); - symbols.put("raise",new Integer(0x5e800)); - symbols.put("free",new Integer(0x3ea00)); - symbols.put("getsockname",new Integer(0x50200)); + symbols.put("setgrent",Integer.valueOf(0x53c00)); + symbols.put("chroot",Integer.valueOf(0x4cc00)); + symbols.put("strcpy",Integer.valueOf(0x42e00)); + symbols.put("cliargs_get_rest",Integer.valueOf(0x16e00)); + symbols.put("flow_store_doloop_condition",Integer.valueOf(0x19600)); + symbols.put("fabs",Integer.valueOf(0x32a00)); + symbols.put("waitpid",Integer.valueOf(0x4ae00)); + symbols.put("vcpu_set_pc",Integer.valueOf(0x27e00)); + symbols.put("flow_parse_block_else_block",Integer.valueOf(0x19a00)); + symbols.put("Output_passinit",Integer.valueOf(0x27600)); + symbols.put("Input_until_terminator",Integer.valueOf(0x1d800)); + symbols.put("GetQuotedByte",Integer.valueOf(0x1c600)); + symbols.put("getgid",Integer.valueOf(0x496c4)); + symbols.put("sysconf",Integer.valueOf(0x4a400)); + symbols.put("printf",Integer.valueOf(0x3fc00)); + symbols.put("vsprintf",Integer.valueOf(0x65200)); + symbols.put("utime",Integer.valueOf(0x4a800)); + symbols.put("Input_skip_or_store_block",Integer.valueOf(0x1d400)); + symbols.put("getlogin",Integer.valueOf(0x54e00)); + symbols.put("recv",Integer.valueOf(0x4fe00)); + symbols.put("Input_skip_remainder",Integer.valueOf(0x1c800)); + symbols.put("connect",Integer.valueOf(0x4e200)); + symbols.put("ungetc",Integer.valueOf(0x6a800)); + symbols.put("GetByte",Integer.valueOf(0x1c000)); + symbols.put("safe_malloc",Integer.valueOf(0x1b400)); + symbols.put("geteuid",Integer.valueOf(0x496b4)); + symbols.put("symbol_fix_forward_anon_name",Integer.valueOf(0x30800)); + symbols.put("memmove",Integer.valueOf(0x5b600)); + symbols.put("keyword_is_65816mnemo",Integer.valueOf(0x24e00)); + symbols.put("snprintf",Integer.valueOf(0x41c00)); + symbols.put("pathconf",Integer.valueOf(0x4c800)); + symbols.put("Tree_easy_scan",Integer.valueOf(0x31000)); + symbols.put("mknod",Integer.valueOf(0x4ce00)); + symbols.put("getgrgid",Integer.valueOf(0x53800)); + symbols.put("dump_tree",Integer.valueOf(0x31400)); + symbols.put("getenv",Integer.valueOf(0x3e000)); + symbols.put("fchmod",Integer.valueOf(0x4c000)); + symbols.put("floor",Integer.valueOf(0x37600)); + symbols.put("getegid",Integer.valueOf(0x496d4)); + symbols.put("setpriority",Integer.valueOf(0x4e000)); + symbols.put("getpriority",Integer.valueOf(0x4de00)); + symbols.put("usleep",Integer.valueOf(0x4d200)); + symbols.put("typesystem_want_imm",Integer.valueOf(0x32000)); + symbols.put("getpagesize",Integer.valueOf(0x48b80)); + symbols.put("fchown",Integer.valueOf(0x4ba00)); + symbols.put("fgets",Integer.valueOf(0x59400)); + symbols.put("dirname",Integer.valueOf(0x55000)); + symbols.put("Section_finalize",Integer.valueOf(0x2e600)); + symbols.put("Throw_warning",Integer.valueOf(0x1ac00)); + symbols.put("memcpy",Integer.valueOf(0x48fdc)); + symbols.put("DynaBuf_to_lower",Integer.valueOf(0x18000)); + symbols.put("Throw_error",Integer.valueOf(0x1b000)); + symbols.put("ALU_any_result",Integer.valueOf(0x16400)); + symbols.put("readlink",Integer.valueOf(0x4b600)); + symbols.put("puts",Integer.valueOf(0x40400)); + symbols.put("dup2",Integer.valueOf(0x4ac00)); + symbols.put("mbrtowc",Integer.valueOf(0x66200)); + symbols.put("getpass",Integer.valueOf(0x54a00)); + symbols.put("getuid",Integer.valueOf(0x496a4)); + symbols.put("malloc",Integer.valueOf(0x3e800)); + symbols.put("isatty",Integer.valueOf(0x5a200)); + symbols.put("symbol_define",Integer.valueOf(0x30200)); + symbols.put("iswspace",Integer.valueOf(0x65e00)); + symbols.put("endpwent",Integer.valueOf(0x54800)); + symbols.put("sleep",Integer.valueOf(0x48af0)); + symbols.put("vsnprintf",Integer.valueOf(0x64e00)); + symbols.put("recvfrom",Integer.valueOf(0x4f800)); + symbols.put("strtoul",Integer.valueOf(0x5fc00)); + symbols.put("ACME_finalize",Integer.valueOf(0x11800)); + symbols.put("typesystem_force_address_statement",Integer.valueOf(0x31e00)); + symbols.put("gethostbyaddr",Integer.valueOf(0x53200)); + symbols.put("rmdir",Integer.valueOf(0x4a200)); + symbols.put("Input_append_keyword_to_global_dynabuf",Integer.valueOf(0x1dc00)); + symbols.put("socket",Integer.valueOf(0x4e400)); + symbols.put("select",Integer.valueOf(0x4fa00)); + symbols.put("readdir",Integer.valueOf(0x52600)); + symbols.put("lchown",Integer.valueOf(0x4bc00)); + symbols.put("setgroups",Integer.valueOf(0x50e00)); + symbols.put("fflush",Integer.valueOf(0x39a00)); + symbols.put("cliargs_safe_get_next",Integer.valueOf(0x16a00)); + symbols.put("ftruncate",Integer.valueOf(0x4d000)); + symbols.put("realpath",Integer.valueOf(0x4d800)); + symbols.put("keyword_is_6510mnemo",Integer.valueOf(0x23000)); + symbols.put("chown",Integer.valueOf(0x4b800)); + symbols.put("send",Integer.valueOf(0x4fc00)); + symbols.put("vcpu_check_and_set_reg_length",Integer.valueOf(0x17200)); + symbols.put("Output_init",Integer.valueOf(0x26e00)); + symbols.put("chmod",Integer.valueOf(0x4be00)); + symbols.put("alarm",Integer.valueOf(0x49a64)); + symbols.put("Parse_optional_block",Integer.valueOf(0x1b600)); + symbols.put("strtol",Integer.valueOf(0x43600)); + symbols.put("pipe",Integer.valueOf(0x4aa00)); + symbols.put("encoding_passinit",Integer.valueOf(0x18e00)); + symbols.put("uname",Integer.valueOf(0x55400)); + symbols.put("symbol_parse_definition",Integer.valueOf(0x2fc00)); + symbols.put("Output_start_segment",Integer.valueOf(0x27a00)); + symbols.put("accept",Integer.valueOf(0x4ea00)); + symbols.put("rint",Integer.valueOf(0x38000)); + symbols.put("basename",Integer.valueOf(0x55200)); + symbols.put("ALU_optional_defined_int",Integer.valueOf(0x15a00)); + symbols.put("output_set_output_filename",Integer.valueOf(0x26c00)); + symbols.put("DynaBuf_get_copy",Integer.valueOf(0x17a00)); + symbols.put("output_set_output_format",Integer.valueOf(0x26800)); + symbols.put("strtod",Integer.valueOf(0x68800)); + symbols.put("Throw_first_pass_warning",Integer.valueOf(0x1ae00)); + symbols.put("Parse_until_eob_or_eof",Integer.valueOf(0x1a400)); + symbols.put("sysctl",Integer.valueOf(0x4dc00)); + symbols.put("fstat",Integer.valueOf(0x62e00)); + symbols.put("fprintf",Integer.valueOf(0x3bc00)); + symbols.put("cputype_passinit",Integer.valueOf(0x17400)); + symbols.put("setpwent",Integer.valueOf(0x54600)); + symbols.put("bind",Integer.valueOf(0x4f000)); + symbols.put("inet_addr",Integer.valueOf(0x56200)); + symbols.put("mkfifo",Integer.valueOf(0x4d400)); + symbols.put("chdir",Integer.valueOf(0x4a600)); + symbols.put("Input_read_keyword",Integer.valueOf(0x1ec00)); + symbols.put("cputype_find",Integer.valueOf(0x17000)); + symbols.put("initgroups",Integer.valueOf(0x56600)); + symbols.put("Macro_parse_definition",Integer.valueOf(0x20600)); + symbols.put("endgrent",Integer.valueOf(0x53e00)); + symbols.put("setsockopt",Integer.valueOf(0x4ee00)); + symbols.put("fseek",Integer.valueOf(0x3d400)); + symbols.put("cos",Integer.valueOf(0x32800)); + symbols.put("memchr",Integer.valueOf(0x5b400)); + symbols.put("output_le16",Integer.valueOf(0x26000)); + symbols.put("umask",Integer.valueOf(0x49694)); + symbols.put("symbol_set_value",Integer.valueOf(0x2f600)); + symbols.put("symbol_find",Integer.valueOf(0x2f400)); + symbols.put("lstat",Integer.valueOf(0x4c200)); + symbols.put("sin",Integer.valueOf(0x32c00)); + symbols.put("setgid",Integer.valueOf(0x50a00)); + symbols.put("signal",Integer.valueOf(0x5ea00)); + symbols.put("output_8",Integer.valueOf(0x25e00)); + symbols.put("strncmp",Integer.valueOf(0x43000)); + symbols.put("DynaBuf_create",Integer.valueOf(0x17600)); + symbols.put("pow",Integer.valueOf(0x33400)); + symbols.put("keyword_is_6502mnemo",Integer.valueOf(0x22600)); + symbols.put("strncpy",Integer.valueOf(0x5f400)); + symbols.put("DynaBuf_add_string",Integer.valueOf(0x17e00)); + symbols.put("Input_read_filename",Integer.valueOf(0x1fc00)); + symbols.put("sync",Integer.valueOf(0x51e00)); + symbols.put("ALU_int_result",Integer.valueOf(0x15c00)); + symbols.put("sendto",Integer.valueOf(0x4f600)); + symbols.put("realloc",Integer.valueOf(0x40600)); + symbols.put("vcpu_get_statement_size",Integer.valueOf(0x28200)); + symbols.put("Section_passinit",Integer.valueOf(0x2e800)); + symbols.put("listen",Integer.valueOf(0x4f200)); + symbols.put("fork",Integer.valueOf(0x62c00)); + symbols.put("sscanf",Integer.valueOf(0x5f000)); + symbols.put("getgrent",Integer.valueOf(0x53a00)); + symbols.put("sigaction",Integer.valueOf(0x52200)); + symbols.put("fread",Integer.valueOf(0x3c600)); + symbols.put("inet_aton",Integer.valueOf(0x55e00)); + symbols.put("Input_read_and_lower_keyword",Integer.valueOf(0x1f400)); + symbols.put("symlink",Integer.valueOf(0x4b400)); + symbols.put("encoding_find",Integer.valueOf(0x19200)); + symbols.put("Tree_add_table",Integer.valueOf(0x30e00)); + symbols.put("DynaBuf_init",Integer.valueOf(0x18200)); + symbols.put("fopen",Integer.valueOf(0x3b800)); + symbols.put("memset",Integer.valueOf(0x48fec)); + symbols.put("main",Integer.valueOf(0x11c00)); + symbols.put("typesystem_want_addr",Integer.valueOf(0x32200)); + symbols.put("Output_end_segment",Integer.valueOf(0x27800)); + symbols.put("Output_fake",Integer.valueOf(0x25c00)); + symbols.put("add_node_to_tree",Integer.valueOf(0x30c00)); + symbols.put("pseudoopcodes_init",Integer.valueOf(0x2e000)); + symbols.put("DynaBuf_append",Integer.valueOf(0x17c00)); + symbols.put("matherr",Integer.valueOf(0x37c00)); + symbols.put("fclose",Integer.valueOf(0x39200)); + symbols.put("inet_ntoa",Integer.valueOf(0x52a00)); + symbols.put("getppid",Integer.valueOf(0x491bc)); + symbols.put("opendir",Integer.valueOf(0x52400)); + symbols.put("getgroups",Integer.valueOf(0x50000)); + symbols.put("keyword_is_c64dtv2mnemo",Integer.valueOf(0x23a00)); + symbols.put("getgrouplist",Integer.valueOf(0x56400)); + symbols.put("seteuid",Integer.valueOf(0x50800)); + symbols.put("keyword_is_65c02mnemo",Integer.valueOf(0x24400)); + symbols.put("putc",Integer.valueOf(0x40000)); + symbols.put("output_initmem",Integer.valueOf(0x26600)); + symbols.put("finite",Integer.valueOf(0x37400)); + symbols.put("herror",Integer.valueOf(0x53000)); + symbols.put("strcmp",Integer.valueOf(0x42c00)); + symbols.put("flow_forloop",Integer.valueOf(0x19400)); + symbols.put("shutdown",Integer.valueOf(0x4f400)); + symbols.put("tan",Integer.valueOf(0x32e00)); + symbols.put("ttyname",Integer.valueOf(0x52000)); + symbols.put("vfiprintf",Integer.valueOf(0x64a00)); + symbols.put("getpwuid",Integer.valueOf(0x54200)); + symbols.put("Input_read_zone_and_keyword",Integer.valueOf(0x1e400)); + symbols.put("dup",Integer.valueOf(0x4c600)); + symbols.put("copysign",Integer.valueOf(0x38800)); + symbols.put("getcwd",Integer.valueOf(0x4b000)); + symbols.put("Section_new_zone",Integer.valueOf(0x2e400)); + symbols.put("encoding_load",Integer.valueOf(0x19000)); + symbols.put("gethostbyname",Integer.valueOf(0x53400)); + symbols.put("getpwnam",Integer.valueOf(0x54000)); + symbols.put("getservbyname",Integer.valueOf(0x52c00)); + symbols.put("atan",Integer.valueOf(0x32400)); + symbols.put("gethostname",Integer.valueOf(0x55a00)); + symbols.put("sprintf",Integer.valueOf(0x42000)); + symbols.put("pseudoopcode_parse",Integer.valueOf(0x2e200)); + symbols.put("Tree_hard_scan",Integer.valueOf(0x31200)); + symbols.put("strtof",Integer.valueOf(0x68a00)); + symbols.put("atexit",Integer.valueOf(0x38a00)); + symbols.put("cliargs_get_next",Integer.valueOf(0x16600)); + symbols.put("output_le32",Integer.valueOf(0x26400)); + symbols.put("asin",Integer.valueOf(0x33200)); + symbols.put("setlocale",Integer.valueOf(0x5b000)); + symbols.put("getpeername",Integer.valueOf(0x50400)); + symbols.put("Throw_serious_error",Integer.valueOf(0x1b200)); + symbols.put("nanf",Integer.valueOf(0x66a00)); + symbols.put("cliargs_init",Integer.valueOf(0x16c00)); + symbols.put("scalbn",Integer.valueOf(0x38400)); + symbols.put("vfork",Integer.valueOf(0x4ca00)); + symbols.put("ALU_liberal_int",Integer.valueOf(0x16200)); + symbols.put("symbols_vicelabels",Integer.valueOf(0x30600)); + symbols.put("fsync",Integer.valueOf(0x51200)); + symbols.put("Mnemo_init",Integer.valueOf(0x22400)); + symbols.put("fputc",Integer.valueOf(0x3c000)); + symbols.put("symbol_set_label",Integer.valueOf(0x2f800)); + symbols.put("DynaBuf_enlarge",Integer.valueOf(0x17800)); + symbols.put("getsockopt",Integer.valueOf(0x4ec00)); + symbols.put("typesystem_force_address_block",Integer.valueOf(0x31c00)); + symbols.put("hstrerror",Integer.valueOf(0x52e00)); + symbols.put("vcpu_end_statement",Integer.valueOf(0x28400)); + symbols.put("nan",Integer.valueOf(0x37e00)); + symbols.put("Macro_parse_call",Integer.valueOf(0x20a00)); + symbols.put("encoding_encode_char",Integer.valueOf(0x18c00)); + symbols.put("Input_new_file",Integer.valueOf(0x1be00)); + symbols.put("localeconv",Integer.valueOf(0x5b200)); + symbols.put("Tree_dump_forest",Integer.valueOf(0x31800)); + symbols.put("Bug_found",Integer.valueOf(0x1b800)); + symbols.put("Output_save_file",Integer.valueOf(0x27000)); + symbols.put("fwrite",Integer.valueOf(0x3dc00)); + symbols.put("vcpu_read_pc",Integer.valueOf(0x28000)); + symbols.put("access",Integer.valueOf(0x4a000)); + symbols.put("ALU_any_int",Integer.valueOf(0x15e00)); + symbols.put("notreallypo_setpc",Integer.valueOf(0x2de00)); + symbols.put("getdents",Integer.valueOf(0x4c400)); + symbols.put("Input_accept_comma",Integer.valueOf(0x20000)); + symbols.put("exit",Integer.valueOf(0x38e00)); + symbols.put("klogctl",Integer.valueOf(0x4d600)); + symbols.put("typesystem_says_address",Integer.valueOf(0x31a00)); + symbols.put("getgrnam",Integer.valueOf(0x53600)); + symbols.put("make_hash",Integer.valueOf(0x30a00)); + symbols.put("output_prefer_cbm_file_format",Integer.valueOf(0x26a00)); + symbols.put("AnyOS_entry",Integer.valueOf(0x28600)); + symbols.put("Macro_init",Integer.valueOf(0x20400)); + symbols.put("ALU_init",Integer.valueOf(0x15800)); + symbols.put("getpwent",Integer.valueOf(0x54400)); + symbols.put("daemon",Integer.valueOf(0x55c00)); + symbols.put("strlen",Integer.valueOf(0x10268)); + symbols.put("open",Integer.valueOf(0x63000)); + symbols.put("Input_get_force_bit",Integer.valueOf(0x20200)); + symbols.put("fputs",Integer.valueOf(0x59a00)); + symbols.put("setsid",Integer.valueOf(0x51000)); + symbols.put("setegid",Integer.valueOf(0x50c00)); + symbols.put("closedir",Integer.valueOf(0x52800)); + symbols.put("cliargs_handle_options",Integer.valueOf(0x16800)); + symbols.put("acos",Integer.valueOf(0x33000)); + symbols.put("output_le24",Integer.valueOf(0x26200)); + symbols.put("vasprintf",Integer.valueOf(0x54c00)); + symbols.put("Input_ensure_EOS",Integer.valueOf(0x1ce00)); + symbols.put("setuid",Integer.valueOf(0x50600)); + symbols.put("mkdir",Integer.valueOf(0x49e00)); + symbols.put("ALU_defined_int",Integer.valueOf(0x16000)); + symbols.put("wcrtomb",Integer.valueOf(0x6ac00)); + symbols.put("close",Integer.valueOf(0x62a00)); + symbols.put("symbols_list",Integer.valueOf(0x30400)); + symbols.put("flow_parse_and_close_file",Integer.valueOf(0x19c00)); + symbols.put("flow_doloop",Integer.valueOf(0x19800)); + symbols.put("_call_helper",Integer.valueOf(0x10284)); + symbols.put("vfprintf",Integer.valueOf(0x48000)); + symbols.put("fputwc",Integer.valueOf(0x65c00)); + symbols.put("raise",Integer.valueOf(0x5e800)); + symbols.put("free",Integer.valueOf(0x3ea00)); + symbols.put("getsockname",Integer.valueOf(0x50200)); } public int lookupSymbol(String symbol) { Integer i = (Integer) symbols.get(symbol); return i==null ? -1 : i.intValue(); } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/assembly/AssemblyHandler.java b/Platform/Apple/tools/jace/src/main/java/jace/assembly/AssemblyHandler.java index 2846674a..bd62aa60 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/assembly/AssemblyHandler.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/assembly/AssemblyHandler.java @@ -1,16 +1,18 @@ package jace.assembly; -import jace.Emulator; -import jace.core.RAM; -import jace.ide.CompileResult; -import jace.ide.LanguageHandler; -import jace.ide.Program; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; +import jace.Emulator; +import jace.core.RAM; +import jace.ide.CompileResult; +import jace.ide.HeadlessProgram; +import jace.ide.LanguageHandler; +import jace.ide.Program; + /** * * @author blurry @@ -28,24 +30,27 @@ public class AssemblyHandler implements LanguageHandler { return compiler; } - @Override - public void execute(CompileResult lastResult) { + public void compileToRam(String code) { + HeadlessProgram prg = new HeadlessProgram(Program.DocumentType.assembly); + prg.setValue(code); + + CompileResult lastResult = compile(prg); if (lastResult.isSuccessful()) { - RAM memory = Emulator.getComputer().getMemory(); + Emulator.withComputer(c -> { + RAM memory = c.getMemory(); try { FileInputStream input = new FileInputStream(lastResult.getCompiledAsset()); int startLSB = input.read(); int startMSB = input.read(); int start = startLSB + startMSB << 8; - System.out.printf("Issuing JSR to $%s%n", Integer.toHexString(start)); - Emulator.getComputer().getCpu().whileSuspended(() -> { + System.out.printf("Storing assembled code to $%s%n", Integer.toHexString(start)); + c.getCpu().whileSuspended(() -> { try { int pos = start; int next; while ((next=input.read()) != -1) { memory.write(pos++, (byte) next, false, true); } - Emulator.getComputer().getCpu().JSR(start); } catch (IOException ex) { Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex); } @@ -53,6 +58,37 @@ public class AssemblyHandler implements LanguageHandler { } catch (IOException ex) { Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex); } + }); + } + } + + @Override + public void execute(CompileResult lastResult) { + if (lastResult.isSuccessful()) { + Emulator.withComputer(c -> { + RAM memory = c.getMemory(); + try { + FileInputStream input = new FileInputStream(lastResult.getCompiledAsset()); + int startLSB = input.read(); + int startMSB = input.read(); + int start = startLSB + startMSB << 8; + System.out.printf("Issuing JSR to $%s%n", Integer.toHexString(start)); + c.getCpu().whileSuspended(() -> { + try { + int pos = start; + int next; + while ((next=input.read()) != -1) { + memory.write(pos++, (byte) next, false, true); + } + c.getCpu().JSR(start); + } catch (IOException ex) { + Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex); + } + }); + } catch (IOException ex) { + Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex); + } + }); } clean(lastResult); } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/cheat/MetaCheat.java b/Platform/Apple/tools/jace/src/main/java/jace/cheat/MetaCheat.java index 49cb600e..cc201efc 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/cheat/MetaCheat.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/cheat/MetaCheat.java @@ -1,26 +1,38 @@ package jace.cheat; -import jace.Emulator; -import jace.LawlessLegends; -import jace.core.*; -import jace.state.State; -import jace.ui.MetacheatUI; -import javafx.application.Platform; -import javafx.beans.property.*; -import javafx.beans.value.ObservableValue; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +import jace.Emulator; +import jace.LawlessLegends; +import jace.core.CPU; +import jace.core.Computer; +import jace.core.RAMEvent; +import jace.core.RAMListener; +import jace.state.State; +import jace.ui.MetacheatUI; +import javafx.application.Platform; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.Property; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + public class MetaCheat extends Cheats { static final ScriptEngine NASHORN_ENGINE = new ScriptEngineManager().getEngineByName("nashorn"); @@ -219,54 +231,56 @@ public class MetaCheat extends Cheats { } public void newSearch() { - RAM memory = Emulator.getComputer().getMemory(); - resultList.clear(); - int compare = parseInt(searchValueProperty.get()); - for (int i = 0; i < 0x10000; i++) { - boolean signed = signedProperty.get(); - int val - = byteSized - ? signed ? memory.readRaw(i) : memory.readRaw(i) & 0x0ff - : signed ? memory.readWordRaw(i) : memory.readWordRaw(i) & 0x0ffff; - if (!searchType.equals(SearchType.VALUE) || val == compare) { - SearchResult result = new SearchResult(i, val); - resultList.add(result); + Emulator.withMemory(memory -> { + resultList.clear(); + int compare = parseInt(searchValueProperty.get()); + for (int i = 0; i < 0x10000; i++) { + boolean signed = signedProperty.get(); + int val + = byteSized + ? signed ? memory.readRaw(i) : memory.readRaw(i) & 0x0ff + : signed ? memory.readWordRaw(i) : memory.readWordRaw(i) & 0x0ffff; + if (!searchType.equals(SearchType.VALUE) || val == compare) { + SearchResult result = new SearchResult(i, val); + resultList.add(result); + } } - } + }); } public void performSearch() { - RAM memory = Emulator.getComputer().getMemory(); - boolean signed = signedProperty.get(); - resultList.removeIf((SearchResult result) -> { - int val = byteSized - ? signed ? memory.readRaw(result.address) : memory.readRaw(result.address) & 0x0ff - : signed ? memory.readWordRaw(result.address) : memory.readWordRaw(result.address) & 0x0ffff; - int last = result.lastObservedValue; - result.lastObservedValue = val; - switch (searchType) { - case VALUE: - int compare = parseInt(searchValueProperty.get()); - return compare != val; - case CHANGE: - switch (searchChangeType) { - case AMOUNT: - int amount = parseInt(searchChangeByProperty().getValue()); - return (val - last) != amount; - case GREATER: - return val <= last; - case ANY_CHANGE: - return val == last; - case LESS: - return val >= last; - case NO_CHANGE: - return val != last; - } - break; - case TEXT: - break; - } - return false; + Emulator.withMemory(memory -> { + boolean signed = signedProperty.get(); + resultList.removeIf((SearchResult result) -> { + int val = byteSized + ? signed ? memory.readRaw(result.address) : memory.readRaw(result.address) & 0x0ff + : signed ? memory.readWordRaw(result.address) : memory.readWordRaw(result.address) & 0x0ffff; + int last = result.lastObservedValue; + result.lastObservedValue = val; + switch (searchType) { + case VALUE: + int compare = parseInt(searchValueProperty.get()); + return compare != val; + case CHANGE: + switch (searchChangeType) { + case AMOUNT: + int amount = parseInt(searchChangeByProperty().getValue()); + return (val - last) != amount; + case GREATER: + return val <= last; + case ANY_CHANGE: + return val == last; + case LESS: + return val >= last; + case NO_CHANGE: + return val != last; + } + break; + case TEXT: + break; + } + return false; + }); }); } @@ -278,23 +292,24 @@ public class MetaCheat extends Cheats { } public void initMemoryView() { - RAM memory = Emulator.getComputer().getMemory(); - for (int addr = getStartAddress(); addr <= getEndAddress(); addr++) { - if (getMemoryCell(addr) == null) { - MemoryCell cell = new MemoryCell(); - cell.address = addr; - cell.value.set(memory.readRaw(addr)); - memoryCells.put(addr, cell); + Emulator.withMemory(memory -> { + for (int addr = getStartAddress(); addr <= getEndAddress(); addr++) { + if (getMemoryCell(addr) == null) { + MemoryCell cell = new MemoryCell(); + cell.address = addr; + cell.value.set(memory.readRaw(addr)); + memoryCells.put(addr, cell); + } } - } - if (memoryViewListener == null) { - memoryViewListener = memory.observe(RAMEvent.TYPE.ANY, startAddress, endAddress, this::processMemoryEvent); - listeners.add(memoryViewListener); - } + if (memoryViewListener == null) { + memoryViewListener = memory.observe(RAMEvent.TYPE.ANY, startAddress, endAddress, this::processMemoryEvent); + listeners.add(memoryViewListener); + } + }); } int fadeCounter = 0; - int FADE_TIMER_VALUE = (int) (Emulator.getComputer().getMotherboard().getSpeedInHz() / 60); + int FADE_TIMER_VALUE = Emulator.withComputer(c-> (int) (c.getMotherboard().getSpeedInHz() / 60), 100); @Override public void tick() { @@ -328,107 +343,90 @@ public class MetaCheat extends Cheats { private void processMemoryEvent(RAMEvent e) { MemoryCell cell = getMemoryCell(e.getAddress()); if (cell != null) { - CPU cpu = Emulator.getComputer().getCpu(); - int pc = cpu.getProgramCounter(); - String trace = cpu.getLastTrace(); - switch (e.getType()) { - case EXECUTE: - cell.execInstructionsDisassembly.add(trace); - if (cell.execInstructionsDisassembly.size() > historyLength) { - cell.execInstructionsDisassembly.remove(0); - } - case READ_OPERAND: - cell.execCount.set(Math.min(255, cell.execCount.get() + lightRate)); - break; - case WRITE: - cell.writeCount.set(Math.min(255, cell.writeCount.get() + lightRate)); - if (ui.isInspecting(cell.address)) { - if (pendingInspectorUpdates.incrementAndGet() < 5) { - Platform.runLater(() -> { - pendingInspectorUpdates.decrementAndGet(); - cell.writeInstructions.add(pc); - cell.writeInstructionsDisassembly.add(trace); - if (cell.writeInstructions.size() > historyLength) { - cell.writeInstructions.remove(0); - cell.writeInstructionsDisassembly.remove(0); - } - }); + Emulator.withComputer(c -> { + CPU cpu = c.getCpu(); + int pc = cpu.getProgramCounter(); + String trace = cpu.getLastTrace(); + switch (e.getType()) { + case EXECUTE: + cell.execInstructionsDisassembly.add(trace); + if (cell.execInstructionsDisassembly.size() > historyLength) { + cell.execInstructionsDisassembly.remove(0); } - } else { - cell.writeInstructions.add(cpu.getProgramCounter()); - cell.writeInstructionsDisassembly.add(cpu.getLastTrace()); - if (cell.writeInstructions.size() > historyLength) { - cell.writeInstructions.remove(0); - cell.writeInstructionsDisassembly.remove(0); + case READ_OPERAND: + cell.execCount.set(Math.min(255, cell.execCount.get() + lightRate)); + break; + case WRITE: + cell.writeCount.set(Math.min(255, cell.writeCount.get() + lightRate)); + if (ui.isInspecting(cell.address)) { + if (pendingInspectorUpdates.incrementAndGet() < 5) { + Platform.runLater(() -> { + pendingInspectorUpdates.decrementAndGet(); + cell.writeInstructions.add(pc); + cell.writeInstructionsDisassembly.add(trace); + if (cell.writeInstructions.size() > historyLength) { + cell.writeInstructions.remove(0); + cell.writeInstructionsDisassembly.remove(0); + } + }); + } + } else { + cell.writeInstructions.add(cpu.getProgramCounter()); + cell.writeInstructionsDisassembly.add(cpu.getLastTrace()); + if (cell.writeInstructions.size() > historyLength) { + cell.writeInstructions.remove(0); + cell.writeInstructionsDisassembly.remove(0); + } } - } - break; - default: - cell.readCount.set(Math.min(255, cell.readCount.get() + lightRate)); - if (ui.isInspecting(cell.address)) { - if (pendingInspectorUpdates.incrementAndGet() < 5) { - Platform.runLater(() -> { - pendingInspectorUpdates.decrementAndGet(); - cell.readInstructions.add(pc); - cell.readInstructionsDisassembly.add(trace); - if (cell.readInstructions.size() > historyLength) { - cell.readInstructions.remove(0); - cell.readInstructionsDisassembly.remove(0); - } - }); + break; + default: + cell.readCount.set(Math.min(255, cell.readCount.get() + lightRate)); + if (ui.isInspecting(cell.address)) { + if (pendingInspectorUpdates.incrementAndGet() < 5) { + Platform.runLater(() -> { + pendingInspectorUpdates.decrementAndGet(); + cell.readInstructions.add(pc); + cell.readInstructionsDisassembly.add(trace); + if (cell.readInstructions.size() > historyLength) { + cell.readInstructions.remove(0); + cell.readInstructionsDisassembly.remove(0); + } + }); + } + } else { + cell.readInstructions.add(cpu.getProgramCounter()); + cell.readInstructionsDisassembly.add(cpu.getLastTrace()); + if (cell.readInstructions.size() > historyLength) { + cell.readInstructions.remove(0); + cell.readInstructionsDisassembly.remove(0); + } } - } else { - cell.readInstructions.add(cpu.getProgramCounter()); - cell.readInstructionsDisassembly.add(cpu.getLastTrace()); - if (cell.readInstructions.size() > historyLength) { - cell.readInstructions.remove(0); - cell.readInstructionsDisassembly.remove(0); - } - } - } - cell.value.set(e.getNewValue()); + } + cell.value.set(e.getNewValue()); + }); } } public void saveCheats(File saveFile) { - FileWriter writer = null; - try { - writer = new FileWriter(saveFile); + try (FileWriter writer = new FileWriter(saveFile)) { for (DynamicCheat cheat : cheatList) { writer.write(cheat.serialize()); writer.write("\n"); } - writer.close(); } catch (IOException ex) { Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex); - } finally { - try { - writer.close(); - } catch (IOException ex) { - Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex); - } } } public void loadCheats(File saveFile) { - BufferedReader in = null; - try { - in = new BufferedReader(new FileReader(saveFile)); - StringBuilder guts = new StringBuilder(); + try (BufferedReader in = new BufferedReader(new FileReader(saveFile))) { String line; while ((line = in.readLine()) != null) { DynamicCheat cheat = DynamicCheat.deserialize(line); addCheat(cheat); } - in.close(); } catch (IOException ex) { Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex); - } finally { - try { - in.close(); - } catch (IOException ex) { - Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex); - } } } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/cheat/MontezumasRevengeCheats.java b/Platform/Apple/tools/jace/src/main/java/jace/cheat/MontezumasRevengeCheats.java index 76f194b1..b30e4d5e 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/cheat/MontezumasRevengeCheats.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/cheat/MontezumasRevengeCheats.java @@ -4,7 +4,6 @@ import jace.Emulator; import jace.EmulatorUILogic; import jace.config.ConfigurableField; import jace.core.Computer; -import jace.core.RAM; import jace.core.RAMEvent; import javafx.event.EventHandler; import javafx.scene.Node; @@ -71,7 +70,6 @@ public class MontezumasRevengeCheats extends Cheats { @Override public void registerListeners() { - RAM memory = Emulator.getComputer().memory; if (repulsiveHack) { addCheat(RAMEvent.TYPE.WRITE, this::repulsiveBehavior, 0x1508, 0x1518); } @@ -91,22 +89,24 @@ public class MontezumasRevengeCheats extends Cheats { } if (safePassage) { - //blank out pattern for floors/doors - for (int addr = 0x0b54; addr <= 0xb5f; addr++) { - memory.write(addr, (byte) 0, false, false); - memory.write(addr + 0x0400, (byte) 0, false, false); - } - memory.write(0x0b50, (byte) 0b11010111, false, false); - memory.write(0x0b51, (byte) 0b00010000, false, false); - memory.write(0x0b52, (byte) 0b10001000, false, false); - memory.write(0x0b53, (byte) 0b10101010, false, false); - memory.write(0x0f50, (byte) 0b10101110, false, false); - memory.write(0x0f51, (byte) 0b00001000, false, false); - memory.write(0x0f52, (byte) 0b10000100, false, false); - memory.write(0x0f53, (byte) 0b11010101, false, false); - forceValue(32, FLOOR_TIMER); - forceValue(32, HAZARD_TIMER); - forceValue(1, HAZARD_FLAG); + Emulator.withMemory(memory -> { + //blank out pattern for floors/doors + for (int addr = 0x0b54; addr <= 0xb5f; addr++) { + memory.write(addr, (byte) 0, false, false); + memory.write(addr + 0x0400, (byte) 0, false, false); + } + memory.write(0x0b50, (byte) 0b11010111, false, false); + memory.write(0x0b51, (byte) 0b00010000, false, false); + memory.write(0x0b52, (byte) 0b10001000, false, false); + memory.write(0x0b53, (byte) 0b10101010, false, false); + memory.write(0x0f50, (byte) 0b10101110, false, false); + memory.write(0x0f51, (byte) 0b00001000, false, false); + memory.write(0x0f52, (byte) 0b10000100, false, false); + memory.write(0x0f53, (byte) 0b11010101, false, false); + forceValue(32, FLOOR_TIMER); + forceValue(32, HAZARD_TIMER); + forceValue(1, HAZARD_FLAG); + }); } if (scoreHack) { @@ -198,7 +198,7 @@ public class MontezumasRevengeCheats extends Cheats { private void mouseClicked(MouseButton button) { byte newX = (byte) (mouseX * X_MAX); byte newY = (byte) (mouseY * Y_MAX); - computer.memory.write(PLAYER_X, newX, false, false); - computer.memory.write(PLAYER_Y, newY, false, false); + computer.getMemory().write(PLAYER_X, newX, false, false); + computer.getMemory().write(PLAYER_Y, newY, false, false); } } \ No newline at end of file diff --git a/Platform/Apple/tools/jace/src/main/java/jace/config/Configuration.java b/Platform/Apple/tools/jace/src/main/java/jace/config/Configuration.java index fdbbc5b3..aa974ebc 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/config/Configuration.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/config/Configuration.java @@ -18,6 +18,35 @@ */ package jace.config; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InvalidClassException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Stream; + import jace.Emulator; import jace.EmulatorUILogic; import jace.core.Computer; @@ -27,13 +56,6 @@ import javafx.collections.ObservableList; import javafx.scene.control.TreeItem; import javafx.scene.image.ImageView; -import java.io.*; -import java.lang.reflect.*; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Stream; - /** * Manages the configuration state of the emulator components. * @@ -104,7 +126,7 @@ public class Configuration implements Reconfigurable { public transient ConfigNode root; public transient ConfigNode parent; - private transient final ObservableList children; + private transient ObservableList children; public transient Reconfigurable subject; private transient boolean changed = true; @@ -124,6 +146,9 @@ public class Configuration implements Reconfigurable { private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + if (children == null) { + children = getChildren(); + } children.setAll(super.getChildren()); id = (String) in.readObject(); name = (String) in.readObject(); @@ -254,13 +279,18 @@ public class Configuration implements Reconfigurable { } public static ConfigNode BASE; public static EmulatorUILogic ui = Emulator.logic; - public static Computer emulator = Emulator.getComputer(); @ConfigurableField(name = "Autosave Changes", description = "If unchecked, changes are only saved when the Save button is pressed.") public static boolean saveAutomatically = false; public static void buildTree() { BASE = new ConfigNode(new Configuration()); - buildTree(BASE, new LinkedHashSet()); + Set visited = new LinkedHashSet(); + buildTree(BASE, visited); + Emulator.withComputer(c->{ + ConfigNode computer = new ConfigNode(BASE, c); + BASE.putChild(c.getName(), computer); + buildTree(computer, visited); + }); } private static void buildTree(ConfigNode node, Set visited) { @@ -299,8 +329,7 @@ public class Configuration implements Reconfigurable { continue; } - if (o instanceof Reconfigurable) { - Reconfigurable r = (Reconfigurable) o; + if (o instanceof Reconfigurable r) { ConfigNode child = node.findChild(r.getName()); if (child == null || !child.subject.equals(o)) { child = new ConfigNode(node, r); @@ -317,8 +346,7 @@ public class Configuration implements Reconfigurable { if (Optional.class.isAssignableFrom(type)) { Type genericTypes = f.getGenericType(); // System.out.println("Looking at generic parmeters " + genericTypes.getTypeName() + " for reconfigurable class, type " + genericTypes.getClass().getName()); - if (genericTypes instanceof GenericArrayType) { - GenericArrayType aType = (GenericArrayType) genericTypes; + if (genericTypes instanceof GenericArrayType aType) { ParameterizedType pType = (ParameterizedType) aType.getGenericComponentType(); if (pType.getActualTypeArguments().length != 1) { continue; @@ -450,7 +478,7 @@ public class Configuration implements Reconfigurable { public static boolean applySettings(ConfigNode node) { boolean resume = false; if (node == BASE) { - resume = Emulator.getComputer().pause(); + resume = Emulator.withComputer(c->c.pause(), false); } boolean hasChanged = false; if (node.changed) { @@ -469,7 +497,7 @@ public class Configuration implements Reconfigurable { } if (resume) { - Emulator.getComputer().resume(); + Emulator.withComputer(Computer::resume); } return hasChanged; diff --git a/Platform/Apple/tools/jace/src/main/java/jace/config/InvokableAction.java b/Platform/Apple/tools/jace/src/main/java/jace/config/InvokableAction.java index 739dcca1..16dce814 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/config/InvokableAction.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/config/InvokableAction.java @@ -26,19 +26,19 @@ import java.lang.annotation.Target; /** * A invokable action annotation means that an object method can be called by the end-user. * This serves as a hook for keybindings as well as semantic navigation potential. - *
+ *
* Name should be short, meaningful, and succinct. e.g. "Insert disk" - *
+ *
* Category can be used to group actions by overall topic, for example an automated table of contents - *
+ *
* Description is descriptive text which provides additional clarity, e.g. * "This will present you with a file selection dialog to pick a floppy disk image. * Currently, dos-ordered (DSK, DO), Prodos-ordered (PO), and Nibble (NIB) formats are supported. - *
+ *
* Alternatives should be delimited by semicolons) can provide more powerful search * For "insert disk", alternatives might be "change disk;switch disk" and * reboot might have alternatives as "warm start;cold start;boot;restart". - *
+ *
* NOTE: Any method that implements this must be public and take no parameters! * If a method signature is not correct, it will result in a runtime exception * when the action is triggered. There is no way to offer a compiler diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/CPU.java b/Platform/Apple/tools/jace/src/main/java/jace/core/CPU.java index a54c87da..4f0bd7b7 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/CPU.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/CPU.java @@ -127,11 +127,7 @@ public abstract class CPU extends Device { // If the debugger is active and we aren't ready for the next step, sleep and exit // Without the sleep, this would constitute a very rapid-fire loop and would eat // an unnecessary amount of CPU. - try { - Thread.sleep(10); - } catch (InterruptedException ex) { - Logger.getLogger(CPU.class.getName()).log(Level.SEVERE, null, ex); - } + Thread.onSpinWait(); return; } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/Computer.java b/Platform/Apple/tools/jace/src/main/java/jace/core/Computer.java index 7a1b33b1..c32bf0ef 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/Computer.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/Computer.java @@ -18,15 +18,6 @@ */ package jace.core; -import jace.config.ConfigurableField; -import jace.config.InvokableAction; -import jace.config.Reconfigurable; -import jace.lawless.FPSMonitorDevice; -import jace.state.StateManager; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.value.ChangeListener; - import java.io.IOException; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -34,6 +25,16 @@ import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; +import jace.LawlessLegends; +import jace.apple2e.SoftSwitches; +import jace.config.ConfigurableField; +import jace.config.InvokableAction; +import jace.config.Reconfigurable; +import jace.state.StateManager; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.value.ChangeListener; + /** * This is a very generic stub of a Computer and provides a generic set of * overall functionality, namely boot, pause and resume features. What sort of @@ -44,9 +45,9 @@ import java.util.logging.Logger; */ public abstract class Computer implements Reconfigurable { - public RAM memory; + private RAM memory; public CPU cpu; - public Video video; + private Video video; public Keyboard keyboard; public StateManager stateManager; public Motherboard motherboard; @@ -100,12 +101,20 @@ public abstract class Computer implements Reconfigurable { public void setMemory(RAM memory) { if (this.memory != memory) { + // for (SoftSwitches s : SoftSwitches.values()) { + // s.getSwitch().unregister(); + // } if (this.memory != null) { this.memory.detach(); } - memory.attach(); + this.memory = memory; + if (memory != null) { + memory.attach(); + for (SoftSwitches s : SoftSwitches.values()) { + s.getSwitch().register(); + } + } } - this.memory = memory; } public void waitForNextCycle() { @@ -118,6 +127,9 @@ public abstract class Computer implements Reconfigurable { public void setVideo(Video video) { this.video = video; + if (LawlessLegends.getApplication() != null) { + LawlessLegends.getApplication().controller.connectVideo(video); + } } public CPU getCpu() { @@ -159,6 +171,7 @@ public abstract class Computer implements Reconfigurable { Thread delayedStart = new Thread(() -> { try { romLoaded.get(); + memory.resetState(); coldStart(); } catch (InterruptedException | ExecutionException ex) { Logger.getLogger(Computer.class.getName()).log(Level.SEVERE, null, ex); @@ -166,6 +179,7 @@ public abstract class Computer implements Reconfigurable { }); delayedStart.start(); } else { + memory.resetState(); coldStart(); } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/Device.java b/Platform/Apple/tools/jace/src/main/java/jace/core/Device.java index ea86ecf1..415cb81f 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/Device.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/Device.java @@ -20,10 +20,9 @@ package jace.core; import jace.config.Reconfigurable; import jace.state.Stateful; -import org.eclipse.collections.api.factory.Sets; -import org.eclipse.collections.api.set.MutableSet; - import java.util.Collection; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; /** * Device is a very simple abstraction of any emulation component. A device @@ -41,11 +40,12 @@ import java.util.Collection; public abstract class Device implements Reconfigurable { protected Computer computer; - private final MutableSet children; + private final Set children; private Device[] childrenArray = new Device[0]; + private Runnable tickHandler = this::__doTickNotRunning; private Device() { - children = Sets.mutable.empty(); + children = new CopyOnWriteArraySet<>(); } public Device(Computer computer) { @@ -59,7 +59,9 @@ public abstract class Device implements Reconfigurable { @Stateful private boolean run = false; @Stateful - public boolean isPaused = false; + // Pausing a device overrides its run state, and is not reset by resuming directly + // Therefore a caller pausing a device must unpause it directly! + private boolean paused = false; @Stateful public boolean isAttached = false; @@ -68,10 +70,9 @@ public abstract class Device implements Reconfigurable { return; } children.add(d); - if (isAttached) { - d.attach(); - } - childrenArray = children.toArray(new Device[0]); + d.attach(); + childrenArray = children.toArray(Device[]::new); + updateTickHandler(); } public void removeChildDevice(Device d) { @@ -80,10 +81,9 @@ public abstract class Device implements Reconfigurable { } children.remove(d); d.suspend(); - if (isAttached) { - d.detach(); - } - childrenArray = children.toArray(new Device[0]); + d.detach(); + childrenArray = children.toArray(Device[]::new); + updateTickHandler(); } public void addAllDevices(Iterable devices) { @@ -91,7 +91,7 @@ public abstract class Device implements Reconfigurable { } public Iterable getChildren() { - return children.asUnmodifiable(); + return children; } public void setAllDevices(Collection newDevices) { @@ -111,28 +111,61 @@ public abstract class Device implements Reconfigurable { waitCycles = wait; } - public void doTick() { - if (isRunning()) { - for (Device d : childrenArray) { - d.doTick(); - } - if (waitCycles > 0) { - waitCycles--; - return; - } - tick(); + private void updateTickHandler() { + if (!isRunning() || isPaused()) { + tickHandler = this::__doTickNotRunning; + } else if (childrenArray.length == 0) { + tickHandler = this::__doTickNoDevices; + } else { + tickHandler = this::__doTickIsRunning; } } + + private void __doTickNotRunning() { + // Do nothing + } + + private void __doTickIsRunning() { + for (Device d : childrenArray) { + d.doTick(); + } + if (waitCycles <= 0) { + tick(); + return; + } + waitCycles--; + } + + private void __doTickNoDevices() { + if (waitCycles <= 0) { + tick(); + return; + } + waitCycles--; + } + + public final void doTick() { + tickHandler.run(); + } public boolean isRunning() { return run; } - - public synchronized void setRun(boolean run) { - isPaused = false; - this.run = run; + + public boolean isPaused() { + return paused; } + public final synchronized void setRun(boolean run) { + this.run = run; + updateTickHandler(); + } + + public final synchronized void setPaused(boolean paused) { + this.paused = paused; + updateTickHandler(); + } + protected abstract String getDeviceName(); @Override @@ -141,35 +174,25 @@ public abstract class Device implements Reconfigurable { } public abstract void tick(); - - private Runnable buildSuspendedRunnable(Runnable r) { - Runnable finalProcess = () -> { - if (isRunning()) { - suspend(); - r.run(); - resume(); - } else { - r.run(); - } - }; - for (Device child : getChildren()) { - finalProcess = child.buildSuspendedRunnable(finalProcess); - } - return finalProcess; - } public void whileSuspended(Runnable r) { - buildSuspendedRunnable(r).run(); + if (isRunning()) { + suspend(); + r.run(); + resume(); + } else { + r.run(); + } } public boolean suspend() { - children.forEach(Device::suspend); if (isRunning()) { // System.out.println(getName() + " Suspended"); // Utility.printStackTrace(); setRun(false); return true; } + children.forEach(Device::suspend); return false; } @@ -189,8 +212,8 @@ public abstract class Device implements Reconfigurable { } public void detach() { - Keyboard.unregisterAllHandlers(this); children.forEach(Device::suspend); children.forEach(Device::detach); + Keyboard.unregisterAllHandlers(this); } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/Font.java b/Platform/Apple/tools/jace/src/main/java/jace/core/Font.java index d142fa9b..896c16bd 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/Font.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/Font.java @@ -45,7 +45,7 @@ public class Font { initialized = true; font = new int[256][8]; Thread fontLoader = new Thread(() -> { - InputStream in = Font.class.getClassLoader().getResourceAsStream("jace/data/font.png"); + InputStream in = Font.class.getResourceAsStream("/jace/data/font.png"); Image image = new Image(in); PixelReader reader = image.getPixelReader(); for (int i = 0; i < 256; i++) { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/Keyboard.java b/Platform/Apple/tools/jace/src/main/java/jace/core/Keyboard.java index 8c1bfdb7..aa8ccbec 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/Keyboard.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/Keyboard.java @@ -18,15 +18,7 @@ */ package jace.core; -import jace.Emulator; -import jace.apple2e.SoftSwitches; -import jace.config.InvokableAction; -import jace.config.Reconfigurable; -import javafx.event.EventHandler; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyEvent; - -import java.awt.*; +import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.UnsupportedFlavorException; @@ -42,6 +34,14 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import jace.Emulator; +import jace.apple2e.SoftSwitches; +import jace.config.InvokableAction; +import jace.config.Reconfigurable; +import javafx.event.EventHandler; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; + /** * Keyboard manages all keyboard-related activities. For now, all hotkeys are * hard-coded. The eventual direction for this class is to only manage key @@ -104,7 +104,7 @@ public class Keyboard implements Reconfigurable { registerKeyHandler(new KeyHandler(code) { @Override public boolean handleKeyUp(KeyEvent e) { - Emulator.getComputer().getKeyboard().shiftPressed = e.isShiftDown(); + Emulator.withComputer(c -> c.getKeyboard().shiftPressed = e.isShiftDown()); if (action == null || !action.notifyOnRelease()) { return false; } @@ -128,7 +128,7 @@ public class Keyboard implements Reconfigurable { @Override public boolean handleKeyDown(KeyEvent e) { // System.out.println("Key down: "+method.toString()); - Emulator.getComputer().getKeyboard().shiftPressed = e.isShiftDown(); + Emulator.withComputer(c -> c.getKeyboard().shiftPressed = e.isShiftDown()); Object returnValue = null; try { if (method.getParameterCount() > 0) { @@ -251,7 +251,7 @@ public class Keyboard implements Reconfigurable { default: } - Emulator.getComputer().getKeyboard().shiftPressed = e.isShiftDown(); + Emulator.withComputer(compter -> computer.getKeyboard().shiftPressed = e.isShiftDown()); if (e.isShiftDown()) { c = fixShiftedChar(c); } @@ -311,20 +311,20 @@ public class Keyboard implements Reconfigurable { @InvokableAction(name = "Open Apple Key", alternatives = "OA", category = "Keyboard", notifyOnRelease = true, defaultKeyMapping = "Alt", consumeKeyEvent = false) public void openApple(boolean pressed) { - boolean isRunning = computer.pause(); + // boolean isRunning = computer.pause(); SoftSwitches.PB0.getSwitch().setState(pressed); - if (isRunning) { - computer.resume(); - } + // if (isRunning) { + // computer.resume(); + // } } @InvokableAction(name = "Closed Apple Key", alternatives = "CA", category = "Keyboard", notifyOnRelease = true, defaultKeyMapping = {"Shortcut","Meta","Command"}, consumeKeyEvent = false) public void solidApple(boolean pressed) { - boolean isRunning = computer.pause(); + // boolean isRunning = computer.pause(); SoftSwitches.PB1.getSwitch().setState(pressed); - if (isRunning) { - computer.resume(); - } + // if (isRunning) { + // computer.resume(); + // } } public static void pasteFromString(String text) { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/Motherboard.java b/Platform/Apple/tools/jace/src/main/java/jace/core/Motherboard.java index 9b20bcb6..dc087741 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/Motherboard.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/Motherboard.java @@ -18,12 +18,12 @@ */ package jace.core; +import java.util.HashSet; + import jace.apple2e.SoftSwitches; import jace.apple2e.Speaker; import jace.config.ConfigurableField; -import java.util.HashSet; - /** * Motherboard is the heart of the computer. It can have a list of cards * inserted (the behavior and number of cards is determined by the Memory class) @@ -75,15 +75,18 @@ public class Motherboard extends TimedDevice { public String getShortName() { return "mb"; } - @ConfigurableField(category = "advanced", shortName = "cpuPerClock", name = "CPU per clock", defaultValue = "1", description = "Number of CPU cycles per clock cycle (normal = 1)") - public static int cpuPerClock = 1; + @ConfigurableField(category = "advanced", shortName = "cpuPerClock", name = "CPU per clock", defaultValue = "1", description = "Number of extra CPU cycles per clock cycle (normal = 1)") + public static int cpuPerClock = 0; public int clockCounter = 1; @Override public void tick() { // Extra CPU cycles requested, other devices are called by the TimedDevice abstraction for (int i=1; i < cpuPerClock; i++) { - computer.getCpu().doTick(); + computer.getCpu().doTick(); + if (Speaker.force1mhz) { + speaker.tick(); + } } /* try { @@ -106,12 +109,12 @@ public class Motherboard extends TimedDevice { } // From the holy word of Sather 3:5 (Table 3.1) :-) // This average speed averages in the "long" cycles - public static long SPEED = 1020484L; // (NTSC) + public static final long DEFAULT_SPEED = 1020484L; // (NTSC) //public static long SPEED = 1015625L; // (PAL) @Override public long defaultCyclesPerSecond() { - return SPEED; + return DEFAULT_SPEED; } @Override @@ -126,13 +129,10 @@ public class Motherboard extends TimedDevice { try { if (speaker == null) { speaker = new Speaker(computer); - if (computer.mixer.lineAvailable) { - speaker.attach(); - } else { - System.out.print("No lines available! Speaker not running."); - } + speaker.attach(); } speaker.reconfigure(); + addChildDevice(speaker); } catch (Throwable t) { System.out.println("Unable to initalize sound -- deactivating speaker out"); } @@ -157,12 +157,14 @@ public class Motherboard extends TimedDevice { } void adjustRelativeSpeeds() { - if (isMaxSpeed()) { - computer.getVideo().setWaitPerCycle(8); - } else if (getSpeedInHz() > SPEED) { - computer.getVideo().setWaitPerCycle(getSpeedInHz()/SPEED); - } else { - computer.getVideo().setWaitPerCycle(0); - } + if (computer.getVideo() != null) { + if (isMaxSpeed()) { + computer.getVideo().setWaitPerCycle(8); + } else if (getSpeedInHz() > DEFAULT_SPEED) { + computer.getVideo().setWaitPerCycle(getSpeedInHz() / DEFAULT_SPEED); + } else { + computer.getVideo().setWaitPerCycle(0); + } + } } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/PagedMemory.java b/Platform/Apple/tools/jace/src/main/java/jace/core/PagedMemory.java index 5336a7a3..fd0b77d3 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/PagedMemory.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/PagedMemory.java @@ -18,9 +18,10 @@ */ package jace.core; +import java.util.Arrays; + import jace.state.StateManager; import jace.state.Stateful; -import java.util.Arrays; /** * This represents bank-switchable ram which can reside at fixed portions of the @@ -77,7 +78,7 @@ public class PagedMemory { loadData(romData); } - public void loadData(byte[] romData) { + public final void loadData(byte[] romData) { for (int i = 0; i < romData.length; i += 256) { byte[] b = new byte[256]; System.arraycopy(romData, i, b, 0, 256); diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/RAM.java b/Platform/Apple/tools/jace/src/main/java/jace/core/RAM.java index f1c1287e..be6dd540 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/RAM.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/RAM.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; /** * RAM is a 64K address space of paged memory. It also manages sets of memory @@ -125,7 +126,7 @@ public abstract class RAM implements Reconfigurable { public void writeWord(int address, int w, boolean generateEvent, boolean requireSynchronization) { write(address, (byte) (w & 0x0ff), generateEvent, requireSynchronization); - write(address + 1, (byte) (w >> 8), generateEvent, requireSynchronization); + write(address + 1, (byte) ((w >> 8) & 0x0ff), generateEvent, requireSynchronization); } public byte readRaw(int address) { @@ -137,7 +138,8 @@ public abstract class RAM implements Reconfigurable { // if (address >= 65536) return 0; byte value = activeRead.getMemoryPage(address)[address & 0x0FF]; // if (triggerEvent || ((address & 0x0FF00) == 0x0C000)) { - if (triggerEvent || (address & 0x0FFF0) == 0x0c030) { +// if (triggerEvent || (address & 0x0FFF0) == 0x0c030) { + if (triggerEvent) { value = callListener(eventType, address, value, value, requireSyncronization); } return value; @@ -165,7 +167,7 @@ public abstract class RAM implements Reconfigurable { } ioListeners.add(l); } else { - int index = address >> 8; + int index = (address >> 8) & 0x0FF; Set otherListeners = listenerMap[index]; if (otherListeners == null) { otherListeners = Collections.synchronizedSet(new HashSet<>()); @@ -179,11 +181,12 @@ public abstract class RAM implements Reconfigurable { if (l.getScope() == RAMEvent.SCOPE.ADDRESS) { mapListener(l, l.getScopeStart()); } else { - int start = 0; - int end = 0x0ffff; - if (l.getScope() == RAMEvent.SCOPE.RANGE) { - start = l.getScopeStart(); - end = l.getScopeEnd(); + int start = l.getScopeStart(); + int end = l.getScopeEnd(); + if (l.getScope() == RAMEvent.SCOPE.ANY) { + Thread.dumpStack(); + start = 0; + end = 0x0FFFF; } for (int i = start; i <= end; i++) { mapListener(l, i); @@ -269,17 +272,31 @@ public abstract class RAM implements Reconfigurable { } public RAMListener addListener(final RAMListener l) { - boolean restart = computer.pause(); if (listeners.contains(l)) { return l; } - listeners.add(l); - addListenerRange(l); - if (restart) { - computer.resume(); - } + computer.cpu.whileSuspended(()->{ + listeners.add(l); + addListenerRange(l); + }); return l; } + + public RAMListener addExecutionTrap(int address, Consumer handler) { + RAMListener listener = new RAMListener(RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) { + @Override + protected void doConfig() { + setScopeStart(address); + } + + @Override + protected void doEvent(RAMEvent e) { + handler.accept(e); + } + }; + addListener(listener); + return listener; + } public void removeListener(final RAMListener l) { boolean restart = computer.pause(); @@ -330,4 +347,6 @@ public abstract class RAM implements Reconfigurable { abstract public void performExtendedCommand(int i); abstract public String getState(); + + abstract public void resetState(); } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/RAMEvent.java b/Platform/Apple/tools/jace/src/main/java/jace/core/RAMEvent.java index 1e0d5c50..18aaec5b 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/RAMEvent.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/RAMEvent.java @@ -39,8 +39,8 @@ public class RAMEvent { READ(true), READ_DATA(true), - EXECUTE(true), READ_OPERAND(true), + EXECUTE(true), WRITE(false), ANY(false); boolean read; @@ -98,6 +98,9 @@ public class RAMEvent { } public final void setType(TYPE type) { + if (type == TYPE.ANY) { + throw new RuntimeException("Event type=Any is reserved for listeners, not for triggering events!"); + } this.type = type; } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/RAMListener.java b/Platform/Apple/tools/jace/src/main/java/jace/core/RAMListener.java index c12b1227..c02de2e9 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/RAMListener.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/RAMListener.java @@ -118,16 +118,6 @@ public abstract class RAMListener implements RAMEvent.RAMEventHandler { } public boolean isRelevant(RAMEvent e) { - // Skip event if it's not the right type - if (type != TYPE.ANY && type != e.getType()) { - if (e.getType() != TYPE.ANY && type == TYPE.READ) { - if (!e.getType().isRead()) { - return false; - } - } else { - return false; - } - } // Skip event if it's not in the scope we care about if (scope != RAMEvent.SCOPE.ANY) { if (scope == RAMEvent.SCOPE.ADDRESS && e.getAddress() != scopeStart) { @@ -137,6 +127,11 @@ public abstract class RAMListener implements RAMEvent.RAMEventHandler { } } + // Skip event if it's not the right type + if (!(type == TYPE.ANY || type == e.getType() || (type == TYPE.READ && e.getType().isRead()))) { + return false; + } + // Skip event if the value modification is uninteresting if (value != RAMEvent.VALUE.ANY) { if (value == RAMEvent.VALUE.CHANGE_BY && e.getNewValue() - e.getOldValue() != valueAmount) { @@ -154,7 +149,7 @@ public abstract class RAMListener implements RAMEvent.RAMEventHandler { } @Override - public void handleEvent(RAMEvent e) { + public final void handleEvent(RAMEvent e) { if (isRelevant(e)) { doEvent(e); } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/SoftSwitch.java b/Platform/Apple/tools/jace/src/main/java/jace/core/SoftSwitch.java index 8a937197..aba74d74 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/SoftSwitch.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/SoftSwitch.java @@ -18,11 +18,13 @@ */ package jace.core; -import jace.state.Stateful; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import jace.Emulator; +import jace.state.Stateful; + /** * A softswitch is a hidden bit that lives in the MMU, it can be activated or * deactivated to change operating characteristics of the computer such as video @@ -47,7 +49,6 @@ public abstract class SoftSwitch { private final List exclusionQuery = new ArrayList<>(); private final String name; private boolean toggleType = false; - protected Computer computer; /** * Creates a new instance of SoftSwitch @@ -145,7 +146,7 @@ public abstract class SoftSwitch { @Override protected void doEvent(RAMEvent e) { if (e.getType().isRead()) { - e.setNewValue(computer.getVideo().getFloatingBus()); + e.setNewValue(Emulator.withComputer(c->c.getVideo().getFloatingBus(), (byte) 0)); } if (!exclusionActivate.contains(e.getAddress())) { // System.out.println("Access to "+Integer.toHexString(e.getAddress())+" ENABLES switch "+getName()); @@ -239,16 +240,16 @@ public abstract class SoftSwitch { } } - public void register(Computer computer) { - this.computer = computer; - listeners.forEach(computer.getMemory()::addListener); + public void register() { + Emulator.withMemory(m -> { + listeners.forEach(m::addListener); + }); } public void unregister() { - if (computer != null && computer.getMemory() != null) { - listeners.forEach(computer.getMemory()::removeListener); - } - this.computer = null; + Emulator.withMemory(m -> { + listeners.forEach(m::removeListener); + }); } public void setState(boolean newState) { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/SoundMixer.java b/Platform/Apple/tools/jace/src/main/java/jace/core/SoundMixer.java index fe0b5910..25e4c1dd 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/SoundMixer.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/SoundMixer.java @@ -18,27 +18,17 @@ */ package jace.core; -import jace.config.ConfigurableField; -import jace.config.DynamicSelection; -import jace.config.Reconfigurable; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; -import javax.sound.sampled.Line; import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.Mixer; -import javax.sound.sampled.Mixer.Info; import javax.sound.sampled.SourceDataLine; +import jace.config.ConfigurableField; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.sound.sampled.FloatControl; + /** * Manages sound resources used by various audio devices (such as speaker and * mockingboard cards.) The plumbing is managed in this class so that the @@ -50,8 +40,6 @@ import javax.sound.sampled.SourceDataLine; */ public class SoundMixer extends Device { - private final Set availableLines = Collections.synchronizedSet(new HashSet<>()); - private final Map activeLines = Collections.synchronizedMap(new HashMap<>()); /** * Bits per sample */ @@ -65,37 +53,36 @@ public class SoundMixer extends Device { @ConfigurableField(name = "Mute", shortName = "mute") public static boolean MUTE = false; - /** - * Sound format used for playback - */ - private AudioFormat af; - /** - * Is sound line available for playback at all? - */ - public boolean lineAvailable; - @ConfigurableField(name = "Audio device", description = "Audio output device") - public static DynamicSelection preferredMixer = new DynamicSelection(null) { - @Override - public boolean allowNull() { - return false; - } - - @Override - public LinkedHashMap getSelections() { - Info[] mixerInfo = AudioSystem.getMixerInfo(); - LinkedHashMap out = new LinkedHashMap<>(); - for (Info i : mixerInfo) { - out.put(i.getName(), i.getName()); - } - return out; - } - }; - private Mixer theMixer; - public SoundMixer(Computer computer) { super(computer); } + /** + * Get a javafx media sourcedataline for stereo 44.1KHz 16-bit signed PCM data. + * Confirm the line is open before using it. + * + * @return + */ + public SourceDataLine getLine() { + if (MUTE) { + return null; + } + + SourceDataLine line = null; + try { + // WAV is a little endian format, so it makes sense to stick with that. + AudioFormat format = new AudioFormat(RATE, BITS, 2, true, false); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); + line = (SourceDataLine) AudioSystem.getLine(info); + line.open(format); + line.start(); +// Logger.getLogger(getClass().getName()).log(Level.INFO, "Obtained source data line: %s, buffer size %d".formatted(line.getFormat(), line.getBufferSize())); + } catch (LineUnavailableException e) { + Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Error getting sound line: {0}", e.getMessage()); + } + return line; + } + @Override public String getDeviceName() { return "Sound Output"; @@ -110,91 +97,9 @@ public class SoundMixer extends Device { public synchronized void reconfigure() { if (MUTE) { detach(); - } else if (isConfigDifferent()) { - detach(); - try { - initMixer(); - if (lineAvailable) { - initAudio(); - } else { - System.out.println("Sound not stared: Line not available"); - } - } catch (LineUnavailableException ex) { - System.out.println("Unable to start sound"); - Logger.getLogger(SoundMixer.class.getName()).log(Level.SEVERE, null, ex); - } - attach(); } } - private AudioFormat getAudioFormat() { - return new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, RATE, BITS, 2, BITS / 4, RATE, true); - } - - /** - * Obtain sound playback line if available - * - * @throws javax.sound.sampled.LineUnavailableException If there is no line - * available - */ - private void initAudio() throws LineUnavailableException { - af = getAudioFormat(); - DataLine.Info dli = new DataLine.Info(SourceDataLine.class, af); - lineAvailable = AudioSystem.isLineSupported(dli); - } - - public synchronized SourceDataLine getLine(Object requester) throws LineUnavailableException { - if (activeLines.containsKey(requester)) { - return activeLines.get(requester); - } - SourceDataLine sdl; - if (availableLines.isEmpty()) { - sdl = getNewLine(); - } else { - sdl = availableLines.iterator().next(); - availableLines.remove(sdl); - } - sdl.start(); - activeLines.put(requester, sdl); - return sdl; - } - - public void returnLine(Object requester) { - if (activeLines.containsKey(requester)) { - SourceDataLine sdl = activeLines.remove(requester); - if (sdl.isRunning()) { - sdl.flush(); - sdl.stop(); - } - availableLines.add(sdl); - } - } - - private SourceDataLine getNewLine() throws LineUnavailableException { - SourceDataLine l = null; -// Line.Info[] info = theMixer.getSourceLineInfo(); - DataLine.Info dli = new DataLine.Info(SourceDataLine.class, af); - System.out.println("Maximum output lines: " + theMixer.getMaxLines(dli)); - System.out.println("Allocated output lines: " + theMixer.getSourceLines().length); - System.out.println("Getting source line from " + theMixer.getMixerInfo().toString() + ": " + af.toString()); - try { - l = (SourceDataLine) theMixer.getLine(dli); - } catch (IllegalArgumentException e) { - lineAvailable = false; - throw new LineUnavailableException(e.getMessage()); - } catch (LineUnavailableException e) { - lineAvailable = false; - throw e; - } - if (!(l instanceof SourceDataLine)) { - lineAvailable = false; - throw new LineUnavailableException("Line is not an output line!"); - } - final SourceDataLine sdl = l; - sdl.open(); - return sdl; - } - public byte randomByte() { return (byte) (Math.random() * 256); } @@ -202,70 +107,4 @@ public class SoundMixer extends Device { @Override public void tick() { } - - @Override - public void attach() { - } - - @Override - public void detach() { - availableLines.stream().forEach((line) -> { - line.flush(); - line.stop(); - line.close(); - }); - Set requesters = new HashSet(activeLines.keySet()); - availableLines.clear(); - activeLines.clear(); - requesters.stream().map((o) -> { - if (o instanceof Device) { - ((Device) o).detach(); - } - return o; - }).filter((o) -> (o instanceof Card)).forEach((o) -> { - ((Reconfigurable) o).reconfigure(); - }); - super.detach(); - } - - private void initMixer() { - Info selected; - Info[] mixerInfo = AudioSystem.getMixerInfo(); - - if (mixerInfo == null || mixerInfo.length == 0) { - theMixer = null; - lineAvailable = false; - System.out.println("No sound mixer is available!"); - return; - } - - String mixer = preferredMixer.getValue(); - selected = mixerInfo[0]; - for (Info i : mixerInfo) { - if (i.getName().equalsIgnoreCase(mixer)) { - selected = i; - break; - } - } - theMixer = AudioSystem.getMixer(selected); -// for (Line l : theMixer.getSourceLines()) { -// l.close(); -// } - lineAvailable = true; - } - - String oldPreferredMixer = null; - - private boolean isConfigDifferent() { - boolean changed = false; - AudioFormat newAf = getAudioFormat(); - changed |= (af == null || !newAf.matches(af)); - if (oldPreferredMixer == null) { - changed |= preferredMixer.getValue() != null; - } else { - changed |= !oldPreferredMixer.matches(Pattern.quote(preferredMixer.getValue())); - } - oldPreferredMixer = preferredMixer.getValue(); - return changed; - } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/TimedDevice.java b/Platform/Apple/tools/jace/src/main/java/jace/core/TimedDevice.java index 42d791cf..23ebf03f 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/TimedDevice.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/TimedDevice.java @@ -20,8 +20,6 @@ package jace.core; import jace.config.ConfigurableField; -import java.util.concurrent.locks.LockSupport; - /** * A timed device is a device which executes so many ticks in a given time interval. This is the core of the emulator * timing mechanics. @@ -75,7 +73,7 @@ public abstract class TimedDevice extends Device { if (!isRunning()) { return false; } - isPaused = true; + setPaused(true); try { // KLUDGE: Sleeping to wait for worker thread to hit paused state. We might be inside the worker (?) Thread.sleep(10); @@ -84,10 +82,20 @@ public abstract class TimedDevice extends Device { return true; } + /** + * This is used in unit tests where we want the device + * to act like it is resumed, but not actual free-running. + * This allows tests to step manually to check behaviors, etc. + */ + public void resumeInThread() { + super.resume(); + setPaused(false); + } + @Override public void resume() { super.resume(); - isPaused = false; + setPaused(false); if (worker != null && worker.isAlive()) { return; } @@ -96,9 +104,9 @@ public abstract class TimedDevice extends Device { while (isRunning()) { hasStopped = false; doTick(); - while (isPaused) { + while (isPaused() && isRunning()) { hasStopped = true; - LockSupport.parkNanos(1000); + Thread.onSpinWait(); } if (!maxspeed) { resync(); @@ -154,9 +162,6 @@ public abstract class TimedDevice extends Device { setSpeedInHz(cyclesPerSecond); } - long skip = 0; - long wait = 0; - public final void resetSyncTimer() { nextSync = System.nanoTime() + nanosPerInterval; cycleTimer = 0; @@ -173,7 +178,7 @@ public abstract class TimedDevice extends Device { protected void resync() { if (++cycleTimer >= cyclesPerInterval) { - if (tempSpeedDuration > 0) { + if (tempSpeedDuration > 0 || isMaxSpeed()) { tempSpeedDuration -= cyclesPerInterval; resetSyncTimer(); return; diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/Utility.java b/Platform/Apple/tools/jace/src/main/java/jace/core/Utility.java index bded0f4d..effe5de5 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/Utility.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/Utility.java @@ -18,6 +18,25 @@ */ package jace.core; +import java.io.File; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.reflections.Reflections; + import jace.config.Configuration; import jace.config.InvokableAction; import javafx.application.Platform; @@ -31,16 +50,6 @@ import javafx.scene.effect.DropShadow; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.paint.Color; -import org.reflections.Reflections; - -import java.io.File; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; /** * This is a set of helper functions which do not belong anywhere else. @@ -159,7 +168,7 @@ public class Utility { if (isHeadless) { return Optional.empty(); } - InputStream stream = Utility.class.getClassLoader().getResourceAsStream("jace/data/" + filename); + InputStream stream = Utility.class.getResourceAsStream("/jace/data/" + filename); return Optional.of(new Image(stream)); } @@ -171,8 +180,7 @@ public class Utility { Label label = new Label() { @Override public boolean equals(Object obj) { - if (obj instanceof Label) { - Label l2 = (Label) obj; + if (obj instanceof Label l2) { return super.equals(l2) || l2.getText().equals(getText()); } else { return super.equals(obj); @@ -218,9 +226,9 @@ public class Utility { Optional response = confirm.showAndWait(); response.ifPresent(b -> { if (b.getButtonData() == ButtonData.LEFT && aAction != null) { - (new Thread(aAction)).start(); + Platform.runLater(aAction); } else if (b.getButtonData() == ButtonData.RIGHT && bAction != null) { - (new Thread(bAction)).start(); + Platform.runLater(bAction); } }); }); @@ -326,8 +334,8 @@ public class Utility { if (s == null) { return -1; } - if (s instanceof Integer) { - return (Integer) s; + if (s instanceof Integer integer) { + return integer; } String val = String.valueOf(s).trim(); int base = 10; @@ -347,17 +355,27 @@ public class Utility { } public static void gripe(final String message) { + gripe(message, false, null); + } + + public static void gripe(final String message, boolean wait, Runnable andThen) { Platform.runLater(() -> { Alert errorAlert = new Alert(Alert.AlertType.ERROR); errorAlert.setContentText(message); errorAlert.setTitle("Error"); - errorAlert.show(); + if (wait) { + errorAlert.showAndWait(); + if (andThen != null) { + andThen.run(); + } + } else { + errorAlert.show(); + } }); } public static Object findChild(Object object, String fieldName) { - if (object instanceof Map) { - Map map = (Map) object; + if (object instanceof Map map) { for (Object key : map.keySet()) { if (key.toString().equalsIgnoreCase(fieldName)) { return map.get(key); @@ -444,28 +462,28 @@ public class Utility { if (type.equals(Integer.TYPE) || type == Integer.class) { value = value.replaceAll(hex ? "[^0-9\\-A-Fa-f]" : "[^0-9\\-]", ""); try { - return Integer.parseInt(value, radix); + return Integer.valueOf(value, radix); } catch (NumberFormatException ex) { return null; } } else if (type.equals(Short.TYPE) || type == Short.class) { value = value.replaceAll(hex ? "[^0-9\\-\\.A-Fa-f]" : "[^0-9\\-\\.]", ""); try { - return Short.parseShort(value, radix); + return Short.valueOf(value, radix); } catch (NumberFormatException ex) { return null; } } else if (type.equals(Long.TYPE) || type == Long.class) { value = value.replaceAll(hex ? "[^0-9\\-\\.A-Fa-f]" : "[^0-9\\-\\.]", ""); try { - return Long.parseLong(value, radix); + return Long.valueOf(value, radix); } catch (NumberFormatException ex) { return null; } } else if (type.equals(Byte.TYPE) || type == Byte.class) { try { value = value.replaceAll(hex ? "[^0-9\\-A-Fa-f]" : "[^0-9\\-]", ""); - return Byte.parseByte(value, radix); + return Byte.valueOf(value, radix); } catch (NumberFormatException ex) { return null; } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/Video.java b/Platform/Apple/tools/jace/src/main/java/jace/core/Video.java index 29b3beea..1f3cb4fb 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/Video.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/Video.java @@ -19,9 +19,9 @@ package jace.core; import jace.Emulator; -import jace.state.Stateful; import jace.config.ConfigurableField; import jace.config.InvokableAction; +import jace.state.Stateful; import javafx.scene.image.Image; import javafx.scene.image.WritableImage; @@ -66,7 +66,6 @@ public abstract class Video extends Device { private boolean screenDirty = true; private boolean lineDirty = true; private boolean isVblank = false; - static final VideoWriter[][] writerCheck = new VideoWriter[40][192]; static void initLookupTables() { for (int i = 0; i < 192; i++) { @@ -81,7 +80,6 @@ public abstract class Video extends Device { } } private int forceRedrawRowCount = 0; - Thread updateThread; /** * Creates a new instance of Video @@ -276,9 +274,7 @@ public abstract class Video extends Device { alternatives = "redraw", defaultKeyMapping = {"ctrl+shift+r"}) public static void forceRefresh() { - if (Emulator.getComputer().video != null) { - Emulator.getComputer().video._forceRefresh(); - } + Emulator.withVideo(v->v._forceRefresh()); } private void _forceRefresh() { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardAppleMouse.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardAppleMouse.java index 29f03595..a463ccf5 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardAppleMouse.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardAppleMouse.java @@ -28,16 +28,16 @@ import jace.core.Computer; import jace.core.PagedMemory; import jace.core.RAMEvent; import jace.core.RAMEvent.TYPE; -import jace.state.Stateful; import jace.core.Utility; +import jace.state.Stateful; import javafx.event.EventHandler; import javafx.geometry.Bounds; +import javafx.geometry.Point2D; +import javafx.geometry.Rectangle2D; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; -import javafx.geometry.Point2D; -import javafx.geometry.Rectangle2D; /** * Apple Mouse interface implementation. This is fully compatible with several @@ -359,7 +359,7 @@ public class CardAppleMouse extends Card { * //gs homes mouse to low address, but //c and //e do not */ private void clampMouse() { - RAM128k memory = (RAM128k) computer.memory; + RAM128k memory = (RAM128k) computer.getMemory(); byte clampMinLo = memory.getMainMemory().readByte(0x0478); byte clampMaxLo = memory.getMainMemory().readByte(0x04F8); byte clampMinHi = memory.getMainMemory().readByte(0x0578); @@ -444,30 +444,14 @@ public class CardAppleMouse extends Card { byte reg = computer.getMemory().readRaw(0x0478); byte val = 0; switch (reg - 0x047) { - case 0: - val = (byte) ((int) clampWindow.getMinX() >> 8); - break; - case 1: - val = (byte) ((int) clampWindow.getMinY() >> 8); - break; - case 2: - val = (byte) ((int) clampWindow.getMinX() & 255); - break; - case 3: - val = (byte) ((int) clampWindow.getMinY() & 255); - break; - case 4: - val = (byte) ((int) clampWindow.getMaxX() >> 8); - break; - case 5: - val = (byte) ((int) clampWindow.getMaxY() >> 8); - break; - case 6: - val = (byte) ((int) clampWindow.getMaxX() & 255); - break; - case 7: - val = (byte) ((int) clampWindow.getMaxY() & 255); - break; + case 0 -> val = (byte) ((int) clampWindow.getMinX() >> 8); + case 1 -> val = (byte) ((int) clampWindow.getMinY() >> 8); + case 2 -> val = (byte) ((int) clampWindow.getMinX() & 255); + case 3 -> val = (byte) ((int) clampWindow.getMinY() & 255); + case 4 -> val = (byte) ((int) clampWindow.getMaxX() >> 8); + case 5 -> val = (byte) ((int) clampWindow.getMaxY() >> 8); + case 6 -> val = (byte) ((int) clampWindow.getMaxX() & 255); + case 7 -> val = (byte) ((int) clampWindow.getMaxY() & 255); } computer.getMemory().write(0x0578, val, false, false); } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardDiskII.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardDiskII.java index 79eea458..9a07b1ee 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardDiskII.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardDiskII.java @@ -62,7 +62,7 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar public CardDiskII(Computer computer) { super(computer); try { - loadRom("jace/data/DiskII.rom"); + loadRom("/jace/data/DiskII.rom"); } catch (IOException ex) { Logger.getLogger(CardDiskII.class.getName()).log(Level.SEVERE, null, ex); } @@ -168,7 +168,8 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar } public void loadRom(String path) throws IOException { - InputStream romFile = CardDiskII.class.getClassLoader().getResourceAsStream(path); + InputStream romFile = CardDiskII.class.getResourceAsStream(path); + final int cxRomLength = 0x100; byte[] romData = new byte[cxRomLength]; try { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardMockingboard.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardMockingboard.java index 7dc52c85..7f62adfc 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardMockingboard.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardMockingboard.java @@ -131,8 +131,8 @@ public class CardMockingboard extends Card implements Runnable { return "timer" + j; } - public void doTick() { - super.doTick(); + public void tick() { + super.tick(); if (controller == 0) { doSoundTick(); } @@ -345,8 +345,13 @@ public class CardMockingboard extends Card implements Runnable { * This is the audio playback thread */ public void run() { + SourceDataLine out = null; try { - SourceDataLine out = computer.mixer.getLine(this); + out = computer.mixer.getLine(); + if (out == null) { + setRun(false); + return; + } int[] leftBuffer = new int[BUFFER_LENGTH]; int[] rightBuffer = new int[BUFFER_LENGTH]; int frameSize = out.getFormat().getFrameSize(); @@ -354,7 +359,7 @@ public class CardMockingboard extends Card implements Runnable { System.out.println("Mockingboard playback started"); int bytesPerSample = frameSize / 2; buildMixerTable(); - ticksBetweenPlayback = (int) ((Motherboard.SPEED * BUFFER_LENGTH) / SAMPLE_RATE); + ticksBetweenPlayback = (int) ((Motherboard.DEFAULT_SPEED * BUFFER_LENGTH) / SAMPLE_RATE); System.out.println("Ticks between playback: "+ticksBetweenPlayback); ticksSinceLastPlayback = 0; int zeroSamples = 0; @@ -426,15 +431,15 @@ public class CardMockingboard extends Card implements Runnable { } } } - } catch (LineUnavailableException ex) { - Logger.getLogger(CardMockingboard.class - .getName()).log(Level.SEVERE, null, ex); } catch (InterruptedException ex) { Logger.getLogger(CardMockingboard.class.getName()).log(Level.SEVERE, null, ex); } finally { computer.getMotherboard().cancelSpeedRequest(this); System.out.println("Mockingboard playback stopped"); - computer.mixer.returnLine(this); + if (out != null && out.isRunning()) { + out.drain(); + out.close(); + } } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardRamFactor.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardRamFactor.java index c2131074..f50efb2d 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardRamFactor.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardRamFactor.java @@ -68,7 +68,7 @@ public class CardRamFactor extends Card { super(computer); indicator = Utility.loadIconLabel("ram.png"); try { - loadRom("jace/data/RAMFactor14.rom"); + loadRom("/jace/data/RAMFactor14.rom"); } catch (IOException ex) { Logger.getLogger(CardRamFactor.class.getName()).log(Level.SEVERE, null, ex); } @@ -215,7 +215,7 @@ public class CardRamFactor extends Card { final int cxRomLength = 0x02000; byte[] romData = new byte[cxRomLength]; public void loadRom(String path) throws IOException { - InputStream romFile = CardRamFactor.class.getClassLoader().getResourceAsStream(path); + InputStream romFile = CardRamFactor.class.getResourceAsStream(path); try { if (romFile.read(romData) != cxRomLength) { throw new IOException("Bad RamFactor rom size"); diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardSSC.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardSSC.java index 67014a4e..29976a14 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardSSC.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardSSC.java @@ -111,7 +111,7 @@ public class CardSSC extends Card implements Reconfigurable { @Override public void setSlot(int slot) { try { - loadRom("jace/data/SSC.rom"); + loadRom("/jace/data/SSC.rom"); } catch (IOException ex) { Logger.getLogger(CardSSC.class.getName()).log(Level.SEVERE, null, ex); } @@ -178,7 +178,7 @@ public class CardSSC extends Card implements Reconfigurable { public void loadRom(String path) throws IOException { // Load rom file, first 0x0700 bytes are C8 rom, last 0x0100 bytes are CX rom // CF00-CFFF are unused by the SSC - InputStream romFile = CardSSC.class.getClassLoader().getResourceAsStream(path); + InputStream romFile = CardSSC.class.getResourceAsStream(path); final int cxRomLength = 0x0100; final int c8RomLength = 0x0700; byte[] romxData = new byte[cxRomLength]; diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardThunderclock.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardThunderclock.java index 5419acbf..98248f49 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardThunderclock.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardThunderclock.java @@ -62,7 +62,7 @@ public class CardThunderclock extends Card { public CardThunderclock(Computer computer) { super(computer); try { - loadRom("jace/data/thunderclock_plus.rom"); + loadRom("/jace/data/thunderclock_plus.rom"); } catch (IOException ex) { Logger.getLogger(CardDiskII.class.getName()).log(Level.SEVERE, null, ex); } @@ -169,13 +169,13 @@ public class CardThunderclock extends Card { if (timerEnabled) { switch (value & 0x038) { case 0x020: - timerRate = (int) (Motherboard.SPEED / 64); + timerRate = (int) (Motherboard.DEFAULT_SPEED / 64); break; case 0x028: - timerRate = (int) (Motherboard.SPEED / 256); + timerRate = (int) (Motherboard.DEFAULT_SPEED / 256); break; case 0x030: - timerRate = (int) (Motherboard.SPEED / 2048); + timerRate = (int) (Motherboard.DEFAULT_SPEED / 2048); break; default: timerEnabled = false; @@ -261,7 +261,7 @@ public class CardThunderclock extends Card { } public void loadRom(String path) throws IOException { - InputStream romFile = CardThunderclock.class.getClassLoader().getResourceAsStream(path); + InputStream romFile = CardThunderclock.class.getResourceAsStream(path); final int cxRomLength = 0x0100; final int c8RomLength = 0x0700; byte[] romxData = new byte[cxRomLength]; @@ -272,7 +272,7 @@ public class CardThunderclock extends Card { } getCxRom().loadData(romxData); romFile.close(); - romFile = CardThunderclock.class.getClassLoader().getResourceAsStream(path); + romFile = CardThunderclock.class.getResourceAsStream(path); if (romFile.read(rom8Data) != c8RomLength) { throw new IOException("Bad Thunderclock rom size"); } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/Joystick.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/Joystick.java index a362cf94..cee5acfb 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/Joystick.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/Joystick.java @@ -24,7 +24,6 @@ import jace.config.ConfigurableField; import jace.config.InvokableAction; import jace.core.Computer; import jace.core.Device; -import jace.core.Keyboard; import jace.core.RAMEvent; import jace.core.RAMListener; import jace.state.Stateful; diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/ZipWarpAccelerator.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/ZipWarpAccelerator.java index 18ae71f4..7c88eac4 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/ZipWarpAccelerator.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/ZipWarpAccelerator.java @@ -52,8 +52,8 @@ public class ZipWarpAccelerator extends Device { public ZipWarpAccelerator(Computer computer) { super(computer); - zipListener = computer.memory.observe(RAMEvent.TYPE.ANY, ENABLE_ADDR, SET_SPEED, this::handleZipChipEvent); - transwarpListener = computer.memory.observe(RAMEvent.TYPE.ANY, TRANSWARP, this::handleTranswarpEvent); + zipListener = computer.getMemory().observe(RAMEvent.TYPE.ANY, ENABLE_ADDR, SET_SPEED, this::handleZipChipEvent); + transwarpListener = computer.getMemory().observe(RAMEvent.TYPE.ANY, TRANSWARP, this::handleTranswarpEvent); } private void handleZipChipEvent(RAMEvent e) { @@ -85,24 +85,22 @@ public class ZipWarpAccelerator extends Device { } } else if (!zipLocked && isWrite) { switch (e.getAddress()) { - case MAX_SPEED: + case MAX_SPEED -> { setSpeed(SPEED.MAX); if (debugMessagesEnabled) { System.out.println("MAXIMUM WARP!"); } - break; - case SET_SPEED: + } + case SET_SPEED -> { SPEED s = lookupSpeedSetting(e.getNewValue()); setSpeed(s); if (debugMessagesEnabled) { System.out.println("Set speed to " + s.ratio); } - break; - case REGISTERS: - zipRegisters = e.getNewValue(); - break; - default: - break; + } + case REGISTERS -> zipRegisters = e.getNewValue(); + default -> { + } } } else if (!zipLocked && e.getAddress() == REGISTERS) { e.setNewValue(zipRegisters); @@ -177,15 +175,17 @@ public class ZipWarpAccelerator extends Device { private void setSpeed(SPEED speed) { speedValue = speed.val; - if (speed.max) { - Emulator.getComputer().getMotherboard().setMaxSpeed(true); - Motherboard.cpuPerClock = 3; - } else { - Emulator.getComputer().getMotherboard().setMaxSpeed(false); - Emulator.getComputer().getMotherboard().setSpeedInPercentage((int) (speed.ratio * 100)); - Motherboard.cpuPerClock = 1; - } - Emulator.getComputer().getMotherboard().reconfigure(); + Emulator.withComputer(c -> { + if (speed.max) { + c.getMotherboard().setMaxSpeed(true); + Motherboard.cpuPerClock = 3; + } else { + c.getMotherboard().setMaxSpeed(false); + c.getMotherboard().setSpeedInPercentage((int) (speed.ratio * 100)); + Motherboard.cpuPerClock = 1; + } + c.getMotherboard().reconfigure(); + }); } private void turnOffAcceleration() { @@ -202,15 +202,15 @@ public class ZipWarpAccelerator extends Device { @Override public void attach() { - computer.memory.addListener(zipListener); - computer.memory.addListener(transwarpListener); + computer.getMemory().addListener(zipListener); + computer.getMemory().addListener(transwarpListener); } @Override public void detach() { super.detach(); - computer.memory.removeListener(zipListener); - computer.memory.removeListener(transwarpListener); + computer.getMemory().removeListener(zipListener); + computer.getMemory().removeListener(transwarpListener); } @Override diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/massStorage/CardMassStorage.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/massStorage/CardMassStorage.java index 89b3eb47..addc531b 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/massStorage/CardMassStorage.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/massStorage/CardMassStorage.java @@ -159,6 +159,7 @@ public class CardMassStorage extends Card implements MediaConsumerParent { if (drive1.getCurrentDisk() != null && getSlot() == 7 && (pc >= 0x0c65e && pc <= 0x0c66F)) { // If the computer is in a loop trying to boot from cards 6, fast-boot from here instead // This is a convenience to boot a hard-drive if the emulator has started waiting for a currentDisk + System.out.println("Fast-booting to mass storage drive"); currentDrive = drive1; EmulatorUILogic.simulateCtrlAppleReset(); } @@ -178,7 +179,7 @@ public class CardMassStorage extends Card implements MediaConsumerParent { @Override protected void handleFirmwareAccess(int offset, TYPE type, int value, RAMEvent e) { MOS65C02 cpu = (MOS65C02) computer.getCpu(); -// System.out.println(e.getType()+" "+Integer.toHexString(e.getAddress())+" from instruction at "+Integer.toHexString(cpu.getProgramCounter())); +// System.out.println(e.getType()+" "+Integer.toHexString(e.getAddress())+" from instruction at "+Integer.toHexString(cpu.getProgramCounter())); if (type.isRead()) { // Emulator.getFrame().addIndicator(this, currentDrive.getIcon()); if (drive1.getCurrentDisk() == null && drive2.getCurrentDisk() == null) { @@ -228,19 +229,10 @@ public class CardMassStorage extends Card implements MediaConsumerParent { e.setNewValue(cardSignature[offset]); } else { switch (offset) { - // Disk capacity = 65536 blocks - case 0x0FC: - e.setNewValue(0x0ff); - break; - case 0x0FD: - e.setNewValue(0x07f); - break; - // Status bits - case 0x0FE: - e.setNewValue(0x0D7); - break; - case 0x0FF: - e.setNewValue(DEVICE_DRIVER_OFFSET); + case 0x0FC -> e.setNewValue(0x0ff); // Disk capacity = 65536 blocks + case 0x0FD -> e.setNewValue(0x07f); + case 0x0FE -> e.setNewValue(0x0D7); // Status bits + case 0x0FF -> e.setNewValue(DEVICE_DRIVER_OFFSET); } } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/massStorage/LargeDisk.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/massStorage/LargeDisk.java index 2fca811e..bde511f5 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/massStorage/LargeDisk.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/massStorage/LargeDisk.java @@ -82,19 +82,26 @@ public class LargeDisk implements IDisk { } @Override - public void boot0(int slot, Computer computer) throws IOException { - computer.pause(); - mliRead(0, 0x0800, computer.getMemory()); - byte slot16 = (byte) (slot << 4); - ((MOS65C02) computer.getCpu()).X = slot16; - RAM memory = computer.getMemory(); - memory.write(CardMassStorage.SLT16, slot16, false, false); - memory.write(MLI_COMMAND, (byte) MLI_COMMAND_TYPE.READ.intValue, false, false); - memory.write(MLI_UNITNUMBER, slot16, false, false); - // Write location to block read routine to zero page - memory.writeWord(0x048, 0x0c000 + CardMassStorage.DEVICE_DRIVER_OFFSET + (slot * 0x0100), false, false); - computer.getCpu().setProgramCounter(0x0800); - computer.resume(); + public void boot0(int slot, Computer computer) { + computer.getCpu().whileSuspended(()->{ + try { +// System.out.println("Loading boot0 to $800"); + mliRead(0, 0x0800, computer.getMemory()); + } catch (IOException ex) { + Logger.getLogger(LargeDisk.class.getName()).log(Level.SEVERE, null, ex); + } + byte slot16 = (byte) (slot << 4); +// System.out.println("X = "+Integer.toHexString(slot16)); + ((MOS65C02) computer.getCpu()).X = slot16; + RAM memory = computer.getMemory(); + memory.write(CardMassStorage.SLT16, slot16, false, false); + memory.write(MLI_COMMAND, (byte) MLI_COMMAND_TYPE.READ.intValue, false, false); + memory.write(MLI_UNITNUMBER, slot16, false, false); + // Write location to block read routine to zero page + memory.writeWord(0x048, 0x0c000 + CardMassStorage.DEVICE_DRIVER_OFFSET + (slot * 0x0100), false, false); +// System.out.println("JMP $800 issued"); + computer.getCpu().setProgramCounter(0x0800); + }); } public File getPhysicalPath() { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/mockingboard/R6522.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/mockingboard/R6522.java index f9cd7100..38e2cbda 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/mockingboard/R6522.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/mockingboard/R6522.java @@ -19,7 +19,6 @@ package jace.hardware.mockingboard; import jace.core.Computer; -import jace.core.Device; import jace.core.TimedDevice; /** @@ -151,7 +150,34 @@ public abstract class R6522 extends TimedDevice { @Override public void tick() { if (!unclocked) { - doTick(); + if (timer1running) { + timer1counter--; + if (timer1counter < 0) { + timer1counter = timer1latch; + if (!timer1freerun) { + timer1running = false; + } + if (timer1interruptEnabled) { + // System.out.println("Timer 1 generated interrupt"); + timer1IRQ = true; + computer.getCpu().generateInterrupt(); + } + } + } + if (timer2running) { + timer2counter--; + if (timer2counter < 0) { + timer2running = false; + timer2counter = timer2latch; + if (timer2interruptEnabled) { + timer2IRQ = true; + computer.getCpu().generateInterrupt(); + } + } + } + if (!timer1running && !timer2running) { + setRun(false); + } } } @@ -159,37 +185,6 @@ public abstract class R6522 extends TimedDevice { this.unclocked = unclocked; } - public void doTick() { - if (timer1running) { - timer1counter--; - if (timer1counter < 0) { - timer1counter = timer1latch; - if (!timer1freerun) { - timer1running = false; - } - if (timer1interruptEnabled) { -// System.out.println("Timer 1 generated interrupt"); - timer1IRQ = true; - computer.getCpu().generateInterrupt(); - } - } - } - if (timer2running) { - timer2counter--; - if (timer2counter < 0) { - timer2running = false; - timer2counter = timer2latch; - if (timer2interruptEnabled) { - timer2IRQ = true; - computer.getCpu().generateInterrupt(); - } - } - } - if (!timer1running && !timer2running) { - setRun(false); - } - } - @Override public void attach() { // Start chip @@ -294,47 +289,58 @@ public abstract class R6522 extends TimedDevice { Register r = Register.fromInt(reg); // System.out.println("Reading register "+r.toString()); switch (r) { - case ORB: + case ORB -> { if (dataDirectionB == 0x0ff) { break; } return receiveOutputB() & (dataDirectionB ^ 0x0ff); - case ORA: - case ORAH: + } + case ORA, ORAH -> { if (dataDirectionA == 0x0ff) { break; } return receiveOutputA() & (dataDirectionA ^ 0x0ff); - case DDRB: + } + case DDRB -> { return dataDirectionB; - case DDRA: + } + case DDRA -> { return dataDirectionA; - case T1CL: + } + case T1CL -> { timer1IRQ = false; return timer1counter & 0x0ff; - case T1CH: + } + case T1CH -> { return (timer1counter & 0x0ff00) >> 8; - case T1LL: + } + case T1LL -> { return timer1latch & 0x0ff; - case T1LH: + } + case T1LH -> { return (timer1latch & 0x0ff00) >> 8; - case T2CL: + } + case T2CL -> { timer2IRQ = false; return timer2counter & 0x0ff; - case T2CH: + } + case T2CH -> { return (timer2counter & 0x0ff00) >> 8; - case SR: + } + case SR -> { // SHIFT REGISTER NOT IMPLEMENTED return 0; - case ACR: + } + case ACR -> { // SHIFT REGISTER NOT IMPLEMENTED if (timer1freerun) { return 64; } return 0; - case PCR: - break; - case IFR: + } + case PCR -> { + } + case IFR -> { int val = 0; if (timer1IRQ) { val |= 64; @@ -346,8 +352,9 @@ public abstract class R6522 extends TimedDevice { val |= 128; } return val; - case IER: - val = 128; + } + case IER -> { + int val = 128; if (timer1interruptEnabled) { val |= 64; } @@ -355,6 +362,7 @@ public abstract class R6522 extends TimedDevice { val |= 32; } return val; + } } return 0; } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/ide/HeadlessProgram.java b/Platform/Apple/tools/jace/src/main/java/jace/ide/HeadlessProgram.java index f63f2bed..f64c9cb5 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/ide/HeadlessProgram.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/ide/HeadlessProgram.java @@ -16,7 +16,6 @@ package jace.ide; import java.util.Collections; -import java.util.Map; /** * This is a program that is intended to be defined and executed outside of a IDE session diff --git a/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessComputer.java b/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessComputer.java index 326f0bb9..d94dd823 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessComputer.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessComputer.java @@ -1,22 +1,21 @@ package jace.lawless; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.logging.Level; +import java.util.logging.Logger; + import jace.apple2e.Apple2e; import jace.apple2e.RAM128k; import jace.apple2e.SoftSwitches; import jace.apple2e.VideoNTSC; import jace.config.ConfigurableField; -import jace.core.Card; import jace.core.Video; import jace.library.MediaConsumer; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.Semaphore; -import java.util.logging.Level; -import java.util.logging.Logger; /** * Extends standard implementation to provide different cold start behavior @@ -31,10 +30,9 @@ public class LawlessComputer extends Apple2e { public LawlessComputer() { super(); - motherboard.whileSuspended(this::initLawlessLegendsConfiguration); } - private void initLawlessLegendsConfiguration() { + public void initLawlessLegendsConfiguration() { reconfigure(); // Required before anything so that memory is initialized this.cheatEngine.setValue(LawlessHacks.class); this.activeCheatEngine = new LawlessHacks(this); @@ -64,12 +62,14 @@ public class LawlessComputer extends Apple2e { (new Thread(this::startAnimation)).start(); } else { finishColdStart(); - getMotherboard().requestSpeed(this); } } public void startAnimation() { - getCpu().suspend(); + cpu.setPaused(true); + for (SoftSwitches s : SoftSwitches.values()) { + s.getSwitch().reset(); + } SoftSwitches._80COL.getSwitch().setState(true); SoftSwitches.TEXT.getSwitch().setState(false); SoftSwitches.HIRES.getSwitch().setState(true); @@ -84,64 +84,64 @@ public class LawlessComputer extends Apple2e { try { performedBootAnimation = true; waitForVBL(); + renderWithMask(0, 0, 0, 0); + renderWithMask(0x0, 0x10, 0, 0x40, 0, 0x01, 0, 0x4); + renderWithMask(0x8, 0x10, 0x02, 0x40, 0, 0x01, 0x20, 0x4); renderWithMask(0x8, 0x11, 0x22, 0x44); - Video.forceRefresh(); - waitForVBL(10); + renderWithMask(0x8, 0x19, 0x22, 0x66, 0x4c, 0x11, 0x33, 044); renderWithMask(0x4c, 0x19, 0x33, 0x66); - Video.forceRefresh(); - waitForVBL(10); + renderWithMask(0x4c, 0x5d, 0x33, 0x77, 0x6e, 0x19, 0x3B, 0x66); renderWithMask(0x6e, 0x5d, 0x3B, 0x77); - Video.forceRefresh(); - waitForVBL(10); + renderWithMask(0x6e, 0x7f, 0x3b, 0x7f, 0x7f, 0x5d, 0x7f, 0x77); renderWithMask(0x7f, 0x7f, 0x7f, 0x7f); - Video.forceRefresh(); - waitForVBL(250); -// renderWithMask(0x6e, 0x5d, 0x3B, 0x77); + waitForVBL(230); + renderWithMask(0x7f, 0x6e, 0x7f, 0x3b, 0x77, 0x6e, 0x5d, 0x3b); renderWithMask(0x77, 0x6e, 0x5d, 0x3b); - Video.forceRefresh(); - waitForVBL(10); -// renderWithMask(0x4c, 0x19, 0x33, 0x66); + renderWithMask(0x77, 0x66, 0x5d, 0x19, 0x33, 0x6e, 0x4c, 0x3b); renderWithMask(0x33, 0x66, 0x4c, 0x19); - Video.forceRefresh(); - waitForVBL(10); -// renderWithMask(0x8, 0x11, 0x22, 0x44); + renderWithMask(0x33, 0x22, 0x4c, 0x8, 0x11, 0x66, 0x44, 0x19); renderWithMask(0x11, 0x22, 0x44, 0x8); - Video.forceRefresh(); - waitForVBL(10); + renderWithMask(0x11, 0, 0x44, 0, 0, 0x22, 0, 0x8); + renderWithMask(0, 0, 0, 0); } catch (InterruptedException ex) { Logger.getLogger(LawlessComputer.class.getName()).log(Level.SEVERE, null, ex); } } + cpu.setPaused(false); finishColdStart(); } - private void renderWithMask(int i1, int i2, int i3, int i4) { + private void renderWithMask(int... mask) throws InterruptedException { RAM128k ram = (RAM128k) getMemory(); byte[] framebuffer = getBootScreen(); + int maskOffset = 0; for (int i = 0; i < 0x02000; i += 2) { - int next = 0; - if (i < 0x02000) { - next = (framebuffer[i] & 1) << 6; - } + int next = (framebuffer[i] & 1) << 6; Byte b1 = (byte) ((framebuffer[i + 0x02000] & 0x07f) >> 1 | next); - ram.getAuxMemory().writeByte(0x02000 + i, (byte) (b1 & i1 | 0x080)); + ram.getAuxMemory().writeByte(0x02000 + i, (byte) (b1 & mask[maskOffset] | 0x080)); + if (i < 0x01FFF) { next = (framebuffer[i + 0x02001] & 1) << 6; - } - Byte b2 = (byte) ((framebuffer[i] & 0x07f) >> 1 | next); - ram.getMainMemory().writeByte(0x02000 + i, (byte) (b2 & i2 | 0x080)); - if (i < 0x01FFF) { + Byte b2 = (byte) ((framebuffer[i] & 0x07f) >> 1 | next); + ram.getMainMemory().writeByte(0x02000 + i, (byte) (b2 & mask[maskOffset + 1] | 0x080)); + next = (framebuffer[i + 1] & 1) << 6; + Byte b3 = (byte) ((framebuffer[i + 0x02001] & 0x07f) >> 1 | next); + ram.getAuxMemory().writeByte(0x02001 + i, (byte) (b3 & mask[maskOffset + 2] | 0x080)); } - Byte b3 = (byte) ((framebuffer[i + 0x02001] & 0x07f) >> 1 | next); - ram.getAuxMemory().writeByte(0x02001 + i, (byte) (b3 & i3 | 0x080)); + if (i < 0x01FFE) { next = (framebuffer[i + 0x02002] & 1) << 6; + Byte b4 = (byte) ((framebuffer[i + 1] & 0x07f) >> 1 | next); + ram.getMainMemory().writeByte(0x02001 + i, (byte) (b4 & mask[maskOffset + 3] | 0x080)); + } + if (i % 20 != 0) { + maskOffset = (maskOffset + 4) % mask.length; } - Byte b4 = (byte) ((framebuffer[i + 1] & 0x07f) >> 1 | next); - ram.getMainMemory().writeByte(0x02001 + i, (byte) (b4 & i4 | 0x080)); } + Video.forceRefresh(); + waitForVBL(5); } List vblCallbacks = Collections.synchronizedList(new ArrayList<>()); @@ -175,9 +175,6 @@ public class LawlessComputer extends Apple2e { public void finishColdStart() { try { - for (Optional c : getMemory().getAllCards()) { - c.ifPresent(Card::reset); - } waitForVBL(); reboot(); } catch (InterruptedException ex) { @@ -187,7 +184,7 @@ public class LawlessComputer extends Apple2e { private byte[] getBootScreen() { if (bootScreen == null) { - InputStream in = getClass().getClassLoader().getResourceAsStream("jace/data/bootscreen.bin"); + InputStream in = getClass().getResourceAsStream("/jace/data/bootscreen.bin"); bootScreen = new byte[0x04000]; int len, offset = 0; try { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessHacks.java b/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessHacks.java index 1607b416..3361f897 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessHacks.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessHacks.java @@ -1,13 +1,5 @@ package jace.lawless; -import jace.cheat.Cheats; -import jace.core.Computer; -import jace.core.RAMEvent; -import jace.lawless.LawlessVideo.RenderEngine; -import javafx.beans.property.DoubleProperty; -import javafx.scene.media.Media; -import javafx.scene.media.MediaPlayer; - import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; @@ -21,6 +13,14 @@ import java.util.Optional; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; + +import jace.cheat.Cheats; +import jace.core.Computer; +import jace.core.RAMEvent; +import jace.lawless.LawlessVideo.RenderEngine; +import javafx.beans.property.DoubleProperty; +import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; import javafx.util.Duration; /** @@ -94,6 +94,7 @@ public class LawlessHacks extends Cheats { boolean isMusic = soundNumber >= 0; int track = soundNumber & 0x03f; repeatSong = (soundNumber & 0x040) > 0; +// System.out.println("(invoked sound on "+getName()+")"); if (track == 0) { if (isMusic) { System.out.println("Stop music"); @@ -129,9 +130,9 @@ public class LawlessHacks extends Cheats { private Media getAudioTrack(int number) { String filename = getSongName(number); - String pathStr = "jace/data/sound/" + filename; + String pathStr = "/jace/data/sound/" + filename; // System.out.println("looking in "+pathStr); - URL path = getClass().getClassLoader().getResource(pathStr); + URL path = getClass().getResource(pathStr); if (path == null) { return null; } @@ -316,7 +317,7 @@ public class LawlessHacks extends Cheats { private final Set autoResume = new HashSet<>(); private final Map lastTime = new HashMap<>(); private void readScores() { - InputStream data = getClass().getClassLoader().getResourceAsStream("jace/data/sound/scores.txt"); + InputStream data = getClass().getResourceAsStream("/jace/data/sound/scores.txt"); readScores(data); } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessImageTool.java b/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessImageTool.java index 5e247908..968ce2eb 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessImageTool.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessImageTool.java @@ -1,16 +1,5 @@ package jace.lawless; -import jace.Emulator; -import jace.LawlessLegends; -import jace.apple2e.RAM128k; -import jace.core.Keyboard; -import jace.core.RAM; -import jace.core.Utility; -import jace.hardware.massStorage.CardMassStorage; -import jace.library.DiskType; -import jace.library.MediaConsumer; -import jace.library.MediaEntry; -import jace.library.MediaEntry.MediaFile; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -20,7 +9,20 @@ import java.nio.file.StandardCopyOption; import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; + +import jace.Emulator; +import jace.LawlessLegends; +import jace.apple2e.RAM128k; +import jace.core.Computer; +import jace.core.Keyboard; +import jace.core.Utility; +import jace.hardware.massStorage.CardMassStorage; +import jace.library.DiskType; +import jace.library.MediaConsumer; +import jace.library.MediaEntry; +import jace.library.MediaEntry.MediaFile; import javafx.scene.control.Label; +import javafx.stage.FileChooser; /** * @@ -88,15 +90,13 @@ public class LawlessImageTool implements MediaConsumer { } private void insertHardDisk(int drive, MediaEntry entry, MediaFile file) { - RAM memory = Emulator.getComputer().memory; - - memory.getCard(7).ifPresent(card -> { + Emulator.withMemory(m->m.getCard(7).ifPresent(card -> { try { ((CardMassStorage) card).getConsumers()[drive].insertMedia(entry, file); } catch (IOException ex) { Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex); } - }); + })); if (drive == 0) { gameMediaEntry = entry; @@ -106,20 +106,16 @@ public class LawlessImageTool implements MediaConsumer { } private void readCurrentDisk(int drive) { - RAM memory = Emulator.getComputer().memory; - - memory.getCard(7).ifPresent(card -> { + Emulator.withMemory(m->m.getCard(7).ifPresent(card -> { gameMediaEntry = ((CardMassStorage) card).getConsumers()[drive].getMediaEntry(); gameMediaFile = ((CardMassStorage) card).getConsumers()[drive].getMediaFile(); - }); + })); } private void ejectHardDisk(int drive) { - RAM memory = Emulator.getComputer().memory; - - memory.getCard(7).ifPresent(card -> { + Emulator.withMemory(m->m.getCard(7).ifPresent(card -> { ((CardMassStorage) card).getConsumers()[drive].eject(); - }); + })); if (drive == 0) { gameMediaEntry = null; @@ -156,16 +152,20 @@ public class LawlessImageTool implements MediaConsumer { path = System.getProperty("user.home"); } if (path == null) { - path = "."; + path = System.getProperty("user.dir"); } File base = new File(path); File appPath = new File(base, "lawless-legends"); appPath.mkdirs(); return appPath; } + + private File getUserGameFile() { + return new File(getApplicationStoragePath(), "game.2mg"); + } private void copyResource(String filename, File target) { - File localResource = new File(".", filename); + File localResource = getUserGameFile(); InputStream in = null; if (localResource.exists()) { try { @@ -174,7 +174,7 @@ public class LawlessImageTool implements MediaConsumer { Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex); } } else { - in = getClass().getClassLoader().getResourceAsStream("jace/data/" + filename); + in = getClass().getResourceAsStream("/jace/data/" + filename); } if (in != null) { try { @@ -184,8 +184,28 @@ public class LawlessImageTool implements MediaConsumer { } } else { Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, "Unable to find resource {0}", filename); + Utility.decision("Unable to find game", "Sorry partner, we can't find yer game disk. What're ya' gonna do about it?", "I have it", "Tuck tail and leave", this::selectGameFile, ()->System.exit(1)); } } + + private void selectGameFile() { + try { + FileChooser fileChooser = new FileChooser(); + fileChooser.setInitialFileName("game.2mg"); + fileChooser.setTitle("Please locate your Lawless Legends game.2mg file to continue"); + File gameFile = fileChooser.showOpenDialog(null); + if (gameFile == null || !gameFile.exists()) { + Utility.gripe("Sorry pardner, can't help ya' this time.", true, ()->{System.exit(1);}); + } else { + java.nio.file.Files.copy(gameFile.toPath(), getUserGameFile().toPath(), StandardCopyOption.REPLACE_EXISTING); + loadGame(); + Emulator.withComputer(Computer::coldStart); + } + } catch (IOException ex) { + Logger.getLogger(LawlessImageTool.class.getName()).log(Level.SEVERE, null, ex); + Utility.gripe("Couldn't load yer game, friend. Heard some fellow mumbling something about " + ex.getMessage(), true, ()->{System.exit(1);}); + } + } private void performGameReplace(MediaEntry e, MediaFile f) { try { @@ -194,7 +214,7 @@ public class LawlessImageTool implements MediaConsumer { java.nio.file.Files.copy(f.path.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING); f.path = target; insertHardDisk(0, e, f); - Emulator.getComputer().coldStart(); + Emulator.withComputer(Computer::coldStart); System.out.println("Upgrade completed"); } catch (IOException ex) { Logger.getLogger(LawlessImageTool.class.getName()).log(Level.SEVERE, null, ex); @@ -212,17 +232,17 @@ public class LawlessImageTool implements MediaConsumer { // Put in new disk and boot it -- we want to use its importer in case that importer works better! ejectHardDisk(0); insertHardDisk(0, e, f); - Emulator.getComputer().coldStart(); + Emulator.withComputer(Computer::coldStart); if (!waitForText("I)mport", 1)) { - Emulator.getComputer().coldStart(); - if (!waitForText("I)mport", 1000)) { + Emulator.withComputer(Computer::coldStart); + if (!waitForText("I)mport", 2000)) { throw new Exception("Unable to detect upgrade prompt - Upgrade aborted."); } } System.out.println("Menu Propmt detected"); Keyboard.pasteFromString("i"); - if (!waitForText("Insert disk for import", 100)) { + if (!waitForText("Insert disk for import", 1500)) { throw new Exception("Unable to detect first insert prompt - Upgrade aborted."); } System.out.println("First Propmt detected"); @@ -232,7 +252,7 @@ public class LawlessImageTool implements MediaConsumer { insertHardDisk(0, originalEntry, originalFile); Keyboard.pasteFromString(" "); - if (!waitForText("Game imported", 100)) { + if (!waitForText("Game imported", 2000)) { throw new Exception("Unable to detect second insert prompt - Upgrade aborted."); } System.out.println("Completing upgrade"); @@ -251,17 +271,23 @@ public class LawlessImageTool implements MediaConsumer { } private boolean waitForText(String message, int timeout) throws InterruptedException { - LawlessComputer compy = Emulator.getComputer(); - RAM128k mem = (RAM128k) compy.getMemory(); while (timeout-- > 0) { StringBuilder allText = new StringBuilder(); - for (int i = 0x0400; i < 0x07ff; i++) { - allText.append((char) (mem.getMainMemory().readByte(i) & 0x07f)); - } + Emulator.withMemory(mem -> { + for (int i = 0x0400; i < 0x07ff; i++) { + allText.append((char) ((RAM128k) mem).getMainMemory().readByte(i) & 0x07f); + } + }); if (allText.toString().contains(message)) { return true; } else { - compy.waitForVBL(); + Emulator.withComputer(c->{ + try { + ((LawlessComputer)c).waitForVBL(); + } catch (InterruptedException ex) { + // Ignore + } + }); } } return false; diff --git a/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessVideo.java b/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessVideo.java index 4327f5f2..6e60333f 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessVideo.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessVideo.java @@ -1,5 +1,7 @@ package jace.lawless; +import java.util.Arrays; + import jace.Emulator; import jace.apple2e.RAM128k; import jace.apple2e.VideoNTSC; @@ -8,8 +10,6 @@ import jace.core.PagedMemory; import jace.core.Video; import javafx.scene.image.WritableImage; -import java.util.Arrays; - /** * Lawless-enhanced video output for readable text */ @@ -88,7 +88,8 @@ public class LawlessVideo extends VideoNTSC { for (int y=0; y < 192; y++) { System.arraycopy(activeEngine.colorMask[y], 0, activeMask[y], 0, 80); } - Emulator.getComputer().onNextVBL(Video::forceRefresh); + + Emulator.withComputer(c->c.onNextVBL(Video::forceRefresh)); System.out.println("Detected engine: " + e.name()); } else { System.out.println("Detected engine same as before: " + e.name()); diff --git a/Platform/Apple/tools/jace/src/main/java/jace/state/StateManager.java b/Platform/Apple/tools/jace/src/main/java/jace/state/StateManager.java index e62ce55d..e24be157 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/state/StateManager.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/state/StateManager.java @@ -18,6 +18,19 @@ */ package jace.state; +import java.awt.image.BufferedImage; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + import jace.Emulator; import jace.apple2e.SoftSwitches; import jace.config.ConfigurableField; @@ -29,14 +42,6 @@ import jace.core.Video; import javafx.scene.image.Image; import javafx.scene.image.WritableImage; -import java.awt.image.BufferedImage; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; - /** * * @author Brendan Robert (BLuRry) brendan.robert@gmail.com @@ -407,7 +412,7 @@ public class StateManager implements Reconfigurable { defaultKeyMapping = {"ctrl+shift+Open Bracket"} ) public static void beKindRewind() { - StateManager manager = getInstance(Emulator.getComputer()); + StateManager manager = Emulator.withComputer(StateManager::getInstance, null); new Thread(()->manager.rewind(60 / manager.captureFrequency)).start(); } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/ui/MetacheatUI.java b/Platform/Apple/tools/jace/src/main/java/jace/ui/MetacheatUI.java index 243f2e01..50bc1629 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/ui/MetacheatUI.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/ui/MetacheatUI.java @@ -1,6 +1,14 @@ package jace.ui; -import com.sun.glass.ui.Application; +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + import jace.Emulator; import jace.LawlessLegends; import jace.cheat.DynamicCheat; @@ -10,14 +18,6 @@ import jace.cheat.MetaCheat.SearchChangeType; import jace.cheat.MetaCheat.SearchResult; import jace.cheat.MetaCheat.SearchType; import jace.state.State; -import java.io.File; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; @@ -167,32 +167,32 @@ public class MetacheatUI { @FXML void loadCheats(ActionEvent event) { - boolean resume = Emulator.getComputer().pause(); - FileChooser chooser = new FileChooser(); - chooser.setTitle("Load cheats"); - chooser.setInitialFileName("cheat.txt"); - File saveFile = chooser.showOpenDialog(LawlessLegends.getApplication().primaryStage); - if (saveFile != null) { - cheatEngine.loadCheats(saveFile); - } - if (resume) { - Emulator.getComputer().resume(); - } + Emulator.withComputer(c -> { + c.getMotherboard().whileSuspended(() -> { + FileChooser chooser = new FileChooser(); + chooser.setTitle("Load cheats"); + chooser.setInitialFileName("cheat.txt"); + File saveFile = chooser.showOpenDialog(LawlessLegends.getApplication().primaryStage); + if (saveFile != null) { + cheatEngine.loadCheats(saveFile); + } + }); + }); } @FXML void saveCheats(ActionEvent event) { - boolean resume = Emulator.getComputer().pause(); - FileChooser chooser = new FileChooser(); - chooser.setTitle("Save current cheats"); - chooser.setInitialFileName("cheat.txt"); - File saveFile = chooser.showSaveDialog(LawlessLegends.getApplication().primaryStage); - if (saveFile != null) { - cheatEngine.saveCheats(saveFile); - } - if (resume) { - Emulator.getComputer().resume(); - } + Emulator.withComputer(c -> { + c.getMotherboard().whileSuspended(() -> { + FileChooser chooser = new FileChooser(); + chooser.setTitle("Save current cheats"); + chooser.setInitialFileName("cheat.txt"); + File saveFile = chooser.showSaveDialog(LawlessLegends.getApplication().primaryStage); + if (saveFile != null) { + cheatEngine.saveCheats(saveFile); + } + }); + }); } @FXML @@ -205,12 +205,14 @@ public class MetacheatUI { @FXML void pauseClicked(ActionEvent event) { - Application.invokeLater(() -> { - if (Emulator.getComputer().isRunning()) { - Emulator.getComputer().pause(); - } else { - Emulator.getComputer().resume(); - } + Platform.runLater(() -> { + Emulator.withComputer(c->{ + if (c.isRunning()) { + c.pause(); + } else { + c.resume(); + } + }); }); } @@ -262,10 +264,10 @@ public class MetacheatUI { isRetina = Screen.getPrimary().getDpi() >= 110; - Emulator.getComputer().getRunningProperty().addListener((val, oldVal, newVal) -> { - Platform.runLater(() -> pauseButton.setText(newVal ? "Pause" : "Resume")); - }); - + Emulator.withComputer(c -> c.getRunningProperty().addListener((val, oldVal, newVal) -> + Platform.runLater(() -> pauseButton.setText(newVal ? "Pause" : "Resume")) + )); + searchTypesTabPane.getTabs().get(0).setUserData(SearchType.VALUE); searchTypesTabPane.getTabs().get(1).setUserData(SearchType.CHANGE); searchTypesTabPane.getTabs().get(2).setUserData(SearchType.TEXT); @@ -372,10 +374,10 @@ public class MetacheatUI { searchValueField.textProperty().bindBidirectional(cheatEngine.searchValueProperty()); searchChangeByField.textProperty().bindBidirectional(cheatEngine.searchChangeByProperty()); - Application.invokeLater(this::redrawMemoryView); + Platform.runLater(this::redrawMemoryView); } - ChangeListener addressRangeListener = (prop, oldVal, newVal) -> Application.invokeLater(this::redrawMemoryView); + ChangeListener addressRangeListener = (prop, oldVal, newVal) -> Platform.runLater(this::redrawMemoryView); public static final int MEMORY_BOX_SIZE = 4; public static final int MEMORY_BOX_GAP = 2; @@ -429,13 +431,12 @@ public class MetacheatUI { } private void processMemoryViewUpdates() { - if (!Emulator.getComputer().getRunningProperty().get()) { - return; - } + boolean isRunning = Emulator.withComputer(c->c.getRunningProperty().get(), false); + if (!isRunning) return; GraphicsContext context = memoryViewCanvas.getGraphicsContext2D(); Set draw = new HashSet<>(redrawNodes); redrawNodes.clear(); - Application.invokeLater(() -> { + Platform.runLater(() -> { draw.stream().forEach((jace.cheat.MemoryCell cell) -> { if (showValuesCheckbox.isSelected()) { int val = cell.value.get() & 0x0ff; @@ -457,49 +458,47 @@ public class MetacheatUI { if (cheatEngine == null) { return; } - boolean resume = Emulator.getComputer().pause(); + Emulator.withComputer(c -> { + c.getMotherboard().whileSuspended(() -> { + if (animationTimer == null) { + animationTimer = new ScheduledThreadPoolExecutor(1); + } - if (animationTimer == null) { - animationTimer = new ScheduledThreadPoolExecutor(1); - } + if (animationFuture != null) { + animationFuture.cancel(false); + } - if (animationFuture != null) { - animationFuture.cancel(false); - } + animationFuture = animationTimer.scheduleAtFixedRate(this::processMemoryViewUpdates, FRAME_RATE, FRAME_RATE, TimeUnit.MILLISECONDS); - animationFuture = animationTimer.scheduleAtFixedRate(this::processMemoryViewUpdates, FRAME_RATE, FRAME_RATE, TimeUnit.MILLISECONDS); + cheatEngine.initMemoryView(); + int pixelsPerBlock = 16 * MEMORY_BOX_TOTAL_SIZE; + memoryViewColumns = (int) (memoryViewPane.getWidth() / pixelsPerBlock) * 16; + memoryViewRows = ((cheatEngine.getEndAddress() - cheatEngine.getStartAddress()) / memoryViewColumns) + 1; + double canvasHeight = memoryViewRows * MEMORY_BOX_TOTAL_SIZE * drawScale; - cheatEngine.initMemoryView(); - int pixelsPerBlock = 16 * MEMORY_BOX_TOTAL_SIZE; - memoryViewColumns = (int) (memoryViewPane.getWidth() / pixelsPerBlock) * 16; - memoryViewRows = ((cheatEngine.getEndAddress() - cheatEngine.getStartAddress()) / memoryViewColumns) + 1; - double canvasHeight = memoryViewRows * MEMORY_BOX_TOTAL_SIZE * drawScale; - - memoryViewContents.setPrefHeight(canvasHeight); - memoryViewCanvas.setHeight(canvasHeight); - GraphicsContext context = memoryViewCanvas.getGraphicsContext2D(); - context.setFill(Color.rgb(40, 40, 40)); - context.fillRect(0, 0, memoryViewCanvas.getWidth(), memoryViewCanvas.getHeight()); - for (int addr = cheatEngine.getStartAddress(); addr <= cheatEngine.getEndAddress(); addr++) { - int col = (addr - cheatEngine.getStartAddress()) % memoryViewColumns; - int row = (addr - cheatEngine.getStartAddress()) / memoryViewColumns; - MemoryCell cell = cheatEngine.getMemoryCell(addr); - cell.setRect( - (int) (col * MEMORY_BOX_TOTAL_SIZE * drawScale), - (int) (row * MEMORY_BOX_TOTAL_SIZE * drawScale), - (int) (MEMORY_BOX_SIZE * drawScale), - (int) (MEMORY_BOX_SIZE * drawScale)); - redrawNodes.add(cell); - } - MemoryCell.setListener((javafx.beans.value.ObservableValue prop, jace.cheat.MemoryCell oldCell, jace.cheat.MemoryCell newCell) -> { - redrawNodes.add(newCell); + memoryViewContents.setPrefHeight(canvasHeight); + memoryViewCanvas.setHeight(canvasHeight); + GraphicsContext context = memoryViewCanvas.getGraphicsContext2D(); + context.setFill(Color.rgb(40, 40, 40)); + context.fillRect(0, 0, memoryViewCanvas.getWidth(), memoryViewCanvas.getHeight()); + for (int addr = cheatEngine.getStartAddress(); addr <= cheatEngine.getEndAddress(); addr++) { + int col = (addr - cheatEngine.getStartAddress()) % memoryViewColumns; + int row = (addr - cheatEngine.getStartAddress()) / memoryViewColumns; + MemoryCell cell = cheatEngine.getMemoryCell(addr); + cell.setRect( + (int) (col * MEMORY_BOX_TOTAL_SIZE * drawScale), + (int) (row * MEMORY_BOX_TOTAL_SIZE * drawScale), + (int) (MEMORY_BOX_SIZE * drawScale), + (int) (MEMORY_BOX_SIZE * drawScale)); + redrawNodes.add(cell); + } + MemoryCell.setListener((javafx.beans.value.ObservableValue prop, jace.cheat.MemoryCell oldCell, jace.cheat.MemoryCell newCell) -> { + redrawNodes.add(newCell); + }); + + setZoom(1/drawScale); + }); }); - - setZoom(1/drawScale); - - if (resume) { - Emulator.getComputer().resume(); - } } private void changeZoom(double amount) { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/ui/Watch.java b/Platform/Apple/tools/jace/src/main/java/jace/ui/Watch.java index 4bc8354d..84ddf1b0 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/ui/Watch.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/ui/Watch.java @@ -5,14 +5,15 @@ */ package jace.ui; -import jace.Emulator; -import jace.cheat.MemoryCell; -import jace.core.RAMListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; + +import jace.Emulator; +import jace.cheat.MemoryCell; +import jace.core.RAMListener; import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.geometry.Insets; @@ -20,7 +21,6 @@ import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; -import javafx.scene.input.MouseEvent; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.CornerRadii; @@ -74,9 +74,9 @@ class Watch extends VBox { } public void redraw() { - if (!Emulator.getComputer().getRunningProperty().get()) { - return; - } + boolean isRunning = Emulator.withComputer(c->c.getRunningProperty().get(), false); + if (!isRunning) return; + int val = cell.value.get() & 0x0ff; if (!holding.get()) { value = val; @@ -117,7 +117,7 @@ class Watch extends VBox { outer.cheatEngine.removeListener(holdListener); holdListener = null; } else { - value = Emulator.getComputer().memory.readRaw(address) & 0x0ff; + value = Emulator.withComputer(c->c.getMemory().readRaw(address) & 0x0ff, 0); holdListener = outer.cheatEngine.forceValue(value, address); } } diff --git a/Platform/Apple/tools/jace/src/main/java/module-info.java b/Platform/Apple/tools/jace/src/main/java/module-info.java new file mode 100644 index 00000000..5d70e1de --- /dev/null +++ b/Platform/Apple/tools/jace/src/main/java/module-info.java @@ -0,0 +1,47 @@ +/* + * Copyright 2023 org.badvision. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module lawlesslegends { + requires nestedvm; + requires java.base; + requires java.logging; + requires java.desktop; + requires java.datatransfer; + requires java.scripting; + requires javafx.fxmlEmpty; + requires javafx.fxml; + requires javafx.swing; + requires javafx.controlsEmpty; + requires javafx.controls; + requires javafx.baseEmpty; + requires javafx.base; + requires javafx.webEmpty; + requires javafx.web; + requires javafx.graphicsEmpty; + requires javafx.graphics; + requires javafx.mediaEmpty; + requires javafx.media; + requires jdk.jsobject; + requires org.reflections; + + opens jace to javafx.graphics, javafx.fxml, javafx.controls; + opens jace.config to javafx.fxml, javafx.controls; + opens jace.data to javafx.graphics, javafx.fxml, javafx.controls; + opens fxml to javafx.graphics, javafx.fxml, javafx.controls; + opens styles to javafx.graphics, javafx.fxml, javafx.controls; + + exports jace; +} diff --git a/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/filter-file.json b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/filter-file.json new file mode 100644 index 00000000..d173ee02 --- /dev/null +++ b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/filter-file.json @@ -0,0 +1,11 @@ +{ "rules": [ + {"excludeClasses" : "com.sun.glass.ui.mac.*"}, + {"excludeClasses" : "com.sun.glass.ui.gtk.*"}, + {"excludeClasses" : "com.sun.glass.ui.win.*"}, + {"excludeClasses" : "com.sun.prism.es2.*"}, + {"excludeClasses" : "com.sun.prism.d3d.*"}, + {"excludeClasses" : "com.sun.scenario.effect.impl.es2.*"}, + {"excludeClasses" : "com.sun.scenario.effect.impl.hw.d3d.*"}, + {"excludeClasses" : "com.gluonhq.attach.**"} + ] +} diff --git a/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/jni-config.json b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/jni-config.json new file mode 100644 index 00000000..8c67d4e2 --- /dev/null +++ b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/jni-config.json @@ -0,0 +1,450 @@ +[ +{ + "name":"[Lcom.sun.glass.ui.Screen;" +}, +{ + "name":"[Ljava.lang.String;" +}, +{ + "name":"[Lsun.java2d.loops.GraphicsPrimitive;" +}, +{ + "name":"com.apple.eawt._AppEventHandler", + "methods":[{"name":"handleNativeNotification","parameterTypes":["int"] }] +}, +{ + "name":"com.apple.eawt._AppMenuBarHandler", + "methods":[{"name":"initMenuStates","parameterTypes":["boolean","boolean","boolean","boolean"] }] +}, +{ + "name":"com.sun.glass.ui.Application", + "methods":[ + {"name":"notifyDidBecomeActive","parameterTypes":[] }, + {"name":"notifyDidFinishLaunching","parameterTypes":[] }, + {"name":"notifyDidHide","parameterTypes":[] }, + {"name":"notifyDidResignActive","parameterTypes":[] }, + {"name":"notifyDidUnhide","parameterTypes":[] }, + {"name":"notifyOpenFiles","parameterTypes":["java.lang.String[]"] }, + {"name":"notifyWillBecomeActive","parameterTypes":[] }, + {"name":"notifyWillFinishLaunching","parameterTypes":[] }, + {"name":"notifyWillHide","parameterTypes":[] }, + {"name":"notifyWillResignActive","parameterTypes":[] }, + {"name":"notifyWillUnhide","parameterTypes":[] } + ] +}, +{ + "name":"com.sun.glass.ui.Menu", + "methods":[ + {"name":"notifyMenuClosed","parameterTypes":[] }, + {"name":"notifyMenuOpening","parameterTypes":[] } + ] +}, +{ + "name":"com.sun.glass.ui.MenuItem$Callback", + "methods":[ + {"name":"action","parameterTypes":[] }, + {"name":"validate","parameterTypes":[] } + ] +}, +{ + "name":"com.sun.glass.ui.Screen", + "methods":[ + {"name":"","parameterTypes":["long","int","int","int","int","int","int","int","int","int","int","int","int","int","int","int","float","float","float","float"] }, + {"name":"notifySettingsChanged","parameterTypes":[] } + ] +}, +{ + "name":"com.sun.glass.ui.Size", + "methods":[{"name":"","parameterTypes":["int","int"] }] +}, +{ + "name":"com.sun.glass.ui.View", + "fields":[{"name":"ptr"}], + "methods":[ + {"name":"getAccessible","parameterTypes":[] }, + {"name":"notifyDragDrop","parameterTypes":["int","int","int","int","int"] }, + {"name":"notifyDragEnd","parameterTypes":["int"] }, + {"name":"notifyDragEnter","parameterTypes":["int","int","int","int","int"] }, + {"name":"notifyDragLeave","parameterTypes":[] }, + {"name":"notifyDragOver","parameterTypes":["int","int","int","int","int"] }, + {"name":"notifyInputMethod","parameterTypes":["java.lang.String","int[]","int[]","byte[]","int","int","int"] }, + {"name":"notifyInputMethodCandidatePosRequest","parameterTypes":["int"] }, + {"name":"notifyKey","parameterTypes":["int","int","char[]","int"] }, + {"name":"notifyMenu","parameterTypes":["int","int","int","int","boolean"] }, + {"name":"notifyMouse","parameterTypes":["int","int","int","int","int","int","int","boolean","boolean"] }, + {"name":"notifyRepaint","parameterTypes":["int","int","int","int"] }, + {"name":"notifyResize","parameterTypes":["int","int"] }, + {"name":"notifyView","parameterTypes":["int"] } + ] +}, +{ + "name":"com.sun.javafx.font.coretext.CGAffineTransform", + "fields":[ + {"name":"a"}, + {"name":"b"}, + {"name":"c"}, + {"name":"d"}, + {"name":"tx"}, + {"name":"ty"} + ], + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.javafx.font.coretext.CGPoint", + "fields":[ + {"name":"x"}, + {"name":"y"} + ], + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.javafx.font.coretext.CGRect", + "fields":[ + {"name":"origin"}, + {"name":"size"} + ], + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.javafx.font.coretext.CGSize", + "fields":[ + {"name":"height"}, + {"name":"width"} + ], + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.media.sound.DirectAudioDevice", + "methods":[{"name":"addFormat","parameterTypes":["java.util.Vector","int","int","int","float","int","boolean","boolean"] }] +}, +{ + "name":"com.sun.media.sound.DirectAudioDeviceProvider$DirectAudioDeviceInfo", + "methods":[{"name":"","parameterTypes":["int","int","int","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }] +}, +{ + "name":"com.sun.media.sound.PortMixerProvider$PortMixerInfo", + "methods":[{"name":"","parameterTypes":["int","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }] +}, +{ + "name":"java.awt.AlphaComposite", + "fields":[ + {"name":"extraAlpha"}, + {"name":"rule"} + ] +}, +{ + "name":"java.awt.Color", + "methods":[{"name":"getRGB","parameterTypes":[] }] +}, +{ + "name":"java.awt.DisplayMode", + "methods":[{"name":"","parameterTypes":["int","int","int","int"] }] +}, +{ + "name":"java.awt.event.InputEvent", + "methods":[{"name":"getButtonDownMasks","parameterTypes":[] }] +}, +{ + "name":"java.awt.geom.AffineTransform", + "fields":[ + {"name":"m00"}, + {"name":"m01"}, + {"name":"m02"}, + {"name":"m10"}, + {"name":"m11"}, + {"name":"m12"} + ] +}, +{ + "name":"java.awt.geom.Path2D", + "fields":[ + {"name":"numTypes"}, + {"name":"pointTypes"}, + {"name":"windingRule"} + ] +}, +{ + "name":"java.awt.geom.Path2D$Float", + "fields":[{"name":"floatCoords"}] +}, +{ + "name":"java.awt.geom.Point2D$Double", + "methods":[{"name":"","parameterTypes":["double","double"] }] +}, +{ + "name":"java.awt.geom.Rectangle2D$Double", + "methods":[{"name":"","parameterTypes":["double","double","double","double"] }] +}, +{ + "name":"java.awt.image.ColorModel", + "fields":[ + {"name":"colorSpace"}, + {"name":"colorSpaceType"}, + {"name":"isAlphaPremultiplied"}, + {"name":"is_sRGB"}, + {"name":"nBits"}, + {"name":"numComponents"}, + {"name":"pData"}, + {"name":"supportsAlpha"}, + {"name":"transparency"} + ], + "methods":[{"name":"getRGBdefault","parameterTypes":[] }] +}, +{ + "name":"java.awt.image.IndexColorModel", + "fields":[ + {"name":"allgrayopaque"}, + {"name":"map_size"}, + {"name":"rgb"}, + {"name":"transparent_index"} + ] +}, +{ + "name":"java.lang.Boolean", + "methods":[{"name":"booleanValue","parameterTypes":[] }] +}, +{ + "name":"java.lang.Class", + "methods":[{"name":"forName","parameterTypes":["java.lang.String","boolean","java.lang.ClassLoader"] }] +}, +{ + "name":"java.lang.Integer", + "methods":[ + {"name":"","parameterTypes":["int"] }, + {"name":"intValue","parameterTypes":[] } + ] +}, +{ + "name":"java.lang.Long", + "methods":[{"name":"longValue","parameterTypes":[] }] +}, +{ + "name":"java.lang.Runnable", + "methods":[{"name":"run","parameterTypes":[] }] +}, +{ + "name":"java.lang.String", + "methods":[ + {"name":"lastIndexOf","parameterTypes":["int"] }, + {"name":"substring","parameterTypes":["int"] } + ] +}, +{ + "name":"java.lang.System", + "methods":[ + {"name":"getProperty","parameterTypes":["java.lang.String"] }, + {"name":"load","parameterTypes":["java.lang.String"] }, + {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] } + ] +}, +{ + "name":"java.util.List", + "methods":[{"name":"add","parameterTypes":["java.lang.Object"] }] +}, +{ + "name":"java.util.Map", + "methods":[{"name":"get","parameterTypes":["java.lang.Object"] }] +}, +{ + "name":"sun.awt.AWTAutoShutdown", + "methods":[{"name":"notifyToolkitThreadFree","parameterTypes":[] }] +}, +{ + "name":"sun.awt.SunHints", + "fields":[{"name":"INTVAL_STROKE_PURE"}] +}, +{ + "name":"sun.java2d.Disposer", + "methods":[{"name":"addRecord","parameterTypes":["java.lang.Object","long","long"] }] +}, +{ + "name":"sun.java2d.InvalidPipeException" +}, +{ + "name":"sun.java2d.NullSurfaceData" +}, +{ + "name":"sun.java2d.SunGraphics2D", + "fields":[ + {"name":"clipRegion"}, + {"name":"composite"}, + {"name":"eargb"}, + {"name":"lcdTextContrast"}, + {"name":"pixel"}, + {"name":"strokeHint"} + ] +}, +{ + "name":"sun.java2d.SurfaceData", + "fields":[ + {"name":"pData"}, + {"name":"valid"} + ] +}, +{ + "name":"sun.java2d.loops.Blit", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.BlitBg", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.CompositeType", + "fields":[ + {"name":"AnyAlpha"}, + {"name":"Src"}, + {"name":"SrcNoEa"}, + {"name":"SrcOver"}, + {"name":"SrcOverNoEa"}, + {"name":"Xor"} + ] +}, +{ + "name":"sun.java2d.loops.DrawGlyphList", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.DrawGlyphListAA", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.DrawGlyphListLCD", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.DrawLine", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.DrawParallelogram", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.DrawPath", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.DrawPolygons", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.DrawRect", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.FillParallelogram", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.FillPath", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.FillRect", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.FillSpans", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.GraphicsPrimitive", + "fields":[{"name":"pNativePrim"}] +}, +{ + "name":"sun.java2d.loops.GraphicsPrimitiveMgr", + "methods":[{"name":"register","parameterTypes":["sun.java2d.loops.GraphicsPrimitive[]"] }] +}, +{ + "name":"sun.java2d.loops.MaskBlit", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.MaskFill", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.ScaledBlit", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.SurfaceType", + "fields":[ + {"name":"Any3Byte"}, + {"name":"Any4Byte"}, + {"name":"AnyByte"}, + {"name":"AnyColor"}, + {"name":"AnyInt"}, + {"name":"AnyShort"}, + {"name":"ByteBinary1Bit"}, + {"name":"ByteBinary2Bit"}, + {"name":"ByteBinary4Bit"}, + {"name":"ByteGray"}, + {"name":"ByteIndexed"}, + {"name":"ByteIndexedBm"}, + {"name":"FourByteAbgr"}, + {"name":"FourByteAbgrPre"}, + {"name":"Index12Gray"}, + {"name":"Index8Gray"}, + {"name":"IntArgb"}, + {"name":"IntArgbBm"}, + {"name":"IntArgbPre"}, + {"name":"IntBgr"}, + {"name":"IntRgb"}, + {"name":"IntRgbx"}, + {"name":"OpaqueColor"}, + {"name":"ThreeByteBgr"}, + {"name":"Ushort4444Argb"}, + {"name":"Ushort555Rgb"}, + {"name":"Ushort555Rgbx"}, + {"name":"Ushort565Rgb"}, + {"name":"UshortGray"}, + {"name":"UshortIndexed"} + ] +}, +{ + "name":"sun.java2d.loops.TransformHelper", + "methods":[{"name":"","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }] +}, +{ + "name":"sun.java2d.loops.XORComposite", + "fields":[ + {"name":"alphaMask"}, + {"name":"xorColor"}, + {"name":"xorPixel"} + ] +}, +{ + "name":"sun.java2d.opengl.OGLSurfaceData", + "fields":[ + {"name":"isBIOpShaderEnabled"}, + {"name":"isFBObjectEnabled"}, + {"name":"isGradShaderEnabled"}, + {"name":"isLCDShaderEnabled"} + ] +}, +{ + "name":"sun.java2d.pipe.Region", + "fields":[ + {"name":"bands"}, + {"name":"endIndex"}, + {"name":"hix"}, + {"name":"hiy"}, + {"name":"lox"}, + {"name":"loy"} + ] +}, +{ + "name":"sun.java2d.pipe.RegionIterator", + "fields":[ + {"name":"curIndex"}, + {"name":"numXbands"}, + {"name":"region"} + ] +}, +{ + "name":"sun.launcher.LauncherHelper$FXHelper", + "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] +} +] diff --git a/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/predefined-classes-config.json b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/predefined-classes-config.json new file mode 100644 index 00000000..0e79b2c5 --- /dev/null +++ b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/predefined-classes-config.json @@ -0,0 +1,8 @@ +[ + { + "type":"agent-extracted", + "classes":[ + ] + } +] + diff --git a/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/proxy-config.json b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/proxy-config.json new file mode 100644 index 00000000..0d4f101c --- /dev/null +++ b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/proxy-config.json @@ -0,0 +1,2 @@ +[ +] diff --git a/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/reflect-config.json b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/reflect-config.json new file mode 100644 index 00000000..e0a70242 --- /dev/null +++ b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/reflect-config.json @@ -0,0 +1,857 @@ +[ +{ + "name":"[D" +}, +{ + "name":"com.sun.glass.ui.Application" +}, +{ + "name":"com.sun.glass.ui.Menu" +}, +{ + "name":"com.sun.glass.ui.MenuItem$Callback" +}, +{ + "name":"com.sun.glass.ui.Size" +}, +{ + "name":"com.sun.glass.ui.View" +}, +{ + "name":"com.sun.javafx.font.coretext.CTFactory", + "methods":[{"name":"getFactory","parameterTypes":[] }] +}, +{ + "name":"com.sun.javafx.logging.PrintLogger", + "methods":[{"name":"createInstance","parameterTypes":[] }] +}, +{ + "name":"com.sun.javafx.logging.jfr.JFRPulseLogger", + "methods":[{"name":"createInstance","parameterTypes":[] }] +}, +{ + "name":"com.sun.javafx.reflect.Trampoline", + "methods":[{"name":"invoke","parameterTypes":["java.lang.reflect.Method","java.lang.Object","java.lang.Object[]"] }] +}, +{ + "name":"com.sun.javafx.scene.control.skin.Utils", + "methods":[{"name":"getResource","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"com.sun.javafx.tk.quantum.QuantumToolkit", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.prism.GraphicsPipeline", + "methods":[ + {"name":"getFontFactory","parameterTypes":[] }, + {"name":"getPipeline","parameterTypes":[] } + ] +}, +{ + "name":"com.sun.prism.shader.FillPgram_Color_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, +{ + "name":"com.sun.prism.shader.FillPgram_LinearGradient_PAD_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, +{ + "name":"com.sun.prism.shader.FillRoundRect_Color_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, +{ + "name":"com.sun.prism.shader.FillRoundRect_LinearGradient_PAD_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, +{ + "name":"com.sun.prism.shader.Mask_TextureSuper_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, +{ + "name":"com.sun.prism.shader.Solid_TextureRGB_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, +{ + "name":"com.sun.prism.shader.Texture_Color_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, +{ + "name":"com.sun.prism.shader.Texture_LinearGradient_PAD_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, +{ + "name":"com.sun.scenario.effect.impl.prism.PrRenderer", + "methods":[{"name":"createRenderer","parameterTypes":["com.sun.scenario.effect.FilterContext"] }] +}, +{ + "name":"com.sun.scenario.effect.impl.prism.ps.PPSLinearConvolveShadowPeer", + "methods":[{"name":"","parameterTypes":["com.sun.scenario.effect.FilterContext","com.sun.scenario.effect.impl.Renderer","java.lang.String"] }] +}, +{ + "name":"com.sun.scenario.effect.impl.prism.ps.PPSRenderer", + "methods":[{"name":"createRenderer","parameterTypes":["com.sun.scenario.effect.FilterContext"] }] +}, +{ + "name":"com.sun.xml.internal.stream.XMLInputFactoryImpl", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"jace.EmulatorUILogic", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[ + {"name":"scaleIntegerRatio","parameterTypes":[] }, + {"name":"showConfig","parameterTypes":[] }, + {"name":"showIDE","parameterTypes":[] }, + {"name":"toggleFullscreen","parameterTypes":[] } + ] +}, +{ + "name":"jace.JaceUIController", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"initialize","parameterTypes":[] } + ] +}, +{ + "name":"jace.LawlessLegends", + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"main","parameterTypes":["java.lang.String[]"] } + ] +}, +{ + "name":"jace.apple2e.Apple2e", + "fields":[ + {"name":"PRODUCTION_MODE"}, + {"name":"acceleratorEnabled"}, + {"name":"card1"}, + {"name":"card2"}, + {"name":"card3"}, + {"name":"card4"}, + {"name":"card5"}, + {"name":"card6"}, + {"name":"card7"}, + {"name":"cheatEngine"}, + {"name":"clockEnabled"}, + {"name":"enableHints"}, + {"name":"joy1enabled"}, + {"name":"joy2enabled"}, + {"name":"ramCard"}, + {"name":"showSpeedMonitors"}, + {"name":"useConsoleProbe"}, + {"name":"useDebugRom"}, + {"name":"videoRenderer"} + ] +}, +{ + "name":"jace.apple2e.MOS65C02", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.apple2e.MOS65C02$AddressCalculator" +}, +{ + "name":"jace.apple2e.MOS65C02$CommandProcessor" +}, +{ + "name":"jace.apple2e.Speaker", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.apple2e.VideoDHGR" +}, +{ + "name":"jace.apple2e.VideoNTSC", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[ + {"name":"","parameterTypes":["jace.core.Computer"] }, + {"name":"changeVideoMode","parameterTypes":[] } + ] +}, +{ + "name":"jace.cheat.MetaCheat" +}, +{ + "name":"jace.cheat.MontezumasRevengeCheats" +}, +{ + "name":"jace.cheat.PrinceOfPersiaCheats" +}, +{ + "name":"jace.config.Configuration", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.config.ConfigurationUIController", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"applyConfig","parameterTypes":["javafx.scene.input.MouseEvent"] }, + {"name":"initialize","parameterTypes":[] } + ] +}, +{ + "name":"jace.config.Reconfigurable" +}, +{ + "name":"jace.core.CPU", + "fields":[ + {"name":"trace"}, + {"name":"traceLength"} + ] +}, +{ + "name":"jace.core.Computer", + "fields":[{"name":"enableStateManager"}], + "methods":[{"name":"invokeWarmStart","parameterTypes":[] }] +}, +{ + "name":"jace.core.Debugger" +}, +{ + "name":"jace.core.KeyHandler" +}, +{ + "name":"jace.core.Keyboard", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"solidApple","parameterTypes":["boolean"] }] +}, +{ + "name":"jace.core.Motherboard", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.core.RAMEvent$RAMEventHandler" +}, +{ + "name":"jace.core.SoftSwitch" +}, +{ + "name":"jace.core.SoundMixer", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.core.TimedDevice", + "fields":[ + {"name":"maxspeed"}, + {"name":"speedRatio"} + ] +}, +{ + "name":"jace.core.Video", + "fields":[ + {"name":"MIN_SCREEN_REFRESH"}, + {"name":"hblankOffsetX"}, + {"name":"hblankOffsetY"}, + {"name":"waitsPerCycle"} + ] +}, +{ + "name":"jace.core.VideoWriter" +}, +{ + "name":"jace.hardware.CardAppleMouse", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":["jace.core.Computer"] }] +}, +{ + "name":"jace.hardware.CardDiskII", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":["jace.core.Computer"] }] +}, +{ + "name":"jace.hardware.CardExt80Col", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":["jace.core.Computer"] }] +}, +{ + "name":"jace.hardware.CardHayesMicromodem" +}, +{ + "name":"jace.hardware.CardMockingboard", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":["jace.core.Computer"] }] +}, +{ + "name":"jace.hardware.CardMockingboard$1", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.hardware.CardRamFactor", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":["jace.core.Computer"] }] +}, +{ + "name":"jace.hardware.CardRamworks", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":["jace.core.Computer"] }] +}, +{ + "name":"jace.hardware.CardSSC" +}, +{ + "name":"jace.hardware.CardThunderclock", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":["jace.core.Computer"] }] +}, +{ + "name":"jace.hardware.Joystick", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.hardware.NoSlotClock", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.hardware.PassportMidiInterface", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":["jace.core.Computer"] }] +}, +{ + "name":"jace.hardware.ProdosDriver" +}, +{ + "name":"jace.hardware.SmartportDriver" +}, +{ + "name":"jace.hardware.ZipWarpAccelerator", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.hardware.massStorage.CardMassStorage", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":["jace.core.Computer"] }] +}, +{ + "name":"jace.hardware.massStorage.DiskNode" +}, +{ + "name":"jace.hardware.massStorage.IDisk" +}, +{ + "name":"jace.hardware.mockingboard.TimedGenerator" +}, +{ + "name":"jace.ide.CompileResult" +}, +{ + "name":"jace.ide.IdeController", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"jace.ide.LanguageHandler" +}, +{ + "name":"jace.ide.Program" +}, +{ + "name":"jace.lawless.LawlessComputer", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.lawless.LawlessHacks", + "allPublicFields":true, + "queryAllPublicMethods":true +}, +{ + "name":"jace.lawless.LawlessVideo", + "allPublicFields":true, + "queryAllPublicMethods":true, + "methods":[{"name":"","parameterTypes":["jace.core.Computer"] }] +}, +{ + "name":"jace.library.MediaConsumer" +}, +{ + "name":"jace.library.MediaConsumerParent" +}, +{ + "name":"java.awt.datatransfer.Transferable" +}, +{ + "name":"java.awt.event.FocusListener" +}, +{ + "name":"java.awt.event.KeyAdapter" +}, +{ + "name":"java.awt.event.MouseListener" +}, +{ + "name":"java.io.FileFilter" +}, +{ + "name":"java.io.Serializable" +}, +{ + "name":"java.lang.Character", + "methods":[{"name":"isIdeographic","parameterTypes":["int"] }] +}, +{ + "name":"java.lang.Class", + "methods":[ + {"name":"forName","parameterTypes":["java.lang.Module","java.lang.String"] }, + {"name":"getModule","parameterTypes":[] } + ] +}, +{ + "name":"java.lang.Comparable" +}, +{ + "name":"java.lang.Enum" +}, +{ + "name":"java.lang.Module", + "methods":[ + {"name":"getDescriptor","parameterTypes":[] }, + {"name":"getLayer","parameterTypes":[] }, + {"name":"getName","parameterTypes":[] }, + {"name":"getResourceAsStream","parameterTypes":["java.lang.String"] } + ] +}, +{ + "name":"java.lang.ModuleLayer", + "methods":[ + {"name":"boot","parameterTypes":[] }, + {"name":"findModule","parameterTypes":["java.lang.String"] } + ] +}, +{ + "name":"java.lang.Runnable" +}, +{ + "name":"java.lang.String", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.Object"] }] +}, +{ + "name":"java.lang.StringBuilder" +}, +{ + "name":"java.lang.annotation.Annotation" +}, +{ + "name":"java.lang.invoke.CallSite" +}, +{ + "name":"java.net.URL" +}, +{ + "name":"java.nio.ByteBuffer", + "methods":[{"name":"order","parameterTypes":["java.nio.ByteOrder"] }] +}, +{ + "name":"java.nio.ByteOrder", + "methods":[{"name":"nativeOrder","parameterTypes":[] }] +}, +{ + "name":"java.util.ArrayList" +}, +{ + "name":"java.util.Comparator" +}, +{ + "name":"java.util.HashMap" +}, +{ + "name":"java.util.List", + "methods":[{"name":"copyOf","parameterTypes":["java.util.Collection"] }] +}, +{ + "name":"java.util.Map$Entry" +}, +{ + "name":"java.util.Optional", + "methods":[{"name":"isEmpty","parameterTypes":[] }] +}, +{ + "name":"java.util.TimerTask" +}, +{ + "name":"java.util.TreeMap" +}, +{ + "name":"java.util.TreeSet" +}, +{ + "name":"java.util.function.Function" +}, +{ + "name":"java.util.zip.DeflaterInputStream" +}, +{ + "name":"javafx.application.Application" +}, +{ + "name":"javafx.collections.FXCollections", + "methods":[{"name":"observableArrayList","parameterTypes":[] }] +}, +{ + "name":"javafx.fxml.FXMLLoader" +}, +{ + "name":"javafx.geometry.Insets", + "queryAllPublicMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":["double"] }, + {"name":"","parameterTypes":["double","double","double","double"] } + ] +}, +{ + "name":"javafx.geometry.NodeOrientation", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"javafx.geometry.Pos", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"javafx.scene.Camera" +}, +{ + "name":"javafx.scene.Group" +}, +{ + "name":"javafx.scene.Node", + "queryAllDeclaredMethods":true, + "methods":[ + {"name":"getId","parameterTypes":[] }, + {"name":"getStyleClass","parameterTypes":[] }, + {"name":"setId","parameterTypes":["java.lang.String"] }, + {"name":"setLayoutX","parameterTypes":["double"] }, + {"name":"setLayoutY","parameterTypes":["double"] }, + {"name":"setMouseTransparent","parameterTypes":["boolean"] }, + {"name":"setNodeOrientation","parameterTypes":["javafx.geometry.NodeOrientation"] }, + {"name":"setOnMouseClicked","parameterTypes":["javafx.event.EventHandler"] }, + {"name":"setPickOnBounds","parameterTypes":["boolean"] }, + {"name":"setStyle","parameterTypes":["java.lang.String"] }, + {"name":"setVisible","parameterTypes":["boolean"] } + ] +}, +{ + "name":"javafx.scene.ParallelCamera" +}, +{ + "name":"javafx.scene.Parent", + "queryAllDeclaredMethods":true, + "methods":[{"name":"getStylesheets","parameterTypes":[] }] +}, +{ + "name":"javafx.scene.Scene" +}, +{ + "name":"javafx.scene.control.Button", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"javafx.scene.control.ButtonBase", + "queryAllDeclaredMethods":true +}, +{ + "name":"javafx.scene.control.ComboBox", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"setItems","parameterTypes":["javafx.collections.ObservableList"] } + ] +}, +{ + "name":"javafx.scene.control.ComboBoxBase", + "queryAllDeclaredMethods":true, + "methods":[{"name":"setValue","parameterTypes":["java.lang.Object"] }] +}, +{ + "name":"javafx.scene.control.ContentDisplay", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"javafx.scene.control.Control", + "queryAllDeclaredMethods":true +}, +{ + "name":"javafx.scene.control.Label", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"javafx.scene.control.Labeled", + "queryAllDeclaredMethods":true, + "methods":[ + {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] }, + {"name":"setContentDisplay","parameterTypes":["javafx.scene.control.ContentDisplay"] }, + {"name":"setGraphic","parameterTypes":["javafx.scene.Node"] }, + {"name":"setMnemonicParsing","parameterTypes":["boolean"] }, + {"name":"setText","parameterTypes":["java.lang.String"] } + ] +}, +{ + "name":"javafx.scene.control.ScrollPane", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"setContent","parameterTypes":["javafx.scene.Node"] }, + {"name":"setFitToHeight","parameterTypes":["boolean"] }, + {"name":"setFitToWidth","parameterTypes":["boolean"] } + ] +}, +{ + "name":"javafx.scene.control.Slider", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"setBlockIncrement","parameterTypes":["double"] }, + {"name":"setMajorTickUnit","parameterTypes":["double"] }, + {"name":"setMax","parameterTypes":["double"] }, + {"name":"setMinorTickCount","parameterTypes":["int"] }, + {"name":"setShowTickLabels","parameterTypes":["boolean"] }, + {"name":"setShowTickMarks","parameterTypes":["boolean"] }, + {"name":"setSnapToTicks","parameterTypes":["boolean"] }, + {"name":"setValue","parameterTypes":["double"] } + ] +}, +{ + "name":"javafx.scene.control.SplitPane", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getItems","parameterTypes":[] }, + {"name":"setDividerPositions","parameterTypes":["double[]"] } + ] +}, +{ + "name":"javafx.scene.control.ToolBar", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getItems","parameterTypes":[] } + ] +}, +{ + "name":"javafx.scene.control.TreeItem" +}, +{ + "name":"javafx.scene.control.TreeView", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"javafx.scene.effect.Effect" +}, +{ + "name":"javafx.scene.image.Image" +}, +{ + "name":"javafx.scene.image.ImageView", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"setFitHeight","parameterTypes":["double"] }, + {"name":"setFitWidth","parameterTypes":["double"] }, + {"name":"setImage","parameterTypes":["javafx.scene.image.Image"] } + ] +}, +{ + "name":"javafx.scene.layout.AnchorPane", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getBottomAnchor","parameterTypes":["javafx.scene.Node"] }, + {"name":"getLeftAnchor","parameterTypes":["javafx.scene.Node"] }, + {"name":"getRightAnchor","parameterTypes":["javafx.scene.Node"] }, + {"name":"getTopAnchor","parameterTypes":["javafx.scene.Node"] }, + {"name":"setBottomAnchor","parameterTypes":["javafx.scene.Node","java.lang.Double"] }, + {"name":"setLeftAnchor","parameterTypes":["javafx.scene.Node","java.lang.Double"] }, + {"name":"setRightAnchor","parameterTypes":["javafx.scene.Node","java.lang.Double"] }, + {"name":"setTopAnchor","parameterTypes":["javafx.scene.Node","java.lang.Double"] } + ] +}, +{ + "name":"javafx.scene.layout.BorderPane", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getAlignment","parameterTypes":["javafx.scene.Node"] }, + {"name":"setAlignment","parameterTypes":["javafx.scene.Node","javafx.geometry.Pos"] }, + {"name":"setBottom","parameterTypes":["javafx.scene.Node"] }, + {"name":"setCenter","parameterTypes":["javafx.scene.Node"] }, + {"name":"setTop","parameterTypes":["javafx.scene.Node"] } + ] +}, +{ + "name":"javafx.scene.layout.HBox", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getHgrow","parameterTypes":["javafx.scene.Node"] }, + {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] }, + {"name":"setFillHeight","parameterTypes":["boolean"] }, + {"name":"setHgrow","parameterTypes":["javafx.scene.Node","javafx.scene.layout.Priority"] }, + {"name":"setMargin","parameterTypes":["javafx.scene.Node","javafx.geometry.Insets"] } + ] +}, +{ + "name":"javafx.scene.layout.Pane", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getChildren","parameterTypes":[] } + ] +}, +{ + "name":"javafx.scene.layout.Priority", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"javafx.scene.layout.Region", + "queryAllDeclaredMethods":true, + "methods":[ + {"name":"setMaxHeight","parameterTypes":["double"] }, + {"name":"setMinHeight","parameterTypes":["double"] }, + {"name":"setPadding","parameterTypes":["javafx.geometry.Insets"] }, + {"name":"setPrefHeight","parameterTypes":["double"] }, + {"name":"setPrefWidth","parameterTypes":["double"] } + ] +}, +{ + "name":"javafx.scene.layout.StackPane", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getAlignment","parameterTypes":["javafx.scene.Node"] }, + {"name":"setAlignment","parameterTypes":["javafx.scene.Node","javafx.geometry.Pos"] }, + {"name":"setMargin","parameterTypes":["javafx.scene.Node","javafx.geometry.Insets"] } + ] +}, +{ + "name":"javafx.scene.layout.TilePane", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getAlignment","parameterTypes":["javafx.scene.Node"] }, + {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] }, + {"name":"setAlignment","parameterTypes":["javafx.scene.Node","javafx.geometry.Pos"] }, + {"name":"setHgap","parameterTypes":["double"] }, + {"name":"setVgap","parameterTypes":["double"] } + ] +}, +{ + "name":"javafx.scene.layout.VBox", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"javafx.scene.shape.LineTo" +}, +{ + "name":"javafx.scene.shape.MoveTo" +}, +{ + "name":"javafx.scene.shape.Path" +}, +{ + "name":"javafx.scene.shape.PathElement" +}, +{ + "name":"javafx.scene.shape.Rectangle" +}, +{ + "name":"javafx.scene.shape.SVGPath" +}, +{ + "name":"javafx.scene.shape.Shape" +}, +{ + "name":"javafx.scene.text.Font" +}, +{ + "name":"javafx.scene.text.Text" +}, +{ + "name":"javafx.scene.transform.Transform" +}, +{ + "name":"javafx.scene.web.WebView" +}, +{ + "name":"javafx.stage.PopupWindow" +}, +{ + "name":"javafx.stage.Stage" +}, +{ + "name":"javafx.stage.Window" +}, +{ + "name":"javafx.util.StringConverter" +}, +{ + "name":"javafx.util.converter.IntegerStringConverter" +}, +{ + "name":"javax.swing.JLabel" +}, +{ + "name":"javax.swing.JPanel" +}, +{ + "name":"javax.swing.tree.TreeModel" +}, +{ + "name":"org.ibex.nestedvm.UnixRuntime" +}, +{ + "name":"sun.java2d.marlin.DMarlinRenderingEngine", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.misc.Unsafe", + "fields":[{"name":"theUnsafe"}] +}, +{ + "name":"sun.security.provider.MD5", + "methods":[{"name":"","parameterTypes":[] }] +} +] diff --git a/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/resource-config.json b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/resource-config.json new file mode 100644 index 00000000..4ede14a0 --- /dev/null +++ b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/resource-config.json @@ -0,0 +1,24 @@ +{ + "resources":{ + "includes":[{ + "pattern":"\\Qjace\\E" + }]}, + "bundles":[ + { + "name":"com.sun.javafx.tk.quantum.QuantumMessagesBundle", + "locales":[""] + }, + { + "name":"com/sun/javafx/scene/control/skin/resources/controls", + "locales":[""] + }, + { + "name":"sun.awt.resources.awt", + "classNames":["sun.awt.resources.awt"] + }, + { + "name":"sun.awt.resources.awtosx", + "classNames":["sun.awt.resources.awtosx"] + } + ] +} diff --git a/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/serialization-config.json b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/serialization-config.json new file mode 100644 index 00000000..f3d7e06e --- /dev/null +++ b/Platform/Apple/tools/jace/src/main/resources/META-INF/native-image/serialization-config.json @@ -0,0 +1,8 @@ +{ + "types":[ + ], + "lambdaCapturingTypes":[ + ], + "proxies":[ + ] +} diff --git a/Platform/Apple/tools/jace/src/main/resources/fxml/Configuration.fxml b/Platform/Apple/tools/jace/src/main/resources/fxml/Configuration.fxml index a9ea776f..2b6cc5dc 100644 --- a/Platform/Apple/tools/jace/src/main/resources/fxml/Configuration.fxml +++ b/Platform/Apple/tools/jace/src/main/resources/fxml/Configuration.fxml @@ -10,7 +10,7 @@ - + diff --git a/Platform/Apple/tools/jace/src/main/resources/jace/data/sound/scores.txt b/Platform/Apple/tools/jace/src/main/resources/jace/data/sound/scores.txt index 1554b031..1ec22d33 100644 --- a/Platform/Apple/tools/jace/src/main/resources/jace/data/sound/scores.txt +++ b/Platform/Apple/tools/jace/src/main/resources/jace/data/sound/scores.txt @@ -57,8 +57,8 @@ Common 3 8 Bit Weapon - Lawless Legends Original Double Score - 23 Tragedy in the West 8-Bit Samples.mp3 4 8 Bit Weapon - Lawless Legends Original Double Score - 24 Victory! 8-Bit Samples.mp3 5 8 Bit Weapon - Lawless Legends Original Double Score - 25 Grub Gulch 8-Bit Samples.mp3 -6 8 Bit Weapon - Lawless Legends Original Double Score - 26 Mines of Mystery 8-Bit Samples.mp3 -7 8 Bit Weapon - Lawless Legends Original Double Score - 27 Texas Flats 8-Bit Samples.mp3 +6 8 Bit Weapon - Lawless Legends Original Double Score - 27 Mines of Mystery 8-Bit Samples.mp3 +7 8 Bit Weapon - Lawless Legends Original Double Score - 26 Texas Flats 8-Bit Samples.mp3 8 8 Bit Weapon - Lawless Legends Original Double Score - 28 Strange Tales 8-Bit Samples.mp3 9 8 Bit Weapon - Lawless Legends Original Double Score - 29 Oro's Villa 8-Bit Samples.mp3 10 8 Bit Weapon - Lawless Legends Original Double Score - 30 Tahnku Village 8-Bit Samples.mp3 diff --git a/Platform/Apple/tools/jace/src/test/java/jace/TestUtils.java b/Platform/Apple/tools/jace/src/test/java/jace/TestUtils.java new file mode 100644 index 00000000..d17b75f2 --- /dev/null +++ b/Platform/Apple/tools/jace/src/test/java/jace/TestUtils.java @@ -0,0 +1,79 @@ +/* + * Copyright 2023 org.badvision. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jace; + +import jace.apple2e.MOS65C02; +import jace.assembly.AssemblyHandler; +import jace.core.CPU; +import jace.core.Device; +import jace.ide.HeadlessProgram; +import jace.ide.Program; + +/** + * + * @author brobert + */ +public class TestUtils { + private TestUtils() { + // Utility class has no constructor + } + + public static void assemble(String code, int addr) { + runAssemblyCode(code, addr, 0); + } + + public static void runAssemblyCode(String code, int ticks) { + runAssemblyCode(code, 0x0300, ticks); + } + + public static void runAssemblyCode(String code, int addr, int ticks) { + CPU cpu = Emulator.getComputer().getCpu(); + cpu.trace = true; + HeadlessProgram program = new HeadlessProgram(Program.DocumentType.assembly); + program.setValue("*=$"+Integer.toHexString(addr)+"\n "+code+"\n NOP\n RTS"); + program.execute(); + if (ticks > 0) { + cpu.resume(); + for (int i=0; i < ticks; i++) { + cpu.doTick(); + } + cpu.suspend(); + } + } + + public static Device createSimpleDevice(Runnable r, String name) { + return new Device(Emulator.getComputer()) { + @Override + public void tick() { + r.run(); + } + + @Override + public String getShortName() { + return name; + } + + @Override + public void reconfigure() { + } + + @Override + protected String getDeviceName() { + return name; + } + }; + } +} diff --git a/Platform/Apple/tools/jace/src/test/java/jace/core/MemoryListenerTest.java b/Platform/Apple/tools/jace/src/test/java/jace/core/MemoryListenerTest.java new file mode 100644 index 00000000..3370d6c7 --- /dev/null +++ b/Platform/Apple/tools/jace/src/test/java/jace/core/MemoryListenerTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 2023 org.badvision. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jace.core; +import jace.Emulator; +import jace.apple2e.MOS65C02; +import jace.apple2e.RAM128k; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; +import static jace.TestUtils.*; + +/** + * Test that memory listeners fire appropriately. + * @author brobert + */ +public class MemoryListenerTest { + static Computer computer; + static MOS65C02 cpu; + static RAM128k ram; + + @BeforeClass + public static void setupClass() { + Utility.setHeadlessMode(true); + SoundMixer.MUTE = true; + computer = Emulator.getComputer(); + cpu = (MOS65C02) computer.getCpu(); + ram = (RAM128k) computer.getMemory(); + } + + + @Test + public void testListenerRelevance() { + AtomicInteger anyEventCaught = new AtomicInteger(); + RAMListener anyListener = new RAMListener(RAMEvent.TYPE.ANY, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) { + @Override + protected void doConfig() { + setScopeStart(0x0100); + } + + @Override + protected void doEvent(RAMEvent e) { + anyEventCaught.incrementAndGet(); + } + }; + + AtomicInteger readAnyEventCaught = new AtomicInteger(); + RAMListener readAnyListener = new RAMListener(RAMEvent.TYPE.READ, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) { + @Override + protected void doConfig() { + setScopeStart(0x0100); + } + + @Override + protected void doEvent(RAMEvent e) { + readAnyEventCaught.incrementAndGet(); + } + }; + + AtomicInteger writeEventCaught = new AtomicInteger(); + RAMListener writeListener = new RAMListener(RAMEvent.TYPE.WRITE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) { + @Override + protected void doConfig() { + setScopeStart(0x0100); + } + + @Override + protected void doEvent(RAMEvent e) { + writeEventCaught.incrementAndGet(); + } + }; + + AtomicInteger executeEventCaught = new AtomicInteger(); + RAMListener executeListener = new RAMListener(RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) { + @Override + protected void doConfig() { + setScopeStart(0x0100); + } + + @Override + protected void doEvent(RAMEvent e) { + executeEventCaught.incrementAndGet(); + } + }; + + + RAMEvent readDataEvent = new RAMEvent(RAMEvent.TYPE.READ_DATA, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY, 0x100, 0, 0); + RAMEvent readOperandEvent = new RAMEvent(RAMEvent.TYPE.READ_OPERAND, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY, 0x100, 0, 0); + RAMEvent executeEvent = new RAMEvent(RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY, 0x100, 0, 0); + RAMEvent writeEvent = new RAMEvent(RAMEvent.TYPE.WRITE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY, 0x100, 0, 0); + + // Any listener + assertTrue("Any listener should handle all events", anyListener.isRelevant(readDataEvent)); + assertTrue("Any listener should handle all events", anyListener.isRelevant(readOperandEvent)); + assertTrue("Any listener should handle all events", anyListener.isRelevant(executeEvent)); + assertTrue("Any listener should handle all events", anyListener.isRelevant(writeEvent)); + + // Read listener + assertTrue("Read listener should handle all read events", readAnyListener.isRelevant(readDataEvent)); + assertTrue("Read listener should handle all read events", readAnyListener.isRelevant(readOperandEvent)); + assertTrue("Read listener should handle all read events", readAnyListener.isRelevant(executeEvent)); + assertFalse("Read listener should ignore write events", readAnyListener.isRelevant(writeEvent)); + + // Write listener + assertFalse("Write listener should ignore all read events", writeListener.isRelevant(readDataEvent)); + assertFalse("Write listener should ignore all read events", writeListener.isRelevant(readOperandEvent)); + assertFalse("Write listener should ignore all read events", writeListener.isRelevant(executeEvent)); + assertTrue("Write listener should handle write events", writeListener.isRelevant(writeEvent)); + + // Execution listener + assertTrue("Execute listener should only catch execution events", executeListener.isRelevant(executeEvent)); + assertFalse("Execute listener should only catch execution events", executeListener.isRelevant(readDataEvent)); + assertFalse("Execute listener should only catch execution events", executeListener.isRelevant(readOperandEvent)); + assertFalse("Execute listener should only catch execution events", executeListener.isRelevant(writeEvent)); + + ram.addListener(anyListener); + ram.addListener(executeListener); + ram.addListener(readAnyListener); + ram.addListener(writeListener); + + runAssemblyCode("NOP", 0x0100, 2); + + assertEquals("Should have no writes for 0x0100", 0, writeEventCaught.get()); + assertEquals("Should have read event for 0x0100", 1, readAnyEventCaught.get()); + assertEquals("Should have execute for 0x0100", 1, executeEventCaught.get()); + } +} diff --git a/Platform/Apple/tools/jace/src/test/java/jace/cpu/Basic6502FuncationalityTest.java b/Platform/Apple/tools/jace/src/test/java/jace/cpu/Basic6502FuncationalityTest.java index 320e8526..7c11ac2a 100644 --- a/Platform/Apple/tools/jace/src/test/java/jace/cpu/Basic6502FuncationalityTest.java +++ b/Platform/Apple/tools/jace/src/test/java/jace/cpu/Basic6502FuncationalityTest.java @@ -21,15 +21,15 @@ import jace.apple2e.RAM128k; import jace.core.Computer; import jace.core.SoundMixer; import jace.core.Utility; -import jace.ide.HeadlessProgram; -import jace.ide.Program; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static jace.TestUtils.*; /** * Basic test functionality to assert correct 6502 decode and execution. @@ -63,11 +63,11 @@ public class Basic6502FuncationalityTest { @Test public void assertMemoryConfiguredCorrectly() { - assertEquals("Active read bank 3 should be main memory page 3", + assertArrayEquals("Active read bank 3 should be main memory page 3", ram.mainMemory.getMemoryPage(3), ram.activeRead.getMemoryPage(3)); - assertEquals("Active write bank 3 should be main memory page 3", + assertArrayEquals("Active write bank 3 should be main memory page 3", ram.mainMemory.getMemoryPage(3), ram.activeWrite.getMemoryPage(3)); } @@ -77,7 +77,7 @@ public class Basic6502FuncationalityTest { cpu.A = 0; cpu.D = false; cpu.C = 0; - runAssemblyCode("adc #1"); + runAssemblyCode("adc #1", 2); assertEquals("0+1 (c=0) = 1", 1, cpu.A); assertFalse("Result is not zero", cpu.Z); assertEquals("Carry is clear", 0, cpu.C); @@ -88,7 +88,7 @@ public class Basic6502FuncationalityTest { cpu.A = 0; cpu.D = false; cpu.C = 1; - runAssemblyCode("adc #1"); + runAssemblyCode("adc #1", 2); assertEquals("0+1 (c=1) = 2", 2, cpu.A); assertFalse("Result is not zero", cpu.Z); assertEquals("Carry is clear", 0, cpu.C); @@ -99,7 +99,7 @@ public class Basic6502FuncationalityTest { cpu.A = 9; cpu.D = true; cpu.C = 0; - runAssemblyCode("adc #1"); + runAssemblyCode("adc #1", 2); assertEquals("9+1 (c=0) = 0x10", 0x10, cpu.A); assertFalse("Result is not zero", cpu.Z); assertEquals("Carry is clear", 0, cpu.C); @@ -110,22 +110,9 @@ public class Basic6502FuncationalityTest { cpu.A = 9; cpu.D = true; cpu.C = 1; - runAssemblyCode("adc #1"); + runAssemblyCode("adc #1", 2); assertEquals("9+1 (c=1) = 0x11", 0x11, cpu.A); assertFalse("Result is not zero", cpu.Z); assertEquals("Carry is clear", 0, cpu.C); } - - private void runAssemblyCode(String code) { - runAssemblyCode(code, 0x0300); - } - - private void runAssemblyCode(String code, int addr) { - cpu.trace = true; - HeadlessProgram program = new HeadlessProgram(Program.DocumentType.assembly); - program.setValue("*=$"+Integer.toHexString(addr)+"\n "+code+"\n NOP\n RTS"); - program.execute(); - cpu.tick(); - cpu.tick(); - } } diff --git a/Platform/Apple/tools/jace/src/test/java/jace/cpu/CycleCountTest.java b/Platform/Apple/tools/jace/src/test/java/jace/cpu/CycleCountTest.java new file mode 100644 index 00000000..66a8dce5 --- /dev/null +++ b/Platform/Apple/tools/jace/src/test/java/jace/cpu/CycleCountTest.java @@ -0,0 +1,148 @@ +/* + * Copyright 2023 org.badvision. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jace.cpu; + +import jace.Emulator; +import jace.core.Computer; +import jace.core.Utility; +import jace.core.SoundMixer; +import jace.apple2e.MOS65C02; +import jace.apple2e.RAM128k; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; +import static jace.TestUtils.*; + + +/** + * More advanced cycle counting tests. These help ensure CPU runs correctly so things + * like vapor lock and speaker sound work as expected. + * @author brobert + */ +public class CycleCountTest { + + static Computer computer; + static MOS65C02 cpu; + static RAM128k ram; + + @BeforeClass + public static void setupClass() { + Utility.setHeadlessMode(true); + SoundMixer.MUTE = true; + computer = Emulator.getComputer(); + cpu = (MOS65C02) computer.getCpu(); + ram = (RAM128k) computer.getMemory(); + + // Let the final stopping point for tests loop indefinitely + // This also lets us figure out how long it's been looping when the test ends + assemble("jmp $1000", 0x01000); + } + + private String BELL = """ +SPKR = $C030 +BELL ldy #$01 +BELL2 lda #$0c + jsr WAIT + lda SPKR + dey + bne BELL2 + jmp $1000 ; Success case is jumping to $1000 +WAIT sec +WAIT2 pha +WAIT3 sbc #$01 + bne WAIT3 + pla + sbc #$01 + bne WAIT2 + rts +"""; + + + /** + * Test that the wait routine for beep takes ~552 cycles + * Calling WAIT with A=#$c (12) should take 535 cycles + * according to the tech ref notes: =1/2*(26+27*A+5*A^2) where A = 12 (0x0c) + * The BELL routine has an additional 17 cycles per iteration + * + * This test manually triggers the CPU to run directly with no other emulation occurring. + */ + @Test + public void testDirectBeeperCycleCount() { + AtomicInteger breakpointEncountered = new AtomicInteger(); + AtomicInteger cycleCount = new AtomicInteger(); + // This listener will increment our breakpoint counter if it reaches our desired stoppoing point in time + ram.addExecutionTrap(0x01000, e -> breakpointEncountered.incrementAndGet()); + + // This faux device counts the number of cycles executed + cpu.addChildDevice(createSimpleDevice(()->{ + if (breakpointEncountered.get() == 0) { + cycleCount.incrementAndGet(); + System.out.print("*"); + } + }, "Cycle Counter")); + + // Now run the CPU and see if we got the expected results + cpu.resume(); + runAssemblyCode(BELL, 552); + assertEquals("Should have encountered the breakpoint", 1, breakpointEncountered.get()); + assertEquals("Should have taken about 551 cycles to complete", 551, cycleCount.get()); + cpu.suspend(); + } + + /** + * This is the same test as before except steps are executed from the motherboard + */ + @Test + public void testMachineBeeperCycleCount() { + AtomicInteger breakpointEncountered = new AtomicInteger(); + AtomicInteger cycleCount = new AtomicInteger(); + // This listener will increment our breakpoint counter if it reaches our desired stoppoing point in time + ram.addExecutionTrap(0x01000, e -> breakpointEncountered.incrementAndGet()); + + // This faux device counts the number of cycles executed + cpu.addChildDevice(createSimpleDevice(()->{ + if (breakpointEncountered.get() == 0) { + cycleCount.incrementAndGet(); + System.out.print("*"); + } + }, "Cycle Counter")); + + // This assembles the code and sets PC but doesn't actually do anything + assemble(BELL, 0x0300); + Emulator.getComputer().getMotherboard().resumeInThread(); + for (int i=0; i < 552; i++) { + Emulator.getComputer().getMotherboard().doTick(); + } + Emulator.getComputer().getMotherboard().suspend(); + + assertEquals("Should have encountered the breakpoint", 1, breakpointEncountered.get()); + assertEquals("Should have taken about 551 cycles to complete", 551, cycleCount.get()); + } + + // The CPU cycle count should work the same even when the emulator is sped up. + @Test + public void testAcceleratedCycleCount() { + Emulator.getComputer().getMotherboard().setMaxSpeed(true); + Emulator.getComputer().getMotherboard().setSpeedInPercentage(20000); + testMachineBeeperCycleCount(); + Emulator.getComputer().getMotherboard().setMaxSpeed(false); + Emulator.getComputer().getMotherboard().setSpeedInPercentage(100); + } + + +}