mirror of
https://github.com/badvision/lawless-legends.git
synced 2025-03-18 08:31:39 +00:00
Massive Java 17 overhual
This commit is contained in:
parent
e43ddccb56
commit
9b51c7d733
9
.gitignore
vendored
9
.gitignore
vendored
@ -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
1
.java-version
Normal file
@ -0,0 +1 @@
|
||||
graalvm64-17.0.6
|
1
OutlawEditor/.java-version
Normal file
1
OutlawEditor/.java-version
Normal file
@ -0,0 +1 @@
|
||||
1.8
|
@ -1 +1 @@
|
||||
1.8
|
||||
graalvm64-17.0.6
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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(); }
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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++) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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];
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
||||