Compare commits

..

1 Commits

101 changed files with 8770 additions and 3489 deletions

4
.gitignore vendored
View File

@ -18,6 +18,4 @@ hs_err_pid*
/.gitignore
/inputFiles.lst
*.DS_Store
!/lib/nestedvm.jar
_acme_tmp*
/.idea
!/lib/nestedvm.jar

View File

@ -1,19 +1,16 @@
language: java
jdk:
- oraclejdk8
addons:
apt:
packages:
- oracle-java8-installer
before_deploy:
- wget http://bit.ly/packrgdx
- java -jar packrgdx --platform mac --jdk http://download.oracle.com/otn-pub/java/jdk/8u151-b12/e758a0de34e24606bca991d704f6dcbf/jdk-8u151-macosx-x64.dmg --executable jace --classpath jace.jar --mainclass jace/JaceApplication --vmargs Xmx1G --minimizejre soft --output out-mac
- git fetch --tags
deploy:
provider: releases
api_key:
secure: 2RMnDGnj27q4O9Oyl9NjLMeaGCMhMtnb+nZ5VeR+JBoEgOzl1SycqpXGVo6FOB54OEcg9ykU/dsNEZrB+n4GNOC7zM8otXkW5cJ0qrZJY4U/YLtYqW8BTk1qajDwYlPmFyJWubsTqDz5CwCCK2UjQrb8Fqmo4eZPQ3lK2fKUH0XdXBOzKD9mHNutyjC2Hgj2w5C0NLQPiau/gkv1VhigI2vFUKhPcKooZXfwOHkDeJXasUVA5jTWTJJNWyefWvwl6u1kGsVtgWqV59T6nFopLP3/vWfCnU23+YU7X8F3p5/oKw0tVOUs1QK68P58RGNBVTu6yUgmFx4gZ0aMZqnxYvjfNG/ubaWK7gdOyU70aSN7W3Jrdw8jXkq7QfY5C6IZz/+Fft1qiQculw7sWKHWFMepKTFhZxZG6ebvXvzuUW9iLUWsmWzfytvn93ZWdRhpz6ZCsmb2XN/qTEoF3+JXJwrgSujw3n/4f9SHUDYsgToD15wb8XYOkZZMSa3tIKDNc+Pv7v8jgAxIjgNpgTacNVFqdIyOlkFy41eHJ+v2ArUWv04uIgvmjahSCfXUwMZDiIyb1fYt8SBnBaC4BLTgvjLd2o5jQZpupMKTtSDJetNJHfCQCMWCml9Cy2wDn0758ZMTpiF8P8newR4DFVQNNHlca27d1E/r7kHc3bI17e8=
file: target/Jace.jar
on:
tags: true
repo: badvision/jace
language: java
jdk:
- oraclejdk8
addons:
apt:
packages:
- oracle-java8-installer
before_deploy: git fetch --tags
deploy:
provider: releases
api_key:
secure: 2RMnDGnj27q4O9Oyl9NjLMeaGCMhMtnb+nZ5VeR+JBoEgOzl1SycqpXGVo6FOB54OEcg9ykU/dsNEZrB+n4GNOC7zM8otXkW5cJ0qrZJY4U/YLtYqW8BTk1qajDwYlPmFyJWubsTqDz5CwCCK2UjQrb8Fqmo4eZPQ3lK2fKUH0XdXBOzKD9mHNutyjC2Hgj2w5C0NLQPiau/gkv1VhigI2vFUKhPcKooZXfwOHkDeJXasUVA5jTWTJJNWyefWvwl6u1kGsVtgWqV59T6nFopLP3/vWfCnU23+YU7X8F3p5/oKw0tVOUs1QK68P58RGNBVTu6yUgmFx4gZ0aMZqnxYvjfNG/ubaWK7gdOyU70aSN7W3Jrdw8jXkq7QfY5C6IZz/+Fft1qiQculw7sWKHWFMepKTFhZxZG6ebvXvzuUW9iLUWsmWzfytvn93ZWdRhpz6ZCsmb2XN/qTEoF3+JXJwrgSujw3n/4f9SHUDYsgToD15wb8XYOkZZMSa3tIKDNc+Pv7v8jgAxIjgNpgTacNVFqdIyOlkFy41eHJ+v2ArUWv04uIgvmjahSCfXUwMZDiIyb1fYt8SBnBaC4BLTgvjLd2o5jQZpupMKTtSDJetNJHfCQCMWCml9Cy2wDn0758ZMTpiF8P8newR4DFVQNNHlca27d1E/r7kHc3bI17e8=
file: target/jfx/app/jace-2.0-SNAPSHOT-jfx.jar
on:
tags: true
all_branches: true

View File

@ -3,29 +3,27 @@ Java Apple Computer Emulator
Download:
* [See releases page for most recent](https://github.com/badvision/jace/releases)
* [Version 2.0 Snapshot](target/jace-2.0-SNAPSHOT.jar)
Or:
* [Version 2.0 Snapshot with dependencies](target/jace-2.0-SNAPSHOT-jar-with-dependencies.jar)
To Run:
* See [run.sh](run.sh)
or `java -jar Jace.jar`
Support JACE:
JACE will always be free, but it does take considerable time to refine and add new features. If you would like to show your support and encourage the author to keep maintaining this emulator, why not throw him some change to buy him a drink? (The emulator was named for the Jack and Cokes consumed during its inception.)
<a href="bitcoin:1TmP94jrEtJNqz7wrCpViA6musGsiTXEq?amount=0.000721&label=Jace%20Donations"><img src="https://sites.google.com/site/brendanrobert/projects/jace/donate.png" height="150px" width="150px"></a>
* <a href="bitcoin:1TmP94jrEtJNqz7wrCpViA6musGsiTXEq?amount=0.000721&label=Jace%20Donations">Donate to support JACE! (BTC address: 1TmP94jrEtJNqz7wrCpViA6musGsiTXEq)</a>
* <a href="https://www.paypal.me/BrendanRobert">Donate to support JACE! (Paypal)</a>
or `java -jar target/jace-2.0-SNAPSHOT-jar-with-dependencies.jar`
To Build:
* See [build.sh](build.sh)
[Download current version with built-in IDE](https://github.com/badvision/jace/releases/download/v2.0b/jace-2.0-SNAPSHOT.zip)
Jace is a java-based Apple //e emulator with many compelling features:
* NEW: UI Control Overlay makes common actions more conveniently available at the click of a button!
* Built-in IDE for writing basic and assembly programs, using ACME to compile and execute directly without leaving the emulator.
* NEW: Built-in IDE for writing basic and assembly programs, using ACME to compile and execute directly without leaving the emulator.
* Disk and Mass-storage (hard drive, 3.5 floppy) images
* Joystick and Mouse are fully supported (Joystick can be emulated with either keyboard or mouse, Java doesn't have native joystick support
* All graphics modes are supported, including Apple RGB "Mixed" and B&W modes.

View File

@ -21,7 +21,7 @@
# 0. Here is some information to clear up the confusion about Java:
#
# The JRE (runtime) is needed to RUN Java programs.
# The JDK (compiler) is needed to COMPILE Java programs.
# The JDK (compiler) is needed to COMPILTE Java programs.
#
# Solution:
#
@ -29,15 +29,6 @@
#
# mvn -version
#
# NOTE: If this command returns:
#
# -bash: mvn: command not found
#
# You either didn't install maven or it isn't in your path
# i.e.
# which mvn
# /usr/local/bin/mvn
#
# 2. Check which version of the Java JRE is installed:
#
# java -version
@ -97,13 +88,6 @@
#
# As the source code is using Java 1.8 langauge features.
if [[ -z "$(which mvn)" ]]; then
echo "ERROR: Maven isn't installed"
echo "Install via:"
echo " brew install maven"
exit
fi
if [[ -z "$JAVA_HOME" ]]; then
echo "WARNING: JAVA_HOME was not set"
echo "... Defaulting to Java 1.8..."

View File

@ -14,6 +14,5 @@ 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>
</properties>
</project-shared-configuration>

154
pom.xml
View File

@ -12,17 +12,37 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mainClass>jace.JaceApplication</mainClass>
<netbeans.hint.license>apache20</netbeans.hint.license>
</properties>
<organization>
<!-- Used as the 'Vendor' for JNLP generation -->
<name>org.badvision</name>
<name>badvision</name>
</organization>
<build>
<finalName>Jace</finalName>
<plugins>
<plugin>
<groupId>com.zenjava</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>8.1.2</version>
<configuration>
JavaAppleComputerEmulator
<mainClass>jace.JaceApplication</mainClass>
<!-- only required if signing the jar file -->
<keyStoreAlias>example-user</keyStoreAlias>
<keyStorePassword>example-password</keyStorePassword>
<allPermissions>true</allPermissions>
<bundleType>ALL</bundleType>
</configuration>
<executions>
<execution>
<id>package</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
@ -42,19 +62,21 @@
</execution>
</executions>
</plugin>
<plugin>
<!-- <plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<version>1.2.1</version>
<executions>
<execution>
<id>unpack-dependencies</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${java.home}/../bin/javapackager</executable>
<executable>${java.home}/../bin/javafxpackager</executable>
<arguments>
<argument>-createjar</argument>
<argument>-nocss2bin</argument>
@ -69,22 +91,77 @@
</arguments>
</configuration>
</execution>
<execution>
<id>default-cli</id>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${java.home}/bin/java</executable>
<commandlineArgs>${runfx.args}</commandlineArgs>
</configuration>
</execution>
</executions>
</plugin>
</plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<compilerArguments>
<bootclasspath>${sun.boot.class.path}${path.separator}${java.home}/lib/jfxrt.jar</bootclasspath>
</compilerArguments>
</configuration>
<version>3.5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18</version>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>${java.home}/lib/jfxrt.jar</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
</plugin>
<!-- BEGIN -->
<!-- https://ivan-site.com/2012/05/download-oracle-java-jre-jdk-using-a-script/ -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>jace.JaceApplication</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- END -->
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javafx-packager</groupId>
<artifactId>javafx-packager</artifactId>
<version>1.7</version>
<systemPath>${java.home}/../lib/ant-javafx.jar</systemPath>
<scope>system</scope>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
@ -93,63 +170,8 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xerial.thirdparty</groupId>
<artifactId>nestedvm</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<!--<url>http://maven.aliyun.com/nexus/content/groups/public/</url>-->
<url>https://maven.aliyun.com/repository/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<repository>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>ias-snapshots</id>
<name>Infinite Automation Snapshot Repository</name>
<url>https://maven.mangoautomation.net/repository/ias-snapshot/</url>
</repository>
<repository>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>ias-releases</id>
<name>Infinite Automation Release Repository</name>
<url>https://maven.mangoautomation.net/repository/ias-release/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<!--<url>http://maven.aliyun.com/nexus/content/groups/public/</url>-->
<url>https://maven.aliyun.com/repository/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

7
run.sh
View File

@ -1,4 +1,9 @@
#!/bin/sh
java -jar target/Jace.jar
#java -jar target/jace-2.0-SNAPSHOT.jar
# http://stackoverflow.com/questions/9689793/cant-execute-jar-file-no-main-manifest-attribute
#java -cp target/jace-2.0-SNAPSHOT.jar jace.JaceApplication
java -jar target/jace-2.0-SNAPSHOT-jar-with-dependencies.jar

View File

@ -26,7 +26,7 @@ import java.util.Map;
/**
* Created on January 15, 2007, 10:10 PM
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class Emulator {
@ -46,15 +46,10 @@ public class Emulator {
* @param args
*/
public Emulator(List<String> args) {
instance = this;
computer = new Apple2e();
Configuration.buildTree();
Configuration.loadSettings();
mainThread = Thread.currentThread();
applyConfiguration(args);
}
public void applyConfiguration(List<String> args) {
Map<String, String> settings = new LinkedHashMap<>();
if (args != null) {
for (int i = 0; i < args.size(); i++) {

View File

@ -21,7 +21,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;
@ -30,6 +29,7 @@ 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;
@ -52,14 +52,11 @@ 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;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import static jace.core.Utility.*;
/**
* This class contains miscellaneous user-invoked actions such as debugger
* operations and running arbitrary files in the emulator. It is possible for
@ -83,18 +80,6 @@ public class EmulatorUILogic implements Reconfigurable {
};
}
@ConfigurableField(
category = "General",
name = "Speed Setting"
)
public int speedSetting = 3;
@ConfigurableField(
category = "General",
name = "Show Drives"
)
public boolean showDrives = true;
public static void updateCPURegisters(MOS65C02 cpu) {
// DebuggerPanel debuggerPanel = Emulator.getFrame().getDebuggerPanel();
// debuggerPanel.valueA.setText(Integer.toHexString(cpu.A));
@ -255,11 +240,28 @@ public class EmulatorUILogic implements Reconfigurable {
Emulator.computer.resume();
}
@InvokableAction(
name = "Adjust display",
category = "display",
description = "Adjusts window size to 1:1 aspect ratio for optimal viewing.",
alternatives = "Adjust screen;Adjust window size;Adjust aspect ratio;Fix screen;Fix window size;Fix aspect ratio;Correct aspect ratio;",
defaultKeyMapping = "ctrl+shift+a")
static public void scaleIntegerRatio() {
// AbstractEmulatorFrame frame = Emulator.getFrame();
// if (frame == null) {
// return;
// }
// Emulator.computer.pause();
// frame.enforceIntegerRatio();
// Emulator.computer.resume();
}
@InvokableAction(
name = "Toggle Debug",
category = "debug",
description = "Show/hide the debug panel",
alternatives = "Show Debug;Hide Debug;Inspect",
alternatives = "Show Debug;Hide Debug",
defaultKeyMapping = "ctrl+shift+d")
public static void toggleDebugPanel() {
// AbstractEmulatorFrame frame = Emulator.getFrame();
@ -271,26 +273,21 @@ public class EmulatorUILogic implements Reconfigurable {
// Emulator.resizeVideo();
}
@InvokableAction(
name = "Toggle fullscreen",
category = "general",
description = "Activate/deactivate fullscreen mode",
alternatives = "fullscreen;maximize",
defaultKeyMapping = "ctrl+shift+f")
public static void toggleFullscreen() {
Platform.runLater(() -> {
Stage stage = JaceApplication.getApplication().primaryStage;
stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
stage.setFullScreen(!stage.isFullScreen());
JaceApplication.getApplication().controller.setAspectRatioEnabled(stage.isFullScreen());
});
// AbstractEmulatorFrame frame = Emulator.getFrame();
// if (frame == null) {
// return;
// }
// Emulator.computer.pause();
// frame.toggleFullscreen();
// Emulator.computer.resume();
}
@InvokableAction(
name = "Save Raw Screenshot",
category = "general",
description = "Save raw (RAM) format of visible screen",
alternatives = "screendump;raw screenshot",
alternatives = "screendump, raw screenshot",
defaultKeyMapping = "ctrl+shift+z")
public static void saveScreenshotRaw() throws FileNotFoundException, IOException {
SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss");
@ -331,7 +328,7 @@ public class EmulatorUILogic implements Reconfigurable {
name = "Save Screenshot",
category = "general",
description = "Save image of visible screen",
alternatives = "Save image;save framebuffer;screenshot",
alternatives = "Save image,save framebuffer,screenshot",
defaultKeyMapping = "ctrl+shift+s")
public static void saveScreenshot() throws IOException {
FileChooser select = new FileChooser();
@ -360,7 +357,7 @@ public class EmulatorUILogic implements Reconfigurable {
name = "Configuration",
category = "general",
description = "Edit emulator configuraion",
alternatives = "Reconfigure;Preferences;Settings;Config",
alternatives = "Reconfigure,Preferences,Settings",
defaultKeyMapping = {"f4", "ctrl+shift+c"})
public static void showConfig() {
FXMLLoader fxmlLoader = new FXMLLoader(EmulatorUILogic.class.getResource("/fxml/Configuration.fxml"));
@ -382,7 +379,7 @@ public class EmulatorUILogic implements Reconfigurable {
name = "Open IDE",
category = "development",
description = "Open new IDE window for Basic/Assembly/Plasma coding",
alternatives = "IDE;dev;development;acme;assembler;editor",
alternatives = "dev,development,acme,assembler,editor",
defaultKeyMapping = {"ctrl+shift+i"})
public static void showIDE() {
FXMLLoader fxmlLoader = new FXMLLoader(EmulatorUILogic.class.getResource("/fxml/editor.fxml"));
@ -400,68 +397,6 @@ public class EmulatorUILogic implements Reconfigurable {
}
}
static int size = -1;
@InvokableAction(
name = "Resize window",
category = "general",
description = "Resize the screen to 1x/1.5x/2x/3x video size",
alternatives = "Aspect;Adjust screen;Adjust window size;Adjust aspect ratio;Fix screen;Fix window size;Fix aspect ratio;Correct aspect ratio;",
defaultKeyMapping = {"ctrl+shift+a"})
public static void scaleIntegerRatio() {
Platform.runLater(() -> {
if (JaceApplication.getApplication() == null
|| JaceApplication.getApplication().primaryStage == null) {
return;
}
Stage stage = JaceApplication.getApplication().primaryStage;
size++;
if (size > 3) {
size = 0;
}
if (stage.isFullScreen()) {
JaceApplication.getApplication().controller.toggleAspectRatio();
} else {
int width = 0, height = 0;
switch (size) {
case 0: // 1x
width = 560;
height = 384;
break;
case 1: // 1.5x
width = 840;
height = 576;
break;
case 2: // 2x
width = 560 * 2;
height = 384 * 2;
break;
case 3: // 3x (retina) 2880x1800
width = 560 * 3;
height = 384 * 3;
break;
default: // 2x
width = 560 * 2;
height = 384 * 2;
}
double vgap = stage.getScene().getY();
double hgap = stage.getScene().getX();
stage.setWidth(hgap * 2 + width);
stage.setHeight(vgap + height);
}
});
}
@InvokableAction(
name = "About",
category = "general",
description = "Display about window",
alternatives = "info;credits",
defaultKeyMapping = {"ctrl+shift+."})
public static void showAboutWindow() {
//TODO: Implement
}
public static boolean confirm(String message) {
// return JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Emulator.getFrame(), message);
return false;
@ -543,12 +478,6 @@ public class EmulatorUILogic implements Reconfigurable {
});
}
public static void notify(String message) {
if (JaceApplication.singleton != null) {
JaceApplication.singleton.controller.displayNotification(message);
}
}
@Override
public String getName() {
return "Jace User Interface";
@ -561,8 +490,5 @@ public class EmulatorUILogic implements Reconfigurable {
@Override
public void reconfigure() {
if (JaceApplication.getApplication() != null) {
JaceApplication.getApplication().controller.setSpeed(speedSetting);
}
}
}

View File

@ -47,22 +47,20 @@ public class JaceApplication extends Application {
Scene s = new Scene(node);
primaryStage.setScene(s);
primaryStage.setTitle("Jace");
EmulatorUILogic.scaleIntegerRatio();
Utility.loadIcon("woz_figure.gif").ifPresent(primaryStage.getIcons()::add);
primaryStage.getIcons().add(Utility.loadIcon("woz_figure.gif"));
} catch (IOException exception) {
throw new RuntimeException(exception);
}
primaryStage.show();
new Thread(() -> {
new Emulator(getParameters().getRaw());
reconnectUIHooks();
EmulatorUILogic.scaleIntegerRatio();
Emulator emulator = new Emulator(getParameters().getRaw());
javafx.application.Platform.runLater(() -> {
while (Emulator.computer.getVideo() == null || Emulator.computer.getVideo().getFrameBuffer() == null) {
Thread.yield();
}
controller.connectComputer(Emulator.computer, primaryStage);
bootWatchdog();
}).start();
});
primaryStage.setOnCloseRequest(event -> {
Emulator.computer.deactivate();
Platform.exit();
@ -70,10 +68,6 @@ public class JaceApplication extends Application {
});
}
public void reconnectUIHooks() {
controller.connectComputer(Emulator.computer, primaryStage);
}
public static JaceApplication getApplication() {
return singleton;
}
@ -92,7 +86,7 @@ public class JaceApplication extends Application {
Scene s = new Scene(node);
cheatStage.setScene(s);
cheatStage.setTitle("Jace: MetaCheat");
Utility.loadIcon("woz_figure.gif").ifPresent(cheatStage.getIcons()::add);
cheatStage.getIcons().add(Utility.loadIcon("woz_figure.gif"));
} catch (IOException exception) {
throw new RuntimeException(exception);
}

View File

@ -3,13 +3,14 @@
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package jace;
import com.sun.glass.ui.Application;
import jace.cheat.MetaCheat;
import jace.core.Card;
import jace.core.Computer;
import jace.core.Motherboard;
import jace.core.Utility;
import jace.core.Keyboard;
import jace.library.MediaCache;
import jace.library.MediaConsumer;
import jace.library.MediaConsumerParent;
@ -30,39 +31,20 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.animation.FadeTransition;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.binding.NumberBinding;
import javafx.beans.binding.When;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.ImageView;
import javafx.scene.input.DragEvent;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
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;
/**
*
@ -75,7 +57,7 @@ public class JaceUIController {
@FXML
private AnchorPane rootPane;
@FXML
private StackPane stackPane;
@ -85,215 +67,31 @@ public class JaceUIController {
@FXML
private ImageView appleScreen;
@FXML
private BorderPane controlOverlay;
@FXML
private Slider speedSlider;
@FXML
private AnchorPane menuButtonPane;
@FXML
private Button menuButton;
Computer computer;
private final BooleanProperty aspectRatioCorrectionEnabled = new SimpleBooleanProperty(false);
@FXML
void initialize() {
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
assert stackPane != null : "fx:id=\"stackPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
assert notificationBox != null : "fx:id=\"notificationBox\" was not injected: check your FXML file 'JaceUI.fxml'.";
assert appleScreen != null : "fx:id=\"appleScreen\" was not injected: check your FXML file 'JaceUI.fxml'.";
speedSlider.setValue(1.0);
controlOverlay.setVisible(false);
menuButtonPane.setVisible(false);
controlOverlay.setFocusTraversable(false);
menuButtonPane.setFocusTraversable(false);
NumberBinding aspectCorrectedWidth = rootPane.heightProperty().multiply(3.0).divide(2.0);
NumberBinding width = new When(
aspectRatioCorrectionEnabled.and(aspectCorrectedWidth.lessThan(rootPane.widthProperty()))
).then(aspectCorrectedWidth).otherwise(rootPane.widthProperty());
appleScreen.fitWidthProperty().bind(width);
appleScreen.fitWidthProperty().bind(rootPane.widthProperty());
appleScreen.fitHeightProperty().bind(rootPane.heightProperty());
appleScreen.setVisible(false);
menuButtonPane.setPickOnBounds(false);
rootPane.setOnDragEntered(this::processDragEnteredEvent);
rootPane.setOnDragExited(this::processDragExitedEvent);
rootPane.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
rootPane.setOnMouseMoved(this::showMenuButton);
rootPane.setOnMouseExited(this::hideControlOverlay);
menuButton.setOnMouseClicked(this::showControlOverlay);
controlOverlay.setOnMouseClicked(this::hideControlOverlay);
delayTimer.getKeyFrames().add(new KeyFrame(Duration.millis(3000), evt -> {
hideControlOverlay(null);
rootPane.requestFocus();
}));
}
private void showMenuButton(MouseEvent evt) {
if (!evt.isPrimaryButtonDown() && !evt.isSecondaryButtonDown() && !controlOverlay.isVisible()) {
resetMenuButtonTimer();
if (!menuButtonPane.isVisible()) {
menuButtonPane.setVisible(true);
FadeTransition ft = new FadeTransition(Duration.millis(500), menuButtonPane);
ft.setFromValue(0.0);
ft.setToValue(1.0);
ft.play();
}
}
public void connectComputer(Computer computer, Stage primaryStage) {
this.computer = computer;
appleScreen.setImage(computer.getVideo().getFrameBuffer());
EventHandler<KeyEvent> keyboardHandler = computer.getKeyboard().getListener();
primaryStage.setOnShowing(evt->computer.getKeyboard().resetState());
rootPane.setFocusTraversable(true);
rootPane.setOnKeyPressed(keyboardHandler);
rootPane.setOnKeyReleased(keyboardHandler);
rootPane.requestFocus();
}
Timeline delayTimer = new Timeline();
private void resetMenuButtonTimer() {
delayTimer.playFromStart();
}
private void showControlOverlay(MouseEvent evt) {
if (!evt.isPrimaryButtonDown() && !evt.isSecondaryButtonDown()) {
delayTimer.stop();
menuButtonPane.setVisible(false);
controlOverlay.setVisible(true);
FadeTransition ft = new FadeTransition(Duration.millis(500), controlOverlay);
ft.setFromValue(0.0);
ft.setToValue(1.0);
ft.play();
rootPane.requestFocus();
}
}
private void hideControlOverlay(MouseEvent evt) {
if (menuButtonPane.isVisible()) {
FadeTransition ft1 = new FadeTransition(Duration.millis(500), menuButtonPane);
ft1.setFromValue(1.0);
ft1.setToValue(0.0);
ft1.setOnFinished(evt1 -> menuButtonPane.setVisible(false));
ft1.play();
}
if (controlOverlay.isVisible()) {
FadeTransition ft2 = new FadeTransition(Duration.millis(500), controlOverlay);
ft2.setFromValue(1.0);
ft2.setToValue(0.0);
ft2.setOnFinished(evt1 -> controlOverlay.setVisible(false));
ft2.play();
}
}
protected double convertSpeedToRatio(Double setting) {
if (setting < 1.0) {
return 0.5;
} else if (setting == 1.0) {
return 1.0;
} else if (setting >= 10) {
return Double.MAX_VALUE;
} else {
double val = Math.pow(2.0, (setting - 1.0) / 1.5);
val = Math.floor(val * 2.0) / 2.0;
if (val > 2.0) {
val = Math.floor(val);
}
return val;
}
}
private void connectControls(Stage primaryStage) {
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>() {
@Override
public String toString(Double val) {
if (val < 1.0) {
return "Half";
} else if (val >= 10.0) {
return "";
}
double v = convertSpeedToRatio(val);
if (v != Math.floor(v)) {
return String.valueOf(v) + "x";
} else {
return String.valueOf((int) v) + "x";
}
}
@Override
public Double fromString(String string) {
return 1.0;
}
});
speedSlider.valueProperty().addListener((val, oldValue, newValue) -> setSpeed(newValue.doubleValue()));
Platform.runLater(() -> {
speedSlider.setValue(Emulator.logic.speedSetting);
// Kind of redundant but make sure speed is properly set as if the user did it
setSpeed(Emulator.logic.speedSetting);
});
}
private void connectButtons(Node n) {
if (n instanceof Button) {
Button button = (Button) n;
Runnable action = Utility.getNamedInvokableAction(button.getText());
button.setOnMouseClicked(evt -> action.run());
} else if (n instanceof Parent) {
for (Node child : ((Parent) n).getChildrenUnmodifiable()) {
connectButtons(child);
}
}
}
protected 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.computer.getMotherboard().setMaxSpeed(true);
Motherboard.cpuPerClock = 3;
} else {
if (speedRatio > 25) {
Motherboard.cpuPerClock = 2;
} else {
Motherboard.cpuPerClock = 1;
}
Emulator.computer.getMotherboard().setMaxSpeed(false);
Emulator.computer.getMotherboard().setSpeedInPercentage((int) (speedRatio * 100));
}
Emulator.computer.getMotherboard().reconfigure();
}
public void toggleAspectRatio() {
setAspectRatioEnabled(aspectRatioCorrectionEnabled.not().get());
}
public void setAspectRatioEnabled(boolean enabled) {
aspectRatioCorrectionEnabled.set(enabled);
}
public void connectComputer(Computer computer, Stage primaryStage) {
if (computer == null) {
return;
}
this.computer = computer;
Platform.runLater(() -> {
connectControls(primaryStage);
appleScreen.setImage(computer.getVideo().getFrameBuffer());
appleScreen.setVisible(true);
rootPane.requestFocus();
});
}
private void processDragEnteredEvent(DragEvent evt) {
MediaEntry media = null;
if (evt.getDragboard().hasFiles()) {
@ -317,89 +115,80 @@ public class JaceUIController {
startDragEvent(media);
}
}
private void processDragExitedEvent(DragEvent evt) {
endDragEvent();
}
private File getDraggedFile(List<File> files) {
if (files == null || files.isEmpty()) {
return null;
}
for (File f : files) {
if (f.exists()) {
return f;
}
if (f.exists()) return f;
}
return null;
}
HBox drivePanel;
private void startDragEvent(MediaEntry media) {
List<MediaConsumer> consumers = getMediaConsumers();
drivePanel = new HBox();
consumers.stream()
.filter((consumer) -> (consumer.isAccepted(media, media.files.get(0))))
.forEach((consumer) -> {
Label icon = consumer.getIcon().orElse(null);
if (icon == null) {
return;
}
icon.setTextFill(Color.WHITE);
icon.setPadding(new Insets(2.0));
drivePanel.getChildren().add(icon);
icon.setOnDragOver(event -> {
event.acceptTransferModes(TransferMode.ANY);
event.consume();
});
icon.setOnDragDropped(event -> {
System.out.println("Dropping media on " + icon.getText());
try {
computer.pause();
consumer.insertMedia(media, media.files.get(0));
computer.resume();
event.setDropCompleted(true);
event.consume();
} catch (IOException ex) {
Logger.getLogger(JaceUIController.class.getName()).log(Level.SEVERE, null, ex);
}
endDragEvent();
});
.filter((consumer) -> (consumer.isAccepted(media, media.files.get(0))))
.forEach((consumer) -> {
Label icon = consumer.getIcon();
icon.setTextFill(Color.WHITE);
icon.setPadding(new Insets(2.0));
drivePanel.getChildren().add(icon);
icon.setOnDragOver(event -> {
event.acceptTransferModes(TransferMode.ANY);
event.consume();
});
stackPane.getChildren().add(drivePanel);
icon.setOnDragDropped(event -> {
System.out.println("Dropping media on "+icon.getText());
try {
computer.pause();
consumer.insertMedia(media, media.files.get(0));
computer.resume();
event.setDropCompleted(true);
event.consume();
} catch (IOException ex) {
Logger.getLogger(JaceUIController.class.getName()).log(Level.SEVERE, null, ex);
}
endDragEvent();
});
});
stackPane.getChildren().add(drivePanel);
drivePanel.setLayoutX(10);
drivePanel.setLayoutY(10);
}
private void endDragEvent() {
stackPane.getChildren().remove(drivePanel);
drivePanel.getChildren().stream().forEach((n) -> {
n.setOnDragDropped(null);
});
}
private List<MediaConsumer> getMediaConsumers() {
List<MediaConsumer> consumers = new ArrayList<>();
if (Emulator.logic.showDrives) {
for (Optional<Card> card : computer.memory.getAllCards()) {
card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent -> {
consumers.addAll(Arrays.asList(((MediaConsumerParent) parent).getConsumers()));
});
}
for (Optional<Card> card : computer.memory.getAllCards()) {
card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent -> {
consumers.addAll(Arrays.asList(((MediaConsumerParent) parent).getConsumers()));
});
}
return consumers;
}
Map<Label, Long> iconTTL = new ConcurrentHashMap<>();
void addIndicator(Label icon) {
addIndicator(icon, 250);
}
void addIndicator(Label icon, long TTL) {
if (!iconTTL.containsKey(icon)) {
Application.invokeLater(() -> {
Application.invokeLater(()->{
if (!notificationBox.getChildren().contains(icon)) {
notificationBox.getChildren().add(icon);
}
@ -409,62 +198,40 @@ public class JaceUIController {
}
void removeIndicator(Label icon) {
Application.invokeLater(() -> {
Application.invokeLater(()->{
notificationBox.getChildren().remove(icon);
iconTTL.remove(icon);
});
}
}
ScheduledExecutorService notificationExecutor = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture ttlCleanupTask = null;
private void trackTTL(Label icon, long TTL) {
iconTTL.put(icon, System.currentTimeMillis() + TTL);
if (ttlCleanupTask == null || ttlCleanupTask.isCancelled()) {
iconTTL.put(icon, System.currentTimeMillis()+TTL);
if (ttlCleanupTask == null || ttlCleanupTask.isCancelled()) {
ttlCleanupTask = notificationExecutor.scheduleWithFixedDelay(this::processTTL, 1, 100, TimeUnit.MILLISECONDS);
}
}
private void processTTL() {
Long now = System.currentTimeMillis();
iconTTL.keySet().stream()
.filter((icon) -> (iconTTL.get(icon) <= now))
.forEach((icon) -> {
removeIndicator(icon);
});
.filter((icon) -> (iconTTL.get(icon) <= now))
.forEach((icon) -> {
removeIndicator(icon);
});
if (iconTTL.isEmpty()) {
ttlCleanupTask.cancel(true);
ttlCleanupTask = null;
}
}
public void addMouseListener(EventHandler<MouseEvent> handler) {
appleScreen.addEventHandler(MouseEvent.ANY, handler);
}
public void removeMouseListener(EventHandler<MouseEvent> handler) {
appleScreen.removeEventHandler(MouseEvent.ANY, handler);
}
Label currentNotification = null;
public void displayNotification(String message) {
Label oldNotification = currentNotification;
Label notification = new Label(message);
currentNotification = notification;
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(() -> {
stackPane.getChildren().remove(oldNotification);
stackPane.getChildren().add(notification);
});
notificationExecutor.schedule(() -> {
Application.invokeLater(() -> {
stackPane.getChildren().remove(notification);
});
}, 4, TimeUnit.SECONDS);
}
}

View File

@ -19,8 +19,6 @@
package jace.apple2e;
import jace.Emulator;
import jace.JaceApplication;
import jace.apple2e.softswitch.VideoSoftSwitch;
import jace.cheat.Cheats;
import jace.config.ClassSelection;
import jace.config.ConfigurableField;
@ -30,7 +28,6 @@ import jace.core.Motherboard;
import jace.core.RAM;
import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.core.Utility;
import jace.state.Stateful;
import jace.core.Video;
import jace.hardware.CardDiskII;
@ -38,27 +35,25 @@ import jace.hardware.CardExt80Col;
import jace.hardware.ConsoleProbe;
import jace.hardware.Joystick;
import jace.hardware.NoSlotClock;
import jace.hardware.ZipWarpAccelerator;
import jace.hardware.massStorage.CardMassStorage;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 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 and resume are implemented by the Motherboard class. This class provides overall
* configuration of the computer, but the actual operation of the computer and its timing characteristics are managed in
* the Motherboard class.
* 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
* and resume are implemented by the Motherboard class. This class provides
* overall configuration of the computer, but the actual operation of the
* computer and its timing characteristics are managed in the Motherboard class.
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
@ -97,8 +92,6 @@ public class Apple2e extends Computer {
public boolean joy2enabled = false;
@ConfigurableField(name = "No-Slot Clock Enabled", shortName = "clock", description = "If checked, no-slot clock will be enabled", enablesDevice = true)
public boolean clockEnabled = true;
@ConfigurableField(name = "Accelerator Enabled", shortName = "zip", description = "If checked, add support for Zip/Transwarp", enablesDevice = true)
public boolean acceleratorEnabled = true;
public Joystick joystick1;
public Joystick joystick2;
@ -106,7 +99,6 @@ public class Apple2e extends Computer {
public ClassSelection cheatEngine = new ClassSelection(Cheats.class, null);
public Cheats activeCheatEngine = null;
public NoSlotClock clock;
public ZipWarpAccelerator accelerator;
/**
* Creates a new instance of Apple2e
@ -128,7 +120,7 @@ public class Apple2e extends Computer {
return "Computer (Apple //e)";
}
protected void reinitMotherboard() {
private void reinitMotherboard() {
if (motherboard != null && motherboard.isRunning()) {
motherboard.suspend();
}
@ -141,26 +133,16 @@ public class Apple2e extends Computer {
public void coldStart() {
pause();
reinitMotherboard();
RAM128k ram = (RAM128k) getMemory();
ram.initMemoryPattern(ram.mainMemory);
ram.initMemoryPattern(ram.getAuxMemory());
for (SoftSwitches s : SoftSwitches.values()) {
s.getSwitch().reset();
}
getMemory().configureActiveMemory();
getVideo().configureVideoMode();
try {
for (Optional<Card> c : getMemory().getAllCards()) {
c.ifPresent(Card::reset);
waitForVBL();
}
} catch (InterruptedException ex) {
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
} finally {
getCpu().resume();
reboot();
resume();
for (Optional<Card> c : getMemory().getAllCards()) {
c.ifPresent(Card::reset);
}
reboot();
resume();
}
public void reboot() {
@ -175,9 +157,7 @@ public class Apple2e extends Computer {
public void warmStart() {
boolean restart = pause();
for (SoftSwitches s : SoftSwitches.values()) {
if (!(s.getSwitch() instanceof VideoSoftSwitch)) {
s.getSwitch().reset();
}
s.getSwitch().reset();
}
getMemory().configureActiveMemory();
getVideo().configureVideoMode();
@ -214,12 +194,6 @@ public class Apple2e extends Computer {
public final void reconfigure() {
boolean restart = pause();
if (Utility.isHeadlessMode()) {
joy1enabled = false;
joy2enabled = false;
}
super.reconfigure();
RAM128k currentMemory = (RAM128k) getMemory();
@ -235,7 +209,9 @@ public class Apple2e extends Computer {
if (getMemory() == null) {
try {
currentMemory = (RAM128k) ramCard.getValue().getConstructor(Computer.class).newInstance(this);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException ex) {
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
}
try {
@ -249,42 +225,39 @@ public class Apple2e extends Computer {
currentMemory.reconfigure();
if (motherboard != null) {
if (accelerator == null) {
accelerator = new ZipWarpAccelerator(this);
}
if (acceleratorEnabled) {
motherboard.addChildDevice(accelerator);
} else {
motherboard.removeChildDevice(accelerator);
}
if (joy1enabled) {
if (joystick1 == null) {
joystick1 = new Joystick(0, this);
motherboard.addChildDevice(joystick1);
motherboard.miscDevices.add(joystick1);
joystick1.attach();
}
} else if (joystick1 != null) {
motherboard.removeChildDevice(joystick1);
joystick1.detach();
motherboard.miscDevices.remove(joystick1);
joystick1 = null;
}
if (joy2enabled) {
if (joystick2 == null) {
joystick2 = new Joystick(1, this);
motherboard.addChildDevice(joystick2);
motherboard.miscDevices.add(joystick2);
joystick2.attach();
}
} else if (joystick2 != null) {
motherboard.removeChildDevice(joystick2);
joystick2.detach();
motherboard.miscDevices.remove(joystick2);
joystick2 = null;
}
if (clockEnabled) {
if (clock == null) {
clock = new NoSlotClock(this);
motherboard.addChildDevice(clock);
motherboard.miscDevices.add(clock);
clock.attach();
}
} else if (clock != null) {
motherboard.removeChildDevice(clock);
motherboard.miscDevices.remove(clock);
clock.detach();
clock = null;
}
}
@ -311,9 +284,6 @@ public class Apple2e extends Computer {
getVideo().configureVideoMode();
getVideo().reconfigure();
Emulator.resizeVideo();
if (JaceApplication.getApplication() != null) {
JaceApplication.getApplication().reconnectUIHooks();
}
getVideo().resume();
} catch (InstantiationException | IllegalAccessException ex) {
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
@ -344,7 +314,7 @@ public class Apple2e extends Computer {
if (cheatEngine.getValue() == null) {
if (activeCheatEngine != null) {
activeCheatEngine.detach();
motherboard.addChildDevice(activeCheatEngine);
motherboard.miscDevices.remove(activeCheatEngine);
}
activeCheatEngine = null;
} else {
@ -353,8 +323,9 @@ public class Apple2e extends Computer {
if (activeCheatEngine.getClass().equals(cheatEngine.getValue())) {
startCheats = false;
} else {
motherboard.removeChildDevice(activeCheatEngine);
activeCheatEngine.detach();
activeCheatEngine = null;
motherboard.miscDevices.remove(activeCheatEngine);
}
}
if (startCheats) {
@ -363,7 +334,8 @@ public class Apple2e extends Computer {
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
}
motherboard.addChildDevice(activeCheatEngine);
activeCheatEngine.attach();
motherboard.miscDevices.add(activeCheatEngine);
}
}
} catch (IOException ex) {
@ -398,35 +370,6 @@ public class Apple2e extends Computer {
// }
private List<RAMListener> hints = new ArrayList<>();
List<Runnable> vblCallbacks = Collections.synchronizedList(new ArrayList<>());
public void waitForVBL() throws InterruptedException {
waitForVBL(0);
}
public void waitForVBL(int count) throws InterruptedException {
Semaphore s = new Semaphore(0);
onNextVBL(s::release);
s.acquire();
if (count > 1) {
waitForVBL(count - 1);
}
}
public void onNextVBL(Runnable r) {
vblCallbacks.add(r);
}
@Override
public void notifyVBLStateChanged(boolean state) {
super.notifyVBLStateChanged(state);
if (state) {
while (vblCallbacks != null && !vblCallbacks.isEmpty()) {
vblCallbacks.remove(0).run();
}
}
}
ScheduledExecutorService animationTimer = new ScheduledThreadPoolExecutor(1);
Runnable drawHints = () -> {
if (getCpu().getProgramCounter() >> 8 != 0x0c6) {
@ -488,10 +431,10 @@ public class Apple2e extends Computer {
private void enableHints() {
if (hints.isEmpty()) {
hints.add(getMemory().observe(RAMEvent.TYPE.EXECUTE, 0x0FB63, (e) -> {
animationTimer.schedule(drawHints, 1, TimeUnit.SECONDS);
animationSchedule
= animationTimer.scheduleAtFixedRate(doAnimation, 1250, 100, TimeUnit.MILLISECONDS);
hints.add(getMemory().observe(RAMEvent.TYPE.EXECUTE, 0x0FB63, (e)->{
animationTimer.schedule(drawHints, 1, TimeUnit.SECONDS);
animationSchedule =
animationTimer.scheduleAtFixedRate(doAnimation, 1250, 100, TimeUnit.MILLISECONDS);
}));
// Latch to the PRODOS SYNTAX CHECK parser
/*
@ -525,4 +468,4 @@ public class Apple2e extends Computer {
public String getShortName() {
return "computer";
}
}
}

View File

@ -24,6 +24,8 @@ import jace.core.Computer;
import jace.core.RAM;
import jace.core.RAMEvent.TYPE;
import jace.state.Stateful;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -37,6 +39,7 @@ import java.util.logging.Logger;
*/
@Stateful
public class MOS65C02 extends CPU {
private static final Logger LOG = Logger.getLogger(MOS65C02.class.getName());
public boolean readAddressTriggersEvent = true;
@ -77,54 +80,38 @@ public class MOS65C02 extends CPU {
public MOS65C02(Computer computer) {
super(computer);
clearState();
}
@Override
public void reconfigure() {
}
@Override
public void clearState() {
A = 0x0ff;
X = 0x0ff;
Y = 0x0ff;
C = 1;
interruptSignalled = false;
Z = true;
I = true;
D = true;
B = true;
V = true;
N = true;
STACK = 0xff;
setWaitCycles(0);
}
public static final boolean USE_6502_TIMINGS = true;
public enum OPCODE {
ADC_IMM(0x0069, COMMAND.ADC, MODE.IMMEDIATE, 2),
ADC_ZP(0x0065, COMMAND.ADC, MODE.ZEROPAGE, 3),
ADC_ZP_X(0x0075, COMMAND.ADC, MODE.ZEROPAGE_X, 4),
ADC_AB(0x006D, COMMAND.ADC, MODE.ABSOLUTE, 4),
ADC_IND_ZP(0x0072, COMMAND.ADC, MODE.INDIRECT_ZP, 5, true),
ADC_IND_ZP_X(0x0061, COMMAND.ADC, MODE.INDIRECT_ZP_X, 6),
ADC_AB_X(0x007D, COMMAND.ADC, MODE.ABSOLUTE_X, 4),
ADC_AB_Y(0x0079, COMMAND.ADC, MODE.ABSOLUTE_Y, 4),
ADC_IND_ZP(0x0072, COMMAND.ADC, MODE.INDIRECT_ZP, 5, true),
ADC_IND_ZP_X(0x0061, COMMAND.ADC, MODE.INDIRECT_ZP_X, 6),
ADC_IND_ZP_Y(0x0071, COMMAND.ADC, MODE.INDIRECT_ZP_Y, 5),
AND_IMM(0x0029, COMMAND.AND, MODE.IMMEDIATE, 2),
AND_ZP(0x0025, COMMAND.AND, MODE.ZEROPAGE, 3),
AND_ZP_X(0x0035, COMMAND.AND, MODE.ZEROPAGE_X, 4),
AND_AB(0x002D, COMMAND.AND, MODE.ABSOLUTE, 4),
AND_IND_ZP(0x0032, COMMAND.AND, MODE.INDIRECT_ZP, 5, true),
AND_IND_ZP_X(0x0021, COMMAND.AND, MODE.INDIRECT_ZP_X, 6),
AND_AB_X(0x003D, COMMAND.AND, MODE.ABSOLUTE_X, 4),
AND_AB_Y(0x0039, COMMAND.AND, MODE.ABSOLUTE_Y, 4),
AND_IND_ZP(0x0032, COMMAND.AND, MODE.INDIRECT_ZP, 5, true),
AND_IND_ZP_X(0x0021, COMMAND.AND, MODE.INDIRECT_ZP_X, 6),
AND_IND_ZP_Y(0x0031, COMMAND.AND, MODE.INDIRECT_ZP_Y, 5),
ASL(0x000A, COMMAND.ASL_A, MODE.IMPLIED, 2),
ASL_ZP(0x0006, COMMAND.ASL, MODE.ZEROPAGE, 5),
ASL_ZP_X(0x0016, COMMAND.ASL, MODE.ZEROPAGE_X, 6),
ASL_AB(0x000E, COMMAND.ASL, MODE.ABSOLUTE, 6),
ASL_AB_X(0x001E, COMMAND.ASL, MODE.ABSOLUTE_X, 7),
ASL_AB_X(0x001E, COMMAND.ASL, MODE.ABSOLUTE_X, USE_6502_TIMINGS ? 7 : 6),
BCC_REL(0x0090, COMMAND.BCC, MODE.RELATIVE, 2),
BCS_REL(0x00B0, COMMAND.BCS, MODE.RELATIVE, 2),
BBR0(0x00f, COMMAND.BBR0, MODE.ZP_REL, 5, true),
@ -143,16 +130,16 @@ public class MOS65C02 extends CPU {
BBS5(0x0df, COMMAND.BBS5, MODE.ZP_REL, 5, true),
BBS6(0x0ef, COMMAND.BBS6, MODE.ZP_REL, 5, true),
BBS7(0x0ff, COMMAND.BBS7, MODE.ZP_REL, 5, true),
BEQ_REL0(0x00F0, COMMAND.BEQ, MODE.RELATIVE, 2),
BEQ_REL(0x00F0, COMMAND.BEQ, MODE.RELATIVE, 2),
BIT_IMM(0x0089, COMMAND.BIT, MODE.IMMEDIATE, 3, true),
BIT_ZP(0x0024, COMMAND.BIT, MODE.ZEROPAGE, 3),
BIT_ZP_X(0x0034, COMMAND.BIT, MODE.ZEROPAGE_X, 3, true),
BIT_ZP_X(0x0034, COMMAND.BIT, MODE.ZEROPAGE_X, 4, true),
BIT_AB(0x002C, COMMAND.BIT, MODE.ABSOLUTE, 4),
BIT_AB_X(0x003C, COMMAND.BIT, MODE.ABSOLUTE_X, 4, true),
BMI_REL(0x0030, COMMAND.BMI, MODE.RELATIVE, 2),
BNE_REL(0x00D0, COMMAND.BNE, MODE.RELATIVE, 2),
BPL_REL(0x0010, COMMAND.BPL, MODE.RELATIVE, 2),
BRA_REL(0x0080, COMMAND.BRA, MODE.RELATIVE, 2, true),
BPL_REL(0x0010, COMMAND.BPL, MODE.RELATIVE, 2), //?
BRA_REL(0x0080, COMMAND.BRA, MODE.RELATIVE, 2),
// BRK(0x0000, COMMAND.BRK, MODE.IMPLIED, 7),
// Do this so that BRK is treated as a two-byte instruction
BRK(0x0000, COMMAND.BRK, MODE.IMMEDIATE, 7),
@ -178,10 +165,10 @@ public class MOS65C02 extends CPU {
CPY_ZP(0x00C4, COMMAND.CPY, MODE.ZEROPAGE, 3),
CPY_AB(0x00CC, COMMAND.CPY, MODE.ABSOLUTE, 4),
DEC(0x003A, COMMAND.DEA, MODE.IMPLIED, 2, true),
DEC_ZP(0x00C6, COMMAND.DEC, MODE.ZEROPAGE, 5),
DEC_ZP(0x00C6, COMMAND.DEC, MODE.ZEROPAGE, 5), // ??
DEC_ZP_X(0x00D6, COMMAND.DEC, MODE.ZEROPAGE_X, 6),
DEC_AB(0x00CE, COMMAND.DEC, MODE.ABSOLUTE, 6),
DEC_AB_X(0x00DE, COMMAND.DEC, MODE.ABSOLUTE_X, 7),
DEC_AB_X(0x00DE, COMMAND.DEC, MODE.ABSOLUTE_X_NODELAY, 7),
DEX(0x00CA, COMMAND.DEX, MODE.IMPLIED, 2),
DEY(0x0088, COMMAND.DEY, MODE.IMPLIED, 2),
EOR_IMM(0x0049, COMMAND.EOR, MODE.IMMEDIATE, 2),
@ -197,11 +184,11 @@ public class MOS65C02 extends CPU {
INC_ZP(0x00E6, COMMAND.INC, MODE.ZEROPAGE, 5),
INC_ZP_X(0x00F6, COMMAND.INC, MODE.ZEROPAGE_X, 6),
INC_AB(0x00EE, COMMAND.INC, MODE.ABSOLUTE, 6),
INC_AB_X(0x00FE, COMMAND.INC, MODE.ABSOLUTE_X, 7),
INC_AB_X(0x00FE, COMMAND.INC, MODE.ABSOLUTE_X_NODELAY, 7),
INX(0x00E8, COMMAND.INX, MODE.IMPLIED, 2),
INY(0x00C8, COMMAND.INY, MODE.IMPLIED, 2),
JMP_AB(0x004C, COMMAND.JMP, MODE.ABSOLUTE, 3, false, false),
JMP_IND(0x006C, COMMAND.JMP, MODE.INDIRECT, 5),
JMP_AB(0x004C, COMMAND.JMP, MODE.ABSOLUTE, 3, false, false), // ??
JMP_IND(0x006C, COMMAND.JMP, MODE.INDIRECT, 6),
JMP_IND_X(0x007C, COMMAND.JMP, MODE.INDIRECT_X, 6, true),
JSR_AB(0x0020, COMMAND.JSR, MODE.ABSOLUTE, 6, false, false),
LDA_IMM(0x00A9, COMMAND.LDA, MODE.IMMEDIATE, 2),
@ -209,7 +196,7 @@ public class MOS65C02 extends CPU {
LDA_ZP_X(0x00B5, COMMAND.LDA, MODE.ZEROPAGE_X, 4),
LDA_AB(0x00AD, COMMAND.LDA, MODE.ABSOLUTE, 4),
LDA_IND_ZP_X(0x00A1, COMMAND.LDA, MODE.INDIRECT_ZP_X, 6),
LDA_AB_X(0x00BD, COMMAND.LDA, MODE.ABSOLUTE_X, 4),
LDA_AB_X(0x00BD, COMMAND.LDA, MODE.ABSOLUTE_X, 4, true), //?
LDA_AB_Y(0x00B9, COMMAND.LDA, MODE.ABSOLUTE_Y, 4),
LDA_IND_ZP_Y(0x00B1, COMMAND.LDA, MODE.INDIRECT_ZP_Y, 5),
LDA_IND_ZP(0x00B2, COMMAND.LDA, MODE.INDIRECT_ZP, 5, true),
@ -227,7 +214,7 @@ public class MOS65C02 extends CPU {
LSR_ZP(0x0046, COMMAND.LSR, MODE.ZEROPAGE, 5),
LSR_ZP_X(0x0056, COMMAND.LSR, MODE.ZEROPAGE_X, 6),
LSR_AB(0x004E, COMMAND.LSR, MODE.ABSOLUTE, 6),
LSR_AB_X(0x005E, COMMAND.LSR, MODE.ABSOLUTE_X, 7),
LSR_AB_X(0x005E, COMMAND.LSR, MODE.ABSOLUTE_X, USE_6502_TIMINGS ? 7 : 6),
NOP(0x00EA, COMMAND.NOP, MODE.IMPLIED, 2),
SPECIAL(0x00FC, COMMAND.NOP_SPECIAL, MODE.ABSOLUTE, 4),
ORA_IMM(0x0009, COMMAND.ORA, MODE.IMMEDIATE, 2),
@ -259,22 +246,22 @@ public class MOS65C02 extends CPU {
ROL_ZP(0x0026, COMMAND.ROL, MODE.ZEROPAGE, 5),
ROL_ZP_X(0x0036, COMMAND.ROL, MODE.ZEROPAGE_X, 6),
ROL_AB(0x002E, COMMAND.ROL, MODE.ABSOLUTE, 6),
ROL_AB_X(0x003E, COMMAND.ROL, MODE.ABSOLUTE_X, 7),
ROL_AB_X(0x003E, COMMAND.ROL, USE_6502_TIMINGS ? MODE.ABSOLUTE_X_NODELAY : MODE.ABSOLUTE_X, USE_6502_TIMINGS ? 7 : 6),
ROR(0x006A, COMMAND.ROR_A, MODE.IMPLIED, 2),
ROR_ZP(0x0066, COMMAND.ROR, MODE.ZEROPAGE, 5),
ROR_ZP_X(0x0076, COMMAND.ROR, MODE.ZEROPAGE_X, 6),
ROR_AB(0x006E, COMMAND.ROR, MODE.ABSOLUTE, 6),
ROR_AB_X(0x007E, COMMAND.ROR, MODE.ABSOLUTE_X, 7),
ROR_AB_X(0x007E, COMMAND.ROR, USE_6502_TIMINGS ? MODE.ABSOLUTE_X_NODELAY : MODE.ABSOLUTE_X, USE_6502_TIMINGS ? 7 : 6),
RTI(0x0040, COMMAND.RTI, MODE.IMPLIED, 6),
RTS(0x0060, COMMAND.RTS, MODE.IMPLIED, 6),
SBC_IMM(0x00E9, COMMAND.SBC, MODE.IMMEDIATE, 2),
SBC_ZP(0x00E5, COMMAND.SBC, MODE.ZEROPAGE, 3),
SBC_ZP_X(0x00F5, COMMAND.SBC, MODE.ZEROPAGE_X, 4),
SBC_AB(0x00ED, COMMAND.SBC, MODE.ABSOLUTE, 4),
SBC_IND_ZP(0x00F2, COMMAND.SBC, MODE.INDIRECT_ZP, 5, true),
SBC_IND_ZP_X(0x00E1, COMMAND.SBC, MODE.INDIRECT_ZP_X, 6),
SBC_AB_X(0x00FD, COMMAND.SBC, MODE.ABSOLUTE_X, 4),
SBC_AB_Y(0x00F9, COMMAND.SBC, MODE.ABSOLUTE_Y, 4),
SBC_IND_ZP(0x00F2, COMMAND.SBC, MODE.INDIRECT_ZP, 5, true),
SBC_IND_ZP_X(0x00E1, COMMAND.SBC, MODE.INDIRECT_ZP_X, 6),
SBC_IND_ZP_Y(0x00F1, COMMAND.SBC, MODE.INDIRECT_ZP_Y, 5),
SEC(0x0038, COMMAND.SEC, MODE.IMPLIED, 2),
SED(0x00F8, COMMAND.SED, MODE.IMPLIED, 2),
@ -288,13 +275,13 @@ public class MOS65C02 extends CPU {
SMB6(0x0e7, COMMAND.SMB6, MODE.ZEROPAGE, 5, true),
SMB7(0x0f7, COMMAND.SMB7, MODE.ZEROPAGE, 5, true),
STA_ZP(0x0085, COMMAND.STA, MODE.ZEROPAGE, 3),
STA_ZP_X(0x0095, COMMAND.STA, MODE.ZEROPAGE_X, 4),
STA_ZP_X(0x0095, COMMAND.STA, MODE.ZEROPAGE_X, 4), // ??
STA_AB(0x008D, COMMAND.STA, MODE.ABSOLUTE, 4),
STA_AB_X(0x009D, COMMAND.STA, MODE.ABSOLUTE_X, 5),
STA_AB_Y(0x0099, COMMAND.STA, MODE.ABSOLUTE_Y, 5),
STA_AB_X(0x009D, COMMAND.STA, MODE.ABSOLUTE_X_NODELAY, 5),
STA_AB_Y(0x0099, COMMAND.STA, MODE.ABSOLUTE_Y_NODELAY, 5),
STA_IND_ZP(0x0092, COMMAND.STA, MODE.INDIRECT_ZP, 5, true),
STA_IND_ZP_X(0x0081, COMMAND.STA, MODE.INDIRECT_ZP_X, 6),
STA_IND_ZP_Y(0x0091, COMMAND.STA, MODE.INDIRECT_ZP_Y, 6),
STA_IND_ZP_Y(0x0091, COMMAND.STA, MODE.INDIRECT_ZP_Y_NODELAY, 6),
STP(0x00DB, COMMAND.STP, MODE.IMPLIED, 3, true),
STX_ZP(0x0086, COMMAND.STX, MODE.ZEROPAGE, 3),
STX_ZP_Y(0x0096, COMMAND.STX, MODE.ZEROPAGE_Y, 4),
@ -306,7 +293,7 @@ public class MOS65C02 extends CPU {
STZ_ZP_X(0x0074, COMMAND.STZ, MODE.ZEROPAGE_X, 4, true),
STZ_AB(0x009C, COMMAND.STZ, MODE.ABSOLUTE, 4, true),
STZ_AB_X(0x009E, COMMAND.STZ, MODE.ABSOLUTE_X, 5, true),
TAX(0x00AA, COMMAND.TAX, MODE.IMPLIED, 2),
TAX(0x00AA, COMMAND.TAX, MODE.IMPLIED, 2), // ??
TAY(0x00A8, COMMAND.TAY, MODE.IMPLIED, 2),
TRB_ZP(0x0014, COMMAND.TRB, MODE.ZEROPAGE, 5, true),
TRB_AB(0x001C, COMMAND.TRB, MODE.ABSOLUTE, 6, true),
@ -387,7 +374,7 @@ public class MOS65C02 extends CPU {
int pc = cpu.getProgramCounter();
int address = pc + 2 + cpu.getMemory().read(pc + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
// The wait cycles are not added unless the branch actually happens!
cpu.setPageBoundaryPenalty((address & 0x00ff00) != ((pc+2) & 0x00ff00));
cpu.setPageBoundaryPenalty((address & 0x00ff00) != (pc & 0x00ff00));
return address;
}, false),
IMMEDIATE(2, "#$~1", (cpu) -> cpu.getProgramCounter() + 1),
@ -419,6 +406,12 @@ public class MOS65C02 extends CPU {
}
return address2;
}),
INDIRECT_ZP_Y_NODELAY(2, "$(~1),Y", (cpu) -> {
int address = 0x00FF & cpu.getMemory().read(cpu.getProgramCounter() + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
address = cpu.getMemory().readWord(address, TYPE.READ_DATA, true, false);
int address2 = address + cpu.Y;
return address2;
}),
ABSOLUTE(3, "$~2~1", (cpu) -> cpu.getMemory().readWord(cpu.getProgramCounter() + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false)),
ABSOLUTE_X(3, "$~2~1,X", (cpu) -> {
int address2 = cpu.getMemory().readWord(cpu.getProgramCounter() + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
@ -428,6 +421,11 @@ public class MOS65C02 extends CPU {
}
return address;
}),
ABSOLUTE_X_NODELAY(3, "$~2~1,X", (cpu) -> {
int address2 = cpu.getMemory().readWord(cpu.getProgramCounter() + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
int address = 0x0FFFF & (address2 + cpu.X);
return address;
}),
ABSOLUTE_Y(3, "$~2~1,Y", (cpu) -> {
int address2 = cpu.getMemory().readWord(cpu.getProgramCounter() + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
int address = 0x0FFFF & (address2 + cpu.Y);
@ -436,21 +434,26 @@ public class MOS65C02 extends CPU {
}
return address;
}),
ZP_REL(3, "$~1,$R", new AddressCalculator() {
ABSOLUTE_Y_NODELAY(3, "$~2~1,Y", (cpu) -> {
int address2 = cpu.getMemory().readWord(cpu.getProgramCounter() + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
int address = 0x0FFFF & (address2 + cpu.Y);
return address;
}),
ZP_REL(2, "$~1,$R", new AddressCalculator() {
@Override
public int calculateAddress(MOS65C02 cpu) {
// Note: This is two's compliment addition and the cpu.getMemory().read() returns a signed 8-bit value
int pc = cpu.getProgramCounter();
int address = pc + 3 + cpu.getMemory().read(pc + 2, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
int address = pc + 2 + cpu.getMemory().read(pc + 2, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
// The wait cycles are not added unless the branch actually happens!
cpu.setPageBoundaryPenalty((address & 0x00ff00) != ((pc+3) & 0x00ff00));
cpu.setPageBoundaryPenalty((address & 0x00ff00) != (pc & 0x00ff00));
return address;
}
@Override
public int getValue(boolean isRead, MOS65C02 cpu) {
int pc = cpu.getProgramCounter();
int address = 0x0ff & cpu.getMemory().read(pc + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
int address = cpu.getMemory().read(pc + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
return cpu.getMemory().read(address, TYPE.READ_DATA, true, false);
}
});
@ -484,6 +487,7 @@ public class MOS65C02 extends CPU {
private MODE(int size, String fmt, AddressCalculator calc) {
this(size, fmt, calc, true);
}
private MODE(int size, String fmt, AddressCalculator calc, boolean fetch) {
this.fetchValue = fetch;
this.size = size;
@ -543,7 +547,10 @@ public class MOS65C02 extends CPU {
@Override
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu) {
if ((value & (1 << bit)) == 0) {
if (((value >> bit) & 1) != 0) {
return;
}
if (cpu.C != 0) {
cpu.setProgramCounter(address);
cpu.addWaitCycles(cpu.pageBoundaryPenalty ? 2 : 1);
}
@ -560,7 +567,10 @@ public class MOS65C02 extends CPU {
@Override
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu) {
if ((value & (1 << bit)) != 0) {
if (((value >> bit) & 1) == 0) {
return;
}
if (cpu.C != 0) {
cpu.setProgramCounter(address);
cpu.addWaitCycles(cpu.pageBoundaryPenalty ? 2 : 1);
}
@ -602,6 +612,7 @@ public class MOS65C02 extends CPU {
int w;
cpu.V = ((cpu.A ^ value) & 0x080) == 0;
if (cpu.D) {
cpu.addWaitCycles(1);
// Decimal Mode
w = (cpu.A & 0x0f) + (value & 0x0f) + cpu.C;
if (w >= 10) {
@ -719,7 +730,7 @@ public class MOS65C02 extends CPU {
}),
BRA((address, value, addressMode, cpu) -> {
cpu.setProgramCounter(address);
cpu.addWaitCycles(cpu.pageBoundaryPenalty ? 1 : 0);
cpu.addWaitCycles(cpu.pageBoundaryPenalty ? 2 : 1);
}),
BRK((address, value, addressMode, cpu) -> {
cpu.BRK();
@ -922,6 +933,7 @@ public class MOS65C02 extends CPU {
cpu.V = ((cpu.A ^ value) & 0x080) != 0;
int w;
if (cpu.D) {
cpu.addWaitCycles(1);
int temp = 0x0f + (cpu.A & 0x0f) - (value & 0x0f) + cpu.C;
if (temp < 0x10) {
w = 0;
@ -1056,6 +1068,54 @@ public class MOS65C02 extends CPU {
}
}
Map<OPCODE, Map<Integer, Integer>> histogram = null;
int ellapsedSeconds = 0;
long startTime = System.currentTimeMillis();
long nextInterval = System.currentTimeMillis() + 1000;
private void addToHistogram(OPCODE o) {
if (histogram == null) {
return;
}
long now = System.currentTimeMillis();
if (now >= nextInterval) {
ellapsedSeconds = (int) ((now - startTime) / 1000);
nextInterval = ellapsedSeconds * 1000 + startTime;
}
Map<Integer, Integer> countPerTimestamp = histogram.get(o);
if (countPerTimestamp == null) {
countPerTimestamp = new HashMap<>();
histogram.put(o, countPerTimestamp);
}
Integer count = countPerTimestamp.get(ellapsedSeconds);
if (count == null) {
count = 0;
}
countPerTimestamp.put(ellapsedSeconds, count+1);
}
private void dumpHistogram(Map<OPCODE, Map<Integer, Integer>> histogram) {
System.out.println("\nOpcode");
for (int i = 0; i <= ellapsedSeconds; i++) {
System.out.print("\t");
System.out.print(i);
}
System.out.println();
for (OPCODE o : histogram.keySet()) {
System.out.print(o.name());
Map<Integer, Integer> countPerTimestamp = histogram.get(o);
for (int i = 0; i <= ellapsedSeconds; i++) {
Integer count = countPerTimestamp.get(i);
if (count == null) {
count = 0;
}
System.out.print("\t");
System.out.print(count);
}
System.out.println();
}
}
@Override
protected void executeOpcode() {
if (interruptSignalled) {
@ -1077,6 +1137,7 @@ public class MOS65C02 extends CPU {
// This makes it possible to trap the memory read of an opcode, when PC == Address, you know it is executing that opcode.
int op = 0x00ff & getMemory().read(pc, TYPE.EXECUTE, true, false);
OPCODE opcode = opcodes[op];
addToHistogram(opcode);
if (traceEntry != null && warnAboutExtendedOpcodes && opcode != null && opcode.isExtendedOpcode) {
LOG.log(Level.WARNING, ">>EXTENDED OPCODE DETECTED {0}<<", Integer.toHexString(opcode.code));
LOG.log(Level.WARNING, traceEntry);
@ -1084,7 +1145,7 @@ public class MOS65C02 extends CPU {
log(">>EXTENDED OPCODE DETECTED " + Integer.toHexString(opcode.code) + "<<");
log(traceEntry);
}
}
}
if (opcode == null) {
// handle bad opcode as a NOP
int wait = 0;
@ -1107,14 +1168,16 @@ public class MOS65C02 extends CPU {
wait = 3;
} else {
wait = 4;
} break;
}
break;
case 0x0c:
bytes = 3;
if ((op & 0x0f0) == 0x050) {
wait = 8;
} else {
wait = 4;
} break;
}
break;
default:
}
incrementProgramCounter(bytes);
@ -1192,8 +1255,8 @@ public class MOS65C02 extends CPU {
@Override
public void JSR(int address) {
pushPC();
setProgramCounter(address);
pushPC();
setProgramCounter(address);
}
public void BRK() {
@ -1238,6 +1301,14 @@ public class MOS65C02 extends CPU {
// Cold/Warm boot procedure
@Override
public void reset() {
if (histogram != null) {
Map<OPCODE, Map<Integer, Integer>> oldHistogram = histogram;
histogram = null;
dumpHistogram(oldHistogram);
}
startTime = System.currentTimeMillis();
ellapsedSeconds = 0;
histogram = new HashMap<>();
pushWord(getProgramCounter());
push(getStatus());
// STACK = 0x0ff;
@ -1326,9 +1397,6 @@ public class MOS65C02 extends CPU {
*/
StringBuilder out = new StringBuilder(o.getCommand().toString());
out.append(" ").append(format);
if (o.getMode().isIndirect()) {
out.append(" >> $").append(Integer.toHexString(o.getMode().getCalculator().calculateAddress(this)));
}
return out.toString();
}
private boolean pageBoundaryPenalty = false;
@ -1343,30 +1411,16 @@ public class MOS65C02 extends CPU {
}
/**
* Special commands -- these are usually treated as NOP but can be reused for emulator controls
* !byte $fc, $65, $00 ; Turn off tracing
* !byte $fc, $65, $01 ; Turn on tracing
* !byte $fc, $50, NN ; print number NN to stdout
* !byte $fc, $5b, NN ; print number NN to stdout with newline
* !byte $fc, $5c, NN ; print character NN to stdout
* Special commands -- these are usually treated as NOP but can be reused
* for emulator controls !byte $fc, $65, $00 ; Turn off tracing !byte $fc,
* $65, $01 ; Turn on tracing
*
* @param param1
* @param param2
* @param param2
*/
public void performExtendedCommand(byte param1, byte param2) {
// LOG.log(Level.INFO, "Extended command {0},{1}", new Object[]{Integer.toHexString(param1), Integer.toHexString(param2)});
LOG.log(Level.INFO, "Extended command {0},{1}", new Object[]{Integer.toHexString(param1), Integer.toHexString(param2)});
switch (param1 & 0x0ff) {
case 0x50:
// System out
System.out.print(param2 & 0x0ff);
break;
case 0x5b:
// System out (with line break)
System.out.println(param2 & 0x0ff);
break;
case 0x5c:
// System out (with line break)
System.out.println((char) (param2 & 0x0ff));
break;
case 0x65:
// CPU functions
switch (param2 & 0x0ff) {

View File

@ -58,13 +58,7 @@ public enum SoftSwitches {
null, RAMEvent.TYPE.ANY, true)),
//Renamed as per Sather 5-7
_80COL(new VideoSoftSwitch("80ColumnVideo (80COL/80VID)", 0x0c00c, 0x0c00d, 0x0c01f, RAMEvent.TYPE.WRITE, false)),
ALTCH(new VideoSoftSwitch("Mousetext", 0x0c00e, 0x0c00f, 0x0c01e, RAMEvent.TYPE.WRITE, false){
@Override
public void stateChanged() {
super.stateChanged();
computer.getVideo().forceRefresh();
}
}),
ALTCH(new VideoSoftSwitch("Mousetext", 0x0c00e, 0x0c00f, 0x0c01e, RAMEvent.TYPE.WRITE, false)),
TEXT(new VideoSoftSwitch("Text", 0x0c050, 0x0c051, 0x0c01a, RAMEvent.TYPE.ANY, true)),
MIXED(new VideoSoftSwitch("Mixed", 0x0c052, 0x0c053, 0x0c01b, RAMEvent.TYPE.ANY, false)),
PAGE2(new VideoSoftSwitch("Page2", 0x0c054, 0x0c055, 0x0c01c, RAMEvent.TYPE.ANY, false) {

View File

@ -297,23 +297,23 @@ public class VideoDHGR extends Video {
if ((xOffset & 0x01) == 1) {
return;
}
int b1 = ((RAM128k) computer.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset );
int b2 = ((RAM128k) computer.getMemory()).getMainMemory() .readByte(rowAddress + xOffset );
int b1 = ((RAM128k) computer.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset);
int b2 = ((RAM128k) computer.getMemory()).getMainMemory().readByte(rowAddress + xOffset);
int b3 = ((RAM128k) computer.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset + 1);
int b4 = ((RAM128k) computer.getMemory()).getMainMemory() .readByte(rowAddress + xOffset + 1);
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) {
useColOffset = 76;
}
useColor[useColOffset ] = (b1 & 0x80) != 0;
useColor[useColOffset] = (b1 & 0x80) != 0;
useColor[useColOffset + 1] = (b2 & 0x80) != 0;
useColor[useColOffset + 2] = (b3 & 0x80) != 0;
useColor[useColOffset + 3] = (b4 & 0x80) != 0;
int dhgrWord = (0x07f & b1) ;
dhgrWord |= (0x07f & b2) << 7;
dhgrWord |= (0x07f & b3) << 14;
dhgrWord |= (0x07f & b4) << 21;
int dhgrWord = 0x07f & b1;
dhgrWord |= (0x07f & b2) << 7;
dhgrWord |= (0x07f & b3) << 14;
dhgrWord |= (0x07f & b4) << 21;
showDhgr(screen, TIMES_14[xOffset], y, dhgrWord);
}
boolean extraHalfBit = false;
@ -635,7 +635,7 @@ public class VideoDHGR extends Video {
}
}
static final Color BLACK = Color.BLACK;
static Color WHITE = Color.WHITE;
static final Color WHITE = Color.WHITE;
static final int[][] XY_OFFSET;
static {

View File

@ -18,10 +18,8 @@
*/
package jace.apple2e;
import jace.Emulator;
import jace.EmulatorUILogic;
import static jace.apple2e.VideoDHGR.BLACK;
import jace.config.ConfigurableField;
import jace.config.InvokableAction;
import jace.core.Computer;
import jace.core.RAM;
import jace.core.RAMEvent;
@ -31,9 +29,6 @@ import java.util.HashSet;
import java.util.Set;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import static jace.apple2e.VideoDHGR.BLACK;
/**
* Provides a clean color monitor simulation, complete with text-friendly
@ -58,17 +53,14 @@ public class VideoNTSC extends VideoDHGR {
public boolean enableVideo7 = true;
// Scanline represents 560 bits, divided up into 28-bit words
int[] scanline = new int[20];
static public int[] divBy28 = new int[560];
@ConfigurableField(name = "Video Mode", category = "video", shortName = "mode", defaultValue = "TextFriendly", description = "Set Video Mode (Color|TextFriendly|Mode7|Mode7TextFriendly|Monochrome|Greenscreen|Amber)")
public static VideoMode videoMode = VideoMode.TextFriendly;
static int[] divBy28 = new int[560];
static {
for (int i = 0; i < 560; i++) {
divBy28[i] = i / 28;
}
}
protected boolean[] colorActive = new boolean[80];
boolean[] colorActive = new boolean[80];
int rowStart = 0;
public VideoNTSC(Computer computer) {
@ -76,77 +68,6 @@ public class VideoNTSC extends VideoDHGR {
registerStateListeners();
}
public static enum VideoMode {
Color("Color"),
TextFriendly("Text-friendly color"),
Mode7("Mode7 Mixed RGB"),
Mode7TextFriendly("Mode7 with Text-friendly palette"),
Monochrome("Mono"),
Greenscreen("Green"),
Amber("Amber");
String name;
VideoMode(String n) {
name = n;
}
}
@InvokableAction(name = "Toggle video mode",
category = "video",
alternatives = "Gfx mode;color;b&w;monochrome",
defaultKeyMapping = {"ctrl+shift+g"})
public static void changeVideoMode() {
VideoNTSC thiss = (VideoNTSC) Emulator.computer.video;
int currentMode = Arrays.asList(VideoMode.values()).indexOf(thiss.getVideoMode());
currentMode++;
if (currentMode >= VideoMode.values().length) {
currentMode = 0;
}
thiss.setVideoMode(VideoMode.values()[currentMode]);
}
public void setVideoMode(VideoMode mode) {
videoMode = mode;
monochomeMode = false;
WHITE = Color.WHITE;
switch (mode) {
case Amber:
monochomeMode = true;
WHITE = Color.web("ff8000");
break;
case Greenscreen:
monochomeMode = true;
WHITE = Color.web("0ccc68");
break;
case Monochrome:
monochomeMode = true;
break;
case Color:
useTextPalette = false;
enableVideo7 = false;
break;
case Mode7:
useTextPalette = false;
enableVideo7 = true;
break;
case Mode7TextFriendly:
useTextPalette = true;
enableVideo7 = true;
break;
case TextFriendly:
useTextPalette = true;
enableVideo7 = false;
break;
}
activePalette = useTextPalette ? TEXT_PALETTE : SOLID_PALETTE;
EmulatorUILogic.notify("Video mode: " + mode.name);
forceRefresh();
}
public VideoMode getVideoMode() {
return videoMode;
}
@Override
protected void showBW(WritableImage screen, int x, int y, int dhgrWord) {
int pos = divBy28[x];
@ -248,8 +169,6 @@ public class VideoNTSC extends VideoDHGR {
}
}
boolean monochomeMode = false;
private void renderScanline(WritableImage screen, int y) {
int p = 0;
if (rowStart != 0) {
@ -283,7 +202,7 @@ public class VideoNTSC extends VideoDHGR {
boolean mixed = enableVideo7 && dhgrMode && graphicsMode == rgbMode.MIX;
for (int i = 0; i < 28; i++) {
if (i % 7 == 0) {
isBW = monochomeMode || !colorActive[byteCounter] || (mixed && !hiresMode && !useColor[byteCounter]);
isBW = !colorActive[byteCounter] || (mixed && !hiresMode && !useColor[byteCounter]);
byteCounter++;
}
if (isBW) {
@ -379,13 +298,12 @@ public class VideoNTSC extends VideoDHGR {
@Override
public void reconfigure() {
setVideoMode(videoMode);
activePalette = useTextPalette ? TEXT_PALETTE : SOLID_PALETTE;
super.reconfigure();
}
// The following section captures changes to the RGB mode
// The details of this are in Brodener's patent application #4631692
// http://www.freepatentsonline.com/4631692.pdf
// http://www.freepatentsonline.com/4631692.pdf
// as well as the AppleColor adapter card manual
// http://apple2.info/download/Ext80ColumnAppleColorCardHR.pdf
rgbMode graphicsMode = rgbMode.MIX;
@ -442,7 +360,7 @@ public class VideoNTSC extends VideoDHGR {
}));
rgbStateListeners.add(memory.observe(RAMEvent.TYPE.EXECUTE, 0x0fa62, (e) -> {
// When reset hook is called, reset the graphics mode
// This is useful in case a program is running that
// This is useful in case a program is running that
// is totally clueless how to set the RGB state correctly.
f1 = true;
f2 = true;

View File

@ -25,7 +25,7 @@ import jace.core.SoftSwitch;
* A memory softswitch is a softswitch which triggers a memory reconfiguration
* after its value is changed.
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class MemorySoftSwitch extends SoftSwitch {
@ -48,15 +48,11 @@ public class MemorySoftSwitch extends SoftSwitch {
// Todo: Implement floating bus, maybe?
@Override
protected byte readSwitch() {
if (computer.getVideo() == null) {
return 0x00;
byte value = computer.getVideo().getFloatingBus();
if (getState()) {
return (byte) (value | 0x080);
} else {
byte value = computer.getVideo().getFloatingBus();
if (getState()) {
return (byte) (value | 0x080);
} else {
return (byte) (value & 0x07f);
}
return (byte) (value & 0x07f);
}
}
}
}

View File

@ -20,8 +20,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;
@ -31,7 +29,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Decode an applesoft program into a list of program lines Right now this is an
@ -43,17 +40,8 @@ import java.util.stream.Collectors;
public class ApplesoftProgram {
List<Line> lines = new ArrayList<>();
public static final int START_OF_PROG_POINTER = 0x067;
public static final int END_OF_PROG_POINTER = 0x0AF;
public static final int VARIABLE_TABLE = 0x069;
public static final int ARRAY_TABLE = 0x06b;
public static final int VARIABLE_TABLE_END = 0x06d;
public static final int STRING_TABLE = 0x06f;
public static final int HIMEM = 0x073;
public static final int startingAddressPointer = 0x067;
public static final int BASIC_RUN = 0x0e000;
public static final int RUNNING_FLAG = 0x076;
public static final int NOT_RUNNING = 0x0FF;
public static final int GOTO_CMD = 0x0D944; //actually starts at D93E
int startingAddress = 0x0801;
public static void main(String... args) {
@ -79,7 +67,7 @@ public class ApplesoftProgram {
}
public static ApplesoftProgram fromMemory(RAM memory) {
int startAddress = memory.readWordRaw(START_OF_PROG_POINTER);
int startAddress = memory.readWordRaw(startingAddressPointer);
int nextCheck = memory.readWordRaw(startAddress);
int pos = startAddress;
List<Byte> bytes = new ArrayList<>();
@ -127,53 +115,35 @@ public class ApplesoftProgram {
out = lines.stream().map((l) -> l.toString() + "\n").reduce(out, String::concat);
return out;
}
public static ApplesoftProgram fromString(String programSource) {
ApplesoftProgram program = new ApplesoftProgram();
for (String line : programSource.split("\\n")) {
if (line.trim().isEmpty()) {
continue;
}
if (line.trim().isEmpty()) continue;
program.lines.add(Line.fromString(line));
}
//correct line linkage
for (int i = 0; i < program.lines.size(); i++) {
for (int i=0; i < program.lines.size(); i++) {
if (i > 0) {
program.lines.get(i).setPrevious(program.lines.get(i - 1));
program.lines.get(i).setPrevious(program.lines.get(i-1));
}
if (i < program.lines.size() - 1) {
program.lines.get(i).setNext(program.lines.get(i + 1));
if (i < program.lines.size()-1) {
program.lines.get(i).setNext(program.lines.get(i+1));
}
}
return program;
}
};
public void run() {
RAM memory = Emulator.computer.memory;
Emulator.computer.pause();
int programStart = memory.readWordRaw(START_OF_PROG_POINTER);
int programEnd = programStart + getProgramSize();
if (isProgramRunning()) {
whenReady(()->{
relocateVariables(programEnd);
injectProgram();
});
} else {
injectProgram();
clearVariables(programEnd);
}
Emulator.computer.resume();
}
private void injectProgram() {
RAM memory = Emulator.computer.memory;
int pos = memory.readWordRaw(START_OF_PROG_POINTER);
int pos = memory.readWordRaw(startingAddressPointer);
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;
int nextPos = pos + line.getLength() + 1;
memory.write(pos++, (byte) (nextPos & 0x0ff), false, true);
memory.write(pos++, (byte) (nextPos>>8 & 0x0ff), false, true);
memory.write(pos++, (byte) (line.getNumber() & 0x0ff), false, true);
memory.write(pos++, (byte) (line.getNumber() >> 8 & 0x0ff), false, true);
boolean isFirst = true;
for (Command command : line.getCommands()) {
if (!isFirst) {
@ -189,69 +159,9 @@ public class ApplesoftProgram {
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.computer.memory;
return (memory.readRaw(RUNNING_FLAG) & 0x0FF) != NOT_RUNNING;
}
/**
* If the program is running, wait until it advances to the next line
*/
private void whenReady(Runnable r) {
RAM memory = Emulator.computer.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);
}
});
}
/**
* Rough approximation of the CLEAR command at $D66A.
* http://www.txbobsc.com/scsc/scdocumentor/D52C.html
* @param programEnd Program ending address
*/
private void clearVariables(int programEnd) {
RAM memory = Emulator.computer.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);
}
/**
* Move variables around to accommodate bigger program
* @param programEnd Program ending address
*/
private void relocateVariables(int programEnd) {
RAM memory = Emulator.computer.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);
}
}
private int getProgramSize() {
int size = lines.stream().collect(Collectors.summingInt(Line::getLength)) + 4;
return size;
memory.write(pos++, (byte) 0, false, true);
// Emulator.computer.cpu.setProgramCounter(BASIC_RUN);
Emulator.computer.resume();
}
}

View File

@ -20,7 +20,6 @@ package jace.applesoft;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* A command is a list of parts, either raw bytes (ascii text) or tokens. When
@ -221,10 +220,14 @@ public class Command {
}
}
}
List<ByteOrToken> parts = new ArrayList<>();
List<ByteOrToken> parts = new ArrayList<ByteOrToken>();
@Override
public String toString() {
return parts.stream().map(ByteOrToken::toString).collect(Collectors.joining());
String out = "";
for (ByteOrToken p : parts) {
out += p.toString();
}
return out;
}
}
}

View File

@ -153,7 +153,7 @@ public class Line {
boolean isComment = false;
Command currentCommand = new Command();
l.commands.add(currentCommand);
l.length = 5; // 4 pointer bytes + 1 null byte at the end
l.length = 4;
String upperLineString = lineString.toUpperCase();
for (int i = 0; i < lineString.length(); i++) {
if (!hasLineNumber) {
@ -211,4 +211,5 @@ public class Line {
}
return l;
}
}
}

View File

@ -89,15 +89,6 @@ public class AcmeCompiler implements CompileResult<File> {
ByteArrayOutputStream baosErr;
PrintStream err;
private String normalizeWindowsPath(String path) {
if (path.contains("\\")) {
char firstLetter = path.toLowerCase().charAt(0);
return "/"+firstLetter+path.substring(1).replaceAll("\\\\", "/");
} else {
return path;
}
}
private void invokeAcme(File sourceFile, File workingDirectory) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IOException {
String oldPath = System.getProperty("user.dir");
redirectSystemOutput();
@ -105,7 +96,7 @@ public class AcmeCompiler implements CompileResult<File> {
compiledAsset = File.createTempFile(sourceFile.getName(), "bin", sourceFile.getParentFile());
System.setProperty("user.dir", workingDirectory.getAbsolutePath());
AcmeCrossAssembler acme = new AcmeCrossAssembler();
String[] params = {"--outfile", normalizeWindowsPath(compiledAsset.getAbsolutePath()), "-f", "cbm", "--maxerrors","16",normalizeWindowsPath(sourceFile.getAbsolutePath())};
String[] params = {"--outfile", compiledAsset.getAbsolutePath(), "-f", "cbm", "--maxerrors","16",sourceFile.getAbsolutePath()};
int status = acme.run("Acme", params);
successful = status == 0;
if (!successful) {

View File

@ -1,9 +1,7 @@
package jace.assembly;
import org.ibex.nestedvm.UnixRuntime;
/* This file was generated from acme by Mips2Java on Tue Jul 14 00:46:52 CDT 2015 */
public final class AcmeCrossAssembler extends UnixRuntime {
public final class AcmeCrossAssembler extends org.ibex.nestedvm.UnixRuntime {
/* program counter */
private int pc = 0;

View File

@ -32,11 +32,7 @@ public class AssemblyHandler implements LanguageHandler<File> {
public void execute(CompileResult<File> lastResult) {
if (lastResult.isSuccessful()) {
try {
boolean resume = false;
if (Emulator.computer.isRunning()) {
resume = true;
Emulator.computer.pause();
}
Emulator.computer.pause();
RAM memory = Emulator.computer.getMemory();
FileInputStream input = new FileInputStream(lastResult.getCompiledAsset());
int startLSB = input.read();
@ -47,9 +43,7 @@ public class AssemblyHandler implements LanguageHandler<File> {
while ((next=input.read()) != -1) {
memory.write(pos++, (byte) next, false, true);
}
if (resume) {
Emulator.computer.resume();
}
Emulator.computer.resume();
} catch (IOException ex) {
Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex);
}

View File

@ -41,7 +41,7 @@ public abstract class Cheats extends Device {
super(computer);
}
@InvokableAction(name = "Toggle Cheats", alternatives = "cheat;Plug-in", defaultKeyMapping = "ctrl+shift+m")
@InvokableAction(name = "Toggle Cheats", alternatives = "cheat", defaultKeyMapping = "ctrl+shift+m")
public void toggleCheats() {
cheatsActive = !cheatsActive;
if (cheatsActive) {
@ -97,7 +97,7 @@ public abstract class Cheats extends Device {
super.detach();
}
public abstract void registerListeners();
abstract void registerListeners();
protected void unregisterListeners() {
listeners.stream().forEach((l) -> {

View File

@ -13,7 +13,6 @@ import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.shape.Shape;
/**
*
@ -32,6 +31,10 @@ public class MemoryCell implements Comparable<MemoryCell> {
public ObservableList<Integer> writeInstructions = FXCollections.observableList(new ArrayList<>());
public ObservableList<String> writeInstructionsDisassembly = FXCollections.observableArrayList(new ArrayList<>());
public ObservableList<String> execInstructionsDisassembly = FXCollections.observableArrayList(new ArrayList<>());
private int x;
private int y;
private int width;
private int height;
public static void setListener(ChangeListener<MemoryCell> l) {
listener = l;
@ -46,6 +49,29 @@ public class MemoryCell implements Comparable<MemoryCell> {
value.addListener(changeListener);
}
public void setRect(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
@Override
public int compareTo(MemoryCell o) {
return address - o.address;
@ -54,14 +80,4 @@ public class MemoryCell implements Comparable<MemoryCell> {
public boolean hasCounts() {
return hasCount.get();
}
private Shape shape;
public void setShape(Shape s) {
shape = s;
}
public Shape getShape() {
return shape;
}
}

View File

@ -1,445 +1,445 @@
package jace.cheat;
import jace.Emulator;
import jace.JaceApplication;
import jace.core.CPU;
import jace.core.Computer;
import jace.core.RAM;
import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.state.State;
import jace.ui.MetacheatUI;
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 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;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class MetaCheat extends Cheats {
static final ScriptEngine NASHORN_ENGINE = new ScriptEngineManager().getEngineByName("nashorn");
static Invocable NASHORN_INVOCABLE = (Invocable) NASHORN_ENGINE;
public static enum SearchType {
VALUE, TEXT, CHANGE
}
public static enum SearchChangeType {
NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT
}
public static class SearchResult {
int address;
int lastObservedValue = 0;
private SearchResult(int address, int val) {
this.address = address;
lastObservedValue = val;
}
@Override
public String toString() {
return Integer.toHexString(address) + ": " + lastObservedValue + " (" + Integer.toHexString(lastObservedValue) + ")";
}
public int getAddress() {
return address;
}
}
MetacheatUI ui;
public int fadeRate = 1;
public int lightRate = 30;
public int historyLength = 10;
private int startAddress = 0;
private int endAddress = 0x0AFFF;
private final StringProperty startAddressProperty = new SimpleStringProperty(Integer.toHexString(startAddress));
private final StringProperty endAddressProperty = new SimpleStringProperty(Integer.toHexString(endAddress));
private boolean byteSized = true;
private SearchType searchType = SearchType.VALUE;
private SearchChangeType searchChangeType = SearchChangeType.NO_CHANGE;
private final BooleanProperty signedProperty = new SimpleBooleanProperty(false);
private final StringProperty searchValueProperty = new SimpleStringProperty("0");
private final StringProperty changeByProperty = new SimpleStringProperty("0");
private final ObservableList<DynamicCheat> cheatList = FXCollections.observableArrayList();
private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList();
private final ObservableList<State> snapshotList = FXCollections.observableArrayList();
public MetaCheat(Computer computer) {
super(computer);
addNumericValidator(startAddressProperty);
addNumericValidator(endAddressProperty);
addNumericValidator(searchValueProperty);
addNumericValidator(changeByProperty);
startAddressProperty.addListener((prop, oldVal, newVal) -> {
startAddress = Math.max(0, Math.min(65535, Integer.parseInt(newVal, 16)));
});
endAddressProperty.addListener((prop, oldVal, newVal) -> {
endAddress = Math.max(0, Math.min(65535, Integer.parseInt(newVal, 16)));
});
}
private void addNumericValidator(StringProperty stringProperty) {
stringProperty.addListener((ObservableValue<? extends String> prop, String oldVal, String newVal) -> {
if (newVal == null || newVal.isEmpty()) {
return;
}
if (!newVal.matches("(\\+|-)?(x|$)?[0-9a-fA-F]*")) {
stringProperty.set("");
}
});
}
public int parseInt(String s) throws NumberFormatException {
if (s == null || s.isEmpty()) {
return 0;
}
if (s.matches("(\\+|-)?[0-9]+")) {
return Integer.parseInt(s);
} else {
String upper = s.toUpperCase();
boolean positive = !upper.startsWith("-");
for (int i = 0; i < upper.length(); i++) {
char c = upper.charAt(i);
if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) {
int value = Integer.parseInt(s.substring(i), 16);
if (!positive) {
value *= -1;
}
return value;
}
}
}
throw new NumberFormatException("Could not interpret int value " + s);
}
@Override
public void registerListeners() {
}
public void addCheat(DynamicCheat cheat) {
cheatList.add(cheat);
computer.getMemory().addListener(cheat);
cheat.addressProperty().addListener((prop, oldVal, newVal) -> {
computer.getMemory().removeListener(cheat);
cheat.doConfig();
computer.getMemory().addListener(cheat);
});
}
public void removeCheat(DynamicCheat cheat) {
cheat.active.set(false);
computer.getMemory().removeListener(cheat);
cheatList.remove(cheat);
}
@Override
protected void unregisterListeners() {
super.unregisterListeners();
cheatList.stream().forEach(computer.getMemory()::removeListener);
}
@Override
protected String getDeviceName() {
return "MetaCheat";
}
@Override
public void detach() {
super.detach();
ui.detach();
}
@Override
public void attach() {
ui = JaceApplication.getApplication().showMetacheat();
ui.registerMetacheatEngine(this);
super.attach();
}
public int getStartAddress() {
return startAddress;
}
public int getEndAddress() {
return endAddress;
}
public void setByteSized(boolean b) {
byteSized = b;
}
public void setSearchType(SearchType searchType) {
this.searchType = searchType;
}
public void setSearchChangeType(SearchChangeType searchChangeType) {
this.searchChangeType = searchChangeType;
}
public Property<Boolean> signedProperty() {
return signedProperty;
}
public Property<String> searchValueProperty() {
return searchValueProperty;
}
public Property<String> searchChangeByProperty() {
return changeByProperty;
}
public ObservableList<DynamicCheat> getCheats() {
return cheatList;
}
public ObservableList<SearchResult> getSearchResults() {
return resultList;
}
public ObservableList<State> getSnapshots() {
return snapshotList;
}
public Property<String> startAddressProperty() {
return startAddressProperty;
}
public Property<String> endAddressProperty() {
return endAddressProperty;
}
public void newSearch() {
RAM memory = Emulator.computer.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);
}
}
}
public void performSearch() {
RAM memory = Emulator.computer.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;
});
}
RAMListener memoryViewListener = null;
private final Map<Integer, MemoryCell> memoryCells = new ConcurrentHashMap<>();
public MemoryCell getMemoryCell(int address) {
return memoryCells.get(address);
}
public void initMemoryView() {
RAM memory = Emulator.computer.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);
}
}
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.computer.getMotherboard().getSpeedInHz() / 60);
@Override
public void tick() {
computer.cpu.performSingleTrace();
if (fadeCounter-- <= 0) {
fadeCounter = FADE_TIMER_VALUE;
memoryCells.values().stream()
.filter((cell) -> cell.hasCounts())
.forEach((cell) -> {
if (cell.execCount.get() > 0) {
cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate));
}
if (cell.readCount.get() > 0) {
cell.readCount.set(Math.max(0, cell.readCount.get() - fadeRate));
}
if (cell.writeCount.get() > 0) {
cell.writeCount.set(Math.max(0, cell.writeCount.get() - fadeRate));
}
if (MemoryCell.listener != null) {
MemoryCell.listener.changed(null, cell, cell);
}
});
}
}
AtomicInteger pendingInspectorUpdates = new AtomicInteger(0);
public void onInspectorChanged() {
pendingInspectorUpdates.set(0);
}
private void processMemoryEvent(RAMEvent e) {
MemoryCell cell = getMemoryCell(e.getAddress());
if (cell != null) {
CPU cpu = Emulator.computer.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);
}
});
}
} 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);
}
});
}
} 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());
}
}
public void saveCheats(File saveFile) {
FileWriter writer = null;
try {
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();
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);
}
}
}
}
package jace.cheat;
import jace.Emulator;
import jace.JaceApplication;
import jace.core.CPU;
import jace.core.Computer;
import jace.core.RAM;
import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.state.State;
import jace.ui.MetacheatUI;
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 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;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class MetaCheat extends Cheats {
static final ScriptEngine NASHORN_ENGINE = new ScriptEngineManager().getEngineByName("nashorn");
static Invocable NASHORN_INVOCABLE = (Invocable) NASHORN_ENGINE;
public static enum SearchType {
VALUE, TEXT, CHANGE
}
public static enum SearchChangeType {
NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT
}
public static class SearchResult {
int address;
int lastObservedValue = 0;
private SearchResult(int address, int val) {
this.address = address;
lastObservedValue = val;
}
@Override
public String toString() {
return Integer.toHexString(address) + ": " + lastObservedValue + " (" + Integer.toHexString(lastObservedValue) + ")";
}
public int getAddress() {
return address;
}
}
MetacheatUI ui;
public int fadeRate = 1;
public int lightRate = 30;
public int historyLength = 10;
private int startAddress = 0;
private int endAddress = 0x0ffff;
private final StringProperty startAddressProperty = new SimpleStringProperty(Integer.toHexString(startAddress));
private final StringProperty endAddressProperty = new SimpleStringProperty(Integer.toHexString(endAddress));
private boolean byteSized = true;
private SearchType searchType = SearchType.VALUE;
private SearchChangeType searchChangeType = SearchChangeType.NO_CHANGE;
private final BooleanProperty signedProperty = new SimpleBooleanProperty(false);
private final StringProperty searchValueProperty = new SimpleStringProperty("0");
private final StringProperty changeByProperty = new SimpleStringProperty("0");
private final ObservableList<DynamicCheat> cheatList = FXCollections.observableArrayList();
private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList();
private final ObservableList<State> snapshotList = FXCollections.observableArrayList();
public MetaCheat(Computer computer) {
super(computer);
addNumericValidator(startAddressProperty);
addNumericValidator(endAddressProperty);
addNumericValidator(searchValueProperty);
addNumericValidator(changeByProperty);
startAddressProperty.addListener((prop, oldVal, newVal) -> {
startAddress = Math.max(0, Math.min(65535, parseInt(newVal)));
});
endAddressProperty.addListener((prop, oldVal, newVal) -> {
endAddress = Math.max(0, Math.min(65535, parseInt(newVal)));
});
}
private void addNumericValidator(StringProperty stringProperty) {
stringProperty.addListener((ObservableValue<? extends String> prop, String oldVal, String newVal) -> {
if (newVal == null || newVal.isEmpty()) {
return;
}
if (!newVal.matches("(\\+|-)?(x|$)?[0-9a-fA-F]*")) {
stringProperty.set("");
}
});
}
public int parseInt(String s) throws NumberFormatException {
if (s == null || s.isEmpty()) {
return 0;
}
if (s.matches("(\\+|-)?[0-9]+")) {
return Integer.parseInt(s);
} else {
String upper = s.toUpperCase();
boolean positive = !upper.startsWith("-");
for (int i = 0; i < upper.length(); i++) {
char c = upper.charAt(i);
if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) {
int value = Integer.parseInt(s.substring(i), 16);
if (!positive) {
value *= -1;
}
return value;
}
}
}
throw new NumberFormatException("Could not interpret int value " + s);
}
@Override
void registerListeners() {
}
public void addCheat(DynamicCheat cheat) {
cheatList.add(cheat);
computer.getMemory().addListener(cheat);
cheat.addressProperty().addListener((prop, oldVal, newVal) -> {
computer.getMemory().removeListener(cheat);
cheat.doConfig();
computer.getMemory().addListener(cheat);
});
}
public void removeCheat(DynamicCheat cheat) {
cheat.active.set(false);
computer.getMemory().removeListener(cheat);
cheatList.remove(cheat);
}
@Override
protected void unregisterListeners() {
super.unregisterListeners();
cheatList.stream().forEach(computer.getMemory()::removeListener);
}
@Override
protected String getDeviceName() {
return "MetaCheat";
}
@Override
public void detach() {
super.detach();
ui.detach();
}
@Override
public void attach() {
ui = JaceApplication.getApplication().showMetacheat();
ui.registerMetacheatEngine(this);
super.attach();
}
public int getStartAddress() {
return startAddress;
}
public int getEndAddress() {
return endAddress;
}
public void setByteSized(boolean b) {
byteSized = b;
}
public void setSearchType(SearchType searchType) {
this.searchType = searchType;
}
public void setSearchChangeType(SearchChangeType searchChangeType) {
this.searchChangeType = searchChangeType;
}
public Property<Boolean> signedProperty() {
return signedProperty;
}
public Property<String> searchValueProperty() {
return searchValueProperty;
}
public Property<String> searchChangeByProperty() {
return changeByProperty;
}
public ObservableList<DynamicCheat> getCheats() {
return cheatList;
}
public ObservableList<SearchResult> getSearchResults() {
return resultList;
}
public ObservableList<State> getSnapshots() {
return snapshotList;
}
public Property<String> startAddressProperty() {
return startAddressProperty;
}
public Property<String> endAddressProperty() {
return endAddressProperty;
}
public void newSearch() {
RAM memory = Emulator.computer.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);
}
}
}
public void performSearch() {
RAM memory = Emulator.computer.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;
});
}
RAMListener memoryViewListener = null;
private final Map<Integer, MemoryCell> memoryCells = new ConcurrentHashMap<>();
public MemoryCell getMemoryCell(int address) {
return memoryCells.get(address);
}
public void initMemoryView() {
RAM memory = Emulator.computer.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);
}
}
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.computer.getMotherboard().cyclesPerSecond / 60);
@Override
public void tick() {
computer.cpu.performSingleTrace();
if (fadeCounter-- <= 0) {
fadeCounter = FADE_TIMER_VALUE;
memoryCells.values().stream()
.filter((cell) -> cell.hasCounts())
.forEach((cell) -> {
if (cell.execCount.get() > 0) {
cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate));
}
if (cell.readCount.get() > 0) {
cell.readCount.set(Math.max(0, cell.readCount.get() - fadeRate));
}
if (cell.writeCount.get() > 0) {
cell.writeCount.set(Math.max(0, cell.writeCount.get() - fadeRate));
}
if (MemoryCell.listener != null) {
MemoryCell.listener.changed(null, cell, cell);
}
});
}
}
AtomicInteger pendingInspectorUpdates = new AtomicInteger(0);
public void onInspectorChanged() {
pendingInspectorUpdates.set(0);
}
private void processMemoryEvent(RAMEvent e) {
MemoryCell cell = getMemoryCell(e.getAddress());
if (cell != null) {
CPU cpu = Emulator.computer.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);
}
});
}
} 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);
}
});
}
} 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());
}
}
public void saveCheats(File saveFile) {
FileWriter writer = null;
try {
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();
String line;
while ((line = in.readLine()) != null) {
DynamicCheat cheat = DynamicCheat.deserialize(line);
addCheat(cheat);
}
in.close();
} catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
in.close();
} catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}

View File

@ -70,7 +70,7 @@ public class MontezumasRevengeCheats extends Cheats {
};
@Override
public void registerListeners() {
void registerListeners() {
RAM memory = Emulator.computer.memory;
if (repulsiveHack) {
addCheat(RAMEvent.TYPE.WRITE, this::repulsiveBehavior, 0x1508, 0x1518);

View File

@ -1,357 +0,0 @@
/*
* Copyright 2018 org.badvision.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jace.cheat;
import jace.EmulatorUILogic;
import jace.apple2e.MOS65C02;
import jace.config.ConfigurableField;
import jace.core.Computer;
import jace.core.RAMEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
/**
* Cheats for the Wolfenstein series games
*/
public class WolfensteinCheats extends Cheats {
// Specific to Wolfenstein
static final int KEYS = 0x04359;
static final int GRENADES = 0x04348;
// Object types
static final int CHEST = 48;
static final int SS = 32;
// Specific to Beyond Wolfenstein
static final int MARKS = 0x0434b;
static final int PASSES = 0x04360;
static final int CLOSET_CONTENTS_CMP = 0x05FB9; // Only locks by type, so mess up the check
// Object types
static final int CLOSET = 32;
static final int ALARM = 48;
static final int SEATED_GUARD = 80;
static final int BW_DOOR = 96;
// Same in both Wolfenstein and Beyond Wolfenstein
static final int PLAYER_LOCATION = 0x04343;
static final int BULLETS = 0x04347;
// Object types
static final int CORPSE = 64;
static final int GUARD = 16;
static final int DOOR = 80;
static final int NOTHING = 0;
public WolfensteinCheats(Computer computer) {
super(computer);
}
private EventHandler<MouseEvent> mouseListener = this::processMouseEvent;
@ConfigurableField(category = "Hack", name = "Beyond Wolfenstein", defaultValue = "false", description = "Make sure cheats work with Beyond Wolfenstein")
public static boolean _isBeyondWolfenstein = false;
@ConfigurableField(category = "Hack", name = "Mouse (1+2)", defaultValue = "false", description = "Left click kills/opens, Right click teleports")
public static boolean mouseMode = true;
@ConfigurableField(category = "Hack", name = "Ammo (1+2)", defaultValue = "false", description = "All the bullets and grenades you'll need")
public static boolean ammo = true;
@ConfigurableField(category = "Hack", name = "Rich (2)", defaultValue = "false", description = "All the money")
public static boolean rich = true;
@ConfigurableField(category = "Hack", name = "Uniform (1)", defaultValue = "false", description = "PUT SOME CLOTHES ON!")
public static boolean uniform = true;
@ConfigurableField(category = "Hack", name = "Vest (1)", defaultValue = "false", description = "Bulletproof vest")
public static boolean vest = true;
@ConfigurableField(category = "Hack", name = "Skeleton Key (1+2)", defaultValue = "false", description = "Open all things")
public static boolean skeletonKey = true;
@ConfigurableField(category = "Hack", name = "Fast Open (1)", defaultValue = "false", description = "Open all things quickly")
public static boolean fastOpen = true;
@ConfigurableField(category = "Hack", name = "All dead (1+2)", defaultValue = "false", description = "Everything is dead")
public static boolean allDead = true;
@ConfigurableField(category = "Hack", name = "Sleepy Time (1+2)", defaultValue = "false", description = "Nobody move, nobody get hurt")
public static boolean sleepyTime = false;
@ConfigurableField(category = "Hack", name = "Legendary (1)", defaultValue = "false", description = "All of them are SS guards!")
public static boolean legendary = false;
@ConfigurableField(category = "Hack", name = "Day at the office (2)", defaultValue = "false", description = "All of them are at desks")
public static boolean dayAtTheOffice = false;
@Override
public void registerListeners() {
if (_isBeyondWolfenstein) {
// Only work in Beyond Wolfenstein
if (rich) {
forceValue(MARKS, 255);
}
if (dayAtTheOffice) {
for (int i = 0x04080; i < 0x04100; i += 0x010) {
addCheat(RAMEvent.TYPE.READ, this::allDesks, i);
}
}
} else {
// Only work in the first Wolfenstein game
if (uniform) {
forceValue(255, 0x04349);
}
if (vest) {
forceValue(255, 0x0434A);
}
if (fastOpen) {
addCheat(RAMEvent.TYPE.WRITE, this::fastOpenHandler, 0x04351);
addCheat(RAMEvent.TYPE.WRITE, this::fastOpenHandler, 0x0587B);
}
}
if (ammo) {
forceValue(10, BULLETS);
if (!_isBeyondWolfenstein) {
forceValue(9, GRENADES);
}
}
if (skeletonKey) {
if (_isBeyondWolfenstein) {
forceValue(255, PASSES);
forceValue(64, CLOSET_CONTENTS_CMP); // Fake it out so it thinks all doors are unlocked
} else {
forceValue(255, KEYS);
}
}
if (allDead) {
for (int i = 0x04080; i < 0x04100; i += 0x010) {
addCheat(RAMEvent.TYPE.READ, this::allDead, i);
}
}
if (sleepyTime) {
for (int i = 0x04080; i < 0x04100; i += 0x010) {
forceValue(0, i + 2);
forceValue(0, i + 3);
// This makes them shout ACHTUNG over and over again... so don't do that.
// forceValue(144, i+12);
forceValue(0, i + 12);
}
}
if (legendary) {
for (int i = 0x04080; i < 0x04100; i += 0x010) {
addCheat(RAMEvent.TYPE.READ, this::legendaryMode, i);
}
}
if (mouseMode) {
EmulatorUILogic.addMouseListener(mouseListener);
} else {
EmulatorUILogic.removeMouseListener(mouseListener);
}
}
private void fastOpenHandler(RAMEvent evt) {
int newVal = evt.getNewValue() & 0x0ff;
if (newVal > 1) {
evt.setNewValue(1);
}
}
private boolean isFinalRoom() {
for (int i = 0x04080; i < 0x04100; i += 0x010) {
int objectType = computer.getMemory().readRaw(i) & 0x0ff;
if (objectType == BW_DOOR) {
return true;
}
}
return false;
}
private void allDesks(RAMEvent evt) {
int location = computer.getMemory().readRaw(evt.getAddress() + 1);
if (!isFinalRoom() || location < 32) {
int type = evt.getNewValue();
if (type == GUARD) {
evt.setNewValue(SEATED_GUARD);
// Reset the status flag to 0 to prevent the boss desk from rendering, but don't revive dead guards!
if (computer.getMemory().readRaw(evt.getAddress() + 4) != 4) {
computer.getMemory().write(evt.getAddress() + 4, (byte) 0, false, false);
}
}
}
}
private void allDead(RAMEvent evt) {
int type = evt.getNewValue();
if (_isBeyondWolfenstein) {
int location = computer.getMemory().readRaw(evt.getAddress() + 1);
if (!isFinalRoom() || location < 32) {
if (type == GUARD) {
evt.setNewValue(CORPSE);
} else if (type == SEATED_GUARD) {
computer.getMemory().write(evt.getAddress() + 4, (byte) 4, false, false);
}
}
} else {
if (type == GUARD || type == SS) {
evt.setNewValue(CORPSE);
}
}
}
private int debugTicks = 0;
private void legendaryMode(RAMEvent evt) {
int type = evt.getNewValue();
if (type == 16) {
evt.setNewValue(32);
}
}
private void processMouseEvent(MouseEvent evt) {
if (evt.isPrimaryButtonDown() || evt.isSecondaryButtonDown()) {
Node source = (Node) evt.getSource();
double mouseX = evt.getSceneX() / source.getBoundsInLocal().getWidth();
double mouseY = evt.getSceneY() / source.getBoundsInLocal().getHeight();
int x = Math.max(0, Math.min(7, (int) ((mouseX - 0.148) * 11)));
int y = Math.max(0, Math.min(7, (int) ((mouseY - 0.101) * 11)));
int location = x + (y << 3);
if (evt.getButton() == MouseButton.PRIMARY) {
killEnemyAt(location);
} else {
teleportTo(location);
}
}
}
private void killEnemyAt(int location) {
System.out.println("Looking for bad guy at " + location);
for (int i = 0x04080; i < 0x04100; i += 0x010) {
int enemyLocation = computer.getMemory().readRaw(i + 1) & 0x0ff;
System.out.print("Location " + enemyLocation);
String type = "";
boolean isAlive = false;
boolean isSeatedGuard = false;
if (_isBeyondWolfenstein) {
switch (computer.getMemory().readRaw(i) & 0x0ff) {
case GUARD:
type = "guard";
isAlive = true;
break;
case SEATED_GUARD:
type = "seated guard";
isAlive = true;
isSeatedGuard = true;
break;
case CLOSET:
type = "closet";
break;
case CORPSE:
type = "corpse";
break;
case NOTHING:
type = "nothing";
break;
default:
type = "unknown type " + (computer.getMemory().readRaw(i) & 0x0ff);
}
} else {
switch (computer.getMemory().readRaw(i) & 0x0ff) {
case GUARD:
type = "guard";
isAlive = true;
break;
case SS:
type = "SS";
isAlive = true;
break;
case CHEST:
type = "chest";
break;
case CORPSE:
type = "corpse";
break;
case DOOR:
type = "door";
break;
case NOTHING:
type = "nothing";
break;
default:
type = "unknown type " + (computer.getMemory().readRaw(i) & 0x0ff);
}
}
System.out.println(" is a " + type);
for (int j = 0x00; j < 0x0f; j++) {
int val = computer.getMemory().readRaw(i + j) & 0x0ff;
System.out.print(Integer.toHexString(val) + " ");
}
System.out.println();
if (isAlive && location == enemyLocation) {
if (isSeatedGuard) {
computer.getMemory().write(i + 4, (byte) 4, false, false);
} else {
computer.getMemory().write(i, (byte) CORPSE, false, true);
}
System.out.println("*BLAM*");
}
}
}
private void teleportTo(int location) {
computer.getMemory().write(0x04343, (byte) location, false, true);
}
@Override
public void unregisterListeners() {
super.unregisterListeners();
EmulatorUILogic.removeMouseListener(mouseListener);
}
public static int BlueType = 0x0b700;
@Override
protected String getDeviceName() {
return "Wolfenstein Cheats";
}
@Override
public void tick() {
if (debugTicks > 0) {
debugTicks--;
if (debugTicks == 0) {
computer.getCpu().setTraceEnabled(false);
}
}
}
/**
* 4147-4247: Room map?
*
* 4080-40ff : Enemies and chests 4090-409f : Enemy 2 40a0-40af : Enemy 1 0: State/Type (0-15 = Nothing?, 16 =
* soldier, 32 = SS, 48 = Chest, 64 = dead) 1: Location 2: Direction (0 = still) 3: Aim (0 = no gun) C: Caution?
* (144 = stickup)
*
* 4341 : Player walking direction (0 = still, 1=D, 2=U, 4=L, 8=R) 4342 : Player gun direction 4343 : Real Player
* location (4 high bits = vertical, 4 low bits = horizontal) .. use this for teleport 4344 : Player Drawing X
* location 4345 : Player Drawing Y location 4347 : Bullets 4348 : Grenades 4349 : Uniform (0 = none, 1+ = yes) 434A
* : Vest (0 = none, 1+ = yes) 434C : Wall collision animation timer 434D/E : Game timer (lo/high) -- no immediate
* effect 4351 : Search / Use timer 4352 : 0 normally, 144/176 opening chest, 160 when searching body, 176 opening
* door 4359 : Keys (8-bit flags, 255=skeleton key) 587B : Search timer
*/
}

View File

@ -49,7 +49,6 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import javafx.collections.ObservableList;
import javafx.scene.control.TreeItem;
import javafx.scene.image.ImageView;
@ -94,8 +93,8 @@ public class Configuration implements Reconfigurable {
return null;
}
public static Optional<ImageView> getChangedIcon() {
return Utility.loadIcon("icon_exclaim.gif").map(ImageView::new);
public static ImageView getChangedIcon() {
return new ImageView(Utility.loadIcon("icon_exclaim.gif"));
}
@Override
@ -189,17 +188,16 @@ public class Configuration implements Reconfigurable {
}
public void setFieldValue(String field, Serializable value) {
setChanged(true);
if (value != null) {
if (value.equals(getFieldValue(field))) {
return;
}
} else {
if (getFieldValue(field) == null) {
setChanged(false);
return;
}
}
setChanged(true);
setRawFieldValue(field, value);
}
@ -253,15 +251,9 @@ public class Configuration implements Reconfigurable {
if (!changed) {
setGraphic(null);
} else {
getChangedIcon().ifPresent(this::setGraphic);
setGraphic(getChangedIcon());
}
}
public Stream<ConfigNode> getTreeAsStream() {
return Stream.concat(
Stream.of(this),
children.stream().flatMap(ConfigNode::getTreeAsStream));
}
}
public static ConfigNode BASE;
public static EmulatorUILogic ui = Emulator.logic;
@ -291,7 +283,7 @@ public class Configuration implements Reconfigurable {
// System.out.println("Evaluating field " + f.getName());
try {
Object o = f.get(node.subject);
if (!f.getType().isPrimitive() && f.getType() != String.class && visited.contains(o)) {
if (!f.getType().isPrimitive() && visited.contains(o)) {
continue;
}
visited.add(o);
@ -308,7 +300,7 @@ public class Configuration implements Reconfigurable {
node.setRawFieldValue(f.getName(), (Serializable) o);
}
continue;
}
}
if (o == null) {
continue;
}
@ -501,9 +493,7 @@ public class Configuration implements Reconfigurable {
newRoot.getChildren().stream().forEach((child) -> {
String childName = child.toString();
ConfigNode oldChild = oldRoot.findChild(childName);
if (oldChild == null) {
oldChild = oldRoot.findChild(child.id);
}
if (oldChild == null) {oldChild = oldRoot.findChild(child.id);}
// System.out.println("Applying settings for " + childName);
applyConfigTree(child, oldChild);
});
@ -607,7 +597,7 @@ public class Configuration implements Reconfigurable {
}
}
if (!found) {
System.err.println("Unable to find property " + fieldName + " for device " + deviceName + ". Try one of these: " + Utility.join(shortFieldNames, ", "));
System.err.println("Unable to find property " + fieldName + " for device " + deviceName + ". Try one of these :" + Utility.join(shortFieldNames, ", "));
}
}
}

View File

@ -38,11 +38,7 @@ public abstract class DynamicSelection<T> implements ISelection<T> {
return currentValue;
} else {
Iterator<? extends T> i = getSelections().keySet().iterator();
if (i.hasNext()) {
return i.next();
} else {
return null;
}
return i.next();
}
}

View File

@ -175,6 +175,4 @@ public abstract class CPU extends Device {
lastTrace = trace;
singleTraceEnabled = false;
}
abstract public void clearState();
}

View File

@ -128,18 +128,10 @@ public abstract class Computer implements Reconfigurable {
}
public void deactivate() {
if (cpu != null) {
cpu.suspend();
}
if (motherboard != null) {
motherboard.suspend();
}
if (video != null) {
video.suspend();
}
if (mixer != null) {
mixer.detach();
}
cpu.suspend();
motherboard.suspend();
video.suspend();
mixer.detach();
}
@InvokableAction(
@ -169,7 +161,7 @@ public abstract class Computer implements Reconfigurable {
name = "Warm boot",
description = "Process user-initatiated reboot (ctrl+apple+reset)",
category = "general",
alternatives = "reboot;reset;three-finger-salute;restart",
alternatives = "reboot;reset;three-finger-salute",
defaultKeyMapping = {"Ctrl+Ignore Alt+Ignore Meta+Backspace", "Ctrl+Ignore Alt+Ignore Meta+Delete"})
public void invokeWarmStart() {
warmStart();
@ -193,7 +185,7 @@ public abstract class Computer implements Reconfigurable {
return result;
}
@InvokableAction(name = "Resume", description = "Resumes the computer if it was previously paused", alternatives = "unpause;unfreeze;resume;play", defaultKeyMapping = {"meta+shift+pause", "alt+shift+pause"})
@InvokableAction(name = "Resume", description = "Resumes the computer if it was previously paused", alternatives = "unpause;unfreeze;resume", defaultKeyMapping = {"meta+shift+pause", "alt+shift+pause"})
public void resume() {
doResume();
getRunningProperty().set(true);

View File

@ -20,10 +20,6 @@ package jace.core;
import jace.state.Stateful;
import jace.config.Reconfigurable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
@ -43,14 +39,9 @@ import javafx.beans.property.SimpleBooleanProperty;
@Stateful
public abstract class Device implements Reconfigurable {
protected Computer computer;
private List<Device> children;
private Device() {
children = Collections.synchronizedList(new ArrayList<>());
private Device() {
}
public Device(Computer computer) {
this();
this.computer = computer;
}
@ -61,31 +52,6 @@ public abstract class Device implements Reconfigurable {
private final BooleanProperty run = new SimpleBooleanProperty(true);
@Stateful
public boolean isPaused = false;
@Stateful
public boolean isAttached = false;
public void addChildDevice(Device d) {
children.add(d);
if (isAttached) {
d.attach();
}
}
public void removeChildDevice(Device d) {
children.remove(d);
d.suspend();
if (isAttached) {
d.detach();
}
}
public void addAllDevices(Collection<Device> devices) {
devices.forEach(this::addChildDevice);
}
public List<Device> getChildren() {
return Collections.unmodifiableList(children);
}
public BooleanProperty getRunningProperty() {
return run;
@ -120,7 +86,6 @@ public abstract class Device implements Reconfigurable {
return;
}
// Implicit else...
children.forEach(Device::doTick);
tick();
}
@ -148,24 +113,17 @@ public abstract class Device implements Reconfigurable {
setRun(false);
return true;
}
children.forEach(Device::suspend);
return false;
}
public void resume() {
setRun(true);
waitCycles = 0;
children.forEach(Device::resume);
}
public void attach() {
isAttached = true;
children.forEach(Device::attach);
}
public abstract void attach();
public void detach() {
Keyboard.unregisterAllHandlers(this);
children.forEach(Device::suspend);
children.forEach(Device::detach);
}
}

View File

@ -45,7 +45,7 @@ public class Font {
initialized = true;
font = new int[256][8];
Thread fontLoader = new Thread(() -> {
InputStream in = Font.class.getClassLoader().getResourceAsStream("jace/data/font.png");
InputStream in = ClassLoader.getSystemResourceAsStream("jace/data/font.png");
Image image = new Image(in);
PixelReader reader = image.getPixelReader();
for (int i = 0; i < 256; i++) {

View File

@ -18,7 +18,6 @@
*/
package jace.core;
import jace.Emulator;
import jace.apple2e.SoftSwitches;
import jace.config.InvokableAction;
import jace.config.Reconfigurable;
@ -69,7 +68,6 @@ public class Keyboard implements Reconfigurable {
return "kbd";
}
static byte currentKey = 0;
public boolean shiftPressed = false;
public static void clearStrobe() {
currentKey = (byte) (currentKey & 0x07f);
@ -104,7 +102,6 @@ public class Keyboard implements Reconfigurable {
registerKeyHandler(new KeyHandler(code) {
@Override
public boolean handleKeyUp(KeyEvent e) {
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
if (action == null || !action.notifyOnRelease()) {
return false;
}
@ -128,7 +125,6 @@ public class Keyboard implements Reconfigurable {
@Override
public boolean handleKeyDown(KeyEvent e) {
// System.out.println("Key down: "+method.toString());
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
Object returnValue = null;
try {
if (method.getParameterCount() > 0) {
@ -252,7 +248,6 @@ public class Keyboard implements Reconfigurable {
default:
}
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
if (e.isShiftDown()) {
c = fixShiftedChar(c);
}

View File

@ -22,6 +22,7 @@ import jace.apple2e.SoftSwitches;
import jace.apple2e.Speaker;
import jace.config.ConfigurableField;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
@ -38,7 +39,8 @@ import java.util.logging.Logger;
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class Motherboard extends TimedDevice {
final public Set<Device> miscDevices = new LinkedHashSet<>();
@ConfigurableField(name = "Enable Speaker", shortName = "speaker", defaultValue = "true")
public static boolean enableSpeaker = true;
public Speaker speaker;
@ -61,11 +63,8 @@ public class Motherboard extends TimedDevice {
public Motherboard(Computer computer, Motherboard oldMotherboard) {
super(computer);
if (oldMotherboard != null) {
addAllDevices(oldMotherboard.getChildren());
miscDevices.addAll(oldMotherboard.miscDevices);
speaker = oldMotherboard.speaker;
accelorationRequestors.addAll(oldMotherboard.accelorationRequestors);
setSpeedInHz(oldMotherboard.getSpeedInHz());
setMaxSpeed(oldMotherboard.isMaxSpeed());
}
}
@ -94,8 +93,11 @@ public class Motherboard extends TimedDevice {
clockCounter = cpuPerClock;
computer.getVideo().doTick();
for (Optional<Card> card : cards) {
card.ifPresent(Card::doTick);
card.ifPresent(c -> c.doTick());
}
miscDevices.stream().forEach((m) -> {
m.doTick();
});
} catch (Throwable t) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, t);
}
@ -123,7 +125,7 @@ public class Motherboard extends TimedDevice {
speaker = new Speaker(computer);
if (computer.mixer.lineAvailable) {
speaker.attach();
addChildDevice(speaker);
miscDevices.add(speaker);
} else {
System.out.print("No lines available! Speaker not running.");
}
@ -131,19 +133,21 @@ public class Motherboard extends TimedDevice {
speaker.reconfigure();
} catch (Throwable t) {
System.out.println("Unable to initalize sound -- deactivating speaker out");
removeChildDevice(speaker);
speaker.detach();
miscDevices.remove(speaker);
}
} else {
System.out.println("Speaker not enabled, leaving it off.");
if (speaker != null) {
removeChildDevice(speaker);
speaker.detach();
miscDevices.remove(speaker);
}
}
if (startAgain && computer.getMemory() != null) {
resume();
}
}
HashSet<Object> accelorationRequestors = new HashSet<>();
static HashSet<Object> accelorationRequestors = new HashSet<>();
public void requestSpeed(Object requester) {
accelorationRequestors.add(requester);
@ -194,6 +198,11 @@ public class Motherboard extends TimedDevice {
@Override
public void detach() {
System.out.println("Detaching motherboard");
miscDevices.stream().forEach((d) -> {
d.suspend();
d.detach();
});
miscDevices.clear();
// halt();
super.detach();
}

View File

@ -21,9 +21,6 @@ package jace.core;
import jace.config.ConfigurableField;
import jace.config.DynamicSelection;
import jace.config.Reconfigurable;
import jace.library.StringUtils;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@ -32,7 +29,6 @@ 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;
@ -53,8 +49,8 @@ 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<>());
private final Set<SourceDataLine> availableLines = Collections.synchronizedSet(new HashSet<SourceDataLine>());
private final Map<Object, SourceDataLine> activeLines = Collections.synchronizedMap(new HashMap<Object, SourceDataLine>());
/**
* Bits per sample
*/
@ -65,9 +61,6 @@ public class SoundMixer extends Device {
*/
@ConfigurableField(name = "Playback Rate", shortName = "freq")
public static int RATE = 48000;
@ConfigurableField(name = "Mute", shortName = "mute")
public static boolean MUTE = false;
/**
* Sound format used for playback
*/
@ -111,9 +104,7 @@ public class SoundMixer extends Device {
@Override
public synchronized void reconfigure() {
if (MUTE) {
detach();
} else if (isConfigDifferent()) {
if (isConfigDifferent()) {
detach();
try {
initMixer();
@ -275,7 +266,7 @@ public class SoundMixer extends Device {
if (oldPreferredMixer == null) {
changed |= preferredMixer.getValue() != null;
} else {
changed |= !oldPreferredMixer.matches(Pattern.quote(preferredMixer.getValue()));
changed |= !oldPreferredMixer.matches(preferredMixer.getValue());
}
oldPreferredMixer = preferredMixer.getValue();
return changed;

View File

@ -21,25 +21,23 @@ package jace.core;
import jace.config.ConfigurableField;
/**
* 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.
* 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.
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public abstract class TimedDevice extends Device {
/**
* Creates a new instance of TimedDevice
*
* @param computer
*/
public TimedDevice(Computer computer) {
super(computer);
setSpeedInHz(cyclesPerSecond);
setSpeed(cyclesPerSecond);
}
@ConfigurableField(name = "Speed", description = "(Percentage)")
public int speedRatio = 100;
private long cyclesPerSecond = defaultCyclesPerSecond();
@ConfigurableField(name = "Speed", description = "(in hertz)")
public long cyclesPerSecond = defaultCyclesPerSecond();
@ConfigurableField(name = "Max speed")
public boolean maxspeed = false;
@ -58,11 +56,10 @@ public abstract class TimedDevice extends Device {
public boolean suspend() {
disableTempMaxSpeed();
boolean result = super.suspend();
Thread w = worker;
if (w != null && w.isAlive()) {
if (worker != null && worker.isAlive()) {
try {
w.interrupt();
w.join(1000);
worker.interrupt();
worker.join(1000);
} catch (InterruptedException ex) {
}
}
@ -116,44 +113,13 @@ public abstract class TimedDevice extends Device {
long cyclesPerInterval; // How many cycles to wait until a pause interval
long nextSync; // When was the last pause?
public final int getSpeedRatio() {
return speedRatio;
}
public final void setMaxSpeed(boolean enabled) {
maxspeed = enabled;
if (!enabled) {
disableTempMaxSpeed();
}
}
public final boolean isMaxSpeed() {
return maxspeed;
}
public final long getSpeedInHz() {
return cyclesPerInterval * 100L;
}
public final void setSpeedInHz(long cyclesPerSecond) {
// System.out.println("Raw set speed for " + getName() + " to " + cyclesPerSecond + "hz");
speedRatio = (int) Math.round(cyclesPerSecond * 100.0 / defaultCyclesPerSecond());
public final void setSpeed(long cyclesPerSecond) {
cyclesPerInterval = cyclesPerSecond / 100L;
nanosPerInterval = (long) (cyclesPerInterval * NANOS_PER_SECOND / cyclesPerSecond);
// System.out.println("Will pause " + nanosPerInterval + " nanos every " + cyclesPerInterval + " cycles");
cycleTimer = 0;
resetSyncTimer();
}
public final void setSpeedInPercentage(int ratio) {
// System.out.println("Setting " + getName() + " speed ratio to " + speedRatio);
cyclesPerSecond = defaultCyclesPerSecond() * ratio / 100;
if (cyclesPerSecond == 0) {
cyclesPerSecond = defaultCyclesPerSecond();
}
setSpeedInHz(cyclesPerSecond);
}
long skip = 0;
long wait = 0;
@ -204,6 +170,10 @@ public abstract class TimedDevice extends Device {
@Override
public void reconfigure() {
if (cyclesPerSecond == 0) {
cyclesPerSecond = defaultCyclesPerSecond();
}
setSpeed(cyclesPerSecond);
}
public abstract long defaultCyclesPerSecond();

View File

@ -18,14 +18,12 @@
*/
package jace.core;
import jace.Emulator;
import jace.config.Configuration;
import jace.config.InvokableAction;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.reflections.Reflections;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -33,23 +31,18 @@ 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 java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import org.reflections.Reflections;
/**
* This is a set of helper functions which do not belong anywhere else.
* Functions vary from introspection, discovery, and string/pattern matching.
@ -58,11 +51,151 @@ import org.reflections.Reflections;
*/
public class Utility {
static Reflections reflections = new Reflections("jace");
//--------------- Introspection utilities
/*
private static Set<Class> findClasses(String pckgname, Class clazz) {
Set<Class> output = new HashSet<>();
// Code from JWhich
// ======
// Translate the package name into an absolute path
String name = pckgname;
if (!name.startsWith("/")) {
name = "/" + name;
}
name = name.replace('.', '/');
// Get a File object for the package
URL url = Utility.class.getResource(name);
if (url == null || url.getFile().contains("jre/lib")) {
return output;
}
if (url.getProtocol().equalsIgnoreCase("jar")) {
return findClassesInJar(url, clazz);
}
File directory = new File(url.getFile());
// New code
// ======
if (directory.exists()) {
// Get the list of the files contained in the package
for (String filename : directory.list()) {
char firstLetter = filename.charAt(0);
if (firstLetter < 'A' || (firstLetter > 'Z' && firstLetter < 'a') || firstLetter > 'z') {
continue;
}
// we are only interested in .class files
if (filename.endsWith(".class")) {
// removes the .class extension
String classname = filename.substring(0, filename.length() - 6);
try {
// Try to create an instance of the object
String className = pckgname + "." + classname;
Class c = Class.forName(className);
if (clazz.isAssignableFrom(c)) {
output.add(c);
}
} catch (Throwable ex) {
System.err.println(ex);
}
} else {
// System.out.println("Skipping non class: " + filename);
}
}
}
return output;
}
private static Set<Class> findClassesInJar(URL jarLocation, Class clazz) {
Set<Class> output = new HashSet<>();
JarFile jarFile = null;
try {
JarURLConnection conn = (JarURLConnection) jarLocation.openConnection();
jarFile = conn.getJarFile();
Enumeration<JarEntry> entries = jarFile.entries();
String last = "";
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
if (jarEntry.getName().equals(last)) {
return output;
}
last = jarEntry.getName();
if (jarEntry.getName().endsWith(".class")) {
String className = jarEntry.getName();
className = className.substring(0, className.length() - 6);
className = className.replaceAll("/", "\\.");
if (className.startsWith("com.sun")) {
continue;
}
if (className.startsWith("java")) {
continue;
}
if (className.startsWith("javax")) {
continue;
}
if (className.startsWith("com.oracle")) {
continue;
}
// removes the .class extension
try {
// Try to create an instance of the object
// System.out.println("Class: " + className);
Class c = Class.forName(className);
if (clazz.isAssignableFrom(c)) {
output.add(c);
}
} catch (ClassNotFoundException cnfex) {
System.err.println(cnfex);
} catch (Throwable cnfex) {
// System.err.println(cnfex);
}
} else {
// System.out.println("Skipping non class: " + jarEntry.getName());
}
}
} catch (IOException ex) {
Logger.getLogger(Utility.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
if (jarFile != null) {
jarFile.close();
}
} catch (IOException ex) {
Logger.getLogger(Utility.class.getName()).log(Level.SEVERE, null, ex);
}
}
return output;
}
private static final Map<Class, Collection<Class>> classCache = new HashMap<>();
*/
static Reflections reflections = new Reflections("jace");
public static Set<Class> findAllSubclasses(Class clazz) {
return reflections.getSubTypesOf(clazz);
}
/*
public static List<Class> findAllSubclasses(Class clazz) {
if (classCache.containsKey(clazz)) {
return (List<Class>) classCache.get(clazz);
}
TreeMap<String, Class> allClasses = new TreeMap<>();
List<Class> values = new ArrayList(allClasses.values());
classCache.put(clazz, values);
for (Package p : Package.getPackages()) {
if (p.getName().startsWith("java")
|| p.getName().startsWith("com.sun")
|| p.getName().startsWith("sun")
|| p.getName().startsWith("com.oracle")) {
continue;
}
findClasses(p.getName(), clazz)
.stream()
.filter((c) -> !(Modifier.isAbstract(c.getModifiers())))
.forEach((c) -> {
allClasses.put(c.getSimpleName(), c);
});
}
return values;
}
*/
//------------------------------ String comparators
/**
@ -72,11 +205,11 @@ public class Utility {
*
* @param s
* @param t
* @return Distance (lower means a closer match, zero is identical)
* @return Distance (higher is better)
*/
public static int levenshteinDistance(String s, String t) {
if (s == null || t == null || s.length() == 0 || t.length() == 0) {
return Integer.MAX_VALUE;
return -1;
}
s = s.toLowerCase().replaceAll("[^a-zA-Z0-9\\s]", "");
@ -102,20 +235,9 @@ public class Utility {
}
}
}
return dist[m][n];
return Math.max(m, n) - dist[m][n];
}
/**
* Normalize distance based on longest string
* @param s
* @param t
* @return Similarity ranking, higher is better
*/
public static int adjustedLevenshteinDistance(String s, String t) {
return Math.max(s.length(), t.length()) - levenshteinDistance(s, t);
}
/**
* Compare strings based on a tally of similar patterns found, using a fixed
* search window The resulting score is heavily penalized if the strings
@ -125,7 +247,7 @@ public class Utility {
* @param c1
* @param c2
* @param width Search window size
* @return Overall similarity score (higher is better)
* @return Overall similarity score (higher is beter)
*/
public static double rankMatch(String c1, String c2, int width) {
double score = 0;
@ -148,36 +270,23 @@ public class Utility {
return score * adjustment * adjustment;
}
public static String join(Collection<String> c, String d) {
return c.stream().collect(Collectors.joining(d));
}
private static boolean isHeadless = false;
public static void setHeadlessMode(boolean headless) {
isHeadless = headless;
if (Emulator.instance == null && headless) {
Emulator.instance = new Emulator(Collections.emptyList());
public static String join(Collection c, String d) {
String result = "";
boolean isFirst = true;
for (Object o : c) {
result += (isFirst ? "" : d) + o.toString();
isFirst = false;
}
return result;
}
public static boolean isHeadlessMode() {
return isHeadless;
}
public static Optional<Image> loadIcon(String filename) {
if (isHeadless) {
return Optional.empty();
}
public static Image loadIcon(String filename) {
InputStream stream = Utility.class.getClassLoader().getResourceAsStream("jace/data/" + filename);
return Optional.of(new Image(stream));
return new Image(stream);
}
public static Optional<Label> loadIconLabel(String filename) {
if (isHeadless) {
return Optional.empty();
}
Image img = loadIcon(filename).get();
public static Label loadIconLabel(String filename) {
Image img = loadIcon(filename);
Label label = new Label() {
@Override
public boolean equals(Object obj) {
@ -200,21 +309,7 @@ public class Utility {
label.setTextFill(Color.WHITE);
DropShadow shadow = new DropShadow(5.0, Color.BLACK);
label.setEffect(shadow);
return Optional.of(label);
}
public static void confirm(String title, String message, Runnable accept) {
Platform.runLater(() -> {
Alert confirm = new Alert(Alert.AlertType.CONFIRMATION);
confirm.setContentText(message);
confirm.setTitle(title);
Optional<ButtonType> response = confirm.showAndWait();
response.ifPresent(b -> {
if (b.getButtonData().isDefaultButton()) {
(new Thread(accept)).start();
}
});
});
return label;
}
// public static void runModalProcess(String title, final Runnable runnable) {
@ -237,6 +332,7 @@ public class Utility {
// frame.dispose();
// }).start();
// }
public static class RankingComparator implements Comparator<String> {
String match;
@ -250,8 +346,8 @@ public class Utility {
@Override
public int compare(String o1, String o2) {
double s1 = adjustedLevenshteinDistance(match, o1);
double s2 = adjustedLevenshteinDistance(match, o2);
double s1 = levenshteinDistance(match, o1);
double s2 = levenshteinDistance(match, o2);
if (s2 == s1) {
s1 = rankMatch(o1, match, 3) + rankMatch(o1, match, 2);
s2 = rankMatch(o2, match, 3) + rankMatch(o2, match, 2);
@ -291,7 +387,7 @@ public class Utility {
// System.out.println(match + "->" + c + ":" + l + " -- "+ m2 + "," + m3 + "," + "(" + (m2 + m3) + ")");
// }
// double score = rankMatch(match, candidates.get(0), 2);
double score = adjustedLevenshteinDistance(match, candidates.get(0));
double score = levenshteinDistance(match, candidates.get(0));
if (score > 1) {
return candidates.get(0);
}
@ -485,47 +581,4 @@ public class Utility {
}
return setChild(object, paths[paths.length - 1], value, hex);
}
static Map<InvokableAction, Runnable> allActions = null;
public static Map<InvokableAction, Runnable> getAllInvokableActions() {
if (allActions == null) {
allActions = new HashMap<>();
Configuration.BASE.getTreeAsStream().forEach((Configuration.ConfigNode node) -> {
for (Method m : node.subject.getClass().getMethods()) {
if (m.isAnnotationPresent(InvokableAction.class)) {
allActions.put(m.getAnnotation(InvokableAction.class), () -> {
try {
m.invoke(node.subject);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(Utility.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
}
});
}
return allActions;
}
public static Runnable getNamedInvokableAction(String action) {
Map<InvokableAction, Runnable> actions = getAllInvokableActions();
List<InvokableAction> actionsList = new ArrayList(actions.keySet());
actionsList.sort((a,b) -> Integer.compare(getActionNameMatch(action, a), getActionNameMatch(action, b)));
// for (InvokableAction a : actionsList) {
// String actionName = a.alternatives() == null ? a.name() : (a.name() + ";" + a.alternatives());
// System.out.println("Score for " + action + " evaluating " + a.name() + ": " + getActionNameMatch(action, a));
// }
return actions.get(actionsList.get(0));
}
private static int getActionNameMatch(String str, InvokableAction action) {
int nameMatch = levenshteinDistance(str, action.name());
if (action.alternatives() != null) {
for (String alt : action.alternatives().split(";")) {
nameMatch = Math.min(nameMatch, levenshteinDistance(str, alt));
}
}
return nameMatch;
}
}

View File

@ -18,7 +18,6 @@
*/
package jace.core;
import jace.Emulator;
import jace.state.Stateful;
import jace.config.ConfigurableField;
import jace.config.InvokableAction;
@ -29,9 +28,10 @@ import javafx.scene.image.WritableImage;
* Generic abstraction of a 560x192 video output device which renders 40 columns
* per scanline. This also triggers VBL and updates the physical screen.
* Subclasses are used to manage actual rendering via ScreenWriter
* implementations. Created on November 10, 2006, 4:29 PM
* implementations.
* Created on November 10, 2006, 4:29 PM
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
@Stateful
public abstract class Video extends Device {
@ -89,7 +89,6 @@ public abstract class Video extends Device {
/**
* Creates a new instance of Video
*
* @param computer
*/
public Video(Computer computer) {
@ -99,21 +98,21 @@ public abstract class Video extends Device {
visible = new WritableImage(560, 192);
vPeriod = 0;
hPeriod = 0;
_forceRefresh();
forceRefresh();
}
public void setWidth(int w) {
width = w;
}
public int getWidth() {
return width;
}
public void setHeight(int h) {
height = h;
}
public int getHeight() {
return height;
}
@ -132,15 +131,12 @@ public abstract class Video extends Device {
public static int MIN_SCREEN_REFRESH = 15;
Runnable redrawScreen = () -> {
if (visible != null && video != null) {
// if (computer.getRunningProperty().get()) {
screenDirty = false;
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
// }
if (computer.getRunningProperty().get()) {
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
}
};
public void redraw() {
screenDirty = false;
javafx.application.Platform.runLater(redrawScreen);
}
@ -212,8 +208,8 @@ public abstract class Video extends Device {
abstract public void configureVideoMode();
protected static int byteDoubler(byte b) {
int num
= // Skip hi-bit because it's not used in display
int num =
// Skip hi-bit because it's not used in display
// ((b&0x080)<<7) |
((b & 0x040) << 6)
| ((b & 0x020) << 5)
@ -274,17 +270,11 @@ public abstract class Video extends Device {
}
@InvokableAction(name = "Refresh screen",
category = "display",
description = "Marks screen contents as changed, forcing full screen redraw",
alternatives = "redraw",
defaultKeyMapping = {"ctrl+shift+r"})
public static final void forceRefresh() {
if (Emulator.computer != null && Emulator.computer.video != null) {
Emulator.computer.video._forceRefresh();
}
}
private void _forceRefresh() {
category = "display",
description = "Marks screen contents as changed, forcing full screen redraw",
alternatives = "redraw",
defaultKeyMapping = "ctrl+shift+r")
public final void forceRefresh() {
lineDirty = true;
screenDirty = true;
forceRedrawRowCount = APPLE_SCREEN_LINES + 1;

View File

@ -84,7 +84,7 @@ public class CardAppleMouse extends Card {
public boolean fullscreenFix = true;
@ConfigurableField(name = "Blazing Paddles fix", shortName = "bpfix", category = "Mouse", description = "Use different clamping values to make Blazing Paddles work more reliably.")
public boolean blazingPaddles = false;
Label mouseActive = Utility.loadIconLabel("input-mouse.png").orElse(null);
Label mouseActive = Utility.loadIconLabel("input-mouse.png");
public boolean movedSinceLastTick = false;
public boolean movedSinceLastRead = false;

View File

@ -56,7 +56,7 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
static public boolean USE_MAX_SPEED = true;
@ConfigurableField(category = "Disk", defaultValue = "", shortName = "d1", name = "Drive 1 disk image", description = "Path of disk 1")
public String disk1;
@ConfigurableField(category = "Disk", defaultValue = "", shortName = "d2", name = "Drive 2 disk image", description = "Path of disk 2")
@ConfigurableField(category = "Disk", defaultValue = "", shortName = "d2", name = "Drive 2 disk image", description = "Path of disk 1")
public String disk2;
public CardDiskII(Computer computer) {
@ -81,8 +81,7 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
currentDrive = drive1;
drive1.reset();
drive2.reset();
EmulatorUILogic.removeIndicators(drive1);
EmulatorUILogic.removeIndicators(drive2);
EmulatorUILogic.removeIndicators(this);
// Motherboard.cancelSpeedRequest(this);
}
@ -105,13 +104,13 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
case 0x8:
// drive off
currentDrive.setOn(false);
currentDrive.removeIndicator();
EmulatorUILogic.removeIndicator(this, currentDrive.getIcon());
break;
case 0x9:
// drive on
currentDrive.setOn(true);
currentDrive.addIndicator();
EmulatorUILogic.addIndicator(this, currentDrive.getIcon());
break;
case 0xA:
@ -128,6 +127,9 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
// read/write latch
currentDrive.write();
e.setNewValue(currentDrive.readLatch());
if (currentDrive.isOn()) {
EmulatorUILogic.addIndicator(this, currentDrive.getIcon());
}
break;
case 0xF:
// write mode
@ -224,8 +226,8 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
@Override
public void setSlot(int slot) {
super.setSlot(slot);
drive1.getIcon().ifPresent(icon->icon.setText("S" + slot + "D1"));
drive2.getIcon().ifPresent(icon->icon.setText("S" + slot + "D2"));
drive1.getIcon().setText("S" + slot + "D1");
drive2.getIcon().setText("S" + slot + "D2");
}
@Override

View File

@ -27,6 +27,7 @@ import jace.core.RAMEvent;
import jace.core.RAMEvent.TYPE;
import jace.core.RAMListener;
import jace.core.SoundMixer;
import static jace.core.Utility.*;
import jace.hardware.mockingboard.PSG;
import jace.hardware.mockingboard.R6522;
import java.util.concurrent.TimeUnit;
@ -79,7 +80,7 @@ public class CardMockingboard extends Card implements Runnable {
Condition playbackFinished = timerSync.newCondition();
@ConfigurableField(name = "Idle sample threshold", description = "Number of samples to wait before suspending sound")
private int MAX_IDLE_SAMPLES = SAMPLE_RATE;
@Override
public String getDeviceName() {
return "Mockingboard";
@ -89,7 +90,7 @@ public class CardMockingboard extends Card implements Runnable {
super(computer);
controllers = new R6522[2];
for (int i = 0; i < 2; i++) {
// has to be final to be used inside of anonymous class below
//don't ask...
final int j = i;
controllers[i] = new R6522(computer) {
int controller = j;
@ -130,13 +131,6 @@ public class CardMockingboard extends Card implements Runnable {
public String getShortName() {
return "timer" + j;
}
public void doTick() {
super.doTick();
if (controller == 0) {
doSoundTick();
}
}
};
}
}
@ -146,18 +140,6 @@ public class CardMockingboard extends Card implements Runnable {
suspend();
}
RAMListener mainListener = null;
boolean heatbeatUnclocked = false;
long heartbeatReclockTime = 0L;
long unclockTime = 5000L;
private void setUnclocked(boolean unclocked) {
heatbeatUnclocked = unclocked;
for (R6522 controller : controllers) {
controller.setUnclocked(unclocked);
}
heartbeatReclockTime = System.currentTimeMillis() + unclockTime;
}
@Override
protected void handleFirmwareAccess(int register, TYPE type, int value, RAMEvent e) {
@ -170,7 +152,7 @@ public class CardMockingboard extends Card implements Runnable {
chip++;
}
if (chip >= 2) {
System.err.println("Could not determine which PSG to communicate to for access to regsiter + " + Integer.toHexString(register));
System.err.println("Could not determine which PSG to communicate to");
e.setNewValue(computer.getVideo().getFloatingBus());
return;
}
@ -195,25 +177,13 @@ public class CardMockingboard extends Card implements Runnable {
@Override
public void tick() {
if (heatbeatUnclocked) {
if (System.currentTimeMillis() - heartbeatReclockTime >= unclockTime) {
setUnclocked(false);
} else {
for (R6522 c : controllers) {
if (c == null || !c.isRunning()) {
continue;
}
c.doTick();
}
for (R6522 c : controllers) {
if (c == null || !c.isRunning()) {
continue;
}
c.tick();
}
}
public boolean isRunning() {
return super.isRunning() && playbackThread != null && playbackThread.isAlive();
}
private void doSoundTick() {
if (isRunning() && !pause) {
// buildMixerTable();
timerSync.lock();
@ -224,7 +194,7 @@ public class CardMockingboard extends Card implements Runnable {
while (isRunning() && ticksSinceLastPlayback >= ticksBetweenPlayback) {
if (!playbackFinished.await(1, TimeUnit.SECONDS)) {
// gripe("The mockingboard playback thread has stalled. Disabling mockingboard.");
suspendSound();
suspend();
}
}
}
@ -293,15 +263,14 @@ public class CardMockingboard extends Card implements Runnable {
@Override
public void resume() {
pause = false;
if (chips == null) {
initPSG();
for (PSG psg : chips) {
psg.setRate(phasorMode ? CLOCK_SPEED * 2 : CLOCK_SPEED, SAMPLE_RATE);
psg.reset();
}
}
if (!isRunning()) {
setUnclocked(true);
if (chips == null) {
initPSG();
for (PSG psg : chips) {
psg.setRate(phasorMode ? CLOCK_SPEED * 2 : CLOCK_SPEED, SAMPLE_RATE);
psg.reset();
}
}
for (R6522 controller : controllers) {
controller.attach();
controller.resume();
@ -321,10 +290,6 @@ public class CardMockingboard extends Card implements Runnable {
controller.suspend();
controller.detach();
}
return suspendSound();
}
public boolean suspendSound() {
if (playbackThread == null || !playbackThread.isAlive()) {
return false;
}
@ -360,11 +325,11 @@ public class CardMockingboard extends Card implements Runnable {
int zeroSamples = 0;
setRun(true);
LockSupport.parkNanos(5000);
while (isRunning() && !Thread.interrupted()) {
while (isRunning()) {
while (isRunning() && !computer.isRunning()) {
Thread.sleep(1000);
Thread.currentThread().yield();
}
if (isRunning() && !Thread.interrupted()) {
if (isRunning()) {
playSound(leftBuffer, rightBuffer);
int p = 0;
for (int idx = 0; idx < BUFFER_LENGTH; idx++) {
@ -429,8 +394,6 @@ 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");

View File

@ -24,12 +24,11 @@ import jace.core.Card;
import jace.core.Computer;
import jace.core.RAMEvent;
import jace.core.RAMEvent.TYPE;
import jace.core.Utility;
import jace.state.Stateful;
import jace.core.Utility;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.scene.control.Label;
@ -63,10 +62,10 @@ public class CardRamFactor extends Card {
public String getDeviceName() {
return "RamFactor";
}
Optional<Label> indicator;
Label indicator;
public CardRamFactor(Computer computer) {
super(computer);
indicator = Utility.loadIconLabel("ram.png");
indicator=Utility.loadIconLabel("ram.png");
try {
loadRom("jace/data/RAMFactor14.rom");
} catch (IOException ex) {
@ -205,9 +204,7 @@ public class CardRamFactor extends Card {
@Override
public void setSlot(int slot) {
super.setSlot(slot);
indicator.ifPresent(icon->
icon.setText("Slot "+getSlot())
);
indicator.setText("Slot "+getSlot());
// Rom has different images for each slot
updateFirmwareMemory();
}

View File

@ -116,10 +116,8 @@ public class CardSSC extends Card implements Reconfigurable {
Logger.getLogger(CardSSC.class.getName()).log(Level.SEVERE, null, ex);
}
super.setSlot(slot);
Utility.loadIconLabel("network-wired.png").ifPresent(icon->{
activityIndicator = icon;
activityIndicator.setText("Slot " + slot);
});
activityIndicator = Utility.loadIconLabel("network-wired.png");
activityIndicator.setText("Slot " + slot);
}
boolean newInputAvailable = false;

View File

@ -32,7 +32,6 @@ import jace.core.Utility;
import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
import java.util.Optional;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -51,7 +50,8 @@ import javafx.scene.control.Label;
@Name("ThunderClock Plus")
public class CardThunderclock extends Card {
Optional<Label> clockIcon;
Label clockIcon;
Label clockFixIcon;
long lastShownIcon = -1;
// Only mention that the clock is read if it hasn't been checked for over 30 seconds
// This is to avoid showing it all the time in programs that poll it constantly
@ -152,14 +152,12 @@ public class CardThunderclock extends Card {
performProdosPatch(computer);
}
getTime();
clockIcon.ifPresent(icon->{
icon.setText("Slot " + getSlot());
long now = System.currentTimeMillis();
if ((now - lastShownIcon) > MIN_WAIT) {
EmulatorUILogic.addIndicator(this, icon, 3000);
}
lastShownIcon = now;
});
clockIcon.setText("Slot " + getSlot());
long now = System.currentTimeMillis();
if ((now - lastShownIcon) > MIN_WAIT) {
EmulatorUILogic.addIndicator(this, clockIcon, 3000);
}
lastShownIcon = now;
}
shiftMode = isShift;
}
@ -326,9 +324,8 @@ public class CardThunderclock extends Card {
ram.writeByte(patchLoc + 1, (byte) year);
ram.writeByte(patchLoc + 2, (byte) MOS65C02.OPCODE.NOP.getCode());
ram.writeByte(patchLoc + 3, (byte) MOS65C02.OPCODE.NOP.getCode());
Utility.loadIconLabel("clock_fix.png").ifPresent(clockFixIcon->{
clockFixIcon.setText("Fixed");
EmulatorUILogic.addIndicator(CardThunderclock.class, clockFixIcon, 4000);
});
Label clockFixIcon = Utility.loadIconLabel("clock_fix.png");
clockFixIcon.setText("Fixed");
EmulatorUILogic.addIndicator(CardThunderclock.class, clockFixIcon, 4000);
}
}

View File

@ -18,7 +18,6 @@
*/
package jace.hardware;
import jace.EmulatorUILogic;
import jace.core.Computer;
import jace.library.MediaConsumer;
import jace.library.MediaEntry;
@ -28,7 +27,6 @@ import jace.state.Stateful;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.locks.LockSupport;
import javafx.scene.control.Label;
@ -227,40 +225,22 @@ public class DiskIIDrive implements MediaConsumer {
void insertDisk(File diskPath) throws IOException {
disk = new FloppyDisk(diskPath, computer);
System.out.println("Inserting "+diskPath.getPath()+" into "+getIcon().getText());
dirtyTracks = new HashSet<>();
// Emulator state has changed significantly, reset state manager
StateManager.getInstance(computer).invalidate();
}
private Optional<Label> icon;
private Label icon;
@Override
public Optional<Label> getIcon() {
public Label getIcon() {
return icon;
}
@Override
public void setIcon(Optional<Label> i) {
public void setIcon(Label i) {
icon = i;
}
// Optionals make some things easier, but they slow down things considerably when called a lot
// This reduces the number of Optional checks when rapidly accessing the disk drive.
long lastAdded = 0;
public void addIndicator() {
long now = System.currentTimeMillis();
if (lastAdded == 0 || now - lastAdded >= 500) {
EmulatorUILogic.addIndicator(this, icon.get());
lastAdded = now;
}
}
public void removeIndicator() {
if (lastAdded > 0) {
EmulatorUILogic.removeIndicator(this, icon.get());
lastAdded = 0;
}
}
private MediaEntry currentMediaEntry;
private MediaFile currentMediaFile;

View File

@ -9,7 +9,6 @@ import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.core.Utility;
import java.util.Calendar;
import java.util.Optional;
import javafx.scene.control.Label;
/**
@ -29,7 +28,7 @@ public class NoSlotClock extends Device {
public boolean writeEnabled = false;
@ConfigurableField(category = "Clock", name = "Patch Prodos", description = "If enabled, prodos clock routines will be patched directly", defaultValue = "false")
public boolean patchProdosClock = false;
Optional<Label> clockIcon;
Label clockIcon;
private final RAMListener listener = new RAMListener(RAMEvent.TYPE.ANY, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
@Override
@ -90,7 +89,7 @@ public class NoSlotClock extends Device {
public NoSlotClock(Computer computer) {
super(computer);
this.clockIcon = Utility.loadIconLabel("clock.png");
this.clockIcon.ifPresent(icon -> icon.setText("No Slot Clock"));
this.clockIcon.setText("No Slot Clock");
}
@Override
@ -134,8 +133,7 @@ public class NoSlotClock extends Device {
storeBCD(now.get(Calendar.MONTH) + 1, 6);
storeBCD(now.get(Calendar.YEAR) % 100, 7);
clockActive = true;
clockIcon.ifPresent(icon
-> EmulatorUILogic.addIndicator(this, icon, 1000));
EmulatorUILogic.addIndicator(this, clockIcon, 1000);
if (patchProdosClock) {
CardThunderclock.performProdosPatch(computer);
}

View File

@ -18,21 +18,17 @@
*/
package jace.hardware;
import jace.config.ConfigurableField;
import jace.config.DynamicSelection;
import jace.config.Name;
import jace.core.Card;
import jace.core.Computer;
import jace.core.RAMEvent;
import jace.core.RAMEvent.TYPE;
import java.util.LinkedHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Synthesizer;
@ -47,8 +43,6 @@ import javax.sound.midi.Synthesizer;
@Name(value = "Passport Midi Interface", description = "MIDI sound card")
public class PassportMidiInterface extends Card {
private Receiver midiOut;
public PassportMidiInterface(Computer computer) {
super(computer);
}
@ -63,31 +57,6 @@ public class PassportMidiInterface extends Card {
CONTINUOUS, SINGLE_SHOT, FREQ_COMPARISON, PULSE_COMPARISON
};
@ConfigurableField(name = "Midi Output Device", description = "Midi output device")
public static DynamicSelection<String> preferredMidiDevice = new DynamicSelection<String>(null) {
@Override
public boolean allowNull() {
return false;
}
@Override
public LinkedHashMap<? extends String, String> getSelections() {
LinkedHashMap<String, String> out = new LinkedHashMap<>();
MidiDevice.Info[] devices = MidiSystem.getMidiDeviceInfo();
for (MidiDevice.Info dev : devices) {
try {
MidiDevice device = MidiSystem.getMidiDevice(dev);
if (device.getMaxReceivers() > 0 || dev instanceof Synthesizer)
System.out.println("MIDI Device found: " + dev);
out.put(dev.getName(), dev.getName());
} catch (MidiUnavailableException ex) {
Logger.getLogger(PassportMidiInterface.class.getName()).log(Level.SEVERE, null, ex);
}
}
return out;
}
};
public static class PTMTimer {
// Configuration values
@ -199,6 +168,8 @@ public class PassportMidiInterface extends Card {
private final boolean receiverACIAOverrun = false;
// True if ACIA generated interrupt request
private final boolean irqRequestedACIA = false;
//--- the synth
private Synthesizer synth;
@Override
public void reset() {
@ -505,13 +476,13 @@ public class PassportMidiInterface extends Card {
// If we have a command to send, then do it
if (sendMessage == true) {
if (midiOut != null) {
if (synth != null && synth.isOpen()) {
// Send message
try {
// System.out.println("Sending MIDI message "+currentMessageStatus+","+currentMessageData1+","+currentMessageData2);
currentMessage.setMessage(currentMessageStatus, currentMessageData1, currentMessageData2);
midiOut.send(currentMessage, -1L);
} catch (InvalidMidiDataException ex) {
synth.getReceiver().send(currentMessage, -1L);
} catch (InvalidMidiDataException | MidiUnavailableException ex) {
Logger.getLogger(PassportMidiInterface.class.getName()).log(Level.SEVERE, null, ex);
}
}
@ -538,32 +509,30 @@ public class PassportMidiInterface extends Card {
@Override
public void resume() {
if (isRunning() && midiOut != null) {
if (isRunning() && synth != null && synth.isOpen()) {
return;
}
try {
MidiDevice selectedDevice = MidiSystem.getSynthesizer();
MidiDevice.Info[] devices = MidiSystem.getMidiDeviceInfo();
if (devices.length == 0) {
System.out.println("No MIDI devices found");
} else {
for (MidiDevice.Info dev : devices) {
if (MidiSystem.getMidiDevice(dev).getMaxReceivers() == 0) {
continue;
}
System.out.println("MIDI Device found: " + dev);
if ((preferredMidiDevice.getValue() == null && dev.getName().contains("Java Sound") && dev instanceof Synthesizer) ||
preferredMidiDevice.getValue().equalsIgnoreCase(dev.getName())
) {
selectedDevice = MidiSystem.getMidiDevice(dev);
break;
if (dev.getName().contains("Java Sound")) {
if (dev instanceof Synthesizer) {
synth = (Synthesizer) dev;
break;
}
}
}
}
if (selectedDevice != null) {
System.out.println("Selected MIDI device: " + selectedDevice.getDeviceInfo().getName());
selectedDevice.open();
midiOut = selectedDevice.getReceiver();
if (synth == null) {
synth = MidiSystem.getSynthesizer();
}
if (synth != null) {
System.out.println("Selected MIDI device: " + synth.getDeviceInfo().getName());
synth.open();
super.resume();
}
} catch (MidiUnavailableException ex) {
@ -574,23 +543,9 @@ public class PassportMidiInterface extends Card {
private void suspendACIA() {
// TODO: Stop ACIA thread...
if (midiOut != null) {
currentMessage = new ShortMessage();
// Send a note-off on every channel
for (int channel = 0; channel < 16; channel++) {
try {
// All Notes Off
currentMessage.setMessage(0x0B0 | channel, 123, 0);
midiOut.send(currentMessage, 0);
// All Oscillators Off
currentMessage.setMessage(0x0B0 | channel, 120, 0);
midiOut.send(currentMessage, 0);
} catch (InvalidMidiDataException ex) {
Logger.getLogger(PassportMidiInterface.class.getName()).log(Level.SEVERE, null, ex);
}
}
midiOut.close();
midiOut = null;
if (synth != null && synth.isOpen()) {
synth.close();
synth = null;
}
}
}

View File

@ -1,227 +0,0 @@
/*
* Copyright 2018 org.badvision.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jace.hardware;
import jace.Emulator;
import jace.config.ConfigurableField;
import jace.core.Computer;
import jace.core.Device;
import jace.core.Motherboard;
import jace.core.RAMEvent;
import jace.core.RAMListener;
/**
* Implements a basic hardware accelerator that is able to adjust the speed of the emulator
*/
public class ZipWarpAccelerator extends Device {
@ConfigurableField(category = "debug", name = "Debug messages")
public boolean debugMessagesEnabled = false;
public static final int ENABLE_ADDR = 0x0c05a;
public static final int MAX_SPEED = 0x0c05b;
public static final int REGISTERS = 0x0c05c;
public static final int SET_SPEED = 0x0c05d;
public static final int UNLOCK_VAL = 0x05A;
public static final int LOCK_VAL = 0x0A5;
public static final double UNLOCK_PENALTY_PER_TICK = 0.19;
public static final double UNLOCK_MIN = 4.0;
public static final int TRANSWARP = 0x0c074;
public static final int TRANSWARP_ON = 1; // Any other value written disables acceleration
boolean zipLocked = true;
double zipUnlockCount = 0;
int zipRegisters = 0;
int speedValue = 0;
RAMListener zipListener;
RAMListener transwarpListener;
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);
}
private void handleZipChipEvent(RAMEvent e) {
boolean isWrite = e.getType() == RAMEvent.TYPE.WRITE;
if (ENABLE_ADDR == e.getAddress() && isWrite) {
if (e.getNewValue() == UNLOCK_VAL) {
zipUnlockCount = Math.ceil(zipUnlockCount) + 1.0;
if (debugMessagesEnabled) {
System.out.println("Unlock sequence detected, new lock value at " + zipUnlockCount + " of " + UNLOCK_MIN);
}
if (zipUnlockCount >= UNLOCK_MIN) {
zipLocked = false;
if (debugMessagesEnabled) {
System.out.println("Zip unlocked!");
}
}
} else {
zipLocked = true;
if (debugMessagesEnabled) {
System.out.println("Zip locked!");
}
zipUnlockCount = 0;
if ((e.getNewValue() & 0x0ff) != LOCK_VAL) {
if (debugMessagesEnabled) {
System.out.println("Warp disabled.");
}
turnOffAcceleration();
}
}
} else if (!zipLocked && isWrite) {
switch (e.getAddress()) {
case MAX_SPEED:
setSpeed(SPEED.MAX);
if (debugMessagesEnabled) {
System.out.println("MAXIMUM WARP!");
}
break;
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;
}
} else if (!zipLocked && e.getAddress() == REGISTERS) {
e.setNewValue(zipRegisters);
}
}
private void handleTranswarpEvent(RAMEvent e) {
if (e.getType().isRead()) {
e.setNewValue(speedValue);
} else {
if (e.getNewValue() == TRANSWARP_ON) {
setSpeed(SPEED.MAX);
if (debugMessagesEnabled) {
System.out.println("MAXIMUM WARP!");
}
} else {
turnOffAcceleration();
if (debugMessagesEnabled) {
System.out.println("Warp disabled.");
}
}
}
}
@Override
protected String getDeviceName() {
return "ZipChip Accelerator";
}
public static enum SPEED {
MAX(4.0, 0b000000000, 0b011111100),
_2_667(2.6667, 0b000000100, 0b011111100),
_3(3.0, 0b000001000, 0b011111000),
_3_2(3.2, 0b000010000, 0b011110000),
_3_333(3.333, 0b000100000, 0b011100000),
_2(2.0, 0b001000000, 0b011111100),
_1_333(1.333, 0b001000100, 0b011111100),
_1_5(1.5, 0b001001000, 0b011111000),
_1_6(1.6, 0b001010000, 0b011110000),
_1_667(1.6667, 0b001100000, 0b011100000),
_1b(1.0, 0b010000000, 0b011111100),
_0_667(0.6667, 0b010000100, 0b011111100),
_0_75(0.75, 0b010001000, 0b011111000),
_0_8(0.8, 0b010010000, 0b011110000),
_0_833(0.833, 0b010100000, 0b011100000),
_1_33b(1.333, 0b011000000, 0b011111100),
_0_889(0.8889, 0b011000100, 0b011111100),
_1(1.0, 0b011001000, 0b011111000),
_1_067(1.0667, 0b011010000, 0b011110000),
_1_111(1.111, 0b011100000, 0b011100000);
double ratio;
int val;
int mask;
boolean max;
SPEED(double speed, int val, int mask) {
this.ratio = speed;
this.val = val;
this.mask = mask;
this.max = speed >= 4.0;
}
}
private SPEED lookupSpeedSetting(int v) {
for (SPEED s : SPEED.values()) {
if ((v & s.mask) == s.val) {
return s;
}
}
return SPEED._1;
}
private void setSpeed(SPEED speed) {
speedValue = speed.val;
if (speed.max) {
Emulator.computer.getMotherboard().setMaxSpeed(true);
Motherboard.cpuPerClock = 3;
} else {
Emulator.computer.getMotherboard().setMaxSpeed(false);
Emulator.computer.getMotherboard().setSpeedInPercentage((int) (speed.ratio * 100));
Motherboard.cpuPerClock = 1;
}
Emulator.computer.getMotherboard().reconfigure();
}
private void turnOffAcceleration() {
// The UI Logic retains the user's desired normal speed, reset to that
Emulator.logic.reconfigure();
}
@Override
public void tick() {
if (zipUnlockCount > 0) {
zipUnlockCount -= UNLOCK_PENALTY_PER_TICK;
}
}
@Override
public void attach() {
computer.memory.addListener(zipListener);
computer.memory.addListener(transwarpListener);
}
@Override
public void detach() {
super.detach();
computer.memory.removeListener(zipListener);
computer.memory.removeListener(transwarpListener);
}
@Override
public String getShortName() {
return "zip";
}
@Override
public void reconfigure() {
zipUnlockCount = 0;
zipLocked = true;
}
}

View File

@ -20,7 +20,6 @@ package jace.hardware.massStorage;
import jace.EmulatorUILogic;
import jace.apple2e.MOS65C02;
import jace.config.ConfigurableField;
import jace.config.Name;
import jace.core.Card;
import jace.core.Computer;
@ -29,11 +28,8 @@ import jace.core.RAMEvent.TYPE;
import jace.core.Utility;
import jace.hardware.ProdosDriver;
import jace.hardware.SmartportDriver;
import jace.library.MediaCache;
import jace.library.MediaConsumer;
import jace.library.MediaConsumerParent;
import jace.library.MediaEntry;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -47,10 +43,6 @@ import java.util.logging.Logger;
@Name("Mass Storage Device")
public class CardMassStorage extends Card implements MediaConsumerParent {
@ConfigurableField(category = "Disk", defaultValue = "", shortName = "d1", name = "Drive 1 disk image", description = "Path of disk 1")
public String disk1;
@ConfigurableField(category = "Disk", defaultValue = "", shortName = "d2", name = "Drive 2 disk image", description = "Path of disk 2")
public String disk2;
MassStorageDrive drive1;
MassStorageDrive drive2;
@ -67,8 +59,8 @@ public class CardMassStorage extends Card implements MediaConsumerParent {
@Override
public void setSlot(int slot) {
super.setSlot(slot);
drive1.getIcon().ifPresent(icon -> icon.setText("S" + getSlot() + "D1"));
drive2.getIcon().ifPresent(icon -> icon.setText("S" + getSlot() + "D2"));
drive1.getIcon().setText("S" + getSlot() + "D1");
drive2.getIcon().setText("S" + getSlot() + "D2");
}
@Override
@ -136,28 +128,10 @@ public class CardMassStorage extends Card implements MediaConsumerParent {
@Override
public void reconfigure() {
unregisterListeners();
if (disk1 != null && !disk1.isEmpty()) {
try {
MediaEntry entry = MediaCache.getMediaFromFile(new File(disk1));
disk1 = null;
drive1.insertMedia(entry, entry.files.get(0));
} catch (IOException ex) {
Logger.getLogger(CardMassStorage.class.getName()).log(Level.SEVERE, null, ex);
}
}
if (disk2 != null && !disk2.isEmpty()) {
try {
MediaEntry entry = MediaCache.getMediaFromFile(new File(disk2));
disk2 = null;
drive2.insertMedia(entry, entry.files.get(0));
} catch (IOException ex) {
Logger.getLogger(CardMassStorage.class.getName()).log(Level.SEVERE, null, ex);
}
}
if (computer.getCpu() != null) {
int pc = computer.getCpu().getProgramCounter();
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
// 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
currentDrive = drive1;
EmulatorUILogic.simulateCtrlAppleReset();

View File

@ -23,7 +23,6 @@ import jace.library.MediaEntry;
import jace.library.MediaEntry.MediaFile;
import java.io.File;
import java.io.IOException;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.scene.control.Label;
@ -34,10 +33,10 @@ import javafx.scene.control.Label;
*/
public class MassStorageDrive implements MediaConsumer {
IDisk disk = null;
Optional<Label> icon = null;
Label icon = null;
@Override
public Optional<Label> getIcon() {
public Label getIcon() {
return icon;
}
@ -46,7 +45,7 @@ public class MassStorageDrive implements MediaConsumer {
* @param i
*/
@Override
public void setIcon(Optional<Label> i) {
public void setIcon(Label i) {
icon = i;
}

View File

@ -0,0 +1,638 @@
/*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package jace.hardware.mockingboard;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jace.hardware.CardMockingboard;
// Port of AY code from AppleWin -- not used buy kept for reference.
/***************************************************************************
*
* ay8910.c
*
*
* Emulation of the AY-3-8910 / YM2149 sound chip.
*
* Based on various code snippets by Ville Hallik, Michael Cuddy,
* Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria.
*
***************************************************************************/
//
// From mame.txt (http://www.mame.net/readme.html)
//
// VI. Reuse of Source Code
// --------------------------
// This chapter might not apply to specific portions of MAME (e.g. CPU
// emulators) which bear different copyright notices.
// The source code cannot be used in a commercial product without the written
// authorization of the authors. Use in non-commercial products is allowed, and
// indeed encouraged. If you use portions of the MAME source code in your
// program, however, you must make the full source code freely available as
// well.
// Usage of the _information_ contained in the source code is free for any use.
// However, given the amount of time and energy it took to collect this
// information, if you find new information we would appreciate if you made it
// freely available as well.
//
public class AY8910_old {
static final int MAX_OUTPUT = 0x007fff;
static final int MAX_AY8910 = 2;
static final int CLOCK = 1789770;
static final int SAMPLE_RATE = 44100;
// See AY8910_set_clock() for definition of STEP
static final int STEP = 0x008000;
static int num = 0, ym_num = 0;
int SampleRate = 0;
/* register id's */
public enum Reg {
AFine(0, 255),
ACoarse(1, 15),
BFine(2, 255),
BCoarse(3, 15),
CFine(4, 255),
CCoarse(5, 15),
NoisePeriod(6, 31),
Enable(7, 255),
AVol(8, 31),
BVol(9, 31),
CVol(10, 31),
EnvFine(11, 255),
EnvCoarse(12, 255),
EnvShape(13, 15),
PortA(14, 255),
PortB(15, 255);
public final int registerNumber;
public final int max;
Reg(int number, int maxValue) {
registerNumber = number;
max=maxValue;
}
static Reg get(int number) {
for (Reg r:Reg.values())
if (r.registerNumber == number) return r;
return null;
}
static public Reg[] preferredOrder = new Reg[]{
Enable,EnvShape,EnvCoarse,EnvFine,NoisePeriod,AVol,BVol,CVol,
AFine,ACoarse,BFine,BCoarse,CFine,CCoarse};
}
public AY8910_old() {
chips = new ArrayList<PSG>();
for (int i=0; i < MAX_AY8910; i++) {
PSG chip = new PSG();
chips.add(chip);
}
initAll(CLOCK, SAMPLE_RATE);
}
///////////////////////////////////////////////////////////
private List<PSG> chips;
static int[] VolTable;
static {
buildMixerTable();
}
private class PSG {
int Channel;
int register_latch;
Map<Reg, Integer> registers;
int lastEnable;
int UpdateStep;
int PeriodA,PeriodB,PeriodC,PeriodN,PeriodE;
int CountA,CountB,CountC,CountN,CountE;
int VolA,VolB,VolC,VolE;
int EnvelopeA,EnvelopeB,EnvelopeC;
int OutputA,OutputB,OutputC,OutputN;
int CountEnv;
int Hold,Alternate,Attack,Holding;
int RNG;
public PSG() {
registers = new HashMap<Reg, Integer>();
for (Reg r: Reg.values())
setReg(r, 0);
}
public void reset() {
register_latch = 0;
RNG = 1;
OutputA = 0;
OutputB = 0;
OutputC = 0;
OutputN = 0x00ff;
lastEnable = -1; /* force a write */
for (Reg r: Reg.values())
writeReg(r, 0);
}
public void setClock(int clock) {
/* the step clock for the tone and noise generators is the chip clock */
/* divided by 8; for the envelope generator of the AY-3-8910, it is half */
/* that much (clock/16), but the envelope of the YM2149 goes twice as */
/* fast, therefore again clock/8. */
/* Here we calculate the number of steps which happen during one sample */
/* at the given sample rate. No. of events = sample rate / (clock/8). */
/* STEP is a multiplier used to turn the fraction into a fixed point */
/* number. */
double clk = clock;
double smprate = SampleRate;
UpdateStep = (int) ((STEP * smprate * 8.0 + clk/2.0) / clk);
}
public void setReg(Reg r, int value) {
registers.put(r,value);
}
public int getReg(Reg r) {
return registers.get(r);
}
public void writeReg(Reg r, int value) {
value &= r.max;
setReg(r, value);
int old;
/* A note about the period of tones, noise and envelope: for speed reasons,*/
/* we count down from the period to 0, but careful studies of the chip */
/* output prove that it instead counts up from 0 until the counter becomes */
/* greater or equal to the period. This is an important difference when the*/
/* program is rapidly changing the period to modulate the sound. */
/* To compensate for the difference, when the period is changed we adjust */
/* our internal counter. */
/* Also, note that period = 0 is the same as period = 1. This is mentioned */
/* in the YM2203 data sheets. However, this does NOT apply to the Envelope */
/* period. In that case, period = 0 is half as period = 1. */
switch(r) {
case ACoarse:
case AFine:
old = PeriodA;
PeriodA = (getReg(Reg.AFine) + 256 * getReg(Reg.ACoarse)) * UpdateStep;
if (PeriodA == 0) PeriodA = UpdateStep;
CountA += PeriodA - old;
if (CountA <= 0) CountA = 1;
break;
case BCoarse:
case BFine:
old = PeriodB;
PeriodB = (getReg(Reg.BFine) + 256 * getReg(Reg.BCoarse)) * UpdateStep;
if (PeriodB == 0) PeriodB = UpdateStep;
CountB += PeriodB - old;
if (CountB <= 0) CountB = 1;
break;
case CCoarse:
case CFine:
setReg(Reg.CCoarse, getReg(Reg.CCoarse) & 0x0f);
old = PeriodC;
PeriodA = (getReg(Reg.CFine) + 256 * getReg(Reg.CCoarse)) * UpdateStep;
if (PeriodC == 0) PeriodC = UpdateStep;
CountC += PeriodC - old;
if (CountC <= 0) CountC = 1;
break;
case NoisePeriod:
old = PeriodN;
PeriodN = getReg(Reg.NoisePeriod) * UpdateStep;
if (PeriodN == 0) PeriodN = UpdateStep;
CountN += PeriodN - old;
if (CountN <= 0) CountN = 1;
break;
case Enable:
lastEnable = value;
break;
case AVol:
EnvelopeA = value & 0x10;
if (EnvelopeA > 0)
VolA = VolE;
else {
if (value > 0)
VolA = CardMockingboard.VolTable[value];
else
VolA = CardMockingboard.VolTable[0];
}
break;
case BVol:
EnvelopeB = value & 0x10;
if (EnvelopeB > 0)
VolB = VolE;
else {
if (value > 0)
VolB = CardMockingboard.VolTable[value];
else
VolB = CardMockingboard.VolTable[0];
}
break;
case CVol:
EnvelopeC = value & 0x10;
if (EnvelopeC > 0)
VolC = VolE;
else {
if (value > 0)
VolC = CardMockingboard.VolTable[value];
else
VolC = CardMockingboard.VolTable[0];
}
break;
case EnvFine:
case EnvCoarse:
old = PeriodE;
PeriodE = ((getReg(Reg.EnvFine) + 256 * getReg(Reg.EnvCoarse))) * UpdateStep;
if (PeriodE == 0) PeriodE = UpdateStep / 2;
CountE += PeriodE - old;
if (CountE <= 0) CountE = 1;
if (PeriodE <= 0) PeriodE = 1;
break;
case EnvShape:
/* envelope shapes:
C AtAlH
0 0 x x \___
0 1 x x /|__
1 0 0 0 \\\\
1 0 0 1 \___
1 0 1 0 \/\/
__
1 0 1 1 \|
1 1 0 0 ////
___
1 1 0 1 /
1 1 1 0 /\/\
1 1 1 1 /|__
The envelope counter on the AY-3-8910 has 16 steps. On the YM2149 it
has twice the steps, happening twice as fast. Since the end result is
just a smoother curve, we always use the YM2149 behaviour.
*/
Attack = (value & 0x04) != 0 ? 0x1f : 0x00;
if ( (value & 0x08) == 0) {
/* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */
Hold = 1;
Alternate = Attack;
} else {
Hold = value & 0x01;
Alternate = value & 0x02;
}
CountE = PeriodE;
CountEnv = 0x1f;
Holding = 0;
VolE = CardMockingboard.VolTable[CountEnv ^ Attack];
if (EnvelopeA != 0) VolA = VolE;
if (EnvelopeB != 0) VolB = VolE;
if (EnvelopeC != 0) VolC = VolE;
break;
case PortA:
case PortB:
break;
}
}
void update(int[][] buffer, int length) {
int[] buf1, buf2, buf3;
int outn;
buf1 = buffer[0];
buf2 = buffer[1];
buf3 = buffer[2];
/* The 8910 has three outputs, each output is the mix of one of the three */
/* tone generators and of the (single) noise generator. The two are mixed */
/* BEFORE going into the DAC. The formula to mix each channel is: */
/* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */
/* Note that this means that if both tone and noise are disabled, the output */
/* is 1, not 0, and can be modulated changing the volume. */
/* If the channels are disabled, set their output to 1, and increase the */
/* counter, if necessary, so they will not be inverted during this update. */
/* Setting the output to 1 is necessary because a disabled channel is locked */
/* into the ON state (see above); and it has no effect if the volume is 0. */
/* If the volume is 0, increase the counter, but don't touch the output. */
if ( (getReg(Reg.Enable) & 0x01) != 0) {
if (CountA <= length*STEP) CountA += length*STEP;
OutputA = 1;
} else if (getReg(Reg.AVol) == 0) {
/* note that I do count += length, NOT count = length + 1. You might think */
/* it's the same since the volume is 0, but doing the latter could cause */
/* interferencies when the program is rapidly modulating the volume. */
if (CountA <= length*STEP) CountA += length*STEP;
}
if ( (getReg(Reg.Enable) & 0x02) != 0) {
if (CountB <= length*STEP) CountB += length*STEP;
OutputB = 1;
} else if (getReg(Reg.BVol) == 0) {
if (CountB <= length*STEP) CountB += length*STEP;
}
if ( (getReg(Reg.Enable) & 0x04) != 0) {
if (CountC <= length*STEP) CountC += length*STEP;
OutputC = 1;
} else if (getReg(Reg.CVol) == 0) {
if (CountC <= length*STEP) CountC += length*STEP;
}
/* for the noise channel we must not touch OutputN - it's also not necessary */
/* since we use outn. */
if ((getReg(Reg.Enable) & 0x38) == 0x38) /* all off */
if (CountN <= length*STEP) CountN += length*STEP;
outn = (OutputN | getReg(Reg.Enable));
int index = 0;
//System.out.println("Length:"+length);
/* buffering loop */
while (length != 0) {
int vola,volb,volc;
int left;
/* vola, volb and volc keep track of how long each square wave stays */
/* in the 1 position during the sample period. */
vola = volb = volc = 0;
//System.out.println("STEP:"+STEP);
left = STEP;
do {
int nextevent;
if (CountN < left) nextevent = CountN;
else nextevent = left;
if ( (outn & 0x08) != 0) {
if (OutputA != 0) vola += CountA;
CountA -= nextevent;
/* PeriodA is the half period of the square wave. Here, in each */
/* loop I add PeriodA twice, so that at the end of the loop the */
/* square wave is in the same status (0 or 1) it was at the start. */
/* vola is also incremented by PeriodA, since the wave has been 1 */
/* exactly half of the time, regardless of the initial position. */
/* If we exit the loop in the middle, OutputA has to be inverted */
/* and vola incremented only if the exit status of the square */
/* wave is 1. */
while (CountA <= 0 && PeriodA > 0) {
CountA += PeriodA;
if (CountA > 0) {
OutputA ^= 1;
if (OutputA != 0) vola += PeriodA;
break;
}
CountA += PeriodA;
vola += PeriodA;
}
if (OutputA != 0) vola -= CountA;
} else {
CountA -= nextevent;
while (CountA <= 0 && PeriodA > 0) {
CountA += PeriodA;
if (CountA > 0) {
OutputA ^= 1;
break;
}
CountA += PeriodA;
}
}
if ((outn & 0x10) != 0) {
if (OutputB != 0) volb += CountB;
CountB -= nextevent;
while (CountB <= 0 && PeriodB > 0) {
CountB += PeriodB;
if (CountB > 0) {
OutputB ^= 1;
if (OutputB != 0) volb += PeriodB;
break;
}
CountB += PeriodB;
volb += PeriodB;
}
if (OutputB != 0) volb -= CountB;
} else {
CountB -= nextevent;
while (CountB <= 0 && PeriodB > 0) {
CountB += PeriodB;
if (CountB > 0) {
OutputB ^= 1;
break;
}
CountB += PeriodB;
}
}
if ( (outn & 0x20) != 0) {
if (OutputC != 0) volc += CountC;
CountC -= nextevent;
while (CountC <= 0 && PeriodC > 0) {
CountC += PeriodC;
if (CountC > 0) {
OutputC ^= 1;
if (OutputC != 0) volc += PeriodC;
break;
}
CountC += PeriodC;
volc += PeriodC;
}
if (OutputC != 0) volc -= CountC;
} else {
CountC -= nextevent;
while (CountC <= 0 && PeriodC > 0) {
CountC += PeriodC;
if (CountC > 0) {
OutputC ^= 1;
break;
}
CountC += PeriodC;
}
}
CountN -= nextevent;
if (CountN <= 0 && PeriodN > 0) {
/* Is noise output going to change? */
/* (bit0^bit1)? */
if (((RNG + 1) & 2) != 0) {
OutputN ^= 0x0FF;
outn = (OutputN | getReg(Reg.Enable));
}
/* The Random Number Generator of the 8910 is a 17-bit shift */
/* register. The input to the shift register is bit0 XOR bit3 */
/* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */
/* The following is a fast way to compute bit17 = bit0^bit3. */
/* Instead of doing all the logic operations, we only check */
/* bit0, relying on the fact that after three shifts of the */
/* register, what now is bit3 will become bit0, and will */
/* invert, if necessary, bit14, which previously was bit17. */
if ((RNG & 1) != 0) RNG ^= 0x0024000; /* This version is called the "Galois configuration". */
RNG >>= 1;
CountN += PeriodN;
}
left -= nextevent;
} while (left > 0);
// System.out.println("End left loop");
/* update envelope */
if (Holding == 0) {
CountE -= STEP;
if (CountE <= 0) {
do {
CountEnv--;
CountE += PeriodE;
} while (CountE <= 0);
/* check envelope current position */
if (CountEnv < 0) {
if (Hold != 0) {
if (Alternate != 0)
Attack ^= 0x1f;
Holding = 1;
CountEnv = 0;
} else {
/* if CountEnv has looped an odd number of times (usually 1), */
/* invert the output. */
if ( (Alternate != 0) && ((CountEnv & 0x20) != 0))
Attack ^= 0x1f;
CountEnv &= 0x1f;
}
}
VolE = VolTable[CountEnv ^ Attack];
/* reload volume */
if (EnvelopeA != 0) VolA = VolE;
if (EnvelopeB != 0) VolB = VolE;
if (EnvelopeC != 0) VolC = VolE;
}
}
// Output PCM wave [-32768...32767] instead of MAME's voltage level [0...32767]
// - This allows for better s/w mixing
buf1[index] = (vola * VolA) / STEP;
buf2[index] = (volb * VolB) / STEP;
buf3[index] = (volc * VolC) / STEP;
/*
if(VolA != 0) {
if (vola != 0) buf1[index] = (vola * VolA) / STEP;
else buf1[index] = -VolA;
} else {
buf1[index] = 0;
}
//
if(VolB != 0) {
if (volb != 0) buf2[index] = (volb * VolB) / STEP;
else buf2[index] = -VolB;
} else
buf2[index] = 0;
//
if(VolC != 0) {
if (volc != 0) buf3[index] = (volc * VolC) / STEP;
else buf3[index] = -VolC;
} else
buf3[index] = 0;
*/
index++;
length--;
}
}
};
public void writeReg(int chipNumber, int register, int value) {
Reg r = Reg.get(register);
writeReg(chipNumber, r, value);
}
public void writeReg(int chipNumber, Reg register, int value) {
chips.get(chipNumber).writeReg(register, value);
}
// /length/ is the number of samples we require
// NB. This should be called at twice the 6522 IRQ rate or (eg) 60Hz if no IRQ.
public void update(int chipNumber,int[][] buffer,int length) {
chips.get(chipNumber).update(buffer, length);
}
int[][] buffers;
int bufferLength = -1;
public int[][] getBuffers(int length) {
if (buffers == null || bufferLength != length) {
buffers = new int[3][length];
bufferLength = length;
}
return buffers;
}
public void playSound(int size, int[] left, int[] right) {
int[][] buffers = getBuffers(left.length);
update(0, buffers, size);
mixDown(left, buffers, size);
update(1, buffers, size);
mixDown(right, buffers, size);
}
public void mixDown(int[] out, int[][] in, int size) {
for (int i=0; i < size; i++) {
int sample = (in[0][i] + in[1][i] + in[2][i]) / 3;
out[i] = sample;
}
}
public void setClock(int chipNumber,int clock) {
chips.get(chipNumber).setClock(clock);
}
public void reset(int chipNumber) {
chips.get(chipNumber).reset();
}
public void initAll(int nClock, int nSampleRate) {
SampleRate = nSampleRate;
for (PSG p:chips) {
p.setClock(nClock);
p.reset();
}
}
public void initClock(int nClock) {
for (PSG p:chips) p.setClock(nClock);
}
static void buildMixerTable() {
VolTable = new int[32];
int SampleRate;
/* calculate the volume->voltage conversion table */
/* The AY-3-8910 has 16 levels, in a logarithmic scale (3dB per step) */
/* The YM2149 still has 16 levels for the tone generators, but 32 for */
/* the envelope generator (1.5dB per step). */
double out = MAX_OUTPUT;
for (int i = 31;i > 0;i--) {
VolTable[i] = (int) (out + 0.5); /* round to nearest */ // [TC: unsigned int cast]
out /= 1.188502227; /* = 10 ^ (1.5/20) = 1.5dB */
}
VolTable[0] = 0;
}
}

View File

@ -1,365 +1,346 @@
/*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package jace.hardware.mockingboard;
import jace.core.Computer;
import jace.core.Device;
import jace.core.TimedDevice;
/**
* Implementation of 6522 VIA chip
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public abstract class R6522 extends TimedDevice {
public static long SPEED = 1020484L; // (NTSC)
public R6522(Computer computer) {
super(computer);
timer1freerun = true;
timer1running = true;
timer1latch = 0x1fff;
timer1interruptEnabled = false;
setSpeedInHz(SPEED);
setRun(true);
}
@Override
public long defaultCyclesPerSecond() {
return SPEED;
}
// 6522 VIA
// http://www.applevault.com/twiki/Main/Mockingboard/6522.pdf
// I/O registers
public static enum Register {
ORB(0), // Output Register B
ORA(1), // Output Register A
DDRB(2),// Data direction reg B
DDRA(3),// Data direction reg A
T1CL(4),// T1 low-order latches (low-order counter for read operations)
T1CH(5),// T1 high-order counter
T1LL(6),// T1 low-order latches
T1LH(7),// T1 high-order latches
T2CL(8),// T2 low-order latches (low-order counter for read operations)
T2CH(9),// T2 high-order counter
SR(10),// Shift register
ACR(11),// Aux control register
PCR(12),// Perripheral control register
IFR(13),// Interrupt flag register
IER(14),// Interrupt enable register
ORAH(15);// Output Register A (no handshake)
int val;
Register(int v) {
val = v;
}
static public Register fromInt(int i) {
for (Register r : Register.values()) {
if (r.val == i) {
return r;
}
}
return null;
}
}
// state variables
public int oraReg = 0;
public int iraReg = 0;
public int orbReg = 0;
public int irbReg = 0;
// DDRA and DDRB must be set to output for mockingboard to do anything
// Common values for this are FF for DDRA and 7 for DDRB
// DDRB bits 0-2 are used to control AY chips but bits 3-7 are not connected.
// that's why it is common to see mockingboard drivers init the port with a 7
public int dataDirectionA = 0;
public int dataDirectionB = 0;
// Though this is necessary for a complete emulation of the 6522, it isn't needed by the mockingboard
// set by bit 0 of ACR
// public boolean latchEnabledA = false;
// set by bit 1 of ACR
// public boolean latchEnabledB = false;
//Bits 2,3,4 of ACR
// static public enum ShiftRegisterControl {
// interruptDisabled(0),
// shiftInT2(4),
// shiftIn02(8),
// shiftInExt(12),
// shiftOutFree(16),
// shiftOutT2(20),
// shiftOut02(24),
// shiftOutExt(28);
//
// int val;
// private ShiftRegisterControl(int v) {
// val = v;
// }
//
// public static ShiftRegisterControl fromBits(int b) {
// b=b&28;
// for (ShiftRegisterControl s : values()) {
// if (s.val == b) return s;
// }
// return null;
// }
// }
// public ShiftRegisterControl shiftMode = ShiftRegisterControl.interruptDisabled;
// //Bit 5 of ACR (false = timed interrupt, true = count down pulses on PB6)
// public boolean t2countPulses = false;
// //Bit 6 of ACR (true = continuous, false = one-shot)
// public boolean t1continuous = false;
// //Bit 7 of ACR (true = enable PB7, false = interruptDisabled)
// public boolean t1enablePB7 = false;
// // NOTE: Mockingboard did not use PB6 or PB7, they are not connected to anything
public boolean timer1interruptEnabled = true;
public boolean timer1IRQ = false; // True if timer interrupt flag is set
public int timer1latch = 0;
public int timer1counter = 0;
public boolean timer1freerun = false;
public boolean timer1running = false;
public boolean timer2interruptEnabled = true;
public boolean timer2IRQ = false; // True if timer interrupt flag is set
public int timer2latch = 0;
public int timer2counter = 0;
public boolean timer2running = false;
public boolean unclocked = false;
@Override
protected String getDeviceName() {
return "6522 VIA Chip";
}
@Override
public void tick() {
if (!unclocked) {
doTick();
}
}
public void setUnclocked(boolean unclocked) {
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
}
@Override
public void reconfigure() {
// Reset
}
public void writeRegister(int reg, int val) {
int value = val & 0x0ff;
Register r = Register.fromInt(reg);
// System.out.println("Writing "+(value&0x0ff)+" to register "+r.toString());
switch (r) {
case ORB:
if (dataDirectionB == 0) {
break;
}
sendOutputB(value & dataDirectionB);
break;
case ORA:
// case ORAH:
if (dataDirectionA == 0) {
break;
}
sendOutputA(value & dataDirectionA);
break;
case DDRB:
dataDirectionB = value;
break;
case DDRA:
dataDirectionA = value;
break;
case T1CL:
case T1LL:
timer1latch = (timer1latch & 0x0ff00) | value;
break;
case T1CH:
timer1latch = (timer1latch & 0x0ff) | (value << 8);
timer1IRQ = false;
timer1counter = timer1latch;
timer1running = true;
setRun(true);
break;
case T1LH:
timer1latch = (timer1latch & 0x0ff) | (value << 8);
timer1IRQ = false;
break;
case T2CL:
timer2latch = (timer2latch & 0x0ff00) | value;
break;
case T2CH:
timer2latch = (timer2latch & 0x0ff) | (value << 8);
timer2IRQ = false;
timer2counter = timer2latch;
timer2running = true;
setRun(true);
break;
case SR:
// SHIFT REGISTER NOT IMPLEMENTED
break;
case ACR:
// SHIFT REGISTER NOT IMPLEMENTED
timer1freerun = (value & 64) != 0;
if (timer1freerun) {
timer1running = true;
setRun(true);
}
break;
case PCR:
// TODO: Implement if Votrax (SSI) is to be supported
break;
case IFR:
if ((value & 64) != 0) {
timer1IRQ = false;
}
if ((value & 32) != 0) {
timer2IRQ = false;
}
break;
case IER:
boolean enable = (value & 128) != 0;
if ((value & 64) != 0) {
timer1interruptEnabled = enable;
}
if ((value & 32) != 0) {
timer2interruptEnabled = enable;
}
break;
default:
}
}
// Whatever uses 6522 will want to know when it is outputting values
// So to hook that in, these abstract methods will be defined as appropriate
public abstract void sendOutputA(int value);
public abstract void sendOutputB(int value);
public int readRegister(int reg) {
Register r = Register.fromInt(reg);
// System.out.println("Reading register "+r.toString());
switch (r) {
case ORB:
if (dataDirectionB == 0x0ff) {
break;
}
return receiveOutputB() & (dataDirectionB ^ 0x0ff);
case ORA:
case ORAH:
if (dataDirectionA == 0x0ff) {
break;
}
return receiveOutputA() & (dataDirectionA ^ 0x0ff);
case DDRB:
return dataDirectionB;
case DDRA:
return dataDirectionA;
case T1CL:
timer1IRQ = false;
return timer1counter & 0x0ff;
case T1CH:
return (timer1counter & 0x0ff00) >> 8;
case T1LL:
return timer1latch & 0x0ff;
case T1LH:
return (timer1latch & 0x0ff00) >> 8;
case T2CL:
timer2IRQ = false;
return timer2counter & 0x0ff;
case T2CH:
return (timer2counter & 0x0ff00) >> 8;
case SR:
// SHIFT REGISTER NOT IMPLEMENTED
return 0;
case ACR:
// SHIFT REGISTER NOT IMPLEMENTED
if (timer1freerun) {
return 64;
}
return 0;
case PCR:
break;
case IFR:
int val = 0;
if (timer1IRQ) {
val |= 64;
}
if (timer2IRQ) {
val |= 32;
}
if (val != 0) {
val |= 128;
}
return val;
case IER:
val = 128;
if (timer1interruptEnabled) {
val |= 64;
}
if (timer2interruptEnabled) {
val |= 32;
}
return val;
}
return 0;
}
public abstract int receiveOutputA();
public abstract int receiveOutputB();
}
/*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package jace.hardware.mockingboard;
import jace.core.Computer;
import jace.core.Device;
/**
* Implementation of 6522 VIA chip
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public abstract class R6522 extends Device {
public R6522(Computer computer) {
super(computer);
timer1freerun = true;
timer1running = true;
timer1latch = 0x1fff;
timer1interruptEnabled = false;
setRun(true);
}
// 6522 VIA
// http://www.applevault.com/twiki/Main/Mockingboard/6522.pdf
// I/O registers
public static enum Register {
ORB(0), // Output Register B
ORA(1), // Output Register A
DDRB(2),// Data direction reg B
DDRA(3),// Data direction reg A
T1CL(4),// T1 low-order latches (low-order counter for read operations)
T1CH(5),// T1 high-order counter
T1LL(6),// T1 low-order latches
T1LH(7),// T1 high-order latches
T2CL(8),// T2 low-order latches (low-order counter for read operations)
T2CH(9),// T2 high-order counter
SR(10),// Shift register
ACR(11),// Aux control register
PCR(12),// Perripheral control register
IFR(13),// Interrupt flag register
IER(14),// Interrupt enable register
ORAH(15);// Output Register A (no handshake)
int val;
Register(int v) {
val = v;
}
static public Register fromInt(int i) {
for (Register r : Register.values()) {
if (r.val == i) {
return r;
}
}
return null;
}
}
// state variables
public int oraReg = 0;
public int iraReg = 0;
public int orbReg = 0;
public int irbReg = 0;
// DDRA and DDRB must be set to output for mockingboard to do anything
// Common values for this are FF for DDRA and 7 for DDRB
// DDRB bits 0-2 are used to control AY chips but bits 3-7 are not connected.
// that's why it is common to see mockingboard drivers init the port with a 7
public int dataDirectionA = 0;
public int dataDirectionB = 0;
// Though this is necessary for a complete emulation of the 6522, it isn't needed by the mockingboard
// set by bit 0 of ACR
// public boolean latchEnabledA = false;
// set by bit 1 of ACR
// public boolean latchEnabledB = false;
//Bits 2,3,4 of ACR
// static public enum ShiftRegisterControl {
// interruptDisabled(0),
// shiftInT2(4),
// shiftIn02(8),
// shiftInExt(12),
// shiftOutFree(16),
// shiftOutT2(20),
// shiftOut02(24),
// shiftOutExt(28);
//
// int val;
// private ShiftRegisterControl(int v) {
// val = v;
// }
//
// public static ShiftRegisterControl fromBits(int b) {
// b=b&28;
// for (ShiftRegisterControl s : values()) {
// if (s.val == b) return s;
// }
// return null;
// }
// }
// public ShiftRegisterControl shiftMode = ShiftRegisterControl.interruptDisabled;
// //Bit 5 of ACR (false = timed interrupt, true = count down pulses on PB6)
// public boolean t2countPulses = false;
// //Bit 6 of ACR (true = continuous, false = one-shot)
// public boolean t1continuous = false;
// //Bit 7 of ACR (true = enable PB7, false = interruptDisabled)
// public boolean t1enablePB7 = false;
// // NOTE: Mockingboard did not use PB6 or PB7, they are not connected to anything
public boolean timer1interruptEnabled = true;
public boolean timer1IRQ = false; // True if timer interrupt flag is set
public int timer1latch = 0;
public int timer1counter = 0;
public boolean timer1freerun = false;
public boolean timer1running = false;
public boolean timer2interruptEnabled = true;
public boolean timer2IRQ = false; // True if timer interrupt flag is set
public int timer2latch = 0;
public int timer2counter = 0;
public boolean timer2running = false;
@Override
protected String getDeviceName() {
return "6522 VIA Chip";
}
@Override
public void tick() {
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
}
@Override
public void reconfigure() {
// Reset
}
public void writeRegister(int reg, int val) {
int value = val & 0x0ff;
Register r = Register.fromInt(reg);
// System.out.println("Writing "+(value&0x0ff)+" to register "+r.toString());
switch (r) {
case ORB:
if (dataDirectionB == 0) {
break;
}
sendOutputB(value & dataDirectionB);
break;
case ORA:
// case ORAH:
if (dataDirectionA == 0) {
break;
}
sendOutputA(value & dataDirectionA);
break;
case DDRB:
dataDirectionB = value;
break;
case DDRA:
dataDirectionA = value;
break;
case T1CL:
case T1LL:
timer1latch = (timer1latch & 0x0ff00) | value;
break;
case T1CH:
timer1latch = (timer1latch & 0x0ff) | (value << 8);
timer1IRQ = false;
timer1counter = timer1latch;
timer1running = true;
setRun(true);
break;
case T1LH:
timer1latch = (timer1latch & 0x0ff) | (value << 8);
timer1IRQ = false;
break;
case T2CL:
timer2latch = (timer2latch & 0x0ff00) | value;
break;
case T2CH:
timer2latch = (timer2latch & 0x0ff) | (value << 8);
timer2IRQ = false;
timer2counter = timer2latch;
timer2running = true;
setRun(true);
break;
case SR:
// SHIFT REGISTER NOT IMPLEMENTED
break;
case ACR:
// SHIFT REGISTER NOT IMPLEMENTED
timer1freerun = (value & 64) != 0;
if (timer1freerun) {
timer1running = true;
setRun(true);
}
break;
case PCR:
// TODO: Implement if Votrax (SSI) is to be supported
break;
case IFR:
if ((value & 64) != 0) {
timer1IRQ = false;
}
if ((value & 32) != 0) {
timer2IRQ = false;
}
break;
case IER:
boolean enable = (value & 128) != 0;
if ((value & 64) != 0) {
timer1interruptEnabled = enable;
}
if ((value & 32) != 0) {
timer2interruptEnabled = enable;
}
break;
default:
}
}
// Whatever uses 6522 will want to know when it is outputting values
// So to hook that in, these abstract methods will be defined as appropriate
public abstract void sendOutputA(int value);
public abstract void sendOutputB(int value);
public int readRegister(int reg) {
Register r = Register.fromInt(reg);
// System.out.println("Reading register "+r.toString());
switch (r) {
case ORB:
if (dataDirectionB == 0x0ff) {
break;
}
return receiveOutputB() & (dataDirectionB ^ 0x0ff);
case ORA:
case ORAH:
if (dataDirectionA == 0x0ff) {
break;
}
return receiveOutputA() & (dataDirectionA ^ 0x0ff);
case DDRB:
return dataDirectionB;
case DDRA:
return dataDirectionA;
case T1CL:
timer1IRQ = false;
return timer1counter & 0x0ff;
case T1CH:
return (timer1counter & 0x0ff00) >> 8;
case T1LL:
return timer1latch & 0x0ff;
case T1LH:
return (timer1latch & 0x0ff00) >> 8;
case T2CL:
timer2IRQ = false;
return timer2counter & 0x0ff;
case T2CH:
return (timer2counter & 0x0ff00) >> 8;
case SR:
// SHIFT REGISTER NOT IMPLEMENTED
return 0;
case ACR:
// SHIFT REGISTER NOT IMPLEMENTED
if (timer1freerun) {
return 64;
}
return 0;
case PCR:
break;
case IFR:
int val = 0;
if (timer1IRQ) {
val |= 64;
}
if (timer2IRQ) {
val |= 32;
}
if (val != 0) {
val |= 128;
}
return val;
case IER:
val = 128;
if (timer1interruptEnabled) {
val |= 64;
}
if (timer2interruptEnabled) {
val |= 32;
}
return val;
}
return 0;
}
public abstract int receiveOutputA();
public abstract int receiveOutputB();
}

View File

@ -1,50 +0,0 @@
/*
* Copyright 2016 Brendan Robert
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jace.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
* @author blurry
*/
public class HeadlessProgram extends Program {
public HeadlessProgram(DocumentType type) {
super(type, Collections.EMPTY_MAP);
}
String program;
@Override
public String getValue() {
return program;
}
@Override
public void setValue(String value) {
program = value;
}
public HeadlessProgram() {
super(null, null);
}
CompileResult lastResult = null;
@Override
protected void manageCompileResult(CompileResult lastResult) {
this.lastResult = lastResult;
}
}

View File

@ -19,8 +19,6 @@ import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
@ -98,42 +96,31 @@ public class IdeController {
@FXML
void newApplesoftBasicClicked(ActionEvent event) {
Program tab = createTab(DocumentType.applesoft, null, true);
}
@FXML
void newApplesoftBasicFromMemoryClicked(ActionEvent event) {
Alert warningAlert = new Alert(Alert.AlertType.CONFIRMATION);
warningAlert.setTitle("Is Applesoft running?");
warningAlert.setContentText("If you proceed and applesoft is not running or there is no active program then the emulator might freeze. Press Cancel if you are unsure.");
Optional<ButtonType> result = warningAlert.showAndWait();
if (result.get() == ButtonType.OK) {
Program tab = createTab(DocumentType.applesoft, null, false);
}
Program tab = createTab(DocumentType.applesoft, null);
}
@FXML
void newAssemblyListingClicked(ActionEvent event) {
createTab(DocumentType.assembly, null, false);
createTab(DocumentType.assembly, null);
}
@FXML
void newHexdataClicked(ActionEvent event) {
createTab(DocumentType.hex, null, false);
createTab(DocumentType.hex, null);
}
@FXML
void newPlainTextClicked(ActionEvent event) {
createTab(DocumentType.plain, null, false);
createTab(DocumentType.plain, null);
}
Map<Tab, Program> openDocuments = new HashMap<>();
Map<Option, Object> globalOptions = new EnumMap<>(Option.class);
private Program createTab(DocumentType type, File document, boolean isBlank) {
private Program createTab(DocumentType type, File document) {
WebView editor = new WebView();
Program proxy = new Program(type, globalOptions);
proxy.initEditor(editor, document, isBlank);
proxy.initEditor(editor, document);
Tab t = new Tab(proxy.getName(), editor);
tabPane.getTabs().add(t);
openDocuments.put(t, proxy);
@ -166,7 +153,7 @@ public class IdeController {
File file = chooser.showOpenDialog(JaceApplication.getApplication().primaryStage);
if (file != null && file.isFile() && file.exists()) {
DocumentType type = DocumentType.fromFile(file);
createTab(type, file, true);
createTab(type, file);
}
}

View File

@ -105,7 +105,7 @@ public class Program {
return Optional.ofNullable(targetFile);
}
public void initEditor(WebView editor, File sourceFile, boolean isBlank) {
public void initEditor(WebView editor, File sourceFile) {
this.editor = editor;
targetFile = sourceFile;
if (targetFile != null) {
@ -117,7 +117,7 @@ public class Program {
if (newState == Worker.State.SUCCEEDED) {
JSObject document = (JSObject) editor.getEngine().executeScript("window");
document.setMember("java", this);
Platform.runLater(()->createEditor(isBlank));
Platform.runLater(this::createEditor);
}
});
@ -132,8 +132,8 @@ public class Program {
editor.getEngine().load(getClass().getResource(CODEMIRROR_EDITOR).toExternalForm());
}
public void createEditor(boolean isBlank) {
String document = targetFile == null ? isBlank ? "" : getHandler().getNewDocumentContent() : getFileContents(targetFile);
public void createEditor() {
String document = targetFile == null ? getHandler().getNewDocumentContent() : getFileContents(targetFile);
String optionString = buildOptions();
editor.getEngine().executeScript("var codeMirror = CodeMirror(document.body, " + optionString + ");");
codeMirror = (JSObject) editor.getEngine().executeScript("codeMirror");
@ -221,7 +221,6 @@ public class Program {
if (lastResult.isSuccessful()) {
getHandler().execute(lastResult);
} else {
lastResult.getOtherMessages().forEach(System.err::println);
getHandler().clean(lastResult);
}
}
@ -232,7 +231,7 @@ public class Program {
getHandler().clean(lastResult);
}
protected void manageCompileResult(CompileResult lastResult) {
private void manageCompileResult(CompileResult lastResult) {
editor.getEngine().executeScript("clearHighlights()");
lastResult.getWarnings().forEach((line, message)
-> editor.getEngine().executeScript("highlightLine(" + line + ",false,\"" + escapeString(message) + "\");")

View File

@ -21,7 +21,6 @@ package jace.library;
import jace.core.Utility;
import jace.hardware.FloppyDisk;
import java.io.File;
import java.util.Optional;
import javafx.scene.image.Image;
/**
@ -41,7 +40,7 @@ public enum DiskType {
public boolean isProdosOrdered = false;
public boolean is140kb = false;
public String description;
public Optional<Image> diskIcon;
public Image diskIcon;
DiskType(String desc, boolean is140, boolean po, String iconPath) {
description = desc;
is140kb = is140;

View File

@ -20,7 +20,6 @@ package jace.library;
import jace.library.MediaEntry.MediaFile;
import java.io.IOException;
import java.util.Optional;
import javafx.scene.control.Label;
/**
@ -28,8 +27,8 @@ import javafx.scene.control.Label;
* @author brobert
*/
public interface MediaConsumer {
public Optional<Label> getIcon();
public void setIcon(Optional<Label> i);
public Label getIcon();
public void setIcon(Label i);
public void insertMedia(MediaEntry e, MediaFile f) throws IOException;
public MediaEntry getMediaEntry();
public MediaFile getMediaFile();

View File

@ -1,30 +0,0 @@
package jace.library;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class StringUtils {
/**
* Get XML String of utf-8
*
* @return XML-Formed string
*/
public static String getUTF8XMLString(String xml) {
// A StringBuffer Object
StringBuffer sb = new StringBuffer();
sb.append(xml);
String xmString = "";
String xmlUTF8 = "";
try {
xmString = new String(sb.toString().getBytes("UTF-8"));
xmlUTF8 = URLEncoder.encode(xmString, "UTF-8");
System.out.println("utf-8 编码:" + xmlUTF8);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// return to String Formed
return xmlUTF8;
}
}

View File

@ -21,7 +21,6 @@ package jace.state;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import javafx.scene.image.Image;
@ -79,7 +78,7 @@ public class State extends HashMap<ObjectGraphNode, StateValue> implements Seria
}
public void apply() {
Set<ObjectGraphNode> applied = new LinkedHashSet<>();
Set<ObjectGraphNode> applied = new HashSet<>();
State current = this;
while (current != null) {
for (StateValue val : current.values()) {

View File

@ -21,17 +21,14 @@ package jace.state;
import jace.Emulator;
import jace.apple2e.SoftSwitches;
import jace.config.ConfigurableField;
import jace.config.InvokableAction;
import jace.config.Reconfigurable;
import jace.core.Computer;
import jace.core.PagedMemory;
import jace.core.Video;
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;
@ -40,7 +37,6 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
/**
*
@ -68,13 +64,12 @@ public class StateManager implements Reconfigurable {
private ObjectGraphNode<BufferedImage> imageGraphNode;
Computer computer;
private StateManager(Computer computer) {
this.computer = computer;
}
private void buildStateMap() {
allStateVariables = new LinkedHashSet<>();
allStateVariables = new HashSet<>();
objectLookup = new WeakHashMap<>();
ObjectGraphNode emulator = new ObjectGraphNode(Emulator.instance);
emulator.name = "Emulator";
@ -300,7 +295,7 @@ public class StateManager implements Reconfigurable {
public void captureState() {
// If the state graph is invalidated it means we have to abandon all
// previously captured states. This helps ensure that rewinding will
// previously captured states. This helps ensure that rewinding will
// not result in an unintended or invalid state.
if (!isValid) {
alphaState = null;
@ -407,17 +402,6 @@ public class StateManager implements Reconfigurable {
captureState();
}
@InvokableAction(
name = "Rewind",
alternatives = "Timewarp",
description = "Go back 1 second",
defaultKeyMapping = {"ctrl+shift+Open Bracket"}
)
public static void beKindRewind() {
StateManager manager = getInstance(Emulator.computer);
new Thread(()->manager.rewind(60 / manager.captureFrequency)).start();
}
public void rewind(int numStates) {
boolean resume = computer.pause();
State state = alphaState.tail;
@ -428,7 +412,7 @@ public class StateManager implements Reconfigurable {
state.apply();
alphaState.tail = state;
state.nextState = null;
Video.forceRefresh();
computer.getVideo().forceRefresh();
System.gc();
if (resume) {
computer.resume();

View File

@ -12,7 +12,6 @@ import jace.cheat.MetaCheat.SearchType;
import jace.state.State;
import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ScheduledExecutorService;
@ -26,8 +25,8 @@ import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.SubScene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
@ -45,22 +44,18 @@ import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.FileChooser;
import javafx.stage.Screen;
import javafx.util.converter.DefaultStringConverter;
import javafx.util.converter.IntegerStringConverter;
public class MetacheatUI {
boolean isRetina;
double drawScale;
@FXML
private Button pauseButton;
@ -75,7 +70,10 @@ public class MetacheatUI {
@FXML
private StackPane memoryViewContents;
@FXML
private Canvas memoryViewCanvas;
@FXML
private TabPane searchTypesTabPane;
@ -240,7 +238,6 @@ public class MetacheatUI {
assert searchStartAddressField != null : "fx:id=\"searchStartAddressField\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert searchEndAddressField != null : "fx:id=\"searchEndAddressField\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert memoryViewPane != null : "fx:id=\"memoryViewPane\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert memoryViewContents != null : "fx:id=\"memoryViewContents\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert searchTypesTabPane != null : "fx:id=\"searchTypesTabPane\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert searchValueField != null : "fx:id=\"searchValueField\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert searchTypeByte != null : "fx:id=\"searchTypeByte\" was not injected: check your FXML file 'Metacheat.fxml'.";
@ -264,7 +261,7 @@ public class MetacheatUI {
assert cheatsTableView != null : "fx:id=\"cheatsTableView\" was not injected: check your FXML file 'Metacheat.fxml'.";
isRetina = Screen.getPrimary().getDpi() >= 110;
Emulator.computer.getRunningProperty().addListener((val, oldVal, newVal) -> {
Platform.runLater(() -> pauseButton.setText(newVal ? "Pause" : "Resume"));
});
@ -304,6 +301,8 @@ public class MetacheatUI {
addWatch(result.getAddress());
});
memoryViewCanvas.setMouseTransparent(false);
memoryViewCanvas.addEventFilter(MouseEvent.MOUSE_CLICKED, this::memoryViewClicked);
showValuesCheckbox.selectedProperty().addListener((prop, oldVal, newVal) -> {
if (newVal) {
redrawMemoryView();
@ -311,6 +310,7 @@ public class MetacheatUI {
});
memoryViewPane.boundsInParentProperty().addListener((prop, oldVal, newVal) -> redrawMemoryView());
drawScale = isRetina ? 0.5 : 1.0;
memoryViewCanvas.widthProperty().bind(memoryViewPane.widthProperty().multiply(drawScale).subtract(8));
watchesPane.setHgap(5);
watchesPane.setVgap(5);
@ -377,10 +377,9 @@ public class MetacheatUI {
ChangeListener<String> addressRangeListener = (prop, oldVal, newVal) -> Application.invokeLater(this::redrawMemoryView);
public static final int MEMORY_BOX_SIZE = 5;
public static final int MEMORY_BOX_GAP = 1;
public static final int MEMORY_BOX_SIZE = 4;
public static final int MEMORY_BOX_GAP = 2;
public static final int MEMORY_BOX_TOTAL_SIZE = (MEMORY_BOX_SIZE + MEMORY_BOX_GAP);
public static final int UPDATE_NODE_LIMIT = 10000;
public int memoryViewColumns;
public int memoryViewRows;
@ -389,14 +388,18 @@ public class MetacheatUI {
ScheduledFuture animationFuture = null;
Tooltip memoryWatchTooltip = new Tooltip();
private void memoryViewClicked(MouseEvent e, MemoryCell cell) {
private void memoryViewClicked(MouseEvent e) {
if (cheatEngine != null) {
Watch currentWatch = (Watch) memoryWatchTooltip.getGraphic();
if (currentWatch != null) {
currentWatch.disconnect();
}
int addr = cell.address;
double x = e.getX() / drawScale;
double y = e.getY() / drawScale;
int col = (int) (x / MEMORY_BOX_TOTAL_SIZE);
int row = (int) (y / MEMORY_BOX_TOTAL_SIZE);
int addr = cheatEngine.getStartAddress() + row * memoryViewColumns + col;
Watch watch = new Watch(addr, this);
Label addWatch = new Label("Watch >>");
@ -421,7 +424,7 @@ public class MetacheatUI {
memoryWatchTooltip.setGraphic(null);
});
memoryWatchTooltip.setGraphic(watch);
memoryWatchTooltip.show(memoryViewPane.getContent(), e.getScreenX() + 5, e.getScreenY() - 15);
memoryWatchTooltip.show(memoryViewContents, e.getScreenX() + 5, e.getScreenY() - 15);
}
}
@ -429,21 +432,22 @@ public class MetacheatUI {
if (!Emulator.computer.getRunningProperty().get()) {
return;
}
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
Set<MemoryCell> draw = new HashSet<>(redrawNodes);
redrawNodes.clear();
Application.invokeLater(() -> {
Iterator<MemoryCell> i = redrawNodes.iterator();
for (int limit = 0; i.hasNext() && limit < UPDATE_NODE_LIMIT; limit++) {
MemoryCell cell = i.next();
i.remove();
draw.stream().forEach((jace.cheat.MemoryCell cell) -> {
if (showValuesCheckbox.isSelected()) {
int val = cell.value.get() & 0x0ff;
cell.getShape().setFill(Color.rgb(val, val, val));
context.setFill(Color.rgb(val, val, val));
} else {
cell.getShape().setFill(Color.rgb(
context.setFill(Color.rgb(
cell.writeCount.get(),
cell.readCount.get(),
cell.execCount.get()));
}
}
context.fillRect(cell.getX(), cell.getY(), cell.getWidth(), cell.getHeight());
});
});
}
@ -469,29 +473,29 @@ public class MetacheatUI {
int pixelsPerBlock = 16 * MEMORY_BOX_TOTAL_SIZE;
memoryViewColumns = (int) (memoryViewPane.getWidth() / pixelsPerBlock) * 16;
memoryViewRows = ((cheatEngine.getEndAddress() - cheatEngine.getStartAddress()) / memoryViewColumns) + 1;
memoryViewContents.prefWidthProperty().bind(memoryViewPane.widthProperty().multiply(drawScale).subtract(8));
Group memoryView = new Group();
memoryViewContents.setBackground(new Background(new BackgroundFill(Color.rgb(40, 40, 40), null, null)));
double canvasHeight = memoryViewRows * MEMORY_BOX_TOTAL_SIZE * drawScale;
memoryViewContents.setPrefHeight(canvasHeight);
memoryViewCanvas.setHeight(canvasHeight);
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
context.setFill(Color.rgb(40, 40, 40));
context.fillRect(0, 0, memoryViewCanvas.getWidth(), memoryViewCanvas.getHeight());
for (int addr = cheatEngine.getStartAddress(); addr <= cheatEngine.getEndAddress(); addr++) {
int col = (addr - cheatEngine.getStartAddress()) % memoryViewColumns;
int row = (addr - cheatEngine.getStartAddress()) / memoryViewColumns;
MemoryCell cell = cheatEngine.getMemoryCell(addr);
Rectangle rect = new Rectangle(col * MEMORY_BOX_TOTAL_SIZE * drawScale, row * MEMORY_BOX_TOTAL_SIZE * drawScale, MEMORY_BOX_SIZE * drawScale, MEMORY_BOX_SIZE * drawScale);
rect.setOnMouseClicked(e -> memoryViewClicked(e, cell));
rect.setFill(Color.GRAY);
cell.setShape(rect);
memoryView.getChildren().add(rect);
cell.setRect(
(int) (col * MEMORY_BOX_TOTAL_SIZE * drawScale),
(int) (row * MEMORY_BOX_TOTAL_SIZE * drawScale),
(int) (MEMORY_BOX_SIZE * drawScale),
(int) (MEMORY_BOX_SIZE * drawScale));
redrawNodes.add(cell);
}
memoryViewContents.getChildren().clear();
memoryViewContents.getChildren().add(memoryView);
MemoryCell.setListener((javafx.beans.value.ObservableValue<? extends jace.cheat.MemoryCell> prop, jace.cheat.MemoryCell oldCell, jace.cheat.MemoryCell newCell) -> {
redrawNodes.add(newCell);
});
setZoom(1 / drawScale);
setZoom(1/drawScale);
if (resume) {
Emulator.computer.resume();
@ -499,19 +503,19 @@ public class MetacheatUI {
}
private void changeZoom(double amount) {
if (memoryViewContents != null) {
double zoom = memoryViewContents.getScaleX();
if (memoryViewCanvas != null) {
double zoom = memoryViewCanvas.getScaleX();
zoom += amount;
setZoom(zoom);
}
}
private void setZoom(double zoom) {
if (memoryViewContents != null) {
memoryViewContents.setScaleX(zoom);
memoryViewContents.setScaleY(zoom);
// StackPane scrollArea = (StackPane) memoryViewCanvas.getParent();
// scrollArea.setPrefSize(memoryViewCanvas.getWidth() * zoom, memoryViewCanvas.getHeight() * zoom);
if (memoryViewCanvas != null) {
memoryViewCanvas.setScaleX(zoom);
memoryViewCanvas.setScaleY(zoom);
StackPane scrollArea = (StackPane) memoryViewCanvas.getParent();
scrollArea.setPrefSize(memoryViewCanvas.getWidth() * zoom, memoryViewCanvas.getHeight() * zoom);
}
}

View File

@ -0,0 +1,787 @@
// Copyright 2000-2005 the Contributors, as shown in the revision logs.
// Licensed under the Apache Public Source License 2.0 ("the License").
// You may not use this file except in compliance with the License.
// Copyright 2003 Brian Alliet
// Based on org.xwt.imp.MIPS by Adam Megacz
// Portions Copyright 2003 Adam Megacz
package org.ibex.nestedvm;
import org.ibex.nestedvm.util.*;
import java.io.*;
public class Interpreter extends UnixRuntime implements Cloneable {
// Registers
private int[] registers = new int[32];
private int hi,lo;
// Floating Point Registers
private int[] fpregs = new int[32];
// 24-31 - unused
// 23 - conditional bit
// 18-22 - unused
// 12-17 - cause bits (unimplemented)
// 7-11 - enables bits (unimplemented)
// 2-6 - flags (unimplemented)
// 0-1 - rounding mode (only implemented for fixed point conversions)
private int fcsr;
private int pc;
// The filename if the binary we're running
public String image;
private ELF.Symtab symtab;
// Register Operations
private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); }
private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ }
private final double getDouble(int r) {
return Double.longBitsToDouble(((fpregs[r+1]&0xffffffffL) << 32) | (fpregs[r]&0xffffffffL));
}
private final void setDouble(int r, double d) {
long l = Double.doubleToLongBits(d);
fpregs[r+1] = (int)(l >>> 32); fpregs[r] = (int)l;
}
private final float getFloat(int r) { return Float.intBitsToFloat(fpregs[r]); }
private final void setFloat(int r, float f) { fpregs[r] = Float.floatToRawIntBits(f); }
protected void _execute() throws ExecutionException {
try {
runSome();
} catch(ExecutionException e) {
e.setLocation(toHex(pc) + ": " + sourceLine(pc));
throw e;
}
}
protected Object clone() throws CloneNotSupportedException {
Interpreter r = (Interpreter) super.clone();
r.registers = (int[]) registers.clone();
r.fpregs = (int[]) fpregs.clone();
return r;
}
// Main interpretor
// the return value is meaningless, its just to catch people typing "return" by accident
private final int runSome() throws FaultException,ExecutionException {
final int PAGE_WORDS = (1<<pageShift)>>2;
int[] r = registers;
int[] f = fpregs;
int pc = this.pc;
int nextPC = pc + 4;
try {
OUTER: for(;;) {
int insn;
try {
insn = readPages[pc>>>pageShift][(pc>>>2)&PAGE_WORDS-1];
} catch (RuntimeException e) {
if(pc == 0xdeadbeef) throw new Error("fell off cpu: r2: " + r[2]);
insn = memRead(pc);
}
int op = (insn >>> 26) & 0xff; // bits 26-31
int rs = (insn >>> 21) & 0x1f; // bits 21-25
int rt = (insn >>> 16) & 0x1f; // bits 16-20
int ft = (insn >>> 16) & 0x1f;
int rd = (insn >>> 11) & 0x1f; // bits 11-15
int fs = (insn >>> 11) & 0x1f;
int shamt = (insn >>> 6) & 0x1f; // bits 6-10
int fd = (insn >>> 6) & 0x1f;
int subcode = insn & 0x3f; // bits 0-5
int jumpTarget = (insn & 0x03ffffff); // bits 0-25
int unsignedImmediate = insn & 0xffff;
int signedImmediate = (insn << 16) >> 16;
int branchTarget = signedImmediate;
int tmp, addr; // temporaries
r[ZERO] = 0;
switch(op) {
case 0: {
switch(subcode) {
case 0: // SLL
if(insn == 0) break;
r[rd] = r[rt] << shamt;
break;
case 2: // SRL
r[rd] = r[rt] >>> shamt;
break;
case 3: // SRA
r[rd] = r[rt] >> shamt;
break;
case 4: // SLLV
r[rd] = r[rt] << (r[rs]&0x1f);
break;
case 6: // SRLV
r[rd] = r[rt] >>> (r[rs]&0x1f);
break;
case 7: // SRAV
r[rd] = r[rt] >> (r[rs]&0x1f);
break;
case 8: // JR
tmp = r[rs]; pc += 4; nextPC = tmp;
continue OUTER;
case 9: // JALR
tmp = r[rs]; pc += 4; r[rd] = pc+4; nextPC = tmp;
continue OUTER;
case 12: // SYSCALL
this.pc = pc;
r[V0] = syscall(r[V0],r[A0],r[A1],r[A2],r[A3],r[T0],r[T1]);
if(state != RUNNING) { this.pc = nextPC; break OUTER; }
break;
case 13: // BREAK
throw new ExecutionException("Break");
case 16: // MFHI
r[rd] = hi;
break;
case 17: // MTHI
hi = r[rs];
break;
case 18: // MFLO
r[rd] = lo;
break;
case 19: // MTLO
lo = r[rs];
break;
case 24: { // MULT
long hilo = ((long)r[rs]) * ((long)r[rt]);
hi = (int) (hilo >>> 32);
lo = (int) hilo;
break;
}
case 25: { // MULTU
long hilo = (r[rs] & 0xffffffffL) * (r[rt] & 0xffffffffL);
hi = (int) (hilo >>> 32);
lo = (int) hilo;
break;
}
case 26: // DIV
hi = r[rs]%r[rt];
lo = r[rs]/r[rt];
break;
case 27: // DIVU
if(rt != 0) {
hi = (int)((r[rs] & 0xffffffffL) % (r[rt] & 0xffffffffL));
lo = (int)((r[rs] & 0xffffffffL) / (r[rt] & 0xffffffffL));
}
break;
case 32: // ADD
throw new ExecutionException("ADD (add with oveflow trap) not suported");
/*This must trap on overflow
r[rd] = r[rs] + r[rt];
break;*/
case 33: // ADDU
r[rd] = r[rs] + r[rt];
break;
case 34: // SUB
throw new ExecutionException("SUB (sub with oveflow trap) not suported");
/*This must trap on overflow
r[rd] = r[rs] - r[rt];
break;*/
case 35: // SUBU
r[rd] = r[rs] - r[rt];
break;
case 36: // AND
r[rd] = r[rs] & r[rt];
break;
case 37: // OR
r[rd] = r[rs] | r[rt];
break;
case 38: // XOR
r[rd] = r[rs] ^ r[rt];
break;
case 39: // NOR
r[rd] = ~(r[rs] | r[rt]);
break;
case 42: // SLT
r[rd] = r[rs] < r[rt] ? 1 : 0;
break;
case 43: // SLTU
r[rd] = ((r[rs] & 0xffffffffL) < (r[rt] & 0xffffffffL)) ? 1 : 0;
break;
default:
throw new ExecutionException("Illegal instruction 0/" + subcode);
}
break;
}
case 1: {
switch(rt) {
case 0: // BLTZ
if(r[rs] < 0) {
pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
continue OUTER;
}
break;
case 1: // BGEZ
if(r[rs] >= 0) {
pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
continue OUTER;
}
break;
case 16: // BLTZAL
if(r[rs] < 0) {
pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp;
continue OUTER;
}
break;
case 17: // BGEZAL
if(r[rs] >= 0) {
pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp;
continue OUTER;
}
break;
default:
throw new ExecutionException("Illegal Instruction");
}
break;
}
case 2: { // J
tmp = (pc&0xf0000000) | (jumpTarget << 2);
pc+=4; nextPC = tmp;
continue OUTER;
}
case 3: { // JAL
tmp = (pc&0xf0000000) | (jumpTarget << 2);
pc+=4; r[RA] = pc+4; nextPC = tmp;
continue OUTER;
}
case 4: // BEQ
if(r[rs] == r[rt]) {
pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
continue OUTER;
}
break;
case 5: // BNE
if(r[rs] != r[rt]) {
pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
continue OUTER;
}
break;
case 6: //BLEZ
if(r[rs] <= 0) {
pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
continue OUTER;
}
break;
case 7: //BGTZ
if(r[rs] > 0) {
pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
continue OUTER;
}
break;
case 8: // ADDI
r[rt] = r[rs] + signedImmediate;
break;
case 9: // ADDIU
r[rt] = r[rs] + signedImmediate;
break;
case 10: // SLTI
r[rt] = r[rs] < signedImmediate ? 1 : 0;
break;
case 11: // SLTIU
r[rt] = (r[rs]&0xffffffffL) < (signedImmediate&0xffffffffL) ? 1 : 0;
break;
case 12: // ANDI
r[rt] = r[rs] & unsignedImmediate;
break;
case 13: // ORI
r[rt] = r[rs] | unsignedImmediate;
break;
case 14: // XORI
r[rt] = r[rs] ^ unsignedImmediate;
break;
case 15: // LUI
r[rt] = unsignedImmediate << 16;
break;
case 16:
throw new ExecutionException("TLB/Exception support not implemented");
case 17: { // FPU
boolean debug = false;
String line = debug ? sourceLine(pc) : "";
boolean debugon = debug && (line.indexOf("dtoa.c:51") >= 0 || line.indexOf("dtoa.c:52") >= 0 || line.indexOf("test.c") >= 0);
if(rs > 8 && debugon)
System.out.println(" FP Op: " + op + "/" + rs + "/" + subcode + " " + line);
if(roundingMode() != 0 && rs != 6 /*CTC.1*/ && !((rs==16 || rs==17) && subcode == 36 /* CVT.W.Z */))
throw new ExecutionException("Non-cvt.w.z operation attempted with roundingMode != round to nearest");
switch(rs) {
case 0: // MFC.1
r[rt] = f[rd];
break;
case 2: // CFC.1
if(fs != 31) throw new ExecutionException("FCR " + fs + " unavailable");
r[rt] = fcsr;
break;
case 4: // MTC.1
f[rd] = r[rt];
break;
case 6: // CTC.1
if(fs != 31) throw new ExecutionException("FCR " + fs + " unavailable");
fcsr = r[rt];
break;
case 8: // BC1F, BC1T
if(((fcsr&0x800000)!=0) == (((insn>>>16)&1)!=0)) {
pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
continue OUTER;
}
break;
case 16: { // Single
switch(subcode) {
case 0: // ADD.S
setFloat(fd,getFloat(fs)+getFloat(ft));
break;
case 1: // SUB.S
setFloat(fd,getFloat(fs)-getFloat(ft));
break;
case 2: // MUL.S
setFloat(fd,getFloat(fs)*getFloat(ft));
break;
case 3: // DIV.S
setFloat(fd,getFloat(fs)/getFloat(ft));
break;
case 5: // ABS.S
setFloat(fd,Math.abs(getFloat(fs)));
break;
case 6: // MOV.S
f[fd] = f[fs];
break;
case 7: // NEG.S
setFloat(fd,-getFloat(fs));
break;
case 33: // CVT.D.S
setDouble(fd,getFloat(fs));
break;
case 36: // CVT.W.S
switch(roundingMode()) {
case 0: f[fd] = (int)Math.floor(getFloat(fs)+0.5f); break; // Round to nearest
case 1: f[fd] = (int)getFloat(fs); break; // Round towards zero
case 2: f[fd] = (int)Math.ceil(getFloat(fs)); break; // Round towards plus infinity
case 3: f[fd] = (int)Math.floor(getFloat(fs)); break; // Round towards minus infinity
}
break;
case 50: // C.EQ.S
setFC(getFloat(fs) == getFloat(ft));
break;
case 60: // C.LT.S
setFC(getFloat(fs) < getFloat(ft));
break;
case 62: // C.LE.S
setFC(getFloat(fs) <= getFloat(ft));
break;
default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
}
break;
}
case 17: { // Double
switch(subcode) {
case 0: // ADD.D
setDouble(fd,getDouble(fs)+getDouble(ft));
break;
case 1: // SUB.D
if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") - f" + ft + " (" + getDouble(ft) + ")");
setDouble(fd,getDouble(fs)-getDouble(ft));
break;
case 2: // MUL.D
if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") * f" + ft + " (" + getDouble(ft) + ")");
setDouble(fd,getDouble(fs)*getDouble(ft));
if(debugon) System.out.println("f" + fd + " = " + getDouble(fd));
break;
case 3: // DIV.D
setDouble(fd,getDouble(fs)/getDouble(ft));
break;
case 5: // ABS.D
setDouble(fd,Math.abs(getDouble(fs)));
break;
case 6: // MOV.D
f[fd] = f[fs];
f[fd+1] = f[fs+1];
break;
case 7: // NEG.D
setDouble(fd,-getDouble(fs));
break;
case 32: // CVT.S.D
setFloat(fd,(float)getDouble(fs));
break;
case 36: // CVT.W.D
if(debugon) System.out.println("CVT.W.D rm: " + roundingMode() + " f" + fs + ":" + getDouble(fs));
switch(roundingMode()) {
case 0: f[fd] = (int)Math.floor(getDouble(fs)+0.5); break; // Round to nearest
case 1: f[fd] = (int)getDouble(fs); break; // Round towards zero
case 2: f[fd] = (int)Math.ceil(getDouble(fs)); break; // Round towards plus infinity
case 3: f[fd] = (int)Math.floor(getDouble(fs)); break; // Round towards minus infinity
}
if(debugon) System.out.println("CVT.W.D: f" + fd + ":" + f[fd]);
break;
case 50: // C.EQ.D
setFC(getDouble(fs) == getDouble(ft));
break;
case 60: // C.LT.D
setFC(getDouble(fs) < getDouble(ft));
break;
case 62: // C.LE.D
setFC(getDouble(fs) <= getDouble(ft));
break;
default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
}
break;
}
case 20: { // Integer
switch(subcode) {
case 32: // CVT.S.W
setFloat(fd,f[fs]);
break;
case 33: // CVT.D.W
setDouble(fd,f[fs]);
break;
default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
}
break;
}
default:
throw new ExecutionException("Invalid Instruction 17/" + rs);
}
break;
}
case 18: case 19:
throw new ExecutionException("No coprocessor installed");
case 32: { // LB
addr = r[rs] + signedImmediate;
try {
tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
switch(addr&3) {
case 0: tmp = (tmp>>>24)&0xff; break;
case 1: tmp = (tmp>>>16)&0xff; break;
case 2: tmp = (tmp>>> 8)&0xff; break;
case 3: tmp = (tmp>>> 0)&0xff; break;
}
if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend
r[rt] = tmp;
break;
}
case 33: { // LH
addr = r[rs] + signedImmediate;
try {
tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
switch(addr&3) {
case 0: tmp = (tmp>>>16)&0xffff; break;
case 2: tmp = (tmp>>> 0)&0xffff; break;
default: throw new ReadFaultException(addr);
}
if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend
r[rt] = tmp;
break;
}
case 34: { // LWL;
addr = r[rs] + signedImmediate;
try {
tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
switch(addr&3) {
case 0: r[rt] = (r[rt]&0x00000000)|(tmp<< 0); break;
case 1: r[rt] = (r[rt]&0x000000ff)|(tmp<< 8); break;
case 2: r[rt] = (r[rt]&0x0000ffff)|(tmp<<16); break;
case 3: r[rt] = (r[rt]&0x00ffffff)|(tmp<<24); break;
}
break;
}
case 35: // LW
addr = r[rs] + signedImmediate;
try {
r[rt] = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
r[rt] = memRead(addr);
}
break;
case 36: { // LBU
addr = r[rs] + signedImmediate;
try {
tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr);
}
switch(addr&3) {
case 0: r[rt] = (tmp>>>24)&0xff; break;
case 1: r[rt] = (tmp>>>16)&0xff; break;
case 2: r[rt] = (tmp>>> 8)&0xff; break;
case 3: r[rt] = (tmp>>> 0)&0xff; break;
}
break;
}
case 37: { // LHU
addr = r[rs] + signedImmediate;
try {
tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
switch(addr&3) {
case 0: r[rt] = (tmp>>>16)&0xffff; break;
case 2: r[rt] = (tmp>>> 0)&0xffff; break;
default: throw new ReadFaultException(addr);
}
break;
}
case 38: { // LWR
addr = r[rs] + signedImmediate;
try {
tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
switch(addr&3) {
case 0: r[rt] = (r[rt]&0xffffff00)|(tmp>>>24); break;
case 1: r[rt] = (r[rt]&0xffff0000)|(tmp>>>16); break;
case 2: r[rt] = (r[rt]&0xff000000)|(tmp>>> 8); break;
case 3: r[rt] = (r[rt]&0x00000000)|(tmp>>> 0); break;
}
break;
}
case 40: { // SB
addr = r[rs] + signedImmediate;
try {
tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
switch(addr&3) {
case 0: tmp = (tmp&0x00ffffff) | ((r[rt]&0xff)<<24); break;
case 1: tmp = (tmp&0xff00ffff) | ((r[rt]&0xff)<<16); break;
case 2: tmp = (tmp&0xffff00ff) | ((r[rt]&0xff)<< 8); break;
case 3: tmp = (tmp&0xffffff00) | ((r[rt]&0xff)<< 0); break;
}
try {
writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
} catch(RuntimeException e) {
memWrite(addr&~3,tmp);
}
break;
}
case 41: { // SH
addr = r[rs] + signedImmediate;
try {
tmp = readPages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)];
} catch(RuntimeException e) {
tmp = memRead(addr&~3);
}
switch(addr&3) {
case 0: tmp = (tmp&0x0000ffff) | ((r[rt]&0xffff)<<16); break;
case 2: tmp = (tmp&0xffff0000) | ((r[rt]&0xffff)<< 0); break;
default: throw new WriteFaultException(addr);
}
try {
writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
} catch(RuntimeException e) {
memWrite(addr&~3,tmp);
}
break;
}
case 42: { // SWL
addr = r[rs] + signedImmediate;
tmp = memRead(addr&~3);
switch(addr&3) {
case 0: tmp=(tmp&0x00000000)|(r[rt]>>> 0); break;
case 1: tmp=(tmp&0xff000000)|(r[rt]>>> 8); break;
case 2: tmp=(tmp&0xffff0000)|(r[rt]>>>16); break;
case 3: tmp=(tmp&0xffffff00)|(r[rt]>>>24); break;
}
try {
writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
} catch(RuntimeException e) {
memWrite(addr&~3,tmp);
}
break;
}
case 43: // SW
addr = r[rs] + signedImmediate;
try {
writePages[addr>>>pageShift][(addr>>>2)&(PAGE_WORDS-1)] = r[rt];
} catch(RuntimeException e) {
memWrite(addr&~3,r[rt]);
}
break;
case 46: { // SWR
addr = r[rs] + signedImmediate;
tmp = memRead(addr&~3);
switch(addr&3) {
case 0: tmp=(tmp&0x00ffffff)|(r[rt]<<24); break;
case 1: tmp=(tmp&0x0000ffff)|(r[rt]<<16); break;
case 2: tmp=(tmp&0x000000ff)|(r[rt]<< 8); break;
case 3: tmp=(tmp&0x00000000)|(r[rt]<< 0); break;
}
memWrite(addr&~3,tmp);
break;
}
// Needs to be atomic w/ threads
case 48: // LWC0/LL
r[rt] = memRead(r[rs] + signedImmediate);
break;
case 49: // LWC1
f[rt] = memRead(r[rs] + signedImmediate);
break;
// Needs to be atomic w/ threads
case 56:
memWrite(r[rs] + signedImmediate,r[rt]);
r[rt] = 1;
break;
case 57: // SWC1
memWrite(r[rs] + signedImmediate,f[rt]);
break;
default:
throw new ExecutionException("Invalid Instruction: " + op);
}
pc = nextPC;
nextPC = pc + 4;
} // for(;;)
} catch(ExecutionException e) {
this.pc = pc;
throw e;
}
return 0;
}
public int lookupSymbol(String name) {
ELF.Symbol sym = symtab.getSymbol(name);
return sym == null ? -1 : sym.addr;
}
private int gp;
protected int gp() { return gp; }
private ELF.Symbol userInfo;
protected int userInfoBae() { return userInfo == null ? 0 : userInfo.addr; }
protected int userInfoSize() { return userInfo == null ? 0 : userInfo.size; }
private int entryPoint;
protected int entryPoint() { return entryPoint; }
private int heapStart;
protected int heapStart() { return heapStart; }
// Image loading function
private void loadImage(Seekable data) throws IOException {
ELF elf = new ELF(data);
symtab = elf.getSymtab();
if(elf.header.type != ELF.ET_EXEC) throw new IOException("Binary is not an executable");
if(elf.header.machine != ELF.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture");
if(elf.ident.data != ELF.ELFDATA2MSB) throw new IOException("Binary is not big endian");
entryPoint = elf.header.entry;
ELF.Symtab symtab = elf.getSymtab();
if(symtab == null) throw new IOException("No symtab in binary (did you strip it?)");
userInfo = symtab.getSymbol("user_info");
ELF.Symbol gpsym = symtab.getSymbol("_gp");
if(gpsym == null) throw new IOException("NO _gp symbol!");
gp = gpsym.addr;
entryPoint = elf.header.entry;
ELF.PHeader[] pheaders = elf.pheaders;
int brk = 0;
int pageSize = (1<<pageShift);
int pageWords = (1<<pageShift) >> 2;
for(int i=0;i<pheaders.length;i++) {
ELF.PHeader ph = pheaders[i];
if(ph.type != ELF.PT_LOAD) continue;
int memsize = ph.memsz;
int filesize = ph.filesz;
if(memsize == 0) continue;
if(memsize < 0) throw new IOException("pheader size too large");
int addr = ph.vaddr;
if(addr == 0x0) throw new IOException("pheader vaddr == 0x0");
brk = max(addr+memsize,brk);
for(int j=0;j<memsize+pageSize-1;j+=pageSize) {
int page = (j+addr) >>> pageShift;
if(readPages[page] == null)
readPages[page] = new int[pageWords];
if(ph.writable()) writePages[page] = readPages[page];
}
if(filesize != 0) {
filesize = filesize & ~3;
DataInputStream dis = new DataInputStream(ph.getInputStream());
do {
readPages[addr >>> pageShift][(addr >>> 2)&(pageWords-1)] = dis.readInt();
addr+=4;
filesize-=4;
} while(filesize > 0);
dis.close();
}
}
heapStart = (brk+pageSize-1)&~(pageSize-1);
}
protected void setCPUState(CPUState state) {
for(int i=1;i<32;i++) registers[i] = state.r[i];
for(int i=0;i<32;i++) fpregs[i] = state.f[i];
hi=state.hi; lo=state.lo; fcsr=state.fcsr;
pc=state.pc;
}
protected void getCPUState(CPUState state) {
for(int i=1;i<32;i++) state.r[i] = registers[i];
for(int i=0;i<32;i++) state.f[i] = fpregs[i];
state.hi=hi; state.lo=lo; state.fcsr=fcsr;
state.pc=pc;
}
public Interpreter(Seekable data) throws IOException {
super(4096,65536);
loadImage(data);
}
public Interpreter(String filename) throws IOException {
this(new Seekable.File(filename,false));
image = filename;
}
public Interpreter(InputStream is) throws IOException { this(new Seekable.InputStream(is)); }
// Debug functions
// NOTE: This probably requires a jdk > 1.1, however, it is only used for debugging
private java.util.HashMap<Integer,String> sourceLineCache;
public String sourceLine(int pc) {
final String addr2line = "mips-unknown-elf-addr2line";
String line = (String) (sourceLineCache == null ? null : sourceLineCache.get(new Integer(pc)));
if(line != null) return line;
if(image==null) return null;
try {
Process p = java.lang.Runtime.getRuntime().exec(new String[]{addr2line,"-e",image,toHex(pc)});
line = new BufferedReader(new InputStreamReader(p.getInputStream())).readLine();
if(line == null) return null;
while(line.startsWith("../")) line = line.substring(3);
if(sourceLineCache == null) sourceLineCache = new java.util.HashMap<Integer,String>();
sourceLineCache.put(new Integer(pc),line);
return line;
} catch(IOException e) {
return null;
}
}
public class DebugShutdownHook implements Runnable {
public void run() {
int pc = Interpreter.this.pc;
if(getState() == RUNNING)
System.err.print("\nCPU Executing " + toHex(pc) + ": " + sourceLine(pc) + "\n");
}
}
public static void main(String[] argv) throws Exception {
String image = argv[0];
Interpreter emu = new Interpreter(image);
java.lang.Runtime.getRuntime().addShutdownHook(new Thread(emu.new DebugShutdownHook()));
int status = emu.run(argv);
System.err.println("Exit status: " + status);
System.exit(status);
}
}

View File

@ -0,0 +1,46 @@
// Copyright 2000-2005 the Contributors, as shown in the revision logs.
// Licensed under the Apache Public Source License 2.0 ("the License").
// You may not use this file except in compliance with the License.
package org.ibex.nestedvm;
interface Registers {
// Register Names
public final static int ZERO = 0; // Immutable, hardwired to 0
public final static int AT = 1; // Reserved for assembler
public final static int K0 = 26; // Reserved for kernel
public final static int K1 = 27; // Reserved for kernel
public final static int GP = 28; // Global pointer (the middle of .sdata/.sbss)
public final static int SP = 29; // Stack pointer
public final static int FP = 30; // Frame Pointer
public final static int RA = 31; // Return Address
// Return values (caller saved)
public final static int V0 = 2;
public final static int V1 = 3;
// Argument Registers (caller saved)
public final static int A0 = 4;
public final static int A1 = 5;
public final static int A2 = 6;
public final static int A3 = 7;
// Temporaries (caller saved)
public final static int T0 = 8;
public final static int T1 = 9;
public final static int T2 = 10;
public final static int T3 = 11;
public final static int T4 = 12;
public final static int T5 = 13;
public final static int T6 = 14;
public final static int T7 = 15;
public final static int T8 = 24;
public final static int T9 = 25;
// Saved (callee saved)
public final static int S0 = 16;
public final static int S1 = 17;
public final static int S2 = 18;
public final static int S3 = 19;
public final static int S4 = 20;
public final static int S5 = 21;
public final static int S6 = 22;
public final static int S7 = 23;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,480 @@
// THIS FILE IS AUTOGENERATED! DO NOT EDIT!
// run "make rebuild-constants" if it needs to be updated
package org.ibex.nestedvm;
public interface UsermodeConstants {
public static final int SYS_null = 0;
public static final int SYS_exit = 1;
public static final int SYS_pause = 2;
public static final int SYS_open = 3;
public static final int SYS_close = 4;
public static final int SYS_read = 5;
public static final int SYS_write = 6;
public static final int SYS_sbrk = 7;
public static final int SYS_fstat = 8;
public static final int SYS_lseek = 10;
public static final int SYS_kill = 11;
public static final int SYS_getpid = 12;
public static final int SYS_calljava = 13;
public static final int SYS_stat = 14;
public static final int SYS_gettimeofday = 15;
public static final int SYS_sleep = 16;
public static final int SYS_times = 17;
public static final int SYS_mkdir = 18;
public static final int SYS_getpagesize = 19;
public static final int SYS_unlink = 20;
public static final int SYS_utime = 21;
public static final int SYS_chdir = 22;
public static final int SYS_pipe = 23;
public static final int SYS_dup2 = 24;
public static final int SYS_fork = 25;
public static final int SYS_waitpid = 26;
public static final int SYS_getcwd = 27;
public static final int SYS_exec = 28;
public static final int SYS_fcntl = 29;
public static final int SYS_rmdir = 30;
public static final int SYS_sysconf = 31;
public static final int SYS_readlink = 32;
public static final int SYS_lstat = 33;
public static final int SYS_symlink = 34;
public static final int SYS_link = 35;
public static final int SYS_getdents = 36;
public static final int SYS_memcpy = 37;
public static final int SYS_memset = 38;
public static final int SYS_dup = 39;
public static final int SYS_vfork = 40;
public static final int SYS_chroot = 41;
public static final int SYS_mknod = 42;
public static final int SYS_lchown = 43;
public static final int SYS_ftruncate = 44;
public static final int SYS_usleep = 45;
public static final int SYS_getppid = 46;
public static final int SYS_mkfifo = 47;
public static final int SYS_klogctl = 51;
public static final int SYS_realpath = 52;
public static final int SYS_sysctl = 53;
public static final int SYS_setpriority = 54;
public static final int SYS_getpriority = 55;
public static final int SYS_socket = 56;
public static final int SYS_connect = 57;
public static final int SYS_resolve_hostname = 58;
public static final int SYS_accept = 59;
public static final int SYS_setsockopt = 60;
public static final int SYS_getsockopt = 61;
public static final int SYS_listen = 62;
public static final int SYS_bind = 63;
public static final int SYS_shutdown = 64;
public static final int SYS_sendto = 65;
public static final int SYS_recvfrom = 66;
public static final int SYS_select = 67;
public static final int SYS_getuid = 68;
public static final int SYS_getgid = 69;
public static final int SYS_geteuid = 70;
public static final int SYS_getegid = 71;
public static final int SYS_getgroups = 72;
public static final int SYS_umask = 73;
public static final int SYS_chmod = 74;
public static final int SYS_fchmod = 75;
public static final int SYS_chown = 76;
public static final int SYS_fchown = 77;
public static final int SYS_access = 78;
public static final int SYS_alarm = 79;
public static final int SYS_setuid = 80;
public static final int SYS_setgid = 81;
public static final int SYS_send = 82;
public static final int SYS_recv = 83;
public static final int SYS_getsockname = 84;
public static final int SYS_getpeername = 85;
public static final int SYS_seteuid = 86;
public static final int SYS_setegid = 87;
public static final int SYS_setgroups = 88;
public static final int SYS_resolve_ip = 89;
public static final int SYS_setsid = 90;
public static final int SYS_fsync = 91;
public static final int AF_UNIX = 1;
public static final int AF_INET = 2;
public static final int SOCK_STREAM = 1;
public static final int SOCK_DGRAM = 2;
public static final int HOST_NOT_FOUND = 1;
public static final int TRY_AGAIN = 2;
public static final int NO_RECOVERY = 3;
public static final int NO_DATA = 4;
public static final int SOL_SOCKET = 0xffff;
public static final int SO_REUSEADDR = 0x0004;
public static final int SO_KEEPALIVE = 0x0008;
public static final int SO_BROADCAST = 0x0020;
public static final int SO_TYPE = 0x1008;
public static final int SHUT_RD = 0;
public static final int SHUT_WR = 1;
public static final int SHUT_RDWR = 2;
public static final int INADDR_ANY = 0;
public static final int INADDR_LOOPBACK = 0x7f000001;
public static final int INADDR_BROADCAST = 0xffffffff;
public static final int EPERM = 1; /* Not super-user */
public static final int ENOENT = 2; /* No such file or directory */
public static final int ESRCH = 3; /* No such process */
public static final int EINTR = 4; /* Interrupted system call */
public static final int EIO = 5; /* I/O error */
public static final int ENXIO = 6; /* No such device or address */
public static final int E2BIG = 7; /* Arg list too long */
public static final int ENOEXEC = 8; /* Exec format error */
public static final int EBADF = 9; /* Bad file number */
public static final int ECHILD = 10; /* No children */
public static final int EAGAIN = 11; /* No more processes */
public static final int ENOMEM = 12; /* Not enough core */
public static final int EACCES = 13; /* Permission denied */
public static final int EFAULT = 14; /* Bad address */
public static final int ENOTBLK = 15; /* Block device required */
public static final int EBUSY = 16; /* Mount device busy */
public static final int EEXIST = 17; /* File exists */
public static final int EXDEV = 18; /* Cross-device link */
public static final int ENODEV = 19; /* No such device */
public static final int ENOTDIR = 20; /* Not a directory */
public static final int EISDIR = 21; /* Is a directory */
public static final int EINVAL = 22; /* Invalid argument */
public static final int ENFILE = 23; /* Too many open files in system */
public static final int EMFILE = 24; /* Too many open files */
public static final int ENOTTY = 25; /* Not a typewriter */
public static final int ETXTBSY = 26; /* Text file busy */
public static final int EFBIG = 27; /* File too large */
public static final int ENOSPC = 28; /* No space left on device */
public static final int ESPIPE = 29; /* Illegal seek */
public static final int EROFS = 30; /* Read only file system */
public static final int EMLINK = 31; /* Too many links */
public static final int EPIPE = 32; /* Broken pipe */
public static final int EDOM = 33; /* Math arg out of domain of func */
public static final int ERANGE = 34; /* Math result not representable */
public static final int ENOMSG = 35; /* No message of desired type */
public static final int EIDRM = 36; /* Identifier removed */
public static final int ECHRNG = 37; /* Channel number out of range */
public static final int EL2NSYNC = 38; /* Level 2 not synchronized */
public static final int EL3HLT = 39; /* Level 3 halted */
public static final int EL3RST = 40; /* Level 3 reset */
public static final int ELNRNG = 41; /* Link number out of range */
public static final int EUNATCH = 42; /* Protocol driver not attached */
public static final int ENOCSI = 43; /* No CSI structure available */
public static final int EL2HLT = 44; /* Level 2 halted */
public static final int EDEADLK = 45; /* Deadlock condition */
public static final int ENOLCK = 46; /* No record locks available */
public static final int EBADE = 50; /* Invalid exchange */
public static final int EBADR = 51; /* Invalid request descriptor */
public static final int EXFULL = 52; /* Exchange full */
public static final int ENOANO = 53; /* No anode */
public static final int EBADRQC = 54; /* Invalid request code */
public static final int EBADSLT = 55; /* Invalid slot */
public static final int EDEADLOCK = 56; /* File locking deadlock error */
public static final int EBFONT = 57; /* Bad font file fmt */
public static final int ENOSTR = 60; /* Device not a stream */
public static final int ENODATA = 61; /* No data (for no delay io) */
public static final int ETIME = 62; /* Timer expired */
public static final int ENOSR = 63; /* Out of streams resources */
public static final int ENONET = 64; /* Machine is not on the network */
public static final int ENOPKG = 65; /* Package not installed */
public static final int EREMOTE = 66; /* The object is remote */
public static final int ENOLINK = 67; /* The link has been severed */
public static final int EADV = 68; /* Advertise error */
public static final int ESRMNT = 69; /* Srmount error */
public static final int ECOMM = 70; /* Communication error on send */
public static final int EPROTO = 71; /* Protocol error */
public static final int EMULTIHOP = 74; /* Multihop attempted */
public static final int ELBIN = 75; /* Inode is remote (not really error) */
public static final int EDOTDOT = 76; /* Cross mount point (not really error) */
public static final int EBADMSG = 77; /* Trying to read unreadable message */
public static final int EFTYPE = 79; /* Inappropriate file type or format */
public static final int ENOTUNIQ = 80; /* Given log. name not unique */
public static final int EBADFD = 81; /* f.d. invalid for this operation */
public static final int EREMCHG = 82; /* Remote address changed */
public static final int ELIBACC = 83; /* Can't access a needed shared lib */
public static final int ELIBBAD = 84; /* Accessing a corrupted shared lib */
public static final int ELIBSCN = 85; /* .lib section in a.out corrupted */
public static final int ELIBMAX = 86; /* Attempting to link in too many libs */
public static final int ELIBEXEC = 87; /* Attempting to exec a shared library */
public static final int ENOSYS = 88; /* Function not implemented */
public static final int ENMFILE = 89; /* No more files */
public static final int ENOTEMPTY = 90; /* Directory not empty */
public static final int ENAMETOOLONG = 91; /* File or path name too long */
public static final int ELOOP = 92; /* Too many symbolic links */
public static final int EOPNOTSUPP = 95; /* Operation not supported on transport endpoint */
public static final int EPFNOSUPPORT = 96; /* Protocol family not supported */
public static final int ECONNRESET = 104; /* Connection reset by peer */
public static final int ENOBUFS = 105; /* No buffer space available */
public static final int EAFNOSUPPORT = 106; /* Address family not supported by protocol family */
public static final int EPROTOTYPE = 107; /* Protocol wrong type for socket */
public static final int ENOTSOCK = 108; /* Socket operation on non-socket */
public static final int ENOPROTOOPT = 109; /* Protocol not available */
public static final int ESHUTDOWN = 110; /* Can't send after socket shutdown */
public static final int ECONNREFUSED = 111; /* Connection refused */
public static final int EADDRINUSE = 112; /* Address already in use */
public static final int ECONNABORTED = 113; /* Connection aborted */
public static final int ENETUNREACH = 114; /* Network is unreachable */
public static final int ENETDOWN = 115; /* Network interface is not configured */
public static final int ETIMEDOUT = 116; /* Connection timed out */
public static final int EHOSTDOWN = 117; /* Host is down */
public static final int EHOSTUNREACH = 118; /* Host is unreachable */
public static final int EINPROGRESS = 119; /* Connection already in progress */
public static final int EALREADY = 120; /* Socket already connected */
public static final int EDESTADDRREQ = 121; /* Destination address required */
public static final int EMSGSIZE = 122; /* Message too long */
public static final int EPROTONOSUPPORT = 123; /* Unknown protocol */
public static final int ESOCKTNOSUPPORT = 124; /* Socket type not supported */
public static final int EADDRNOTAVAIL = 125; /* Address not available */
public static final int ENETRESET = 126;
public static final int EISCONN = 127; /* Socket is already connected */
public static final int ENOTCONN = 128; /* Socket is not connected */
public static final int ETOOMANYREFS = 129;
public static final int EPROCLIM = 130;
public static final int EUSERS = 131;
public static final int EDQUOT = 132;
public static final int ESTALE = 133;
public static final int ENOTSUP = 134; /* Not supported */
public static final int ENOMEDIUM = 135; /* No medium (in tape drive) */
public static final int ENOSHARE = 136; /* No such host or network path */
public static final int ECASECLASH = 137; /* Filename exists with different case */
public static final int EILSEQ = 138;
public static final int EOVERFLOW = 139; /* Value too large for defined data type */
public static final int __ELASTERROR = 2000; /* Users can add values starting here */
public static final int F_OK = 0;
public static final int R_OK = 4;
public static final int W_OK = 2;
public static final int X_OK = 1;
public static final int SEEK_SET = 0;
public static final int SEEK_CUR = 1;
public static final int SEEK_END = 2;
public static final int STDIN_FILENO = 0; /* standard input file descriptor */
public static final int STDOUT_FILENO = 1; /* standard output file descriptor */
public static final int STDERR_FILENO = 2; /* standard error file descriptor */
public static final int _SC_ARG_MAX = 0;
public static final int _SC_CHILD_MAX = 1;
public static final int _SC_CLK_TCK = 2;
public static final int _SC_NGROUPS_MAX = 3;
public static final int _SC_OPEN_MAX = 4;
public static final int _SC_JOB_CONTROL = 5;
public static final int _SC_SAVED_IDS = 6;
public static final int _SC_VERSION = 7;
public static final int _SC_PAGESIZE = 8;
public static final int _SC_NPROCESSORS_CONF = 9;
public static final int _SC_NPROCESSORS_ONLN = 10;
public static final int _SC_PHYS_PAGES = 11;
public static final int _SC_AVPHYS_PAGES = 12;
public static final int _SC_MQ_OPEN_MAX = 13;
public static final int _SC_MQ_PRIO_MAX = 14;
public static final int _SC_RTSIG_MAX = 15;
public static final int _SC_SEM_NSEMS_MAX = 16;
public static final int _SC_SEM_VALUE_MAX = 17;
public static final int _SC_SIGQUEUE_MAX = 18;
public static final int _SC_TIMER_MAX = 19;
public static final int _SC_TZNAME_MAX = 20;
public static final int _SC_ASYNCHRONOUS_IO = 21;
public static final int _SC_FSYNC = 22;
public static final int _SC_MAPPED_FILES = 23;
public static final int _SC_MEMLOCK = 24;
public static final int _SC_MEMLOCK_RANGE = 25;
public static final int _SC_MEMORY_PROTECTION = 26;
public static final int _SC_MESSAGE_PASSING = 27;
public static final int _SC_PRIORITIZED_IO = 28;
public static final int _SC_REALTIME_SIGNALS = 29;
public static final int _SC_SEMAPHORES = 30;
public static final int _SC_SHARED_MEMORY_OBJECTS = 31;
public static final int _SC_SYNCHRONIZED_IO = 32;
public static final int _SC_TIMERS = 33;
public static final int _SC_AIO_LISTIO_MAX = 34;
public static final int _SC_AIO_MAX = 35;
public static final int _SC_AIO_PRIO_DELTA_MAX = 36;
public static final int _SC_DELAYTIMER_MAX = 37;
public static final int _SC_THREAD_KEYS_MAX = 38;
public static final int _SC_THREAD_STACK_MIN = 39;
public static final int _SC_THREAD_THREADS_MAX = 40;
public static final int _SC_TTY_NAME_MAX = 41;
public static final int _SC_THREADS = 42;
public static final int _SC_THREAD_ATTR_STACKADDR = 43;
public static final int _SC_THREAD_ATTR_STACKSIZE = 44;
public static final int _SC_THREAD_PRIORITY_SCHEDULING = 45;
public static final int _SC_THREAD_PRIO_INHERIT = 46;
public static final int _SC_THREAD_PRIO_PROTECT = 47;
public static final int _SC_THREAD_PROCESS_SHARED = 48;
public static final int _SC_THREAD_SAFE_FUNCTIONS = 49;
public static final int _SC_GETGR_R_SIZE_MAX = 50;
public static final int _SC_GETPW_R_SIZE_MAX = 51;
public static final int _SC_LOGIN_NAME_MAX = 52;
public static final int _SC_THREAD_DESTRUCTOR_ITERATIONS = 53;
public static final int _SC_STREAM_MAX = 100;
public static final int _SC_PRIORITY_SCHEDULING = 101;
public static final int _PC_LINK_MAX = 0;
public static final int _PC_MAX_CANON = 1;
public static final int _PC_MAX_INPUT = 2;
public static final int _PC_NAME_MAX = 3;
public static final int _PC_PATH_MAX = 4;
public static final int _PC_PIPE_BUF = 5;
public static final int _PC_CHOWN_RESTRICTED = 6;
public static final int _PC_NO_TRUNC = 7;
public static final int _PC_VDISABLE = 8;
public static final int _PC_ASYNC_IO = 9;
public static final int _PC_PRIO_IO = 10;
public static final int _PC_SYNC_IO = 11;
public static final int _PC_POSIX_PERMISSIONS = 90;
public static final int _PC_POSIX_SECURITY = 91;
public static final int MAXPATHLEN = 1024;
public static final int ARG_MAX = 65536; /* max bytes for an exec function */
public static final int CHILD_MAX = 40; /* max simultaneous processes */
public static final int LINK_MAX = 32767; /* max file link count */
public static final int MAX_CANON = 255; /* max bytes in term canon input line */
public static final int MAX_INPUT = 255; /* max bytes in terminal input */
public static final int NAME_MAX = 255; /* max bytes in a file name */
public static final int NGROUPS_MAX = 16; /* max supplemental group id's */
public static final int OPEN_MAX = 64; /* max open files per process */
public static final int PATH_MAX = 1024; /* max bytes in pathname */
public static final int PIPE_BUF = 512; /* max bytes for atomic pipe writes */
public static final int IOV_MAX = 1024; /* max elements in i/o vector */
public static final int BC_BASE_MAX = 99; /* max ibase/obase values in bc(1) */
public static final int BC_DIM_MAX = 2048; /* max array elements in bc(1) */
public static final int BC_SCALE_MAX = 99; /* max scale value in bc(1) */
public static final int BC_STRING_MAX = 1000; /* max const string length in bc(1) */
public static final int COLL_WEIGHTS_MAX = 0; /* max weights for order keyword */
public static final int EXPR_NEST_MAX = 32; /* max expressions nested in expr(1) */
public static final int LINE_MAX = 2048; /* max bytes in an input line */
public static final int RE_DUP_MAX = 255; /* max RE's in interval notation */
public static final int CTL_MAXNAME = 12;
public static final int CTL_UNSPEC = 0; /* unused */
public static final int CTL_KERN = 1; /* "high kernel": proc, limits */
public static final int CTL_VM = 2; /* virtual memory */
public static final int CTL_VFS = 3; /* file system, mount type is next */
public static final int CTL_NET = 4; /* network, see socket.h */
public static final int CTL_DEBUG = 5; /* debugging parameters */
public static final int CTL_HW = 6; /* generic cpu/io */
public static final int CTL_MACHDEP = 7; /* machine dependent */
public static final int CTL_USER = 8; /* user-level */
public static final int CTL_P1003_1B = 9; /* POSIX 1003.1B */
public static final int CTL_MAXID = 10; /* number of valid top-level ids */
public static final int KERN_OSTYPE = 1; /* string: system version */
public static final int KERN_OSRELEASE = 2; /* string: system release */
public static final int KERN_OSREV = 3; /* int: system revision */
public static final int KERN_VERSION = 4; /* string: compile time info */
public static final int KERN_MAXVNODES = 5; /* int: max vnodes */
public static final int KERN_MAXPROC = 6; /* int: max processes */
public static final int KERN_MAXFILES = 7; /* int: max open files */
public static final int KERN_ARGMAX = 8; /* int: max arguments to exec */
public static final int KERN_SECURELVL = 9; /* int: system security level */
public static final int KERN_HOSTNAME = 10; /* string: hostname */
public static final int KERN_HOSTID = 11; /* int: host identifier */
public static final int KERN_CLOCKRATE = 12; /* struct: struct clockrate */
public static final int KERN_VNODE = 13; /* struct: vnode structures */
public static final int KERN_PROC = 14; /* struct: process entries */
public static final int KERN_FILE = 15; /* struct: file entries */
public static final int KERN_PROF = 16; /* node: kernel profiling info */
public static final int KERN_POSIX1 = 17; /* int: POSIX.1 version */
public static final int KERN_NGROUPS = 18; /* int: # of supplemental group ids */
public static final int KERN_JOB_CONTROL = 19; /* int: is job control available */
public static final int KERN_SAVED_IDS = 20; /* int: saved set-user/group-ID */
public static final int KERN_BOOTTIME = 21; /* struct: time kernel was booted */
public static final int KERN_NISDOMAINNAME = 22; /* string: YP domain name */
public static final int KERN_UPDATEINTERVAL = 23; /* int: update process sleep time */
public static final int KERN_OSRELDATE = 24; /* int: OS release date */
public static final int KERN_NTP_PLL = 25; /* node: NTP PLL control */
public static final int KERN_BOOTFILE = 26; /* string: name of booted kernel */
public static final int KERN_MAXFILESPERPROC = 27; /* int: max open files per proc */
public static final int KERN_MAXPROCPERUID = 28; /* int: max processes per uid */
public static final int KERN_DUMPDEV = 29; /* dev_t: device to dump on */
public static final int KERN_IPC = 30; /* node: anything related to IPC */
public static final int KERN_DUMMY = 31; /* unused */
public static final int KERN_PS_STRINGS = 32; /* int: address of PS_STRINGS */
public static final int KERN_USRSTACK = 33; /* int: address of USRSTACK */
public static final int KERN_LOGSIGEXIT = 34; /* int: do we log sigexit procs? */
public static final int KERN_MAXID = 35; /* number of valid kern ids */
public static final int KERN_PROC_ALL = 0; /* everything */
public static final int KERN_PROC_PID = 1; /* by process id */
public static final int KERN_PROC_PGRP = 2; /* by process group id */
public static final int KERN_PROC_SESSION = 3; /* by session of pid */
public static final int KERN_PROC_TTY = 4; /* by controlling tty */
public static final int KERN_PROC_UID = 5; /* by effective uid */
public static final int KERN_PROC_RUID = 6; /* by real uid */
public static final int KERN_PROC_ARGS = 7; /* get/set arguments/proctitle */
public static final int KIPC_MAXSOCKBUF = 1; /* int: max size of a socket buffer */
public static final int KIPC_SOCKBUF_WASTE = 2; /* int: wastage factor in sockbuf */
public static final int KIPC_SOMAXCONN = 3; /* int: max length of connection q */
public static final int KIPC_MAX_LINKHDR = 4; /* int: max length of link header */
public static final int KIPC_MAX_PROTOHDR = 5; /* int: max length of network header */
public static final int KIPC_MAX_HDR = 6; /* int: max total length of headers */
public static final int KIPC_MAX_DATALEN = 7; /* int: max length of data? */
public static final int KIPC_MBSTAT = 8; /* struct: mbuf usage statistics */
public static final int KIPC_NMBCLUSTERS = 9; /* int: maximum mbuf clusters */
public static final int HW_MACHINE = 1; /* string: machine class */
public static final int HW_MODEL = 2; /* string: specific machine model */
public static final int HW_NCPU = 3; /* int: number of cpus */
public static final int HW_BYTEORDER = 4; /* int: machine byte order */
public static final int HW_PHYSMEM = 5; /* int: total memory */
public static final int HW_USERMEM = 6; /* int: non-kernel memory */
public static final int HW_PAGESIZE = 7; /* int: software page size */
public static final int HW_DISKNAMES = 8; /* strings: disk drive names */
public static final int HW_DISKSTATS = 9; /* struct: diskstats[] */
public static final int HW_FLOATINGPT = 10; /* int: has HW floating point? */
public static final int HW_MACHINE_ARCH = 11; /* string: machine architecture */
public static final int HW_MAXID = 12; /* number of valid hw ids */
public static final int USER_CS_PATH = 1; /* string: _CS_PATH */
public static final int USER_BC_BASE_MAX = 2; /* int: BC_BASE_MAX */
public static final int USER_BC_DIM_MAX = 3; /* int: BC_DIM_MAX */
public static final int USER_BC_SCALE_MAX = 4; /* int: BC_SCALE_MAX */
public static final int USER_BC_STRING_MAX = 5; /* int: BC_STRING_MAX */
public static final int USER_COLL_WEIGHTS_MAX = 6; /* int: COLL_WEIGHTS_MAX */
public static final int USER_EXPR_NEST_MAX = 7; /* int: EXPR_NEST_MAX */
public static final int USER_LINE_MAX = 8; /* int: LINE_MAX */
public static final int USER_RE_DUP_MAX = 9; /* int: RE_DUP_MAX */
public static final int USER_POSIX2_VERSION = 10; /* int: POSIX2_VERSION */
public static final int USER_POSIX2_C_BIND = 11; /* int: POSIX2_C_BIND */
public static final int USER_POSIX2_C_DEV = 12; /* int: POSIX2_C_DEV */
public static final int USER_POSIX2_CHAR_TERM = 13; /* int: POSIX2_CHAR_TERM */
public static final int USER_POSIX2_FORT_DEV = 14; /* int: POSIX2_FORT_DEV */
public static final int USER_POSIX2_FORT_RUN = 15; /* int: POSIX2_FORT_RUN */
public static final int USER_POSIX2_LOCALEDEF = 16; /* int: POSIX2_LOCALEDEF */
public static final int USER_POSIX2_SW_DEV = 17; /* int: POSIX2_SW_DEV */
public static final int USER_POSIX2_UPE = 18; /* int: POSIX2_UPE */
public static final int USER_STREAM_MAX = 19; /* int: POSIX2_STREAM_MAX */
public static final int USER_TZNAME_MAX = 20; /* int: POSIX2_TZNAME_MAX */
public static final int USER_MAXID = 21; /* number of valid user ids */
public static final int CTL_P1003_1B_ASYNCHRONOUS_IO = 1; /* boolean */
public static final int CTL_P1003_1B_MAPPED_FILES = 2; /* boolean */
public static final int CTL_P1003_1B_MEMLOCK = 3; /* boolean */
public static final int CTL_P1003_1B_MEMLOCK_RANGE = 4; /* boolean */
public static final int CTL_P1003_1B_MEMORY_PROTECTION = 5; /* boolean */
public static final int CTL_P1003_1B_MESSAGE_PASSING = 6; /* boolean */
public static final int CTL_P1003_1B_PRIORITIZED_IO = 7; /* boolean */
public static final int CTL_P1003_1B_PRIORITY_SCHEDULING = 8; /* boolean */
public static final int CTL_P1003_1B_REALTIME_SIGNALS = 9; /* boolean */
public static final int CTL_P1003_1B_SEMAPHORES = 10; /* boolean */
public static final int CTL_P1003_1B_FSYNC = 11; /* boolean */
public static final int CTL_P1003_1B_SHARED_MEMORY_OBJECTS = 12; /* boolean */
public static final int CTL_P1003_1B_SYNCHRONIZED_IO = 13; /* boolean */
public static final int CTL_P1003_1B_TIMERS = 14; /* boolean */
public static final int CTL_P1003_1B_AIO_LISTIO_MAX = 15; /* int */
public static final int CTL_P1003_1B_AIO_MAX = 16; /* int */
public static final int CTL_P1003_1B_AIO_PRIO_DELTA_MAX = 17; /* int */
public static final int CTL_P1003_1B_DELAYTIMER_MAX = 18; /* int */
public static final int CTL_P1003_1B_MQ_OPEN_MAX = 19; /* int */
public static final int CTL_P1003_1B_PAGESIZE = 20; /* int */
public static final int CTL_P1003_1B_RTSIG_MAX = 21; /* int */
public static final int CTL_P1003_1B_SEM_NSEMS_MAX = 22; /* int */
public static final int CTL_P1003_1B_SEM_VALUE_MAX = 23; /* int */
public static final int CTL_P1003_1B_SIGQUEUE_MAX = 24; /* int */
public static final int CTL_P1003_1B_TIMER_MAX = 25; /* int */
public static final int CTL_P1003_1B_MAXID = 26;
public static final int F_UNLKSYS = 4;
public static final int F_CNVT = 12;
public static final int F_SETFD = 2;
public static final int F_SETFL = 4;
public static final int F_SETLK = 8;
public static final int F_SETOWN = 6;
public static final int F_RDLCK = 1;
public static final int F_WRLCK = 2;
public static final int F_SETLKW = 9;
public static final int F_GETFD = 1;
public static final int F_DUPFD = 0;
public static final int O_WRONLY = 1;
public static final int F_RSETLKW = 13;
public static final int O_RDWR = 2;
public static final int F_RGETLK = 10;
public static final int O_RDONLY = 0;
public static final int F_UNLCK = 3;
public static final int F_GETOWN = 5;
public static final int F_RSETLK = 11;
public static final int F_GETFL = 3;
public static final int F_GETLK = 7;
}

View File

@ -0,0 +1,388 @@
// Copyright 2000-2005 the Contributors, as shown in the revision logs.
// Licensed under the Apache Public Source License 2.0 ("the License").
// You may not use this file except in compliance with the License.
package org.ibex.nestedvm.util;
import java.io.*;
public class ELF {
private static final int ELF_MAGIC = 0x7f454c46; // '\177', 'E', 'L', 'F'
public static final int ELFCLASSNONE = 0;
public static final int ELFCLASS32 = 1;
public static final int ELFCLASS64 = 2;
public static final int ELFDATANONE = 0;
public static final int ELFDATA2LSB = 1;
public static final int ELFDATA2MSB = 2;
public static final int SHT_SYMTAB = 2;
public static final int SHT_STRTAB = 3;
public static final int SHT_NOBITS = 8;
public static final int SHF_WRITE = 1;
public static final int SHF_ALLOC = 2;
public static final int SHF_EXECINSTR = 4;
public static final int PF_X = 0x1;
public static final int PF_W = 0x2;
public static final int PF_R = 0x4;
public static final int PT_LOAD = 1;
public static final short ET_EXEC = 2;
public static final short EM_MIPS = 8;
private Seekable data;
public ELFIdent ident;
public ELFHeader header;
public PHeader[] pheaders;
public SHeader[] sheaders;
private byte[] stringTable;
private boolean sectionReaderActive;
private void readFully(byte[] buf) throws IOException {
int len = buf.length;
int pos = 0;
while(len > 0) {
int n = data.read(buf,pos,len);
if(n == -1) throw new IOException("EOF");
pos += n;
len -= n;
}
}
private int readIntBE() throws IOException {
byte[] buf = new byte[4];
readFully(buf);
return ((buf[0]&0xff)<<24)|((buf[1]&0xff)<<16)|((buf[2]&0xff)<<8)|((buf[3]&0xff)<<0);
}
private int readInt() throws IOException {
int x = readIntBE();
if(ident!=null && ident.data == ELFDATA2LSB)
x = ((x<<24)&0xff000000) | ((x<<8)&0xff0000) | ((x>>>8)&0xff00) | ((x>>24)&0xff);
return x;
}
private short readShortBE() throws IOException {
byte[] buf = new byte[2];
readFully(buf);
return (short)(((buf[0]&0xff)<<8)|((buf[1]&0xff)<<0));
}
private short readShort() throws IOException {
short x = readShortBE();
if(ident!=null && ident.data == ELFDATA2LSB)
x = (short)((((x<<8)&0xff00) | ((x>>8)&0xff))&0xffff);
return x;
}
private byte readByte() throws IOException {
byte[] buf = new byte[1];
readFully(buf);
return buf[0];
}
public class ELFIdent {
public byte klass;
public byte data;
public byte osabi;
public byte abiversion;
ELFIdent() throws IOException {
if(readIntBE() != ELF_MAGIC) throw new ELFException("Bad Magic");
klass = readByte();
if(klass != ELFCLASS32) throw new ELFException("org.ibex.nestedvm.util.ELF does not suport 64-bit binaries");
data = readByte();
if(data != ELFDATA2LSB && data != ELFDATA2MSB) throw new ELFException("Unknown byte order");
readByte(); // version
osabi = readByte();
abiversion = readByte();
for(int i=0;i<7;i++) readByte(); // padding
}
}
public class ELFHeader {
public short type;
public short machine;
public int version;
public int entry;
public int phoff;
public int shoff;
public int flags;
public short ehsize;
public short phentsize;
public short phnum;
public short shentsize;
public short shnum;
public short shstrndx;
ELFHeader() throws IOException {
type = readShort();
machine = readShort();
version = readInt();
if(version != 1) throw new ELFException("version != 1");
entry = readInt();
phoff = readInt();
shoff = readInt();
flags = readInt();
ehsize = readShort();
phentsize = readShort();
phnum = readShort();
shentsize = readShort();
shnum = readShort();
shstrndx = readShort();
}
}
public class PHeader {
public int type;
public int offset;
public int vaddr;
public int paddr;
public int filesz;
public int memsz;
public int flags;
public int align;
PHeader() throws IOException {
type = readInt();
offset = readInt();
vaddr = readInt();
paddr = readInt();
filesz = readInt();
memsz = readInt();
flags = readInt();
align = readInt();
if(filesz > memsz) throw new ELFException("ELF inconsistency: filesz > memsz (" + toHex(filesz) + " > " + toHex(memsz) + ")");
}
public boolean writable() { return (flags & PF_W) != 0; }
public InputStream getInputStream() throws IOException {
return new BufferedInputStream(new SectionInputStream(
offset,offset+filesz));
}
}
public class SHeader {
int nameidx;
public String name;
public int type;
public int flags;
public int addr;
public int offset;
public int size;
public int link;
public int info;
public int addralign;
public int entsize;
SHeader() throws IOException {
nameidx = readInt();
type = readInt();
flags = readInt();
addr = readInt();
offset = readInt();
size = readInt();
link = readInt();
info = readInt();
addralign = readInt();
entsize = readInt();
}
public InputStream getInputStream() throws IOException {
return new BufferedInputStream(new SectionInputStream(
offset, type == SHT_NOBITS ? 0 : offset+size));
}
public boolean isText() { return name.equals(".text"); }
public boolean isData() { return name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"); }
public boolean isBSS() { return name.equals(".bss") || name.equals(".sbss"); }
}
public ELF(String file) throws IOException, ELFException { this(new Seekable.File(file,false)); }
public ELF(Seekable data) throws IOException, ELFException {
this.data = data;
ident = new ELFIdent();
header = new ELFHeader();
pheaders = new PHeader[header.phnum];
for(int i=0;i<header.phnum;i++) {
data.seek(header.phoff+i*header.phentsize);
pheaders[i] = new PHeader();
}
sheaders = new SHeader[header.shnum];
for(int i=0;i<header.shnum;i++) {
data.seek(header.shoff+i*header.shentsize);
sheaders[i] = new SHeader();
}
if(header.shstrndx < 0 || header.shstrndx >= header.shnum) throw new ELFException("Bad shstrndx");
data.seek(sheaders[header.shstrndx].offset);
stringTable = new byte[sheaders[header.shstrndx].size];
readFully(stringTable);
for(int i=0;i<header.shnum;i++) {
SHeader s = sheaders[i];
s.name = getString(s.nameidx);
}
}
private String getString(int off) { return getString(off,stringTable); }
private String getString(int off,byte[] strtab) {
StringBuffer sb = new StringBuffer();
if(off < 0 || off >= strtab.length) return "<invalid strtab entry>";
while(off >= 0 && off < strtab.length && strtab[off] != 0) sb.append((char)strtab[off++]);
return sb.toString();
}
public SHeader sectionWithName(String name) {
for(int i=0;i<sheaders.length;i++)
if(sheaders[i].name.equals(name))
return sheaders[i];
return null;
}
public class ELFException extends IOException { ELFException(String s) { super(s); } }
private class SectionInputStream extends InputStream {
private int pos;
private int maxpos;
SectionInputStream(int start, int end) throws IOException {
if(sectionReaderActive)
throw new IOException("Section reader already active");
sectionReaderActive = true;
pos = start;
data.seek(pos);
maxpos = end;
}
private int bytesLeft() { return maxpos - pos; }
public int read() throws IOException {
byte[] buf = new byte[1];
return read(buf,0,1) == -1 ? -1 : (buf[0]&0xff);
}
public int read(byte[] b, int off, int len) throws IOException {
int n = data.read(b,off,Math.min(len,bytesLeft())); if(n > 0) pos += n; return n;
}
public void close() { sectionReaderActive = false; }
}
private Symtab _symtab;
public Symtab getSymtab() throws IOException {
if(_symtab != null) return _symtab;
if(sectionReaderActive) throw new ELFException("Can't read the symtab while a section reader is active");
SHeader sh = sectionWithName(".symtab");
if(sh == null || sh.type != SHT_SYMTAB) return null;
SHeader sth = sectionWithName(".strtab");
if(sth == null || sth.type != SHT_STRTAB) return null;
byte[] strtab = new byte[sth.size];
DataInputStream dis = new DataInputStream(sth.getInputStream());
dis.readFully(strtab);
dis.close();
return _symtab = new Symtab(sh.offset, sh.size,strtab);
}
public class Symtab {
public Symbol[] symbols;
Symtab(int off, int size, byte[] strtab) throws IOException {
data.seek(off);
int count = size/16;
symbols = new Symbol[count];
for(int i=0;i<count;i++) symbols[i] = new Symbol(strtab);
}
public Symbol getSymbol(String name) {
Symbol sym = null;
for(int i=0;i<symbols.length;i++) {
if(symbols[i].name.equals(name)) {
if(sym == null)
sym = symbols[i];
else
System.err.println("WARNING: Multiple symbol matches for " + name);
}
}
return sym;
}
public Symbol getGlobalSymbol(String name) {
for(int i=0;i<symbols.length;i++)
if(symbols[i].binding == Symbol.STB_GLOBAL && symbols[i].name.equals(name))
return symbols[i];
return null;
}
}
public class Symbol {
public String name;
public int addr;
public int size;
public byte info;
public byte type;
public byte binding;
public byte other;
public short shndx;
public SHeader sheader;
public final static int STT_FUNC = 2;
public final static int STB_GLOBAL = 1;
Symbol(byte[] strtab) throws IOException {
name = getString(readInt(),strtab);
addr = readInt();
size = readInt();
info = readByte();
type = (byte)(info&0xf);
binding = (byte)(info>>4);
other = readByte();
shndx = readShort();
}
}
private static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
/*public static void main(String[] args) throws IOException {
ELF elf = new ELF(new Seekable.InputStream(new FileInputStream(args[0])));
System.out.println("Type: " + toHex(elf.header.type));
System.out.println("Machine: " + toHex(elf.header.machine));
System.out.println("Entry: " + toHex(elf.header.entry));
for(int i=0;i<elf.pheaders.length;i++) {
ELF.PHeader ph = elf.pheaders[i];
System.out.println("PHeader " + toHex(i));
System.out.println("\tOffset: " + ph.offset);
System.out.println("\tVaddr: " + toHex(ph.vaddr));
System.out.println("\tFile Size: " + ph.filesz);
System.out.println("\tMem Size: " + ph.memsz);
}
for(int i=0;i<elf.sheaders.length;i++) {
ELF.SHeader sh = elf.sheaders[i];
System.out.println("SHeader " + toHex(i));
System.out.println("\tName: " + sh.name);
System.out.println("\tOffset: " + sh.offset);
System.out.println("\tAddr: " + toHex(sh.addr));
System.out.println("\tSize: " + sh.size);
System.out.println("\tType: " + toHex(sh.type));
}
Symtab symtab = elf.getSymtab();
if(symtab != null) {
System.out.println("Symbol table:");
for(int i=0;i<symtab.symbols.length;i++)
System.out.println("\t" + symtab.symbols[i].name + " -> " + toHex(symtab.symbols[i].addr));
} else {
System.out.println("Symbol table: None");
}
}*/
}

View File

@ -0,0 +1,207 @@
// Copyright 2000-2005 the Contributors, as shown in the revision logs.
// Licensed under the Apache Public Source License 2.0 ("the License").
// You may not use this file except in compliance with the License.
package org.ibex.nestedvm.util;
// Based on the various org.xwt.util.* classes by Adam Megacz
public class InodeCache {
private static final Object PLACEHOLDER = new Object();
private static final short SHORT_PLACEHOLDER = -2;
private static final short SHORT_NULL = -1;
private static final int LOAD_FACTOR = 2;
private final int maxSize;
private final int totalSlots;
private final int maxUsedSlots;
private final Object[] keys;
private final short[] next;
private final short[] prev;
private final short[] inodes;
private final short[] reverse;
private int size, usedSlots;
private short mru, lru;
public InodeCache() { this(1024); }
public InodeCache(int maxSize) {
this.maxSize = maxSize;
totalSlots = maxSize*LOAD_FACTOR*2 + 3;
maxUsedSlots = totalSlots / LOAD_FACTOR;
if(totalSlots > Short.MAX_VALUE) throw new IllegalArgumentException("cache size too large");
keys = new Object[totalSlots];
next = new short[totalSlots];
prev = new short[totalSlots];
inodes = new short[totalSlots];
reverse = new short[totalSlots];
clear();
}
private static void fill(Object[] a,Object o) { for(int i=0;i<a.length;i++) a[i] = o; }
private static void fill(short[] a, short s) { for(int i=0;i<a.length;i++) a[i] = s; }
public final void clear() {
size = usedSlots = 0;
mru = lru = -1;
fill(keys,null);
fill(inodes,SHORT_NULL);
fill(reverse,SHORT_NULL);
}
public final short get(Object key) {
int hc = key.hashCode() & 0x7fffffff;
int dest = hc % totalSlots;
int odest = dest;
int tries = 1;
boolean plus = true;
Object k;
int placeholder = -1;
while((k = keys[dest]) != null) {
if(k == PLACEHOLDER) {
if(placeholder == -1) placeholder = dest;
} else if(k.equals(key)) {
short inode = inodes[dest];
if(dest == mru) return inode;
if(lru == dest) {
lru = next[lru];
} else {
short p = prev[dest];
short n = next[dest];
next[p] = n;
prev[n] = p;
}
prev[dest] = mru;
next[mru] = (short) dest;
mru = (short) dest;
return inode;
}
dest = Math.abs((odest + (plus ? 1 : -1) * tries * tries) % totalSlots);
if(!plus) tries++;
plus = !plus;
}
// not found
int slot;
if(placeholder == -1) {
// new slot
slot = dest;
if(usedSlots == maxUsedSlots) {
clear();
return get(key);
}
usedSlots++;
} else {
// reuse a placeholder
slot = placeholder;
}
if(size == maxSize) {
// cache is full
keys[lru] = PLACEHOLDER;
inodes[lru] = SHORT_PLACEHOLDER;
lru = next[lru];
} else {
if(size == 0) lru = (short) slot;
size++;
}
int inode;
OUTER: for(inode = hc & 0x7fff;;inode++) {
dest = inode % totalSlots;
odest = dest;
tries = 1;
plus = true;
placeholder = -1;
int r;
while((r = reverse[dest]) != SHORT_NULL) {
int i = inodes[r];
if(i == SHORT_PLACEHOLDER) {
if(placeholder == -1) placeholder = dest;
} else if(i == inode) {
continue OUTER;
}
dest = Math.abs((odest + (plus ? 1 : -1) * tries * tries) % totalSlots);
if(!plus) tries++;
plus = !plus;
}
// found a free inode
if(placeholder != -1) dest = placeholder;
break OUTER;
}
keys[slot] = key;
reverse[dest] = (short) slot;
inodes[slot] = (short) inode;
if(mru != -1) {
prev[slot] = mru;
next[mru] = (short) slot;
}
mru = (short) slot;
return (short) inode;
}
public Object reverse(short inode) {
int dest = inode % totalSlots;
int odest = dest;
int tries = 1;
boolean plus = true;
int r;
while((r = reverse[dest]) != SHORT_NULL) {
if(inodes[r] == inode) return keys[r];
dest = Math.abs((odest + (plus ? 1 : -1) * tries * tries) % totalSlots);
if(!plus) tries++;
plus = !plus;
}
return null;
}
/*private void dump() {
System.err.println("Size " + size);
System.err.println("UsedSlots " + usedSlots);
System.err.println("MRU " + mru);
System.err.println("LRU " + lru);
if(size == 0) return;
for(int i=mru;;i=prev[i]) {
System.err.println("" + i + ": " + keys[i] + " -> " + inodes[i] + "(prev: " + prev[i] + " next: " + next[i] + ")");
if(i == lru) break;
}
}
private void stats() {
int freeKeys = 0;
int freeReverse = 0;
int placeholderKeys = 0;
int placeholderReverse = 0;
for(int i=0;i<totalSlots;i++) {
if(keys[i] == null) freeKeys++;
if(keys[i] == PLACEHOLDER) placeholderKeys++;
if(reverse[i] == SHORT_NULL) freeReverse++;
}
System.err.println("Keys: " + freeKeys + "/" + placeholderKeys);
System.err.println("Reverse: " + freeReverse);
}
public static void main(String[] args) throws Exception {
InodeCache c = new InodeCache();
java.io.BufferedReader br = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
String s;
boolean good = false;
try {
while((s = br.readLine()) != null) {
if(s.charAt(0) == '#') {
short n = Short.parseShort(s.substring(1));
System.err.println("" + n + " -> " + c.reverse(n));
} else {
//System.err.println("Adding " + s);
short n = c.get(s);
System.err.println("Added " + s + " -> " + n);
//c.dump();
}
}
good = true;
} finally {
if(!good) c.stats();
}
}*/
}

View File

@ -0,0 +1,241 @@
// Copyright 2000-2005 the Contributors, as shown in the revision logs.
// Licensed under the Apache Public Source License 2.0 ("the License").
// You may not use this file except in compliance with the License.
package org.ibex.nestedvm.util;
import java.io.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.text.DateFormatSymbols;
/*
GCCLASS_HINT: org.ibex.nestedvm.util.Platform.<clinit> org.ibex.nestedvm.util.Platform$Jdk11.<init>
GCCLASS_HINT: org.ibex.nestedvm.util.Platform.<clinit> org.ibex.nestedvm.util.Platform$Jdk12.<init>
GCCLASS_HINT: org.ibex.nestedvm.util.Platform.<clinit> org.ibex.nestedvm.util.Platform$Jdk13.<init>
GCCLASS_HINT: org.ibex.nestedvm.util.Platform.<clinit> org.ibex.nestedvm.util.Platform$Jdk14.<init>
*/
public abstract class Platform {
Platform() { }
private static final Platform p;
static {
float version;
try {
if(getProperty("java.vm.name").equals("SableVM"))
version = 1.2f;
else
version = Float.valueOf(getProperty("java.specification.version")).floatValue();
} catch(Exception e) {
System.err.println("WARNING: " + e + " while trying to find jvm version - assuming 1.1");
version = 1.1f;
}
String platformClass;
if(version >= 1.4f) platformClass = "Jdk14";
else if(version >= 1.3f) platformClass = "Jdk13";
else if(version >= 1.2f) platformClass = "Jdk12";
else if(version >= 1.1f) platformClass = "Jdk11";
else throw new Error("JVM Specification version: " + version + " is too old. (see org.ibex.util.Platform to add support)");
try {
p = (Platform) Class.forName(Platform.class.getName() + "$" + platformClass).newInstance();
} catch(Exception e) {
e.printStackTrace();
throw new Error("Error instansiating platform class");
}
}
public static String getProperty(String key) {
try {
return System.getProperty(key);
} catch(SecurityException e) {
return null;
}
}
abstract boolean _atomicCreateFile(File f) throws IOException;
public static boolean atomicCreateFile(File f) throws IOException { return p._atomicCreateFile(f); }
abstract Seekable.Lock _lockFile(Seekable s, RandomAccessFile raf, long pos, long size, boolean shared) throws IOException;
public static Seekable.Lock lockFile(Seekable s, RandomAccessFile raf, long pos, long size, boolean shared) throws IOException {
return p._lockFile(s, raf, pos, size, shared); }
abstract void _socketHalfClose(Socket s, boolean output) throws IOException;
public static void socketHalfClose(Socket s, boolean output) throws IOException { p._socketHalfClose(s,output); }
abstract void _socketSetKeepAlive(Socket s, boolean on) throws SocketException;
public static void socketSetKeepAlive(Socket s, boolean on) throws SocketException { p._socketSetKeepAlive(s,on); }
abstract InetAddress _inetAddressFromBytes(byte[] a) throws UnknownHostException;
public static InetAddress inetAddressFromBytes(byte[] a) throws UnknownHostException { return p._inetAddressFromBytes(a); }
abstract String _timeZoneGetDisplayName(TimeZone tz, boolean dst, boolean showlong, Locale l);
public static String timeZoneGetDisplayName(TimeZone tz, boolean dst, boolean showlong, Locale l) { return p._timeZoneGetDisplayName(tz,dst,showlong,l); }
public static String timeZoneGetDisplayName(TimeZone tz, boolean dst, boolean showlong) { return timeZoneGetDisplayName(tz,dst,showlong,Locale.getDefault()); }
abstract void _setFileLength(RandomAccessFile f, int length)
throws IOException;
public static void setFileLength(RandomAccessFile f, int length)
throws IOException { p._setFileLength(f, length); }
abstract File[] _listRoots();
public static File[] listRoots() { return p._listRoots(); }
abstract File _getRoot(File f);
public static File getRoot(File f) { return p._getRoot(f); }
static class Jdk11 extends Platform {
boolean _atomicCreateFile(File f) throws IOException {
// This is not atomic, but its the best we can do on jdk 1.1
if(f.exists()) return false;
new FileOutputStream(f).close();
return true;
}
Seekable.Lock _lockFile(Seekable s, RandomAccessFile raf, long p, long size, boolean shared) throws IOException {
throw new IOException("file locking requires jdk 1.4+");
}
void _socketHalfClose(Socket s, boolean output) throws IOException {
throw new IOException("half closing sockets not supported");
}
InetAddress _inetAddressFromBytes(byte[] a) throws UnknownHostException {
if(a.length != 4) throw new UnknownHostException("only ipv4 addrs supported");
return InetAddress.getByName(""+(a[0]&0xff)+"."+(a[1]&0xff)+"."+(a[2]&0xff)+"."+(a[3]&0xff));
}
void _socketSetKeepAlive(Socket s, boolean on) throws SocketException {
if(on) throw new SocketException("keepalive not supported");
}
String _timeZoneGetDisplayName(TimeZone tz, boolean dst, boolean showlong, Locale l) {
String[][] zs = new DateFormatSymbols(l).getZoneStrings();
String id = tz.getID();
for(int i=0;i<zs.length;i++)
if(zs[i][0].equals(id))
return zs[i][dst ? (showlong ? 3 : 4) : (showlong ? 1 : 2)];
StringBuffer sb = new StringBuffer("GMT");
int off = tz.getRawOffset() / 1000;
if(off < 0) { sb.append("-"); off = -off; }
else sb.append("+");
sb.append(off/3600); off = off%3600;
if(off > 0) sb.append(":").append(off/60); off=off%60;
if(off > 0) sb.append(":").append(off);
return sb.toString();
}
void _setFileLength(RandomAccessFile f, int length) throws IOException{
InputStream in = new FileInputStream(f.getFD());
OutputStream out = new FileOutputStream(f.getFD());
byte[] buf = new byte[1024];
for (int len; length > 0; length -= len) {
len = in.read(buf, 0, Math.min(length, buf.length));
if (len == -1) break;
out.write(buf, 0, len);
}
if (length == 0) return;
// fill the rest of the space with zeros
for (int i=0; i < buf.length; i++) buf[i] = 0;
while (length > 0) {
out.write(buf, 0, Math.min(length, buf.length));
length -= buf.length;
}
}
RandomAccessFile _truncatedRandomAccessFile(File f, String mode) throws IOException {
new FileOutputStream(f).close();
return new RandomAccessFile(f,mode);
}
File[] _listRoots() {
String[] rootProps = new String[]{"java.home","java.class.path","java.library.path","java.io.tmpdir","java.ext.dirs","user.home","user.dir" };
Hashtable<File,Boolean> known = new Hashtable<File,Boolean>();
for(int i=0;i<rootProps.length;i++) {
String prop = getProperty(rootProps[i]);
if(prop == null) continue;
for(;;) {
String path = prop;
int p;
if((p = prop.indexOf(File.pathSeparatorChar)) != -1) {
path = prop.substring(0,p);
prop = prop.substring(p+1);
}
File root = getRoot(new File(path));
//System.err.println(rootProps[i] + ": " + path + " -> " + root);
known.put(root,Boolean.TRUE);
if(p == -1) break;
}
}
File[] ret = new File[known.size()];
int i=0;
for(Enumeration e = known.keys();e.hasMoreElements();)
ret[i++] = (File) e.nextElement();
return ret;
}
File _getRoot(File f) {
if(!f.isAbsolute()) f = new File(f.getAbsolutePath());
String p;
while((p = f.getParent()) != null) f = new File(p);
if(f.getPath().length() == 0) f = new File("/"); // work around a classpath bug
return f;
}
}
static class Jdk12 extends Jdk11 {
boolean _atomicCreateFile(File f) throws IOException {
return f.createNewFile();
}
String _timeZoneGetDisplayName(TimeZone tz, boolean dst, boolean showlong, Locale l) {
return tz.getDisplayName(dst,showlong ? TimeZone.LONG : TimeZone.SHORT, l);
}
void _setFileLength(RandomAccessFile f, int length) throws IOException {
f.setLength(length);
}
File[] _listRoots() { return File.listRoots(); }
}
static class Jdk13 extends Jdk12 {
void _socketHalfClose(Socket s, boolean output) throws IOException {
if(output) s.shutdownOutput();
else s.shutdownInput();
}
void _socketSetKeepAlive(Socket s, boolean on) throws SocketException {
s.setKeepAlive(on);
}
}
static class Jdk14 extends Jdk13 {
InetAddress _inetAddressFromBytes(byte[] a) throws UnknownHostException { return InetAddress.getByAddress(a); }
Seekable.Lock _lockFile(Seekable s, RandomAccessFile r, long pos, long size, boolean shared) throws IOException {
FileLock flock;
try {
flock = pos == 0 && size == 0 ? r.getChannel().lock() :
r.getChannel().tryLock(pos, size, shared);
} catch (OverlappingFileLockException e) { flock = null; }
if (flock == null) return null; // region already locked
return new Jdk14FileLock(s, flock);
}
}
private static final class Jdk14FileLock extends Seekable.Lock {
private final Seekable s;
private final FileLock l;
Jdk14FileLock(Seekable sk, FileLock flock) { s = sk; l = flock; }
public Seekable seekable() { return s; }
public boolean isShared() { return l.isShared(); }
public boolean isValid() { return l.isValid(); }
public void release() throws IOException { l.release(); }
public long position() { return l.position(); }
public long size() { return l.size(); }
public String toString() { return l.toString(); }
}
}

View File

@ -0,0 +1,182 @@
// Copyright 2000-2005 the Contributors, as shown in the revision logs.
// Licensed under the Apache Public Source License 2.0 ("the License").
// You may not use this file except in compliance with the License.
package org.ibex.nestedvm.util;
import java.io.*;
public abstract class Seekable {
public abstract int read(byte[] buf, int offset, int length) throws IOException;
public abstract int write(byte[] buf, int offset, int length) throws IOException;
public abstract int length() throws IOException;
public abstract void seek(int pos) throws IOException;
public abstract void close() throws IOException;
public abstract int pos() throws IOException;
public void sync() throws IOException {
throw new IOException("sync not implemented for " + getClass());
}
public void resize(long length) throws IOException {
throw new IOException("resize not implemented for " + getClass());
}
/** If pos == 0 and size == 0 lock covers whole file. */
public Lock lock(long pos, long size, boolean shared) throws IOException {
throw new IOException("lock not implemented for " + getClass());
}
public int read() throws IOException {
byte[] buf = new byte[1];
int n = read(buf,0,1);
return n == -1 ? -1 : buf[0]&0xff;
}
public int tryReadFully(byte[] buf, int off, int len) throws IOException {
int total = 0;
while(len > 0) {
int n = read(buf,off,len);
if(n == -1) break;
off += n;
len -= n;
total += n;
}
return total == 0 ? -1 : total;
}
public static class ByteArray extends Seekable {
protected byte[] data;
protected int pos;
private final boolean writable;
public ByteArray(byte[] data, boolean writable) {
this.data = data;
this.pos = 0;
this.writable = writable;
}
public int read(byte[] buf, int off, int len) {
len = Math.min(len,data.length-pos);
if(len <= 0) return -1;
System.arraycopy(data,pos,buf,off,len);
pos += len;
return len;
}
public int write(byte[] buf, int off, int len) throws IOException {
if(!writable) throw new IOException("read-only data");
len = Math.min(len,data.length-pos);
if(len <= 0) throw new IOException("no space");
System.arraycopy(buf,off,data,pos,len);
pos += len;
return len;
}
public int length() { return data.length; }
public int pos() { return pos; }
public void seek(int pos) { this.pos = pos; }
public void close() { /*noop*/ }
}
public static class File extends Seekable {
private final java.io.File file;
private final RandomAccessFile raf;
public File(String fileName) throws IOException { this(fileName,false); }
public File(String fileName, boolean writable) throws IOException { this(new java.io.File(fileName),writable,false); }
public File(java.io.File file, boolean writable, boolean truncate) throws IOException {
this.file = file;
String mode = writable ? "rw" : "r";
raf = new RandomAccessFile(file,mode);
if (truncate) Platform.setFileLength(raf, 0);
}
public int read(byte[] buf, int offset, int length) throws IOException { return raf.read(buf,offset,length); }
public int write(byte[] buf, int offset, int length) throws IOException { raf.write(buf,offset,length); return length; }
public void sync() throws IOException { raf.getFD().sync(); }
public void seek(int pos) throws IOException{ raf.seek(pos); }
public int pos() throws IOException { return (int) raf.getFilePointer(); }
public int length() throws IOException { return (int)raf.length(); }
public void close() throws IOException { raf.close(); }
public void resize(long length) throws IOException { Platform.setFileLength(raf, (int)length); }
public boolean equals(Object o) {
return o != null && o instanceof File
&& file.equals(((File)o).file);
}
public Lock lock(long pos, long size, boolean shared)
throws IOException {
return Platform.lockFile(this, raf, pos, size, shared);
}
}
public static class InputStream extends Seekable {
private byte[] buffer = new byte[4096];
private int bytesRead = 0;
private boolean eof = false;
private int pos;
private java.io.InputStream is;
public InputStream(java.io.InputStream is) { this.is = is; }
public int read(byte[] outbuf, int off, int len) throws IOException {
if(pos >= bytesRead && !eof) readTo(pos + 1);
len = Math.min(len,bytesRead-pos);
if(len <= 0) return -1;
System.arraycopy(buffer,pos,outbuf,off,len);
pos += len;
return len;
}
private void readTo(int target) throws IOException {
if(target >= buffer.length) {
byte[] buf2 = new byte[Math.max(buffer.length+Math.min(buffer.length,65536),target)];
System.arraycopy(buffer,0,buf2,0,bytesRead);
buffer = buf2;
}
while(bytesRead < target) {
int n = is.read(buffer,bytesRead,buffer.length-bytesRead);
if(n == -1) {
eof = true;
break;
}
bytesRead += n;
}
}
public int length() throws IOException {
while(!eof) readTo(bytesRead+4096);
return bytesRead;
}
public int write(byte[] buf, int off, int len) throws IOException { throw new IOException("read-only"); }
public void seek(int pos) { this.pos = pos; }
public int pos() { return pos; }
public void close() throws IOException { is.close(); }
}
public abstract static class Lock {
private Object owner = null;
public abstract Seekable seekable();
public abstract boolean isShared();
public abstract boolean isValid();
public abstract void release() throws IOException;
public abstract long position();
public abstract long size();
public void setOwner(Object o) { owner = o; }
public Object getOwner() { return owner; }
public final boolean contains(int start, int len) {
return start >= position() && position() + size() >= start + len;
}
public final boolean contained(int start, int len) {
return start < position() && position() + size() < start + len;
}
public final boolean overlaps(int start, int len) {
return contains(start, len) || contained(start, len);
}
}
}

View File

@ -0,0 +1,47 @@
package org.ibex.nestedvm.util;
public final class Sort {
private Sort() { }
public interface Comparable { public int compareTo(Object o); }
public interface CompareFunc { public int compare(Object a, Object b); }
private static final CompareFunc comparableCompareFunc = new CompareFunc() {
public int compare(Object a,Object b) { return ((Comparable)a).compareTo(b); }
};
public static void sort(Comparable[] a) { sort(a,comparableCompareFunc); }
public static void sort(Object[] a, CompareFunc c) { sort(a,c,0,a.length-1); }
private static void sort(Object[] a, CompareFunc c, int start, int end) {
Object tmp;
if(start >= end) return;
if(end-start <= 6) {
for(int i=start+1;i<=end;i++) {
tmp = a[i];
int j;
for(j=i-1;j>=start;j--) {
if(c.compare(a[j],tmp) <= 0) break;
a[j+1] = a[j];
}
a[j+1] = tmp;
}
return;
}
Object pivot = a[end];
int lo = start - 1;
int hi = end;
do {
while((lo < hi) && c.compare(a[++lo],pivot) < 0) { }
while((hi > lo) && c.compare(a[--hi],pivot) > 0) { }
tmp = a[lo]; a[lo] = a[hi]; a[hi] = tmp;
} while(lo < hi);
tmp = a[lo]; a[lo] = a[end]; a[end] = tmp;
sort(a, c, start, lo-1);
sort(a, c, lo+1, end);
}
}

View File

@ -1,188 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.TilePane?>
<?import javafx.scene.image.*?>
<?import javafx.scene.canvas.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" fx:id="rootPane" prefHeight="384.0" prefWidth="560.0" style="-fx-background-color: black;" stylesheets="@../styles/style.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jace.JaceUIController">
<AnchorPane id="AnchorPane" fx:id="rootPane" prefHeight="384.0" prefWidth="560.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jace.JaceUIController">
<children>
<StackPane fx:id="stackPane" prefHeight="384.0" prefWidth="560.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<ImageView fx:id="appleScreen" fitHeight="384.0" fitWidth="560.0" pickOnBounds="true" style="-fx-background-color: BLACK;" />
<ImageView fx:id="appleScreen" fitHeight="384.0" fitWidth="560.0" pickOnBounds="true" />
<HBox fx:id="notificationBox" alignment="BOTTOM_RIGHT" blendMode="SCREEN" fillHeight="false" maxHeight="45.0" mouseTransparent="true" prefHeight="45.0" prefWidth="560.0" StackPane.alignment="BOTTOM_CENTER" />
<AnchorPane fx:id="menuButtonPane" prefHeight="200.0" prefWidth="200.0">
<children>
<Button fx:id="menuButton" layoutX="494.0" layoutY="14.0" mnemonicParsing="false" styleClass="menuButton" text="☰" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="14.0" />
</children>
</AnchorPane>
<BorderPane fx:id="controlOverlay" visible="false">
<center>
<HBox maxHeight="64.0" prefHeight="64.0" styleClass="uiSpeedSlider" BorderPane.alignment="CENTER">
<children>
<Pane prefHeight="200.0" prefWidth="200.0" HBox.hgrow="SOMETIMES" />
<AnchorPane>
<children>
<ImageView layoutX="2.0" layoutY="2.0">
<image>
<Image url="@../styles/icons/slow.png" />
</image>
</ImageView>
</children>
</AnchorPane>
<Slider fx:id="speedSlider" blockIncrement="1.0" majorTickUnit="5.0" max="10.0" minorTickCount="5" prefHeight="64.0" prefWidth="300.0" showTickLabels="true" showTickMarks="true" snapToTicks="true" value="1.0" HBox.hgrow="ALWAYS">
<HBox.margin>
<Insets />
</HBox.margin>
<padding>
<Insets top="18.0" />
</padding>
</Slider>
<AnchorPane>
<children>
<ImageView layoutX="2.0" layoutY="2.0">
<image>
<Image url="@../styles/icons/fast.png" />
</image>
</ImageView>
</children>
</AnchorPane>
<Pane prefHeight="200.0" prefWidth="200.0" HBox.hgrow="SOMETIMES" />
</children>
</HBox>
</center>
<top>
<HBox fillHeight="false" nodeOrientation="LEFT_TO_RIGHT" BorderPane.alignment="CENTER">
<children>
<TilePane hgap="5.0" nodeOrientation="LEFT_TO_RIGHT" vgap="5.0" HBox.hgrow="SOMETIMES">
<children>
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Info">
<graphic>
<ImageView>
<image>
<Image url="@../styles/icons/info.png" />
</image>
</ImageView>
</graphic>
</Button>
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Config">
<graphic>
<ImageView>
<image>
<Image url="@../styles/icons/config.png" />
</image>
</ImageView>
</graphic>
</Button>
</children>
</TilePane>
<TilePane alignment="TOP_RIGHT" hgap="5.0" vgap="5.0" HBox.hgrow="ALWAYS">
<children>
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="IDE">
<graphic>
<ImageView>
<image>
<Image url="@../styles/icons/ide.png" />
</image>
</ImageView>
</graphic>
</Button>
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Inspect">
<graphic>
<ImageView>
<image>
<Image url="@../styles/icons/inspect.png" />
</image>
</ImageView>
</graphic>
</Button>
<Button alignment="TOP_LEFT" contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Plug-in" TilePane.alignment="TOP_RIGHT">
<graphic>
<ImageView>
<image>
<Image url="@../styles/icons/plugin.png" />
</image>
</ImageView>
</graphic>
</Button>
</children>
</TilePane>
</children>
</HBox>
</top>
<bottom>
<HBox fillHeight="false" nodeOrientation="LEFT_TO_RIGHT" BorderPane.alignment="CENTER">
<children>
<TilePane hgap="5.0" nodeOrientation="LEFT_TO_RIGHT" vgap="5.0" HBox.hgrow="SOMETIMES">
<children>
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Fullscreen">
<graphic>
<ImageView>
<image>
<Image url="@../styles/icons/fullscreen.png" />
</image>
</ImageView>
</graphic>
</Button>
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Aspect">
<graphic>
<ImageView>
<image>
<Image url="@../styles/icons/aspect.png" />
</image>
</ImageView>
</graphic>
</Button>
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Gfx Mode">
<graphic>
<ImageView>
<image>
<Image url="@../styles/icons/screenshot.png" />
</image>
</ImageView>
</graphic>
</Button>
</children>
</TilePane>
<TilePane alignment="TOP_RIGHT" hgap="5.0" vgap="5.0" HBox.hgrow="ALWAYS">
<children>
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Play">
<graphic>
<ImageView>
<image>
<Image url="@../styles/icons/play.png" />
</image>
</ImageView>
</graphic>
</Button>
<Button alignment="TOP_LEFT" contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="Restart" TilePane.alignment="TOP_RIGHT">
<graphic>
<ImageView>
<image>
<Image url="@../styles/icons/reboot.png" />
</image>
</ImageView>
</graphic>
</Button>
</children>
</TilePane>
</children>
</HBox>
</bottom>
<StackPane.margin>
<Insets />
</StackPane.margin>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</BorderPane>
</children>
</StackPane>
</children>

View File

@ -1,36 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.net.URL?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.SubScene?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.TilePane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import java.net.*?>
<?import javafx.scene.canvas.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<!--<?import javafx.scene.canvas.*?>-->
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="520.0" prefWidth="710.0" stylesheets="@../styles/style.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jace.ui.MetacheatUI">
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="520.0" prefWidth="710.0" stylesheets="@../styles/style.css" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jace.ui.MetacheatUI">
<stylesheets>
<URL value="@/styles/style.css" />
</stylesheets>
@ -51,7 +29,11 @@
<items>
<ScrollPane fx:id="memoryViewPane" prefHeight="450.0" prefWidth="391.0">
<content>
<StackPane fx:id="memoryViewContents" prefHeight="150.0" prefWidth="200.0"/>
<StackPane fx:id="memoryViewContents" prefHeight="150.0" prefWidth="200.0">
<children>
<Canvas fx:id="memoryViewCanvas" height="200.0" width="200.0" />
</children>
</StackPane>
</content></ScrollPane>
<ScrollPane fitToHeight="true" fitToWidth="true" prefHeight="200.0" prefWidth="200.0">
<content>

View File

@ -26,8 +26,7 @@
</accelerator>
<items>
<MenuItem mnemonicParsing="false" onAction="#newAssemblyListingClicked" text="Assembly Listing" />
<MenuItem mnemonicParsing="false" onAction="#newApplesoftBasicClicked" text="Applesoft Basic Listing (blank)" />
<MenuItem mnemonicParsing="false" onAction="#newApplesoftBasicFromMemoryClicked" text="Applesoft Basic Listing (from memory)" />
<MenuItem mnemonicParsing="false" onAction="#newApplesoftBasicClicked" text="Applesoft Basic Listing" />
<MenuItem mnemonicParsing="false" onAction="#newPlainTextClicked" text="Plain Text" />
<MenuItem mnemonicParsing="false" onAction="#newHexdataClicked" text="Data (Hex)" />
</items>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 800 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 959 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -3,7 +3,7 @@
}
.setting-row {
-fx-padding: 5 0 0 4;
-fx-padding: 5 0 0 4;
}
.setting-label, .setting-keyboard-shortcut {
@ -19,30 +19,3 @@
-fx-font-size: 9pt;
-fx-font-family: "Courier New";
}
.menuButton {
-fx-font-size:16pt;
-fx-border-radius: 10px;
-fx-background-radius: 10px;
}
.menuButton, .uiActionButton, .uiSpeedSlider ImageView, .uiSpeedSlider Slider, .uiSpeedSlider AnchorPane {
-fx-background-color: rgba(0, 0, 0, 0.75);
-fx-text-fill: #a0FFa0
}
.uiActionButton ImageView, .uiSpeedSlider ImageView {
-fx-effect: dropshadow(gaussian , rgba(128,255,128,0.75) , 2,1.0,0,0);
}
.uiSpeedSlider AnchorPane {
-fx-padding: 0 5 0 5
}
.uiSpeedSlider Slider {
-fx-padding: 18 0 10 0
}
.uiSpeedSlider Slider NumberAxis {
-fx-tick-label-fill: #80ff80
}

View File

@ -1,115 +0,0 @@
/*
* Copyright 2016 Brendan Robert
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jace.cpu;
import jace.Emulator;
import jace.apple2e.Apple2e;
import jace.apple2e.MOS65C02;
import jace.core.Computer;
import jace.core.RAM;
import jace.core.SoundMixer;
import jace.core.Utility;
import jace.ide.HeadlessProgram;
import jace.ide.Program;
import org.junit.AfterClass;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Basic test functionality to assert correct 6502 decode and execution.
*
* @author blurry
*/
public class Basic6502FuncationalityTest {
static Computer computer;
static MOS65C02 cpu;
static RAM ram;
@BeforeClass
public static void setupClass() {
Utility.setHeadlessMode(true);
SoundMixer.MUTE = true;
computer = new Apple2e();
cpu = (MOS65C02) computer.getCpu();
ram = computer.getMemory();
Emulator.computer = (Apple2e) computer;
computer.pause();
}
@AfterClass
public static void teardownClass() {
}
@Before
public void setup() {
cpu.suspend();
cpu.clearState();
}
@Test
public void testAdditionNonDecimal() {
cpu.A = 0;
cpu.D = false;
cpu.C = 0;
assemble(" adc #1");
assertEquals("0+1 (c=0) = 1", 1, cpu.A);
assertFalse("Result is not zero", cpu.Z);
}
@Test
public void testAdditionNonDecimalWithCarry() {
cpu.A = 0;
cpu.D = false;
cpu.C = 1;
assemble(" adc #1");
assertEquals("0+1 (c=1) = 2", 2, cpu.A);
assertFalse("Result is not zero", cpu.Z);
}
@Test
public void testAdditionDecimal() {
cpu.A = 9;
cpu.D = true;
cpu.C = 0;
assemble(" adc #1");
assertEquals("9+1 (c=0) = 0x10", 0x10, cpu.A);
assertFalse("Result is not zero", cpu.Z);
}
@Test
public void testAdditionDecimalWithCarry() {
cpu.A = 9;
cpu.D = true;
cpu.C = 1;
assemble(" adc #1");
assertEquals("9+1 (c=1) = 0x11", 0x11, cpu.A);
assertFalse("Result is not zero", cpu.Z);
}
private void assemble(String code) {
assembleAt(code, 0x0300);
}
private void assembleAt(String code, int addr) {
HeadlessProgram program = new HeadlessProgram(Program.DocumentType.assembly);
program.setValue("*="+Integer.toHexString(addr)+"\n"+code+"\n BRK");
program.execute();
cpu.tick();
}
}

View File

@ -54,14 +54,14 @@ public class ApplesoftTest {
}
@Test
public void deserializeBinaryTest() {
public void deserializeBinary() {
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
assertNotNull(program);
assertNotSame("", program.toString());
}
@Test
public void roundTripStringComparisonTest() {
public void roundTripStringComparison() {
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
String serialized = program.toString();
ApplesoftProgram deserialized = ApplesoftProgram.fromString(serialized);

View File

@ -1,79 +0,0 @@
/*
* Copyright 2019 Brendan Robert
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jace.scripting;
import jace.Emulator;
import jace.apple2e.Apple2e;
import jace.apple2e.MOS65C02;
import jace.apple2e.VideoNTSC;
import jace.apple2e.VideoNTSC.VideoMode;
import jace.core.Computer;
import jace.core.RAM;
import jace.core.SoundMixer;
import jace.core.Utility;
import java.util.Arrays;
import javafx.embed.swing.JFXPanel;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Check out various command line arguments to see if they do the right thing
*/
public class TestCommandlineArgs {
static Computer computer;
static MOS65C02 cpu;
static RAM ram;
@BeforeClass
public static void initJavaFX() {
new JFXPanel();
}
@Before
public void setup() {
Utility.setHeadlessMode(true);
SoundMixer.MUTE = true;
computer = new Apple2e();
cpu = (MOS65C02) computer.getCpu();
ram = computer.getMemory();
Emulator.computer = (Apple2e) computer;
computer.pause();
cpu.suspend();
cpu.clearState();
}
static String GRAPHICS_MODE = "vid.videomode";
@Test
public void testVideoModes() {
for (VideoMode mode : VideoMode.values()) {
Emulator.instance.applyConfiguration(Arrays.asList("-" + GRAPHICS_MODE, mode.name().toLowerCase()));
assertTrue("Should be NTSC video module", computer.video instanceof VideoNTSC);
VideoNTSC video = (VideoNTSC) computer.video;
assertEquals("Should have switched to " + mode.name() + " mode", mode, video.getVideoMode());
Emulator.instance.applyConfiguration(Arrays.asList("-" + GRAPHICS_MODE, mode.name().toUpperCase()));
assertTrue("Should be NTSC video module", computer.video instanceof VideoNTSC);
video = (VideoNTSC) computer.video;
assertEquals("Should have switched to " + mode.name() + " mode", mode, video.getVideoMode());
}
}
}

Some files were not shown because too many files have changed in this diff Show More