Massive Java 17 overhual

This commit is contained in:
Brendan Robert 2023-07-03 15:44:23 -05:00
parent e43ddccb56
commit 9b51c7d733
77 changed files with 3786 additions and 1764 deletions

9
.gitignore vendored
View File

@ -60,4 +60,11 @@ error_stack.txt
# Ignore special links Martin uses
/Platform/Apple/virtual/genBuild
/Platform/Apple/virtual/mapBuild
/Platform/Apple/virtual/plasBuild
/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

1
.java-version Normal file
View File

@ -0,0 +1 @@
graalvm64-17.0.6

View File

@ -0,0 +1 @@
1.8

View File

@ -1 +1 @@
1.8
graalvm64-17.0.6

View File

@ -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.
-->
<org-netbeans-modules-html-editor-lib.default-html-public-id>HTML5</org-netbeans-modules-html-editor-lib.default-html-public-id>
<netbeans.hint.jdkPlatform>JDK_1.8</netbeans.hint.jdkPlatform>
<netbeans.hint.jdkPlatform>GraalVM_JDK_17</netbeans.hint.jdkPlatform>
</properties>
</project-shared-configuration>

View File

@ -6,15 +6,11 @@
<packaging>jar</packaging>
</packagings>
<goals>
<goal>-X</goal>
<goal>-e</goal>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
<goal>clean</goal>
<goal>javafx:run</goal>
</goals>
<properties>
<exec.args>-classpath %classpath jace.LawlessLegends</exec.args>
<exec.executable>java</exec.executable>
</properties>
</action>
<action>
<actionName>profile</actionName>
@ -22,12 +18,13 @@
<packaging>jar</packaging>
</packagings>
<goals>
<goal>clean</goal>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
<goal>javafx:run@ide-debug</goal>
</goals>
<properties>
<exec.args>-classpath %classpath jace.LawlessLegends</exec.args>
<exec.executable>java</exec.executable>
<jpda.listen>true</jpda.listen>
</properties>
</action>
@ -37,12 +34,12 @@
<packaging>jar</packaging>
</packagings>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
<goal>clean</goal>
<goal>javafx:run@ide-debug</goal>
</goals>
<properties>
<exec.args>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath jace.LawlessLegends</exec.args>
<exec.executable>java</exec.executable>
<jpda.listen>true</jpda.listen>
</properties>
</action>

View File

@ -25,86 +25,168 @@
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.2</version>
<executions>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>com.gluonhq</groupId>
<artifactId>gluonfx-maven-plugin</artifactId>
<version>1.0.18</version>
<configuration>
<mainClass>jace.LawlessLegends</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<mainClass>lawlesslegends/jace.LawlessLegends</mainClass>
<executions>
<execution>
<id>unpack-dependencies</id>
<phase>package</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<!-- Default configuration for running -->
<!-- Usage: mvn clean javafx:run -->
<id>default-cli</id>
</execution>
<execution>
<!-- Configuration for manual attach debugging -->
<!-- Usage: mvn clean javafx:run@debug -->
<id>debug</id>
<configuration>
<excludes>**/*.SF,**/*.DSA,**/*.RSA</excludes>
<excludeScope>system</excludeScope>
<excludeGroupIds>junit,org.mockito,org.hamcrest</excludeGroupIds>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<options>
<option>-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:8000</option>
</options>
</configuration>
</execution>
<execution>
<!-- Configuration for automatic IDE debugging -->
<id>ide-debug</id>
<configuration>
<options>
<option>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address}</option>
</options>
</configuration>
</execution>
<execution>
<!-- Configuration for automatic IDE profiling -->
<id>ide-profile</id>
<configuration>
<options>
<option>${profiler.jvmargs.arg1}</option>
<option>${profiler.jvmargs.arg2}</option>
<option>${profiler.jvmargs.arg3}</option>
<option>${profiler.jvmargs.arg4}</option>
<option>${profiler.jvmargs.arg5}</option>
</options>
</configuration>
</execution>
</executions>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
<version>1.0.0.Final</version>
<executions>
<execution>
<id>unpack-dependencies</id>
<phase>package</phase>
<id>add-module-infos</id>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
<goal>add-module-info</goal>
</goals>
<configuration>
<executable>${java.home}/../bin/javapackager</executable>
<arguments>
<argument>-createjar</argument>
<argument>-nocss2bin</argument>
<argument>-appclass</argument>
<argument>${mainClass}</argument>
<argument>-srcdir</argument>
<argument>${project.build.directory}/classes</argument>
<argument>-outdir</argument>
<argument>${project.build.directory}</argument>
<argument>-outfile</argument>
<argument>${project.build.finalName}.jar</argument>
</arguments>
<modules>
<module>
<artifact>
<groupId>org.xerial.thirdparty</groupId>
<artifactId>nestedvm</artifactId>
<version>1.0</version>
</artifact>
<moduleInfoSource>
module nestedvm {
exports org.ibex.nestedvm;
exports org.ibex.nestedvm.util;
}
</moduleInfoSource>
</module>
<module>
<artifact>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</artifact>
<moduleInfoSource>
module reflections {
exports org.reflections;
}
</moduleInfoSource>
</module>
</modules>
<overwriteExistingFiles>true</overwriteExistingFiles>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
<version>3.8.1</version>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections</artifactId>
<version>10.4.0</version>
</dependency>
<dependency>
<groupId>org.xerial.thirdparty</groupId>
<artifactId>nestedvm</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
<version>20</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>20</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>20</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>20</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>20</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>18</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
<type>jar</type>
</dependency>
</dependencies>
</project>

View File

@ -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<LawlessComputer> c) {
LawlessComputer computer = getComputer();
if (computer != null) {
c.accept(computer);
} else {
System.err.println("No computer available!");
Thread.dumpStack();
}
}
public static <T> T withComputer(Function<LawlessComputer, T> 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<RAM> 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<jace.core.Video> 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();

View File

@ -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));
}
}

View File

@ -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<KeyEvent> 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<KeyEvent> 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<Double>() {
@ -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<MediaConsumer> getMediaConsumers() {
List<MediaConsumer> consumers = new ArrayList<>();
consumers.add(Emulator.getComputer().getUpgradeHandler());
Emulator.withComputer(c -> consumers.add(((LawlessComputer) c).getUpgradeHandler()));
if (Emulator.logic.showDrives) {
for (Optional<Card> card : computer.memory.getAllCards()) {
card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent ->
Emulator.withMemory(m -> {
for (Optional<Card> 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);
}
}

View File

@ -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();
}
}
});
}
}

View File

@ -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<Card> 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

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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());
}
}

View File

@ -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 {

View File

@ -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

View File

@ -17,7 +17,7 @@ public class ApplesoftHandler implements LanguageHandler<ApplesoftProgram> {
@Override
public String getNewDocumentContent() {
return ApplesoftProgram.fromMemory(Emulator.getComputer().getMemory()).toString();
return Emulator.withComputer(c->ApplesoftProgram.fromMemory(c.getMemory()).toString(), "");
}
@Override

View File

@ -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() {

View File

@ -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(); }

View File

@ -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<File> {
return compiler;
}
@Override
public void execute(CompileResult<File> lastResult) {
public void compileToRam(String code) {
HeadlessProgram prg = new HeadlessProgram(Program.DocumentType.assembly);
prg.setValue(code);
CompileResult<File> 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<File> {
} catch (IOException ex) {
Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
}
@Override
public void execute(CompileResult<File> 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);
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}

View File

@ -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<ConfigNode> children;
private transient ObservableList<ConfigNode> 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;

View File

@ -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.
* <br/>
* <br>
* Name should be short, meaningful, and succinct. e.g. "Insert disk"
* <br/>
* <br>
* Category can be used to group actions by overall topic, for example an automated table of contents
* <br/>
* <br>
* 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.
* <br/>
* <br>
* 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".
* <hr/>
* <hr>
* 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

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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<Device> children;
private final Set<Device> 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<Device> devices) {
@ -91,7 +91,7 @@ public abstract class Device implements Reconfigurable {
}
public Iterable<Device> getChildren() {
return children.asUnmodifiable();
return children;
}
public void setAllDevices(Collection<Device> 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);
}
}

View File

@ -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++) {

View File

@ -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) {

View File

@ -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);
}
}
}
}

View File

@ -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);

View File

@ -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<RAMListener> 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<RAMEvent> 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();
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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<Integer> 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) {

View File

@ -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<SourceDataLine> availableLines = Collections.synchronizedSet(new HashSet<>());
private final Map<Object, SourceDataLine> 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<String> preferredMixer = new DynamicSelection<String>(null) {
@Override
public boolean allowNull() {
return false;
}
@Override
public LinkedHashMap<? extends String, String> getSelections() {
Info[] mixerInfo = AudioSystem.getMixerInfo();
LinkedHashMap<String, String> 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;
}
}

View File

@ -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;

View File

@ -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<ButtonType> 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;
}

View File

@ -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() {

View File

@ -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);
}

View File

@ -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 {

View File

@ -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();
}
}
}

View File

@ -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");

View File

@ -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];

View File

@ -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");
}

View File

@ -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;

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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() {

View File

@ -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;
}

View File

@ -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

View File

@ -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<Runnable> vblCallbacks = Collections.synchronizedList(new ArrayList<>());
@ -175,9 +175,6 @@ public class LawlessComputer extends Apple2e {
public void finishColdStart() {
try {
for (Optional<Card> 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 {

View File

@ -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<Integer> autoResume = new HashSet<>();
private final Map<Integer, Double> 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);
}

View File

@ -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;

View File

@ -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());

View File

@ -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();
}

View File

@ -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<String> addressRangeListener = (prop, oldVal, newVal) -> Application.invokeLater(this::redrawMemoryView);
ChangeListener<String> 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<MemoryCell> 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<? extends jace.cheat.MemoryCell> 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<? extends jace.cheat.MemoryCell> 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) {

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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.**"}
]
}

View File

@ -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":"<init>","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":"<init>","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":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.javafx.font.coretext.CGPoint",
"fields":[
{"name":"x"},
{"name":"y"}
],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.javafx.font.coretext.CGRect",
"fields":[
{"name":"origin"},
{"name":"size"}
],
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"com.sun.javafx.font.coretext.CGSize",
"fields":[
{"name":"height"},
{"name":"width"}
],
"methods":[{"name":"<init>","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":"<init>","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":"<init>","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":"<init>","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":"<init>","parameterTypes":["double","double"] }]
},
{
"name":"java.awt.geom.Rectangle2D$Double",
"methods":[{"name":"<init>","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":"<init>","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":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.BlitBg",
"methods":[{"name":"<init>","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":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.DrawGlyphListAA",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.DrawGlyphListLCD",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.DrawLine",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.DrawParallelogram",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.DrawPath",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.DrawPolygons",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.DrawRect",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.FillParallelogram",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.FillPath",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.FillRect",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.FillSpans",
"methods":[{"name":"<init>","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":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.MaskFill",
"methods":[{"name":"<init>","parameterTypes":["long","sun.java2d.loops.SurfaceType","sun.java2d.loops.CompositeType","sun.java2d.loops.SurfaceType"] }]
},
{
"name":"sun.java2d.loops.ScaledBlit",
"methods":[{"name":"<init>","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":"<init>","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[]"] }]
}
]

View File

@ -0,0 +1,8 @@
[
{
"type":"agent-extracted",
"classes":[
]
}
]

View File

@ -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":"<init>","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":"<init>","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":"<init>","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":"<init>","parameterTypes":[] },
{"name":"initialize","parameterTypes":[] }
]
},
{
"name":"jace.LawlessLegends",
"methods":[
{"name":"<init>","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":"<init>","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":"<init>","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":"<init>","parameterTypes":["jace.core.Computer"] }]
},
{
"name":"jace.hardware.CardDiskII",
"allPublicFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":["jace.core.Computer"] }]
},
{
"name":"jace.hardware.CardExt80Col",
"allPublicFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":["jace.core.Computer"] }]
},
{
"name":"jace.hardware.CardHayesMicromodem"
},
{
"name":"jace.hardware.CardMockingboard",
"allPublicFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":["jace.core.Computer"] }]
},
{
"name":"jace.hardware.CardMockingboard$1",
"allPublicFields":true,
"queryAllPublicMethods":true
},
{
"name":"jace.hardware.CardRamFactor",
"allPublicFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":["jace.core.Computer"] }]
},
{
"name":"jace.hardware.CardRamworks",
"allPublicFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":["jace.core.Computer"] }]
},
{
"name":"jace.hardware.CardSSC"
},
{
"name":"jace.hardware.CardThunderclock",
"allPublicFields":true,
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","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":"<init>","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":"<init>","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":"<init>","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":"<init>","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":"<init>","parameterTypes":["double"] },
{"name":"<init>","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":"<init>","parameterTypes":[] }]
},
{
"name":"javafx.scene.control.ButtonBase",
"queryAllDeclaredMethods":true
},
{
"name":"javafx.scene.control.ComboBox",
"queryAllDeclaredMethods":true,
"queryAllPublicConstructors":true,
"methods":[
{"name":"<init>","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":"<init>","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":"<init>","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":"<init>","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":"<init>","parameterTypes":[] },
{"name":"getItems","parameterTypes":[] },
{"name":"setDividerPositions","parameterTypes":["double[]"] }
]
},
{
"name":"javafx.scene.control.ToolBar",
"queryAllDeclaredMethods":true,
"queryAllPublicConstructors":true,
"methods":[
{"name":"<init>","parameterTypes":[] },
{"name":"getItems","parameterTypes":[] }
]
},
{
"name":"javafx.scene.control.TreeItem"
},
{
"name":"javafx.scene.control.TreeView",
"queryAllDeclaredMethods":true,
"queryAllPublicConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"javafx.scene.effect.Effect"
},
{
"name":"javafx.scene.image.Image"
},
{
"name":"javafx.scene.image.ImageView",
"queryAllDeclaredMethods":true,
"queryAllPublicConstructors":true,
"methods":[
{"name":"<init>","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":"<init>","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":"<init>","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":"<init>","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":"<init>","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":"<init>","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":"<init>","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":"<init>","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":"<init>","parameterTypes":[] }]
},
{
"name":"sun.misc.Unsafe",
"fields":[{"name":"theUnsafe"}]
},
{
"name":"sun.security.provider.MD5",
"methods":[{"name":"<init>","parameterTypes":[] }]
}
]

View File

@ -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"]
}
]
}

View File

@ -0,0 +1,8 @@
{
"types":[
],
"lambdaCapturingTypes":[
],
"proxies":[
]
}

View File

@ -10,7 +10,7 @@
<AnchorPane id="AnchorPane" prefHeight="426.0" prefWidth="600.0" styleClass="mainFxmlClass" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jace.config.ConfigurationUIController">
<stylesheets>
<URL value="@/styles/style.css" />
<URL value="@../styles/style.css" />
</stylesheets>
<children>
<ToolBar prefHeight="40.0" prefWidth="600.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">

View File

@ -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

View File

@ -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;
}
};
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}