forked from Apple-2-Tools/jace
Compare commits
77 Commits
v2.0b
...
2.0-Stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ce60ea00e | ||
|
|
3bed89e530 | ||
|
|
c773fda3ff | ||
|
|
3678f294df | ||
|
|
90d13dfde0 | ||
|
|
f19b0847ad | ||
|
|
d4e6724bf0 | ||
|
|
3b9d6a232e | ||
|
|
b57d3e06e0 | ||
|
|
8a0e633a63 | ||
|
|
3e676cd3c5 | ||
|
|
29161bcf7e | ||
|
|
0b696a2379 | ||
|
|
fe062e2d15 | ||
|
|
ab5d3472c0 | ||
|
|
a544cae02c | ||
|
|
aef9c086b4 | ||
|
|
271dd70f22 | ||
|
|
e6594514ef | ||
|
|
7668e723ec | ||
|
|
b4ee1fafd8 | ||
|
|
8833f80138 | ||
|
|
eb21dfaec1 | ||
|
|
ad819c5678 | ||
|
|
deee9a2a72 | ||
|
|
4c308f997e | ||
|
|
6515c90856 | ||
|
|
3f115624e6 | ||
|
|
3123ee1eec | ||
|
|
abbe5d007e | ||
|
|
ce6a8ca873 | ||
|
|
a0fb1a2149 | ||
|
|
ef9f7019e5 | ||
|
|
7edbbd9c8d | ||
|
|
5542bb3425 | ||
|
|
609ad08bfd | ||
|
|
4a371ceec3 | ||
|
|
d845a10de4 | ||
|
|
921ce3a0b0 | ||
|
|
58199776a5 | ||
|
|
4769e6c5aa | ||
|
|
5982151cf8 | ||
|
|
756a0c6bce | ||
|
|
76c4ff4cce | ||
|
|
bfca447b4a | ||
|
|
b28a39a059 | ||
|
|
af1bdb6b35 | ||
|
|
97b367ccce | ||
|
|
d5d2424915 | ||
|
|
61356782b6 | ||
|
|
b9fcc6d82d | ||
|
|
8064a804eb | ||
|
|
fa7cab9866 | ||
|
|
9a6737e8f0 | ||
|
|
07c71f0117 | ||
|
|
24f6f7e23f | ||
|
|
bffe4eba22 | ||
|
|
34ef28be1f | ||
|
|
b5c7418b84 | ||
|
|
1548c1b327 | ||
|
|
4c8e809b46 | ||
|
|
676256b6fc | ||
|
|
292b84d1e9 | ||
|
|
c261f0f103 | ||
|
|
ca6a831020 | ||
|
|
732f4768a6 | ||
|
|
5f9352abb3 | ||
|
|
8bcf3a922a | ||
|
|
59ab31f433 | ||
|
|
6c4136841e | ||
|
|
17b8183a31 | ||
|
|
d03c1d9333 | ||
|
|
893052b004 | ||
|
|
9f838d11f1 | ||
|
|
633b514b38 | ||
|
|
4021af3ac6 | ||
|
|
cf87f30e35 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -18,4 +18,5 @@ hs_err_pid*
|
||||
/.gitignore
|
||||
/inputFiles.lst
|
||||
*.DS_Store
|
||||
!/lib/nestedvm.jar
|
||||
!/lib/nestedvm.jar
|
||||
_acme_tmp*
|
||||
|
||||
35
.travis.yml
35
.travis.yml
@@ -1,16 +1,19 @@
|
||||
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
|
||||
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
|
||||
14
README.md
14
README.md
@@ -1,7 +1,19 @@
|
||||
Java Apple Computer Emulator
|
||||
====
|
||||
|
||||
[Download current version with built-in IDE](https://github.com/badvision/jace/releases/download/v2.0b/jace-2.0-SNAPSHOT.zip)
|
||||
Download:
|
||||
|
||||
* [See releases page for most recent](https://github.com/badvision/jace/releases)
|
||||
|
||||
To Run:
|
||||
|
||||
* See [run.sh](run.sh)
|
||||
|
||||
or `java -jar Jace.jar`
|
||||
|
||||
To Build:
|
||||
|
||||
* See [build.sh](build.sh)
|
||||
|
||||
Jace is a java-based Apple //e emulator with many compelling features:
|
||||
* NEW: Built-in IDE for writing basic and assembly programs, using ACME to compile and execute directly without leaving the emulator.
|
||||
|
||||
101
build.sh
Executable file
101
build.sh
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Building Jace requies:
|
||||
#
|
||||
# * Maven
|
||||
# * Java 1.8 JDK
|
||||
#
|
||||
# On OSX the easiest way to install Maven is to use brew
|
||||
#
|
||||
# brew install maven
|
||||
#
|
||||
#
|
||||
# Troubleshooting:
|
||||
# ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.3:compile (default-compile) on project jace: Fatal error compiling: invalid target release: 1.8 -> [Help 1]
|
||||
# org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.3:compile (default-compile) on project jace: Fatal error compiling
|
||||
#
|
||||
# Cause: You probably have the Java 1.8 RUNTIME installed but Maven is (trying to) use the Java 1.7 COMPILER.
|
||||
# OR : You probably have Java 1.7 installed but Maven is (trying to) use Java 1.8
|
||||
# Reference: http://stackoverflow.com/questions/24705877/cant-get-maven-to-recognize-java-1-8
|
||||
#
|
||||
# 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 COMPILTE Java programs.
|
||||
#
|
||||
# Solution:
|
||||
#
|
||||
# 1. Check which verison of Java that Maven is using
|
||||
#
|
||||
# mvn -version
|
||||
#
|
||||
# 2. Check which version of the Java JRE is installed:
|
||||
#
|
||||
# java -version
|
||||
#
|
||||
# You should see something like this:
|
||||
#
|
||||
# java version "1.8.0_66"
|
||||
# Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
|
||||
#
|
||||
# 3. Check which version of the Java JDK is installed:
|
||||
#
|
||||
# javac -version
|
||||
#
|
||||
# If you see something like this:
|
||||
#
|
||||
# javac 1.7.0_75
|
||||
#
|
||||
# Then you will need to proceed to the next step, else you can skip it.
|
||||
#
|
||||
# 4. Install Java 1.8 JDK (if necessary)
|
||||
#
|
||||
# You can download the JDK either via the GUI or the command line:
|
||||
# http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
|
||||
#
|
||||
# To download from the command line:
|
||||
#
|
||||
# For OSX
|
||||
# curl -L -O -H "Cookie: oraclelicense=accept-securebackup-cookie" -k "https://edelivery.oracle.com/otn-pub/java/jdk/8u66-b17/jdk-8u66-macosx-x64.dmg"
|
||||
# open jdk-8u66-macosx-x64.dmg
|
||||
# Double-click on the .pkg
|
||||
#
|
||||
# For Linux
|
||||
# curl -L -O -H "Cookie: oraclelicense=accept-securebackup-cookie" -k "https://edelivery.oracle.com/otn-pub/java/jdk/8u20-b26/jdk-8u20-linux-i586.tar.gz"
|
||||
#
|
||||
# Reference:
|
||||
# Commands / shell script to download JDK / JRE / Java binaries from Oracle website from terminal / shell / command line / command prompt.
|
||||
# https://gist.github.com/P7h/9741922
|
||||
#
|
||||
# 5. Lastly, verify that JAVA_HOME is set:
|
||||
#
|
||||
# echo ${JAVA_HOME}
|
||||
#
|
||||
# If it is blank (or not set), set it via:
|
||||
#
|
||||
# export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
|
||||
#
|
||||
# Then you can (finally!) build JACE. Whew!
|
||||
#
|
||||
# Note: Changing the maven project file 'pom.xml' to use Java 1.7 *won't* work:
|
||||
# <plugin>
|
||||
# <groupId>org.apache.maven.plugins</groupId>
|
||||
# <artifactId>maven-compiler-plugin</artifactId>
|
||||
# <version>3.3</version>
|
||||
# <configuration>
|
||||
# <source>1.7</source>
|
||||
# <target>1.7</target>
|
||||
#
|
||||
# As the source code is using Java 1.8 langauge features.
|
||||
|
||||
if [[ -z "$JAVA_HOME" ]]; then
|
||||
echo "WARNING: JAVA_HOME was not set"
|
||||
echo "... Defaulting to Java 1.8..."
|
||||
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
|
||||
echo "... JAVA_HOME=${JAVA_HOME}"
|
||||
fi
|
||||
|
||||
#mvn clean install -X
|
||||
|
||||
mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
|
||||
|
||||
@@ -14,5 +14,6 @@ That way multiple projects can share the same settings (useful for formatting ru
|
||||
Any value defined here will override the pom.xml file value but is only applicable to the current project.
|
||||
-->
|
||||
<org-netbeans-modules-html-editor-lib.default-html-public-id>HTML5</org-netbeans-modules-html-editor-lib.default-html-public-id>
|
||||
<netbeans.hint.jdkPlatform>JDK_1.8</netbeans.hint.jdkPlatform>
|
||||
</properties>
|
||||
</project-shared-configuration>
|
||||
|
||||
81
pom.xml
81
pom.xml
@@ -12,38 +12,18 @@
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<mainClass>jace.JaceApplication</mainClass>
|
||||
<netbeans.hint.license>apache20</netbeans.hint.license>
|
||||
</properties>
|
||||
|
||||
<organization>
|
||||
<name>badvision</name>
|
||||
<!-- Used as the 'Vendor' for JNLP generation -->
|
||||
<name>org.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>
|
||||
<version>2.10</version>
|
||||
@@ -61,22 +41,20 @@
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>-->
|
||||
|
||||
<!-- <plugin>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.2.1</version>
|
||||
<version>1.4.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack-dependencies</id>
|
||||
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>${java.home}/../bin/javafxpackager</executable>
|
||||
<executable>${java.home}/../bin/javapackager</executable>
|
||||
<arguments>
|
||||
<argument>-createjar</argument>
|
||||
<argument>-nocss2bin</argument>
|
||||
@@ -91,51 +69,21 @@
|
||||
</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>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.3</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<compilerArguments>
|
||||
<bootclasspath>${sun.boot.class.path}${path.separator}${java.home}/lib/jfxrt.jar</bootclasspath>
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.18</version>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<additionalClasspathElements>
|
||||
<additionalClasspathElement>${java.home}/lib/jfxrt.jar</additionalClasspathElement>
|
||||
</additionalClasspathElements>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
<version>3.5</version>
|
||||
</plugin>
|
||||
</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>
|
||||
@@ -147,5 +95,10 @@
|
||||
<version>4.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial.thirdparty</groupId>
|
||||
<artifactId>nestedvm</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -46,6 +46,7 @@ public class Emulator {
|
||||
* @param args
|
||||
*/
|
||||
public Emulator(List<String> args) {
|
||||
instance = this;
|
||||
computer = new Apple2e();
|
||||
Configuration.buildTree();
|
||||
Configuration.loadSettings();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package jace;
|
||||
|
||||
import com.sun.javafx.tk.quantum.OverlayWarning;
|
||||
import jace.apple2e.MOS65C02;
|
||||
import jace.apple2e.RAM128k;
|
||||
import jace.apple2e.SoftSwitches;
|
||||
@@ -52,6 +53,7 @@ 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;
|
||||
@@ -204,10 +206,10 @@ public class EmulatorUILogic implements Reconfigurable {
|
||||
Emulator.computer.resume();
|
||||
return;
|
||||
}
|
||||
runFile(binary);
|
||||
runFileNamed(binary);
|
||||
}
|
||||
|
||||
public static void runFile(File binary) {
|
||||
public static void runFileNamed(File binary) {
|
||||
String fileName = binary.getName().toLowerCase();
|
||||
try {
|
||||
if (fileName.contains("#06")) {
|
||||
@@ -240,23 +242,6 @@ 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",
|
||||
@@ -273,14 +258,18 @@ 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() {
|
||||
// AbstractEmulatorFrame frame = Emulator.getFrame();
|
||||
// if (frame == null) {
|
||||
// return;
|
||||
// }
|
||||
// Emulator.computer.pause();
|
||||
// frame.toggleFullscreen();
|
||||
// Emulator.computer.resume();
|
||||
Platform.runLater(() -> {
|
||||
Stage stage = JaceApplication.getApplication().primaryStage;
|
||||
stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
|
||||
stage.setFullScreen(!stage.isFullScreen());
|
||||
});
|
||||
}
|
||||
|
||||
@InvokableAction(
|
||||
@@ -397,6 +386,51 @@ 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 = "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(() -> {
|
||||
JaceApplication.getApplication().primaryStage.setFullScreen(false);
|
||||
size++;
|
||||
if (size > 3) {
|
||||
size = 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
Stage stage = JaceApplication.getApplication().primaryStage;
|
||||
double vgap = stage.getScene().getY();
|
||||
double hgap = stage.getScene().getX();
|
||||
stage.setWidth(hgap*2 + width);
|
||||
stage.setHeight(vgap + height);
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean confirm(String message) {
|
||||
// return JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Emulator.getFrame(), message);
|
||||
return false;
|
||||
@@ -478,6 +512,12 @@ 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";
|
||||
|
||||
@@ -47,7 +47,8 @@ public class JaceApplication extends Application {
|
||||
Scene s = new Scene(node);
|
||||
primaryStage.setScene(s);
|
||||
primaryStage.setTitle("Jace");
|
||||
primaryStage.getIcons().add(Utility.loadIcon("woz_figure.gif"));
|
||||
EmulatorUILogic.scaleIntegerRatio();
|
||||
Utility.loadIcon("woz_figure.gif").ifPresent(primaryStage.getIcons()::add);
|
||||
} catch (IOException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
@@ -58,7 +59,7 @@ public class JaceApplication extends Application {
|
||||
while (Emulator.computer.getVideo() == null || Emulator.computer.getVideo().getFrameBuffer() == null) {
|
||||
Thread.yield();
|
||||
}
|
||||
controller.connectComputer(Emulator.computer);
|
||||
controller.connectComputer(Emulator.computer, primaryStage);
|
||||
bootWatchdog();
|
||||
});
|
||||
primaryStage.setOnCloseRequest(event -> {
|
||||
@@ -86,7 +87,7 @@ public class JaceApplication extends Application {
|
||||
Scene s = new Scene(node);
|
||||
cheatStage.setScene(s);
|
||||
cheatStage.setTitle("Jace: MetaCheat");
|
||||
cheatStage.getIcons().add(Utility.loadIcon("woz_figure.gif"));
|
||||
Utility.loadIcon("woz_figure.gif").ifPresent(cheatStage.getIcons()::add);
|
||||
} catch (IOException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* 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.Keyboard;
|
||||
import jace.library.MediaCache;
|
||||
import jace.library.MediaConsumer;
|
||||
import jace.library.MediaConsumerParent;
|
||||
@@ -34,15 +34,20 @@ import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Label;
|
||||
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.CornerRadii;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -55,7 +60,7 @@ public class JaceUIController {
|
||||
|
||||
@FXML
|
||||
private AnchorPane rootPane;
|
||||
|
||||
|
||||
@FXML
|
||||
private StackPane stackPane;
|
||||
|
||||
@@ -66,7 +71,7 @@ public class JaceUIController {
|
||||
private ImageView appleScreen;
|
||||
|
||||
Computer computer;
|
||||
|
||||
|
||||
@FXML
|
||||
void initialize() {
|
||||
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
||||
@@ -78,17 +83,18 @@ public class JaceUIController {
|
||||
rootPane.setOnDragEntered(this::processDragEnteredEvent);
|
||||
rootPane.setOnDragExited(this::processDragExitedEvent);
|
||||
}
|
||||
|
||||
public void connectComputer(Computer computer) {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
private void processDragEnteredEvent(DragEvent evt) {
|
||||
MediaEntry media = null;
|
||||
if (evt.getDragboard().hasFiles()) {
|
||||
@@ -112,63 +118,68 @@ 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.isFile()) 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();
|
||||
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);
|
||||
.filter((consumer) -> (consumer.isAccepted(media, media.files.get(0))))
|
||||
.forEach((consumer) -> {
|
||||
Label icon = consumer.getIcon().orElse(null);
|
||||
if (icon == null) {
|
||||
return;
|
||||
}
|
||||
endDragEvent();
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
stackPane.getChildren().add(drivePanel);
|
||||
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<>();
|
||||
for (Optional<Card> card : computer.memory.getAllCards()) {
|
||||
@@ -178,14 +189,16 @@ public class JaceUIController {
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -195,40 +208,61 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ 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;
|
||||
@@ -193,6 +194,12 @@ public class Apple2e extends Computer {
|
||||
@Override
|
||||
public final void reconfigure() {
|
||||
boolean restart = pause();
|
||||
|
||||
if (Utility.isHeadlessMode()) {
|
||||
joy1enabled = false;
|
||||
joy2enabled = false;
|
||||
|
||||
}
|
||||
|
||||
super.reconfigure();
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import jace.core.Computer;
|
||||
import jace.core.RAM;
|
||||
import jace.core.RAMEvent.TYPE;
|
||||
import jace.state.Stateful;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
@@ -36,6 +37,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;
|
||||
static int RESET_VECTOR = 0x00FFFC;
|
||||
@@ -75,12 +77,30 @@ 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 enum OPCODE {
|
||||
ADC_IMM(0x0069, COMMAND.ADC, MODE.IMMEDIATE, 2),
|
||||
ADC_ZP(0x0065, COMMAND.ADC, MODE.ZEROPAGE, 3),
|
||||
@@ -209,6 +229,7 @@ public class MOS65C02 extends CPU {
|
||||
LSR_AB(0x004E, COMMAND.LSR, MODE.ABSOLUTE, 6),
|
||||
LSR_AB_X(0x005E, COMMAND.LSR, MODE.ABSOLUTE_X, 7),
|
||||
NOP(0x00EA, COMMAND.NOP, MODE.IMPLIED, 2),
|
||||
SPECIAL(0x00FC, COMMAND.NOP_SPECIAL, MODE.ABSOLUTE, 4),
|
||||
ORA_IMM(0x0009, COMMAND.ORA, MODE.IMMEDIATE, 2),
|
||||
ORA_ZP(0x0005, COMMAND.ORA, MODE.ZEROPAGE, 3),
|
||||
ORA_ZP_X(0x0015, COMMAND.ORA, MODE.ZEROPAGE_X, 4),
|
||||
@@ -366,7 +387,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 & 0x00ff00));
|
||||
cpu.setPageBoundaryPenalty((address & 0x00ff00) != ((pc+2) & 0x00ff00));
|
||||
return address;
|
||||
}, false),
|
||||
IMMEDIATE(2, "#$~1", (cpu) -> cpu.getProgramCounter() + 1),
|
||||
@@ -415,21 +436,21 @@ public class MOS65C02 extends CPU {
|
||||
}
|
||||
return address;
|
||||
}),
|
||||
ZP_REL(2, "$~1,$R", new AddressCalculator() {
|
||||
ZP_REL(3, "$~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 + 2 + cpu.getMemory().read(pc + 2, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
|
||||
int address = pc + 3 + 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 & 0x00ff00));
|
||||
cpu.setPageBoundaryPenalty((address & 0x00ff00) != ((pc+3) & 0x00ff00));
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getValue(boolean isRead, MOS65C02 cpu) {
|
||||
int pc = cpu.getProgramCounter();
|
||||
int address = cpu.getMemory().read(pc + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
|
||||
int address = 0x0ff & cpu.getMemory().read(pc + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
|
||||
return cpu.getMemory().read(address, TYPE.READ_DATA, true, false);
|
||||
}
|
||||
});
|
||||
@@ -522,10 +543,7 @@ public class MOS65C02 extends CPU {
|
||||
|
||||
@Override
|
||||
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu) {
|
||||
if (((value >> bit) & 1) != 0) {
|
||||
return;
|
||||
}
|
||||
if (cpu.C != 0) {
|
||||
if ((value & (1 << bit)) == 0) {
|
||||
cpu.setProgramCounter(address);
|
||||
cpu.addWaitCycles(cpu.pageBoundaryPenalty ? 2 : 1);
|
||||
}
|
||||
@@ -542,10 +560,7 @@ public class MOS65C02 extends CPU {
|
||||
|
||||
@Override
|
||||
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu) {
|
||||
if (((value >> bit) & 1) == 0) {
|
||||
return;
|
||||
}
|
||||
if (cpu.C != 0) {
|
||||
if ((value & (1 << bit)) != 0) {
|
||||
cpu.setProgramCounter(address);
|
||||
cpu.addWaitCycles(cpu.pageBoundaryPenalty ? 2 : 1);
|
||||
}
|
||||
@@ -583,7 +598,6 @@ public class MOS65C02 extends CPU {
|
||||
}
|
||||
|
||||
public enum COMMAND {
|
||||
|
||||
ADC((address, value, addressMode, cpu) -> {
|
||||
int w;
|
||||
cpu.V = ((cpu.A ^ value) & 0x080) == 0;
|
||||
@@ -824,6 +838,11 @@ public class MOS65C02 extends CPU {
|
||||
}),
|
||||
NOP((address, value, addressMode, cpu) -> {
|
||||
}),
|
||||
NOP_SPECIAL((address, value, addressMode, cpu) -> {
|
||||
byte param1 = (byte) (address & 0x0ff);
|
||||
byte param2 = (byte) (address >> 8);
|
||||
cpu.performExtendedCommand(param1, param2);
|
||||
}),
|
||||
ORA((address, value, addressMode, cpu) -> {
|
||||
cpu.A |= value;
|
||||
cpu.setNZ(cpu.A);
|
||||
@@ -1049,7 +1068,7 @@ public class MOS65C02 extends CPU {
|
||||
traceEntry = getState().toUpperCase() + " " + Integer.toString(pc, 16) + " : " + disassemble();
|
||||
captureSingleTrace(traceEntry);
|
||||
if (isTraceEnabled()) {
|
||||
Logger.getLogger(getClass().getName()).info(traceEntry);
|
||||
LOG.log(Level.INFO, traceEntry);
|
||||
}
|
||||
if (isLogEnabled()) {
|
||||
log(traceEntry);
|
||||
@@ -1059,8 +1078,8 @@ public class MOS65C02 extends CPU {
|
||||
int op = 0x00ff & getMemory().read(pc, TYPE.EXECUTE, true, false);
|
||||
OPCODE opcode = opcodes[op];
|
||||
if (traceEntry != null && warnAboutExtendedOpcodes && opcode != null && opcode.isExtendedOpcode) {
|
||||
System.out.println(">>EXTENDED OPCODE DETECTED " + Integer.toHexString(opcode.code) + "<<");
|
||||
System.out.println(traceEntry);
|
||||
LOG.log(Level.WARNING, ">>EXTENDED OPCODE DETECTED {0}<<", Integer.toHexString(opcode.code));
|
||||
LOG.log(Level.WARNING, traceEntry);
|
||||
if (isLogEnabled()) {
|
||||
log(">>EXTENDED OPCODE DETECTED " + Integer.toHexString(opcode.code) + "<<");
|
||||
log(traceEntry);
|
||||
@@ -1102,9 +1121,7 @@ public class MOS65C02 extends CPU {
|
||||
addWaitCycles(wait);
|
||||
|
||||
if (isLogEnabled() || breakOnBadOpcode) {
|
||||
System.out.println("Unrecognized opcode "
|
||||
+ Integer.toHexString(op)
|
||||
+ " at " + Integer.toHexString(pc));
|
||||
LOG.log(Level.WARNING, "Unrecognized opcode {0} at {1}", new Object[]{Integer.toHexString(op), Integer.toHexString(pc)});
|
||||
}
|
||||
if (breakOnBadOpcode) {
|
||||
OPCODE.BRK.execute(this);
|
||||
@@ -1175,13 +1192,13 @@ public class MOS65C02 extends CPU {
|
||||
|
||||
@Override
|
||||
public void JSR(int address) {
|
||||
pushWord(getProgramCounter() - 1);
|
||||
pushPC();
|
||||
setProgramCounter(address);
|
||||
}
|
||||
|
||||
public void BRK() {
|
||||
if (isLogEnabled()) {
|
||||
System.out.println("BRK at $" + Integer.toString(getProgramCounter(), 16));
|
||||
LOG.log(Level.WARNING, "BRK at ${0}", Integer.toString(getProgramCounter(), 16));
|
||||
dumpTrace();
|
||||
}
|
||||
B = true;
|
||||
@@ -1233,7 +1250,7 @@ public class MOS65C02 extends CPU {
|
||||
// V = true;
|
||||
// Z = true;
|
||||
int newPC = getMemory().readWord(RESET_VECTOR, TYPE.READ_DATA, true, false);
|
||||
System.out.println("Reset called, setting PC to (" + Integer.toString(RESET_VECTOR, 16) + ") = " + Integer.toString(newPC, 16));
|
||||
LOG.log(Level.WARNING, "Reset called, setting PC to ({0}) = {1}", new Object[]{Integer.toString(RESET_VECTOR, 16), Integer.toString(newPC, 16)});
|
||||
setProgramCounter(newPC);
|
||||
}
|
||||
|
||||
@@ -1321,4 +1338,49 @@ public class MOS65C02 extends CPU {
|
||||
public void pushPC() {
|
||||
pushWord(getProgramCounter() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param param1
|
||||
* @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)});
|
||||
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) {
|
||||
case 0x00:
|
||||
// Turn off tracing
|
||||
trace = false;
|
||||
break;
|
||||
case 0x01:
|
||||
// Turn on tracing
|
||||
trace = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x64:
|
||||
// Memory functions
|
||||
getMemory().performExtendedCommand(param2 & 0x0ff);
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import jace.core.RAM;
|
||||
import jace.state.Stateful;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -38,6 +40,74 @@ import java.util.logging.Logger;
|
||||
*/
|
||||
@Stateful
|
||||
abstract public class RAM128k extends RAM {
|
||||
Logger LOG = Logger.getLogger(RAM128k.class.getName());
|
||||
|
||||
Map<String, PagedMemory> banks;
|
||||
|
||||
private Map<String, PagedMemory> getBanks() {
|
||||
if (banks == null) {
|
||||
banks = new HashMap<>();
|
||||
|
||||
banks.put("main", mainMemory);
|
||||
banks.put("lc", languageCard);
|
||||
banks.put("lc2", languageCard2);
|
||||
banks.put("//e rom (80-col)", cPageRom);
|
||||
banks.put("//e rom", rom);
|
||||
banks.put("blank", blank);
|
||||
banks.put("aux", getAuxMemory());
|
||||
banks.put("aux lc", getAuxLanguageCard());
|
||||
banks.put("aux lc2", getAuxLanguageCard2());
|
||||
cards[1].ifPresent(c -> banks.put("card1a", c.getCxRom()));
|
||||
cards[1].ifPresent(c -> banks.put("card1b", c.getC8Rom()));
|
||||
cards[2].ifPresent(c -> banks.put("card2a", c.getCxRom()));
|
||||
cards[2].ifPresent(c -> banks.put("card2b", c.getC8Rom()));
|
||||
cards[3].ifPresent(c -> banks.put("card3a", c.getCxRom()));
|
||||
cards[3].ifPresent(c -> banks.put("card3b", c.getC8Rom()));
|
||||
cards[4].ifPresent(c -> banks.put("card4a", c.getCxRom()));
|
||||
cards[4].ifPresent(c -> banks.put("card4b", c.getC8Rom()));
|
||||
cards[5].ifPresent(c -> banks.put("card5a", c.getCxRom()));
|
||||
cards[5].ifPresent(c -> banks.put("card5b", c.getC8Rom()));
|
||||
cards[6].ifPresent(c -> banks.put("card6a", c.getCxRom()));
|
||||
cards[6].ifPresent(c -> banks.put("card6b", c.getC8Rom()));
|
||||
cards[7].ifPresent(c -> banks.put("card7a", c.getCxRom()));
|
||||
cards[7].ifPresent(c -> banks.put("card7b", c.getC8Rom()));
|
||||
}
|
||||
|
||||
return banks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performExtendedCommand(int param) {
|
||||
switch (param) {
|
||||
case 0xda:
|
||||
// 64 da : Dump all memory mappings
|
||||
System.out.println("Active banks");
|
||||
for (int i = 0; i < 256; i++) {
|
||||
byte[] read = activeRead.get(i);
|
||||
byte[] write = activeWrite.get(i);
|
||||
String readBank = getBanks().keySet().stream().filter(bank->{
|
||||
PagedMemory mem = getBanks().get(bank);
|
||||
for (byte[] page : mem.getMemory()) {
|
||||
if (page == read) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).findFirst().orElse("unknown");
|
||||
String writeBank = getBanks().keySet().stream().filter(bank->{
|
||||
PagedMemory mem = getBanks().get(bank);
|
||||
for (byte[] page : mem.getMemory()) {
|
||||
if (page == write) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).findFirst().orElse("unknown");
|
||||
LOG.log(Level.INFO,"Bank {0}\t{1}\t{2}", new Object[]{Integer.toHexString(i), readBank, writeBank});
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@Stateful
|
||||
public PagedMemory mainMemory;
|
||||
@@ -90,7 +160,7 @@ abstract public class RAM128k extends RAM {
|
||||
// First off, set up read/write for main memory (might get changed later on)
|
||||
activeRead.fillBanks(SoftSwitches.RAMRD.getState() ? getAuxMemory() : mainMemory);
|
||||
activeWrite.fillBanks(SoftSwitches.RAMWRT.getState() ? getAuxMemory() : mainMemory);
|
||||
|
||||
|
||||
// Handle language card softswitches
|
||||
activeRead.fillBanks(rom);
|
||||
//activeRead.fillBanks(cPageRom);
|
||||
@@ -110,7 +180,7 @@ abstract public class RAM128k extends RAM {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (SoftSwitches.LCWRITE.isOn()) {
|
||||
if (SoftSwitches.AUXZP.isOff()) {
|
||||
activeWrite.fillBanks(languageCard);
|
||||
@@ -129,7 +199,7 @@ abstract public class RAM128k extends RAM {
|
||||
activeWrite.set(i, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle 80STORE logic for bankswitching video ram
|
||||
if (SoftSwitches._80STORE.isOn()) {
|
||||
activeRead.setBanks(0x04, 0x04, 0x04,
|
||||
@@ -143,7 +213,7 @@ abstract public class RAM128k extends RAM {
|
||||
SoftSwitches.PAGE2.isOn() ? getAuxMemory() : mainMemory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle zero-page bankswitching
|
||||
if (SoftSwitches.AUXZP.getState()) {
|
||||
// Aux pages 0 and 1
|
||||
@@ -154,13 +224,13 @@ abstract public class RAM128k extends RAM {
|
||||
activeRead.setBanks(0, 2, 0, mainMemory);
|
||||
activeWrite.setBanks(0, 2, 0, mainMemory);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
INTCXROM SLOTC3ROM C1,C2,C4-CF C3
|
||||
0 0 slot rom
|
||||
0 1 slot slot
|
||||
1 - rom rom
|
||||
*/
|
||||
*/
|
||||
if (SoftSwitches.CXROM.getState()) {
|
||||
// Enable C1-CF to point to rom
|
||||
activeRead.setBanks(0, 0x0F, 0x0C1, cPageRom);
|
||||
|
||||
@@ -58,7 +58,13 @@ 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)),
|
||||
ALTCH(new VideoSoftSwitch("Mousetext", 0x0c00e, 0x0c00f, 0x0c01e, RAMEvent.TYPE.WRITE, false){
|
||||
@Override
|
||||
public void stateChanged() {
|
||||
super.stateChanged();
|
||||
computer.getVideo().forceRefresh();
|
||||
}
|
||||
}),
|
||||
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) {
|
||||
|
||||
@@ -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;
|
||||
@@ -389,7 +389,7 @@ public class VideoDHGR extends Video {
|
||||
Color color = Palette.color[c1];
|
||||
// Unrolled loop, faster
|
||||
PixelWriter writer = screen.getPixelWriter();
|
||||
int xx = xOffset * 7;
|
||||
int xx = xOffset * 14;
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
@@ -418,9 +418,9 @@ public class VideoDHGR extends Video {
|
||||
}
|
||||
PixelWriter writer = screen.getPixelWriter();
|
||||
// int yOffset = xyOffset[y][times14[xOffset]];
|
||||
Color color = Palette.color[c1];
|
||||
Color color = Palette.color[FLIP_NYBBLE[c1]];
|
||||
// Unrolled loop, faster
|
||||
int xx = xOffset * 7;
|
||||
int xx = xOffset * 14;
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
@@ -615,16 +615,19 @@ public class VideoDHGR extends Video {
|
||||
}
|
||||
|
||||
protected void showDhgr(WritableImage screen, int xOffset, int y, int dhgrWord) {
|
||||
//Graphics2D g = (Graphics2D) screen.getGraphics();
|
||||
int xx = xOffset * 7;
|
||||
PixelWriter writer = screen.getPixelWriter();
|
||||
try {
|
||||
for (int i = 0; i < 7; i++) {
|
||||
Color color = Palette.color[FLIP_NYBBLE[dhgrWord & 15]];
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
Color color;
|
||||
if (!dhgrMode && hiresMode) {
|
||||
color = Palette.color[dhgrWord & 15];
|
||||
} else {
|
||||
color = Palette.color[FLIP_NYBBLE[dhgrWord & 15]];
|
||||
}
|
||||
writer.setColor(xOffset++, y, color);
|
||||
writer.setColor(xOffset++, y, color);
|
||||
writer.setColor(xOffset++, y, color);
|
||||
writer.setColor(xOffset++, y, color);
|
||||
dhgrWord >>= 4;
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
@@ -632,7 +635,7 @@ public class VideoDHGR extends Video {
|
||||
}
|
||||
}
|
||||
static final Color BLACK = Color.BLACK;
|
||||
static final Color WHITE = Color.WHITE;
|
||||
static Color WHITE = Color.WHITE;
|
||||
static final int[][] XY_OFFSET;
|
||||
|
||||
static {
|
||||
@@ -652,11 +655,13 @@ public class VideoDHGR extends Video {
|
||||
// Also, adding xOffset now makes it additionally 5% faster
|
||||
//int yOffset = ((y << 4) + (y << 5) + (y << 9))+xOffset;
|
||||
|
||||
int xx = xOffset * 7;
|
||||
int xx = xOffset;
|
||||
PixelWriter writer = screen.getPixelWriter();
|
||||
for (int i = 0; i < 28; i++) {
|
||||
if (xx < 560) {
|
||||
// yOffset++ is used instead of yOffset+i, because it is faster
|
||||
writer.setColor(xx++, y, (dhgrWord & 1) == 1 ? WHITE : BLACK);
|
||||
}
|
||||
dhgrWord >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
*/
|
||||
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;
|
||||
@@ -29,6 +32,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javafx.scene.image.PixelWriter;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
/**
|
||||
* Provides a clean color monitor simulation, complete with text-friendly
|
||||
@@ -68,6 +72,67 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
static int currentMode = -1;
|
||||
@InvokableAction(name = "Toggle video mode",
|
||||
category = "video",
|
||||
alternatives = "mode,color,b&w,monochrome",
|
||||
defaultKeyMapping = {"ctrl+shift+g"})
|
||||
public static void changeVideoMode() {
|
||||
VideoNTSC thiss = (VideoNTSC) Emulator.computer.video;
|
||||
currentMode++;
|
||||
if (currentMode >= VideoMode.values().length) {
|
||||
currentMode = 0;
|
||||
}
|
||||
thiss.monochomeMode = false;
|
||||
WHITE = Color.WHITE;
|
||||
switch (VideoMode.values()[currentMode]) {
|
||||
case Amber:
|
||||
thiss.monochomeMode = true;
|
||||
WHITE = Color.web("ff8000");
|
||||
break;
|
||||
case Greenscreen:
|
||||
thiss.monochomeMode = true;
|
||||
WHITE = Color.web("0ccc68");
|
||||
break;
|
||||
case Monochrome:
|
||||
thiss.monochomeMode = true;
|
||||
break;
|
||||
case Color:
|
||||
thiss.useTextPalette = false;
|
||||
thiss.enableVideo7 = false;
|
||||
break;
|
||||
case Mode7:
|
||||
thiss.useTextPalette = false;
|
||||
thiss.enableVideo7 = true;
|
||||
break;
|
||||
case Mode7TextFriendly:
|
||||
thiss.useTextPalette = true;
|
||||
thiss.enableVideo7 = true;
|
||||
break;
|
||||
case TextFriendly:
|
||||
thiss.useTextPalette = true;
|
||||
thiss.enableVideo7 = false;
|
||||
break;
|
||||
}
|
||||
thiss.activePalette = thiss.useTextPalette ? TEXT_PALETTE : SOLID_PALETTE;
|
||||
EmulatorUILogic.notify("Video mode: "+VideoMode.values()[currentMode].name);
|
||||
forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showBW(WritableImage screen, int x, int y, int dhgrWord) {
|
||||
int pos = divBy28[x];
|
||||
@@ -90,22 +155,24 @@ public class VideoNTSC extends VideoDHGR {
|
||||
|
||||
@Override
|
||||
protected void displayLores(WritableImage screen, int xOffset, int y, int rowAddress) {
|
||||
int data = ((RAM128k) computer.getMemory()).getMainMemory().readByte(rowAddress + xOffset) & 0x0FF;
|
||||
int pos = xOffset >> 1;
|
||||
if (rowStart < 0) {
|
||||
rowStart = pos;
|
||||
}
|
||||
colorActive[xOffset * 2] = colorActive[xOffset * 2 + 1] = true;
|
||||
int data = ((RAM128k) computer.getMemory()).getMainMemory().readByte(rowAddress + xOffset) & 0x0FF;
|
||||
colorActive[xOffset * 2] = true;
|
||||
colorActive[xOffset * 2 + 1] = true;
|
||||
if ((xOffset & 1) == 0) {
|
||||
int pat = scanline[pos] & 0x0fffc000;
|
||||
if ((y & 7) < 4) {
|
||||
data &= 15;
|
||||
} else {
|
||||
data >>= 4;
|
||||
}
|
||||
int pat = data | data << 4 | data << 8 | (data & 3) << 12;
|
||||
pat |= data | data << 4 | data << 8 | (data & 3) << 12;
|
||||
scanline[pos] = pat;
|
||||
} else {
|
||||
int pat = scanline[pos];
|
||||
int pat = scanline[pos] & 0x03fff;
|
||||
if ((y & 7) < 4) {
|
||||
data &= 15;
|
||||
} else {
|
||||
@@ -167,6 +234,7 @@ public class VideoNTSC extends VideoDHGR {
|
||||
}
|
||||
}
|
||||
|
||||
boolean monochomeMode = false;
|
||||
private void renderScanline(WritableImage screen, int y) {
|
||||
int p = 0;
|
||||
if (rowStart != 0) {
|
||||
@@ -200,7 +268,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 = !colorActive[byteCounter] || (mixed && !hiresMode && !useColor[byteCounter]);
|
||||
isBW = monochomeMode || !colorActive[byteCounter] || (mixed && !hiresMode && !useColor[byteCounter]);
|
||||
byteCounter++;
|
||||
}
|
||||
if (isBW) {
|
||||
|
||||
@@ -20,6 +20,8 @@ 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;
|
||||
@@ -29,6 +31,7 @@ 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
|
||||
@@ -40,8 +43,17 @@ import java.util.logging.Logger;
|
||||
public class ApplesoftProgram {
|
||||
|
||||
List<Line> lines = new ArrayList<>();
|
||||
public static final int startingAddressPointer = 0x067;
|
||||
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 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) {
|
||||
@@ -67,7 +79,7 @@ public class ApplesoftProgram {
|
||||
}
|
||||
|
||||
public static ApplesoftProgram fromMemory(RAM memory) {
|
||||
int startAddress = memory.readWordRaw(startingAddressPointer);
|
||||
int startAddress = memory.readWordRaw(START_OF_PROG_POINTER);
|
||||
int nextCheck = memory.readWordRaw(startAddress);
|
||||
int pos = startAddress;
|
||||
List<Byte> bytes = new ArrayList<>();
|
||||
@@ -115,35 +127,53 @@ 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 pos = memory.readWordRaw(startingAddressPointer);
|
||||
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);
|
||||
for (Line line : lines) {
|
||||
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);
|
||||
int nextPos = pos + line.getLength();
|
||||
memory.writeWord(pos, nextPos, false, true);
|
||||
pos += 2;
|
||||
memory.writeWord(pos, line.getNumber(), false, true);
|
||||
pos += 2;
|
||||
boolean isFirst = true;
|
||||
for (Command command : line.getCommands()) {
|
||||
if (!isFirst) {
|
||||
@@ -159,9 +189,69 @@ 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);
|
||||
|
||||
// Emulator.computer.cpu.setProgramCounter(BASIC_RUN);
|
||||
Emulator.computer.resume();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ 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
|
||||
@@ -220,14 +221,10 @@ public class Command {
|
||||
}
|
||||
}
|
||||
}
|
||||
List<ByteOrToken> parts = new ArrayList<ByteOrToken>();
|
||||
List<ByteOrToken> parts = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String out = "";
|
||||
for (ByteOrToken p : parts) {
|
||||
out += p.toString();
|
||||
}
|
||||
return out;
|
||||
return parts.stream().map(ByteOrToken::toString).collect(Collectors.joining());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,7 +153,7 @@ public class Line {
|
||||
boolean isComment = false;
|
||||
Command currentCommand = new Command();
|
||||
l.commands.add(currentCommand);
|
||||
l.length = 4;
|
||||
l.length = 5; // 4 pointer bytes + 1 null byte at the end
|
||||
String upperLineString = lineString.toUpperCase();
|
||||
for (int i = 0; i < lineString.length(); i++) {
|
||||
if (!hasLineNumber) {
|
||||
@@ -211,5 +211,4 @@ public class Line {
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,15 @@ 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();
|
||||
@@ -96,7 +105,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", compiledAsset.getAbsolutePath(), "-f", "cbm", "--maxerrors","16",sourceFile.getAbsolutePath()};
|
||||
String[] params = {"--outfile", normalizeWindowsPath(compiledAsset.getAbsolutePath()), "-f", "cbm", "--maxerrors","16",normalizeWindowsPath(sourceFile.getAbsolutePath())};
|
||||
int status = acme.run("Acme", params);
|
||||
successful = status == 0;
|
||||
if (!successful) {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
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 org.ibex.nestedvm.UnixRuntime {
|
||||
public final class AcmeCrossAssembler extends UnixRuntime {
|
||||
/* program counter */
|
||||
private int pc = 0;
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ package jace.assembly;
|
||||
|
||||
import jace.Emulator;
|
||||
import jace.core.RAM;
|
||||
import jace.ide.Program;
|
||||
import jace.ide.CompileResult;
|
||||
import jace.ide.LanguageHandler;
|
||||
import jace.ide.Program;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -32,7 +32,11 @@ public class AssemblyHandler implements LanguageHandler<File> {
|
||||
public void execute(CompileResult<File> lastResult) {
|
||||
if (lastResult.isSuccessful()) {
|
||||
try {
|
||||
Emulator.computer.pause();
|
||||
boolean resume = false;
|
||||
if (Emulator.computer.isRunning()) {
|
||||
resume = true;
|
||||
Emulator.computer.pause();
|
||||
}
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
FileInputStream input = new FileInputStream(lastResult.getCompiledAsset());
|
||||
int startLSB = input.read();
|
||||
@@ -43,7 +47,9 @@ public class AssemblyHandler implements LanguageHandler<File> {
|
||||
while ((next=input.read()) != -1) {
|
||||
memory.write(pos++, (byte) next, false, true);
|
||||
}
|
||||
Emulator.computer.resume();
|
||||
if (resume) {
|
||||
Emulator.computer.resume();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
@@ -53,6 +59,8 @@ public class AssemblyHandler implements LanguageHandler<File> {
|
||||
|
||||
@Override
|
||||
public void clean(CompileResult<File> lastResult) {
|
||||
lastResult.getCompiledAsset().delete();
|
||||
if (lastResult.getCompiledAsset() != null) {
|
||||
lastResult.getCompiledAsset().delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.TreeItem;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
|
||||
/**
|
||||
@@ -93,8 +94,8 @@ public class Configuration implements Reconfigurable {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ImageView getChangedIcon() {
|
||||
return new ImageView(Utility.loadIcon("icon_exclaim.gif"));
|
||||
public static Optional<ImageView> getChangedIcon() {
|
||||
return Utility.loadIcon("icon_exclaim.gif").map(ImageView::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -251,7 +252,7 @@ public class Configuration implements Reconfigurable {
|
||||
if (!changed) {
|
||||
setGraphic(null);
|
||||
} else {
|
||||
setGraphic(getChangedIcon());
|
||||
getChangedIcon().ifPresent(this::setGraphic);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -283,7 +284,7 @@ public class Configuration implements Reconfigurable {
|
||||
// System.out.println("Evaluating field " + f.getName());
|
||||
try {
|
||||
Object o = f.get(node.subject);
|
||||
if (!f.getType().isPrimitive() && visited.contains(o)) {
|
||||
if (!f.getType().isPrimitive() && f.getType() != String.class && visited.contains(o)) {
|
||||
continue;
|
||||
}
|
||||
visited.add(o);
|
||||
@@ -597,7 +598,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, ", "));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,11 @@ public abstract class DynamicSelection<T> implements ISelection<T> {
|
||||
return currentValue;
|
||||
} else {
|
||||
Iterator<? extends T> i = getSelections().keySet().iterator();
|
||||
return i.next();
|
||||
if (i.hasNext()) {
|
||||
return i.next();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,4 +175,6 @@ public abstract class CPU extends Device {
|
||||
lastTrace = trace;
|
||||
singleTraceEnabled = false;
|
||||
}
|
||||
|
||||
abstract public void clearState();
|
||||
}
|
||||
@@ -128,7 +128,8 @@ public abstract class Card extends Device {
|
||||
|
||||
firmwareListener = memory.observe(RAMEvent.TYPE.ANY, baseRom, baseRom + 255, (e) -> {
|
||||
computer.getMemory().setActiveCard(slot);
|
||||
if (SoftSwitches.CXROM.isOff()) {
|
||||
// Sather 6-4: Writes will still go through even when CXROM inhibits slot ROM
|
||||
if (SoftSwitches.CXROM.isOff() || !e.getType().isRead()) {
|
||||
handleFirmwareAccess(e.getAddress() & 0x0ff, e.getType(), e.getNewValue(), e);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -39,6 +39,7 @@ import java.util.logging.Logger;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.stage.WindowEvent;
|
||||
|
||||
/**
|
||||
* Keyboard manages all keyboard-related activities. For now, all hotkeys are
|
||||
@@ -50,6 +51,12 @@ import javafx.scene.input.KeyEvent;
|
||||
*/
|
||||
public class Keyboard implements Reconfigurable {
|
||||
|
||||
public void resetState() {
|
||||
clearStrobe();
|
||||
openApple(false);
|
||||
solidApple(false);
|
||||
}
|
||||
|
||||
private Computer computer;
|
||||
|
||||
public Keyboard(Computer computer) {
|
||||
@@ -246,6 +253,9 @@ public class Keyboard implements Reconfigurable {
|
||||
}
|
||||
|
||||
if (e.isControlDown()) {
|
||||
if (c == 255) {
|
||||
return;
|
||||
}
|
||||
c = (char) (c & 0x01f);
|
||||
}
|
||||
|
||||
|
||||
@@ -335,4 +335,6 @@ public abstract class RAM implements Reconfigurable {
|
||||
abstract public void attach();
|
||||
|
||||
abstract public void detach();
|
||||
|
||||
abstract public void performExtendedCommand(int i);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ 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;
|
||||
@@ -49,8 +50,8 @@ import javax.sound.sampled.SourceDataLine;
|
||||
*/
|
||||
public class SoundMixer extends Device {
|
||||
|
||||
private final Set<SourceDataLine> availableLines = Collections.synchronizedSet(new HashSet<SourceDataLine>());
|
||||
private final Map<Object, SourceDataLine> activeLines = Collections.synchronizedMap(new HashMap<Object, SourceDataLine>());
|
||||
private final Set<SourceDataLine> availableLines = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Map<Object, SourceDataLine> activeLines = Collections.synchronizedMap(new HashMap<>());
|
||||
/**
|
||||
* Bits per sample
|
||||
*/
|
||||
@@ -61,6 +62,9 @@ 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
|
||||
*/
|
||||
@@ -104,7 +108,9 @@ public class SoundMixer extends Device {
|
||||
|
||||
@Override
|
||||
public synchronized void reconfigure() {
|
||||
if (isConfigDifferent()) {
|
||||
if (MUTE) {
|
||||
detach();
|
||||
} else if (isConfigDifferent()) {
|
||||
detach();
|
||||
try {
|
||||
initMixer();
|
||||
@@ -266,7 +272,7 @@ public class SoundMixer extends Device {
|
||||
if (oldPreferredMixer == null) {
|
||||
changed |= preferredMixer.getValue() != null;
|
||||
} else {
|
||||
changed |= !oldPreferredMixer.matches(preferredMixer.getValue());
|
||||
changed |= !oldPreferredMixer.matches(Pattern.quote(preferredMixer.getValue()));
|
||||
}
|
||||
oldPreferredMixer = preferredMixer.getValue();
|
||||
return changed;
|
||||
|
||||
@@ -31,9 +31,11 @@ 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;
|
||||
@@ -50,152 +52,10 @@ import javafx.scene.paint.Color;
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public class Utility {
|
||||
|
||||
//--------------- 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
|
||||
/**
|
||||
@@ -270,23 +130,32 @@ public class Utility {
|
||||
return score * adjustment * adjustment;
|
||||
}
|
||||
|
||||
public static String join(Collection c, String d) {
|
||||
String result = "";
|
||||
boolean isFirst = true;
|
||||
for (Object o : c) {
|
||||
result += (isFirst ? "" : d) + o.toString();
|
||||
isFirst = false;
|
||||
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;
|
||||
}
|
||||
|
||||
public static boolean isHeadlessMode() {
|
||||
return isHeadless;
|
||||
}
|
||||
|
||||
public static Optional<Image> loadIcon(String filename) {
|
||||
if (isHeadless) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Image loadIcon(String filename) {
|
||||
InputStream stream = Utility.class.getClassLoader().getResourceAsStream("jace/data/" + filename);
|
||||
return new Image(stream);
|
||||
return Optional.of(new Image(stream));
|
||||
}
|
||||
|
||||
public static Label loadIconLabel(String filename) {
|
||||
Image img = loadIcon(filename);
|
||||
public static Optional<Label> loadIconLabel(String filename) {
|
||||
if (isHeadless) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Image img = loadIcon(filename).get();
|
||||
Label label = new Label() {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
@@ -309,7 +178,7 @@ public class Utility {
|
||||
label.setTextFill(Color.WHITE);
|
||||
DropShadow shadow = new DropShadow(5.0, Color.BLACK);
|
||||
label.setEffect(shadow);
|
||||
return label;
|
||||
return Optional.of(label);
|
||||
}
|
||||
|
||||
// public static void runModalProcess(String title, final Runnable runnable) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package jace.core;
|
||||
|
||||
import jace.Emulator;
|
||||
import jace.state.Stateful;
|
||||
import jace.config.ConfigurableField;
|
||||
import jace.config.InvokableAction;
|
||||
@@ -28,10 +29,9 @@ 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,6 +89,7 @@ public abstract class Video extends Device {
|
||||
|
||||
/**
|
||||
* Creates a new instance of Video
|
||||
*
|
||||
* @param computer
|
||||
*/
|
||||
public Video(Computer computer) {
|
||||
@@ -98,21 +99,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;
|
||||
}
|
||||
@@ -135,6 +136,7 @@ public abstract class Video extends Device {
|
||||
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
public void redraw() {
|
||||
screenDirty = false;
|
||||
javafx.application.Platform.runLater(redrawScreen);
|
||||
@@ -156,18 +158,18 @@ public abstract class Video extends Device {
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
setScannerLocation(currentWriter.getYOffset(y));
|
||||
setFloatingBus(computer.getMemory().readRaw(scannerAddress + x));
|
||||
if (hPeriod > 0) {
|
||||
hPeriod--;
|
||||
if (hPeriod <= 1) {
|
||||
if (hPeriod == 0) {
|
||||
x = -1;
|
||||
setScannerLocation(currentWriter.getYOffset(y));
|
||||
}
|
||||
} else {
|
||||
if (!isVblank && x < (APPLE_CYCLES_PER_LINE)) {
|
||||
if (!isVblank && x < APPLE_CYCLES_PER_LINE) {
|
||||
draw();
|
||||
}
|
||||
if (x >= APPLE_CYCLES_PER_LINE) {
|
||||
if (x >= APPLE_CYCLES_PER_LINE - 1) {
|
||||
int yy = y + hblankOffsetY;
|
||||
if (yy < 0) {
|
||||
yy += APPLE_SCREEN_LINES;
|
||||
@@ -175,8 +177,7 @@ public abstract class Video extends Device {
|
||||
if (yy >= APPLE_SCREEN_LINES) {
|
||||
yy -= (TOTAL_LINES - APPLE_SCREEN_LINES);
|
||||
}
|
||||
setScannerLocation(currentWriter.getYOffset(yy) + hblankOffsetX + (yy < 64 ? 128 : 0));
|
||||
x = -1;
|
||||
x = hblankOffsetX - 1;
|
||||
if (!isVblank) {
|
||||
if (lineDirty) {
|
||||
screenDirty = true;
|
||||
@@ -209,8 +210,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)
|
||||
@@ -271,11 +272,17 @@ 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 final void forceRefresh() {
|
||||
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() {
|
||||
lineDirty = true;
|
||||
screenDirty = true;
|
||||
forceRedrawRowCount = APPLE_SCREEN_LINES + 1;
|
||||
|
||||
@@ -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");
|
||||
Label mouseActive = Utility.loadIconLabel("input-mouse.png").orElse(null);
|
||||
public boolean movedSinceLastTick = false;
|
||||
public boolean movedSinceLastRead = false;
|
||||
|
||||
|
||||
@@ -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 1")
|
||||
@ConfigurableField(category = "Disk", defaultValue = "", shortName = "d2", name = "Drive 2 disk image", description = "Path of disk 2")
|
||||
public String disk2;
|
||||
|
||||
public CardDiskII(Computer computer) {
|
||||
@@ -81,7 +81,8 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
|
||||
currentDrive = drive1;
|
||||
drive1.reset();
|
||||
drive2.reset();
|
||||
EmulatorUILogic.removeIndicators(this);
|
||||
EmulatorUILogic.removeIndicators(drive1);
|
||||
EmulatorUILogic.removeIndicators(drive2);
|
||||
// Motherboard.cancelSpeedRequest(this);
|
||||
}
|
||||
|
||||
@@ -104,13 +105,13 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
|
||||
case 0x8:
|
||||
// drive off
|
||||
currentDrive.setOn(false);
|
||||
EmulatorUILogic.removeIndicator(this, currentDrive.getIcon());
|
||||
currentDrive.removeIndicator();
|
||||
break;
|
||||
|
||||
case 0x9:
|
||||
// drive on
|
||||
currentDrive.setOn(true);
|
||||
EmulatorUILogic.addIndicator(this, currentDrive.getIcon());
|
||||
currentDrive.addIndicator();
|
||||
break;
|
||||
|
||||
case 0xA:
|
||||
@@ -127,9 +128,6 @@ 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
|
||||
@@ -226,8 +224,8 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
|
||||
@Override
|
||||
public void setSlot(int slot) {
|
||||
super.setSlot(slot);
|
||||
drive1.getIcon().setText("S" + slot + "D1");
|
||||
drive2.getIcon().setText("S" + slot + "D2");
|
||||
drive1.getIcon().ifPresent(icon->icon.setText("S" + slot + "D1"));
|
||||
drive2.getIcon().ifPresent(icon->icon.setText("S" + slot + "D2"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -74,7 +74,7 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
public PSG[] chips;
|
||||
// The 6522 controllr chips (always 2)
|
||||
public R6522[] controllers;
|
||||
static private int ticksBeteenPlayback = 200;
|
||||
static private int ticksBetweenPlayback = 200;
|
||||
Lock timerSync = new ReentrantLock();
|
||||
Condition cpuCountReached = timerSync.newCondition();
|
||||
Condition playbackFinished = timerSync.newCondition();
|
||||
@@ -189,11 +189,11 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
timerSync.lock();
|
||||
try {
|
||||
ticksSinceLastPlayback++;
|
||||
if (ticksSinceLastPlayback >= ticksBeteenPlayback) {
|
||||
if (ticksSinceLastPlayback >= ticksBetweenPlayback) {
|
||||
cpuCountReached.signalAll();
|
||||
while (isRunning() && ticksSinceLastPlayback >= ticksBeteenPlayback) {
|
||||
while (isRunning() && ticksSinceLastPlayback >= ticksBetweenPlayback) {
|
||||
if (!playbackFinished.await(1, TimeUnit.SECONDS)) {
|
||||
gripe("The mockingboard playback thread has stalled. Disabling mockingboard.");
|
||||
// gripe("The mockingboard playback thread has stalled. Disabling mockingboard.");
|
||||
suspend();
|
||||
}
|
||||
}
|
||||
@@ -319,7 +319,8 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
System.out.println("Mockingboard playback started");
|
||||
int bytesPerSample = frameSize / 2;
|
||||
buildMixerTable();
|
||||
ticksBeteenPlayback = (int) ((Motherboard.SPEED * BUFFER_LENGTH) / SAMPLE_RATE);
|
||||
ticksBetweenPlayback = (int) ((Motherboard.SPEED * BUFFER_LENGTH) / SAMPLE_RATE);
|
||||
System.out.println("Ticks between playback: "+ticksBetweenPlayback);
|
||||
ticksSinceLastPlayback = 0;
|
||||
int zeroSamples = 0;
|
||||
setRun(true);
|
||||
@@ -329,7 +330,6 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
Thread.currentThread().yield();
|
||||
}
|
||||
if (isRunning()) {
|
||||
computer.getMotherboard().requestSpeed(this);
|
||||
playSound(leftBuffer, rightBuffer);
|
||||
int p = 0;
|
||||
for (int idx = 0; idx < BUFFER_LENGTH; idx++) {
|
||||
@@ -349,7 +349,7 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
}
|
||||
try {
|
||||
timerSync.lock();
|
||||
ticksSinceLastPlayback -= ticksBeteenPlayback;
|
||||
ticksSinceLastPlayback -= ticksBetweenPlayback;
|
||||
} finally {
|
||||
timerSync.unlock();
|
||||
}
|
||||
@@ -379,8 +379,10 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
try {
|
||||
timerSync.lock();
|
||||
playbackFinished.signalAll();
|
||||
while (isRunning() && ticksSinceLastPlayback < ticksBeteenPlayback) {
|
||||
while (isRunning() && ticksSinceLastPlayback < ticksBetweenPlayback) {
|
||||
computer.getMotherboard().requestSpeed(this);
|
||||
cpuCountReached.await();
|
||||
computer.getMotherboard().cancelSpeedRequest(this);
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
// Do nothing, probably killing playback thread on purpose
|
||||
|
||||
@@ -24,11 +24,12 @@ import jace.core.Card;
|
||||
import jace.core.Computer;
|
||||
import jace.core.RAMEvent;
|
||||
import jace.core.RAMEvent.TYPE;
|
||||
import jace.state.Stateful;
|
||||
import jace.core.Utility;
|
||||
import jace.state.Stateful;
|
||||
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;
|
||||
@@ -62,10 +63,10 @@ public class CardRamFactor extends Card {
|
||||
public String getDeviceName() {
|
||||
return "RamFactor";
|
||||
}
|
||||
Label indicator;
|
||||
Optional<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) {
|
||||
@@ -204,7 +205,9 @@ public class CardRamFactor extends Card {
|
||||
@Override
|
||||
public void setSlot(int slot) {
|
||||
super.setSlot(slot);
|
||||
indicator.setText("Slot "+getSlot());
|
||||
indicator.ifPresent(icon->
|
||||
icon.setText("Slot "+getSlot())
|
||||
);
|
||||
// Rom has different images for each slot
|
||||
updateFirmwareMemory();
|
||||
}
|
||||
|
||||
@@ -116,8 +116,10 @@ public class CardSSC extends Card implements Reconfigurable {
|
||||
Logger.getLogger(CardSSC.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
super.setSlot(slot);
|
||||
activityIndicator = Utility.loadIconLabel("network-wired.png");
|
||||
activityIndicator.setText("Slot " + slot);
|
||||
Utility.loadIconLabel("network-wired.png").ifPresent(icon->{
|
||||
activityIndicator = icon;
|
||||
activityIndicator.setText("Slot " + slot);
|
||||
});
|
||||
}
|
||||
|
||||
boolean newInputAvailable = false;
|
||||
|
||||
@@ -32,6 +32,7 @@ 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;
|
||||
@@ -50,8 +51,7 @@ import javafx.scene.control.Label;
|
||||
@Name("ThunderClock Plus")
|
||||
public class CardThunderclock extends Card {
|
||||
|
||||
Label clockIcon;
|
||||
Label clockFixIcon;
|
||||
Optional<Label> clockIcon;
|
||||
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,12 +152,14 @@ public class CardThunderclock extends Card {
|
||||
performProdosPatch(computer);
|
||||
}
|
||||
getTime();
|
||||
clockIcon.setText("Slot " + getSlot());
|
||||
long now = System.currentTimeMillis();
|
||||
if ((now - lastShownIcon) > MIN_WAIT) {
|
||||
EmulatorUILogic.addIndicator(this, clockIcon, 3000);
|
||||
}
|
||||
lastShownIcon = now;
|
||||
clockIcon.ifPresent(icon->{
|
||||
icon.setText("Slot " + getSlot());
|
||||
long now = System.currentTimeMillis();
|
||||
if ((now - lastShownIcon) > MIN_WAIT) {
|
||||
EmulatorUILogic.addIndicator(this, icon, 3000);
|
||||
}
|
||||
lastShownIcon = now;
|
||||
});
|
||||
}
|
||||
shiftMode = isShift;
|
||||
}
|
||||
@@ -324,8 +326,9 @@ 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());
|
||||
Label clockFixIcon = Utility.loadIconLabel("clock_fix.png");
|
||||
clockFixIcon.setText("Fixed");
|
||||
EmulatorUILogic.addIndicator(CardThunderclock.class, clockFixIcon, 4000);
|
||||
Utility.loadIconLabel("clock_fix.png").ifPresent(clockFixIcon->{
|
||||
clockFixIcon.setText("Fixed");
|
||||
EmulatorUILogic.addIndicator(CardThunderclock.class, clockFixIcon, 4000);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package jace.hardware;
|
||||
|
||||
import jace.EmulatorUILogic;
|
||||
import jace.core.Computer;
|
||||
import jace.library.MediaConsumer;
|
||||
import jace.library.MediaEntry;
|
||||
@@ -27,6 +28,7 @@ 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;
|
||||
@@ -225,22 +227,40 @@ 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 Label icon;
|
||||
private Optional<Label> icon;
|
||||
|
||||
@Override
|
||||
public Label getIcon() {
|
||||
public Optional<Label> getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(Label i) {
|
||||
public void setIcon(Optional<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;
|
||||
|
||||
|
||||
@@ -167,16 +167,17 @@ public class FloppyDisk {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
for (int track = 0; track < TRACK_COUNT; track++) {
|
||||
for (int sector = 0; sector < SECTOR_COUNT; sector++) {
|
||||
int gap2 = (int) ((Math.random() * 5.0) + 4);
|
||||
// 15 junk bytes
|
||||
writeJunkBytes(output, 15);
|
||||
// Address block
|
||||
writeAddressBlock(output, track, sector);
|
||||
// 4 junk bytes
|
||||
writeJunkBytes(output, 4);
|
||||
writeJunkBytes(output, gap2);
|
||||
// Data block
|
||||
nibblizeBlock(output, track, currentSectorOrder[sector], nibbles);
|
||||
// 34 junk bytes
|
||||
writeJunkBytes(output, 34);
|
||||
writeJunkBytes(output, 38 - gap2);
|
||||
}
|
||||
}
|
||||
return output.toByteArray();
|
||||
|
||||
@@ -9,6 +9,7 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -28,7 +29,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;
|
||||
Label clockIcon;
|
||||
Optional<Label> clockIcon;
|
||||
|
||||
private final RAMListener listener = new RAMListener(RAMEvent.TYPE.ANY, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
|
||||
@Override
|
||||
@@ -89,7 +90,7 @@ public class NoSlotClock extends Device {
|
||||
public NoSlotClock(Computer computer) {
|
||||
super(computer);
|
||||
this.clockIcon = Utility.loadIconLabel("clock.png");
|
||||
this.clockIcon.setText("No Slot Clock");
|
||||
this.clockIcon.ifPresent(icon -> icon.setText("No Slot Clock"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,7 +134,8 @@ public class NoSlotClock extends Device {
|
||||
storeBCD(now.get(Calendar.MONTH) + 1, 6);
|
||||
storeBCD(now.get(Calendar.YEAR) % 100, 7);
|
||||
clockActive = true;
|
||||
EmulatorUILogic.addIndicator(this, clockIcon, 1000);
|
||||
clockIcon.ifPresent(icon
|
||||
-> EmulatorUILogic.addIndicator(this, icon, 1000));
|
||||
if (patchProdosClock) {
|
||||
CardThunderclock.performProdosPatch(computer);
|
||||
}
|
||||
|
||||
@@ -18,17 +18,21 @@
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -43,6 +47,8 @@ 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);
|
||||
}
|
||||
@@ -57,6 +63,31 @@ 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
|
||||
@@ -168,8 +199,6 @@ 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() {
|
||||
@@ -476,13 +505,13 @@ public class PassportMidiInterface extends Card {
|
||||
|
||||
// If we have a command to send, then do it
|
||||
if (sendMessage == true) {
|
||||
if (synth != null && synth.isOpen()) {
|
||||
if (midiOut != null) {
|
||||
// Send message
|
||||
try {
|
||||
// System.out.println("Sending MIDI message "+currentMessageStatus+","+currentMessageData1+","+currentMessageData2);
|
||||
currentMessage.setMessage(currentMessageStatus, currentMessageData1, currentMessageData2);
|
||||
synth.getReceiver().send(currentMessage, -1L);
|
||||
} catch (InvalidMidiDataException | MidiUnavailableException ex) {
|
||||
midiOut.send(currentMessage, -1L);
|
||||
} catch (InvalidMidiDataException ex) {
|
||||
Logger.getLogger(PassportMidiInterface.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
@@ -509,30 +538,32 @@ public class PassportMidiInterface extends Card {
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
if (isRunning() && synth != null && synth.isOpen()) {
|
||||
if (isRunning() && midiOut != null) {
|
||||
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 (dev.getName().contains("Java Sound")) {
|
||||
if (dev instanceof Synthesizer) {
|
||||
synth = (Synthesizer) dev;
|
||||
break;
|
||||
}
|
||||
if ((preferredMidiDevice.getValue() == null && dev.getName().contains("Java Sound") && dev instanceof Synthesizer) ||
|
||||
preferredMidiDevice.getValue().equalsIgnoreCase(dev.getName())
|
||||
) {
|
||||
selectedDevice = MidiSystem.getMidiDevice(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (synth == null) {
|
||||
synth = MidiSystem.getSynthesizer();
|
||||
}
|
||||
if (synth != null) {
|
||||
System.out.println("Selected MIDI device: " + synth.getDeviceInfo().getName());
|
||||
synth.open();
|
||||
if (selectedDevice != null) {
|
||||
System.out.println("Selected MIDI device: " + selectedDevice.getDeviceInfo().getName());
|
||||
selectedDevice.open();
|
||||
midiOut = selectedDevice.getReceiver();
|
||||
super.resume();
|
||||
}
|
||||
} catch (MidiUnavailableException ex) {
|
||||
@@ -543,9 +574,23 @@ public class PassportMidiInterface extends Card {
|
||||
|
||||
private void suspendACIA() {
|
||||
// TODO: Stop ACIA thread...
|
||||
if (synth != null && synth.isOpen()) {
|
||||
synth.close();
|
||||
synth = null;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ public abstract class ProdosDriver {
|
||||
MOS65C02 cpu = (MOS65C02) computer.getCpu();
|
||||
cpu.A = returnCode;
|
||||
// Clear carry flag if no error, otherwise set carry flag
|
||||
cpu.C = (returnCode == 0x00) ? 00 : 01;
|
||||
cpu.C = (returnCode == 0x00) ? 00 : 01;
|
||||
}
|
||||
|
||||
private MLI_RETURN prodosMLI() {
|
||||
|
||||
@@ -20,6 +20,7 @@ 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;
|
||||
@@ -28,8 +29,11 @@ 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;
|
||||
@@ -43,6 +47,10 @@ 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;
|
||||
|
||||
@@ -59,8 +67,8 @@ public class CardMassStorage extends Card implements MediaConsumerParent {
|
||||
@Override
|
||||
public void setSlot(int slot) {
|
||||
super.setSlot(slot);
|
||||
drive1.getIcon().setText("S" + getSlot() + "D1");
|
||||
drive2.getIcon().setText("S" + getSlot() + "D2");
|
||||
drive1.getIcon().ifPresent(icon -> icon.setText("S" + getSlot() + "D1"));
|
||||
drive2.getIcon().ifPresent(icon -> icon.setText("S" + getSlot() + "D2"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,10 +136,28 @@ 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();
|
||||
|
||||
@@ -18,38 +18,52 @@
|
||||
*/
|
||||
package jace.hardware.massStorage;
|
||||
|
||||
import static jace.hardware.massStorage.IDisk.BLOCK_SIZE;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Prodos directory node
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
// public static int FILE_ENTRY_SIZE = 38;
|
||||
public static int FILE_ENTRY_SIZE = 0x027;
|
||||
public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, int baseBlock) throws IOException {
|
||||
setBaseBlock(baseBlock);
|
||||
init(ownerFilesystem, physicalDir);
|
||||
|
||||
public static final byte STANDARD_PERMISSIONS = (byte) 0x0c3;
|
||||
public static final int PRODOS_VERSION = 0x023;
|
||||
public static final int FILE_ENTRY_SIZE = 0x027;
|
||||
public static final int ENTRIES_PER_BLOCK = (ProdosVirtualDisk.BLOCK_SIZE - 4) / FILE_ENTRY_SIZE;
|
||||
private boolean isRoot;
|
||||
|
||||
private List<DiskNode> directoryEntries;
|
||||
|
||||
public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, int baseBlock, boolean root) throws IOException {
|
||||
super(ownerFilesystem, baseBlock);
|
||||
init(ownerFilesystem, physicalDir, root);
|
||||
}
|
||||
|
||||
public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir) throws IOException {
|
||||
init(ownerFilesystem, physicalDir);
|
||||
public DirectoryNode(ProdosVirtualDisk ownerFilesystem, File physicalDir, boolean root) throws IOException {
|
||||
super(ownerFilesystem);
|
||||
init(ownerFilesystem, physicalDir, root);
|
||||
}
|
||||
|
||||
|
||||
private void init(ProdosVirtualDisk ownerFilesystem, File physicalFile) throws IOException {
|
||||
private void init(ProdosVirtualDisk ownerFilesystem, File physicalFile, boolean root) throws IOException {
|
||||
isRoot = root;
|
||||
directoryEntries = new ArrayList<>();
|
||||
setPhysicalFile(physicalFile);
|
||||
setType(EntryType.SUBDIRECTORY);
|
||||
setName(physicalFile.getName());
|
||||
setOwnerFilesystem(ownerFilesystem);
|
||||
allocate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,16 +71,16 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAllocate() {
|
||||
File[] files = physicalFile.listFiles(this);
|
||||
int numEntries = files.length;
|
||||
int numBlocks = 1;
|
||||
// First block has 12 entries, subsequent blocks have 13 entries
|
||||
if (numEntries > 12) {
|
||||
numBlocks += (numEntries - 12) / 13;
|
||||
public void doAllocate() throws IOException {
|
||||
for (int i = 1; i < getBlockCount(); i++) {
|
||||
if (isRoot) {
|
||||
new SubNode(i, this, getOwnerFilesystem().getNextFreeBlock(3));
|
||||
} else {
|
||||
new SubNode(i, this);
|
||||
}
|
||||
}
|
||||
|
||||
for (File f : files) {
|
||||
for (File f : physicalFile.listFiles()) {
|
||||
addFile(f);
|
||||
}
|
||||
Collections.sort(children, (DiskNode o1, DiskNode o2) -> o1.getName().compareTo(o2.getName()));
|
||||
@@ -78,65 +92,79 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Checks contents of subdirectory for changes as well as directory itself (super class)
|
||||
* Checks contents of subdirectory for changes as well as directory itself
|
||||
* (super class)
|
||||
*/
|
||||
public boolean checkFile() throws IOException {
|
||||
boolean success = true;
|
||||
if (!super.checkFile()) {
|
||||
return false;
|
||||
}
|
||||
HashSet<String> realFiles = new HashSet<>();
|
||||
File[] realFileList = physicalFile.listFiles(this);
|
||||
for (File f : realFileList) {
|
||||
realFiles.add(f.getName());
|
||||
}
|
||||
for (Iterator<DiskNode> i = getChildren().iterator(); i.hasNext(); ) {
|
||||
DiskNode node = i.next();
|
||||
if (realFiles.contains(node.getPhysicalFile().getName())) {
|
||||
realFiles.remove(node.getPhysicalFile().getName());
|
||||
} else {
|
||||
i.remove();
|
||||
success = false;
|
||||
}
|
||||
if (node.isAllocated()) {
|
||||
if (!(node instanceof DirectoryNode) && !node.checkFile()) {
|
||||
success = false;
|
||||
if (!allocated) {
|
||||
allocate();
|
||||
} else {
|
||||
try {
|
||||
if (!super.checkFile()) {
|
||||
return false;
|
||||
}
|
||||
HashSet<String> realFiles = new HashSet<>();
|
||||
File[] realFileList = physicalFile.listFiles(this);
|
||||
for (File f : realFileList) {
|
||||
realFiles.add(f.getName());
|
||||
}
|
||||
for (Iterator<DiskNode> i = directoryEntries.iterator(); i.hasNext();) {
|
||||
DiskNode node = i.next();
|
||||
if (realFiles.contains(node.getPhysicalFile().getName())) {
|
||||
realFiles.remove(node.getPhysicalFile().getName());
|
||||
} else {
|
||||
i.remove();
|
||||
success = false;
|
||||
}
|
||||
if (node.isAllocated()) {
|
||||
if (!(node instanceof DirectoryNode) && !node.checkFile()) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!realFiles.isEmpty()) {
|
||||
success = false;
|
||||
// New files showed up -- deal with them!
|
||||
realFiles.stream().forEach((fileName) -> {
|
||||
addFile(new File(physicalFile, fileName));
|
||||
});
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!realFiles.isEmpty()) {
|
||||
success = false;
|
||||
// New files showed up -- deal with them!
|
||||
realFiles.stream().forEach((fileName) -> {
|
||||
addFile(new File(physicalFile, fileName));
|
||||
});
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readBlock(int block, byte[] buffer) throws IOException {
|
||||
checkFile();
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
int offset = 4;
|
||||
generatePointers(buffer, block);
|
||||
// System.out.println("Directory "+getName()+" sequence "+block+"; physical block "+getNodeSequence(block).getBaseBlock());
|
||||
if (block == 0) {
|
||||
generateHeader(buffer);
|
||||
for (int i=0; i < 12 && i < children.size(); i++)
|
||||
generateFileEntry(buffer, 4 + (i+1) * FILE_ENTRY_SIZE, i);
|
||||
offset += FILE_ENTRY_SIZE;
|
||||
end = ENTRIES_PER_BLOCK - 1;
|
||||
} else {
|
||||
int start = (block * 13) - 1;
|
||||
int end = start + 13;
|
||||
int offset = 4;
|
||||
|
||||
for (int i=start; i < end && i < children.size(); i++) {
|
||||
// TODO: Add any parts that are not file entries.
|
||||
generateFileEntry(buffer, offset, i);
|
||||
offset += FILE_ENTRY_SIZE;
|
||||
}
|
||||
start = (block * ENTRIES_PER_BLOCK) - 1;
|
||||
end = start + ENTRIES_PER_BLOCK;
|
||||
}
|
||||
for (int i = start; i < end && i < directoryEntries.size(); i++, offset += FILE_ENTRY_SIZE) {
|
||||
// TODO: Add any parts that are not file entries.
|
||||
// System.out.println("Entry "+i+": "+children.get(i).getName()+"; offset "+offset);
|
||||
generateFileEntry(buffer, offset, i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
if (file.getName().endsWith("~")) return false;
|
||||
if (file.getName().endsWith("~")) {
|
||||
return false;
|
||||
}
|
||||
char c = file.getName().charAt(0);
|
||||
if (c == '.' || c == '~') {
|
||||
return false;
|
||||
@@ -144,81 +172,99 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
return !file.isHidden();
|
||||
}
|
||||
|
||||
private void generatePointers(byte[] buffer, int sequence) {
|
||||
DiskNode prev = getNodeSequence(sequence - 1);
|
||||
DiskNode next = getNodeSequence(sequence + 1);
|
||||
// System.out.println("Sequence "+sequence+" prev="+(prev != null ? prev.getBaseBlock() : 0)+"; next="+(next != null ? next.getBaseBlock() : 0));
|
||||
// Previous block (or 0)
|
||||
generateWord(buffer, 0, prev != null ? prev.getBaseBlock() : 0);
|
||||
// Next block (or 0)
|
||||
generateWord(buffer, 0x02, next != null ? next.getBaseBlock() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the directory header found in the base block of a directory
|
||||
*
|
||||
* @param buffer where to write data
|
||||
*/
|
||||
@SuppressWarnings("static-access")
|
||||
private void generateHeader(byte[] buffer) {
|
||||
// System.out.println("Generating directory header");
|
||||
// Previous block = 0
|
||||
generateWord(buffer, 0,0);
|
||||
// Next block
|
||||
int nextBlock = 0;
|
||||
if (!additionalNodes.isEmpty())
|
||||
nextBlock = additionalNodes.get(0).baseBlock;
|
||||
generateWord(buffer, 0x02, nextBlock);
|
||||
// Directory header + name length
|
||||
// Volumme header = 0x0f0; Subdirectory header = 0x0e0
|
||||
buffer[4]= (byte) ((baseBlock == 0x02 ? 0x0f0 : 0x0E0) + getName().length());
|
||||
buffer[4] = (byte) ((isRoot ? 0x0F0 : 0x0E0) | getName().length());
|
||||
generateName(buffer, 5, this);
|
||||
for (int i=0x014 ; i <= 0x01b; i++)
|
||||
buffer[i] = 0;
|
||||
if (!isRoot) {
|
||||
buffer[0x014] = 0x075;
|
||||
buffer[0x015] = PRODOS_VERSION;
|
||||
buffer[0x017] = STANDARD_PERMISSIONS;
|
||||
buffer[0x018] = FILE_ENTRY_SIZE;
|
||||
buffer[0x019] = ENTRIES_PER_BLOCK;
|
||||
}
|
||||
generateTimestamp(buffer, 0x01c, getPhysicalFile().lastModified());
|
||||
// Prodos 1.9
|
||||
buffer[0x020] = 0x019;
|
||||
// Prodos 1.0 = 0
|
||||
buffer[0x020] = PRODOS_VERSION;
|
||||
// Minimum version = 0 (no min)
|
||||
buffer[0x021] = 0x000;
|
||||
// Directory may be read/written to, may not be destroyed or renamed
|
||||
buffer[0x022] = 0x03;
|
||||
buffer[0x022] = STANDARD_PERMISSIONS;
|
||||
// Entry size
|
||||
buffer[0x023] = (byte) FILE_ENTRY_SIZE;
|
||||
// Entries per block
|
||||
buffer[0x024] = (byte) 0x0d;
|
||||
buffer[0x024] = (byte) ENTRIES_PER_BLOCK;
|
||||
// Directory items count
|
||||
generateWord(buffer, 0x025, children.size());
|
||||
// Volume bitmap pointer
|
||||
generateWord(buffer, 0x027, ownerFilesystem.freespaceBitmap.baseBlock);
|
||||
// Total number of blocks
|
||||
generateWord(buffer, 0x029, ownerFilesystem.MAX_BLOCK);
|
||||
generateWord(buffer, 0x025, directoryEntries.size()+1);
|
||||
if (isRoot) {
|
||||
// Volume bitmap pointer
|
||||
generateWord(buffer, 0x027, ownerFilesystem.freespaceBitmap.getBaseBlock());
|
||||
// Total number of blocks
|
||||
generateWord(buffer, 0x029, ownerFilesystem.MAX_BLOCK);
|
||||
} else {
|
||||
// According to the Beneath Apple Prodos supplement
|
||||
int indexInParent = getParent().getChildren().indexOf(this) + 2;
|
||||
int parentBlock = getParent().getNodeSequence(indexInParent / ENTRIES_PER_BLOCK).getBaseBlock();
|
||||
// Parent pointer
|
||||
generateWord(buffer, 0x027, parentBlock);
|
||||
buffer[0x029] = (byte) (indexInParent % ENTRIES_PER_BLOCK);
|
||||
buffer[0x02a] = (byte) FILE_ENTRY_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the entry of a directory
|
||||
*
|
||||
* @param buffer where to write data
|
||||
* @param offset starting offset in buffer to write
|
||||
* @param fileNumber number of file (indexed in Children array) to write
|
||||
*/
|
||||
private void generateFileEntry(byte[] buffer, int offset, int fileNumber) throws IOException {
|
||||
// System.out.println("Generating entry for "+children.get(fileNumber).getName());
|
||||
DiskNode child = children.get(fileNumber);
|
||||
DiskNode child = directoryEntries.get(fileNumber);
|
||||
// Entry Type and length
|
||||
buffer[offset] = (byte) ((child.getType().code << 4) + child.getName().length());
|
||||
buffer[offset] = (byte) ((child.getType().code << 4) | child.getName().length());
|
||||
// Name
|
||||
generateName(buffer, offset+1, child);
|
||||
generateName(buffer, offset + 1, child);
|
||||
// File type
|
||||
buffer[offset + 0x010] = (byte) ((child instanceof DirectoryNode) ? 0x0f : ((FileNode) child).fileType);
|
||||
// Key pointer
|
||||
generateWord(buffer, offset + 0x011, child.getBaseBlock());
|
||||
// Blocks used -- will report only one unless file is actually allocated
|
||||
// child.allocate();
|
||||
generateWord(buffer, offset + 0x013, 1 + child.additionalNodes.size());
|
||||
// EOF
|
||||
// TODO: Verify this is the right thing to do -- is EOF total length or a modulo?
|
||||
int length = ((int) child.physicalFile.length()) & 0x0ffffff;
|
||||
generateWord(buffer, offset + 0x013, child.additionalNodes.size() + 1);
|
||||
// EOF (file size or directory structure size
|
||||
int length = child.getLength();
|
||||
length &= 0x0ffffff;
|
||||
generateWord(buffer, offset + 0x015, length & 0x0ffff);
|
||||
buffer[offset + 0x017] = (byte) ((length >> 16) & 0x0ff);
|
||||
// Creation date
|
||||
generateTimestamp(buffer, offset + 0x018, child.physicalFile.lastModified());
|
||||
// Version = 1.9
|
||||
buffer[offset + 0x01c] = 0x19;
|
||||
// Version = 1.0
|
||||
buffer[offset + 0x01c] = PRODOS_VERSION;
|
||||
// Minimum version = 0
|
||||
buffer[offset + 0x01d] = 0;
|
||||
// Access = all granted
|
||||
buffer[offset + 0x01e] = (byte) 0x0ff;
|
||||
// Access = Read-only
|
||||
buffer[offset + 0x01e] = STANDARD_PERMISSIONS;
|
||||
// AUX type
|
||||
if (child instanceof FileNode)
|
||||
if (child instanceof FileNode) {
|
||||
generateWord(buffer, offset + 0x01f, ((FileNode) child).loadAddress);
|
||||
}
|
||||
// Modification date
|
||||
generateTimestamp(buffer, offset + 0x021, child.physicalFile.lastModified());
|
||||
// Key pointer for directory
|
||||
@@ -231,38 +277,53 @@ public class DirectoryNode extends DiskNode implements FileFilter {
|
||||
|
||||
// yyyyyyym mmmddddd - Byte 0,1
|
||||
// ---hhhhh --mmmmmm - Byte 2,3
|
||||
// buffer[offset+1] = (byte) (((c.get(Calendar.YEAR) - 1990) << 1) + ((c.get(Calendar.MONTH)>> 3) & 1));
|
||||
buffer[offset+0] = 0;
|
||||
buffer[offset+1] = 0;
|
||||
buffer[offset+2] = 0;
|
||||
buffer[offset+3] = 0;
|
||||
// buffer[offset+2] = (byte) ((c.get(Calendar.MONTH)>> 3) & 1);
|
||||
// buffer[offset+3] = (byte) (((c.get(Calendar.MONTH)&7) + c.get(Calendar.DAY_OF_MONTH)) & 0x0ff);
|
||||
// buffer[offset+0] = (byte) c.get(Calendar.HOUR_OF_DAY);
|
||||
// buffer[offset+1] = (byte) c.get(Calendar.MINUTE);
|
||||
buffer[offset + 0] = (byte) (((((c.get(Calendar.MONTH) + 1) & 7) << 5) | c.get(Calendar.DAY_OF_MONTH)) & 0x0ff);
|
||||
buffer[offset + 1] = (byte) (((c.get(Calendar.YEAR) - 2000) << 1) | ((c.get(Calendar.MONTH) + 1) >> 3));
|
||||
buffer[offset + 2] = (byte) c.get(Calendar.MINUTE);
|
||||
buffer[offset + 3] = (byte) c.get(Calendar.HOUR_OF_DAY);
|
||||
}
|
||||
|
||||
private void generateWord(byte[] buffer, int i, int value) {
|
||||
// Little endian format
|
||||
buffer[i] = (byte) (value & 0x0ff);
|
||||
buffer[i+1] = (byte) ((value >> 8) & 0x0ff);
|
||||
buffer[i + 1] = (byte) ((value >> 8) & 0x0ff);
|
||||
}
|
||||
|
||||
private void generateName(byte[] buffer, int offset, DiskNode node) {
|
||||
for (int i=0; i < node.getName().length(); i++) {
|
||||
buffer[offset+i] = (byte) node.getName().charAt(i);
|
||||
for (int i = 0; i < node.getName().length() && i < 15; i++) {
|
||||
buffer[offset + i] = (byte) node.getName().charAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<DiskNode> findChildByFilename(String name) {
|
||||
return directoryEntries.stream().filter((child) -> child.getPhysicalFile().getName().equals(name)).findFirst();
|
||||
}
|
||||
|
||||
private void addFile(File file) {
|
||||
if (!hasChildNamed(file.getName())) {
|
||||
try {
|
||||
if (file.isDirectory()) {
|
||||
addFileEntry(new DirectoryNode(getOwnerFilesystem(), file, false));
|
||||
} else {
|
||||
addFileEntry(new FileNode(getOwnerFilesystem(), file));
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DirectoryNode.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addFileEntry(DiskNode entry) {
|
||||
directoryEntries.add(entry);
|
||||
entry.setParent(this);
|
||||
}
|
||||
|
||||
private void addFile(File file) {
|
||||
try {
|
||||
if (file.isDirectory()) {
|
||||
addChild(new DirectoryNode(getOwnerFilesystem(), file));
|
||||
} else {
|
||||
addChild(new FileNode(getOwnerFilesystem(), file));
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DirectoryNode.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
@Override
|
||||
public int getLength() {
|
||||
return getBlockCount() * BLOCK_SIZE;
|
||||
}
|
||||
|
||||
private int getBlockCount() {
|
||||
return isRoot ? 4 : 1 + (physicalFile.listFiles().length / ENTRIES_PER_BLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Prodos file/directory node abstraction. This provides a lot of the glue for
|
||||
@@ -33,7 +34,6 @@ import java.util.List;
|
||||
public abstract class DiskNode {
|
||||
|
||||
public enum EntryType {
|
||||
|
||||
DELETED(0),
|
||||
SEEDLING(1),
|
||||
SAPLING(2),
|
||||
@@ -50,7 +50,7 @@ public abstract class DiskNode {
|
||||
boolean allocated = false;
|
||||
long allocationTime = -1L;
|
||||
long lastCheckTime = -1L;
|
||||
int baseBlock = -1;
|
||||
private int baseBlock = -1;
|
||||
List<DiskNode> additionalNodes;
|
||||
ProdosVirtualDisk ownerFilesystem;
|
||||
File physicalFile;
|
||||
@@ -59,13 +59,23 @@ public abstract class DiskNode {
|
||||
private EntryType type;
|
||||
private String name;
|
||||
|
||||
public DiskNode() {
|
||||
public DiskNode(ProdosVirtualDisk fs) throws IOException {
|
||||
init(fs);
|
||||
fs.allocateEntry(this);
|
||||
}
|
||||
|
||||
public DiskNode(ProdosVirtualDisk fs, int blockNumber) throws IOException {
|
||||
init(fs);
|
||||
fs.allocateEntryNear(this, blockNumber);
|
||||
}
|
||||
|
||||
private void init(ProdosVirtualDisk fs) throws IOException {
|
||||
additionalNodes = new ArrayList<>();
|
||||
children = new ArrayList<>();
|
||||
setOwnerFilesystem(fs);
|
||||
}
|
||||
|
||||
public boolean checkFile() throws IOException {
|
||||
allocate();
|
||||
if (physicalFile == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -82,7 +92,6 @@ public abstract class DiskNode {
|
||||
doAllocate();
|
||||
allocationTime = System.currentTimeMillis();
|
||||
allocated = true;
|
||||
ownerFilesystem.allocateEntry(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,14 +109,22 @@ public abstract class DiskNode {
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
ownerFilesystem.deallocateEntry(this);
|
||||
public void refresh() throws IOException {
|
||||
deallocate();
|
||||
doRefresh();
|
||||
allocationTime = System.currentTimeMillis();
|
||||
allocated = true;
|
||||
ownerFilesystem.allocateEntry(this);
|
||||
allocate();
|
||||
}
|
||||
|
||||
public DiskNode getNodeSequence(int num) {
|
||||
if (num == 0) {
|
||||
return this;
|
||||
} else if (num > 0 && num <= additionalNodes.size()) {
|
||||
return additionalNodes.get(num-1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the allocated
|
||||
*/
|
||||
@@ -154,12 +171,8 @@ public abstract class DiskNode {
|
||||
* @param ownerFilesystem the ownerFilesystem to set
|
||||
* @throws IOException
|
||||
*/
|
||||
public void setOwnerFilesystem(ProdosVirtualDisk ownerFilesystem) throws IOException {
|
||||
private void setOwnerFilesystem(ProdosVirtualDisk ownerFilesystem) throws IOException {
|
||||
this.ownerFilesystem = ownerFilesystem;
|
||||
if (baseBlock == -1) {
|
||||
setBaseBlock(ownerFilesystem.getNextFreeBlock());
|
||||
}
|
||||
ownerFilesystem.allocateEntry(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,6 +188,7 @@ public abstract class DiskNode {
|
||||
public void setPhysicalFile(File physicalFile) {
|
||||
this.physicalFile = physicalFile;
|
||||
setName(physicalFile.getName());
|
||||
lastCheckTime = physicalFile.lastModified();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,12 +220,21 @@ public abstract class DiskNode {
|
||||
}
|
||||
|
||||
public void addChild(DiskNode child) {
|
||||
child.setParent(this);
|
||||
children.add(child);
|
||||
}
|
||||
|
||||
public void removeChild(DiskNode child) {
|
||||
children.remove(child);
|
||||
}
|
||||
|
||||
public boolean hasChildNamed(String name) {
|
||||
return findChildByFilename(name).isPresent();
|
||||
}
|
||||
|
||||
private Optional<DiskNode> findChildByFilename(String name) {
|
||||
return getChildren().stream().filter((child) -> child.getPhysicalFile().getName().equals(name)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the type
|
||||
@@ -238,10 +261,7 @@ public abstract class DiskNode {
|
||||
* @param name the name to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
if (name.length() > 15) {
|
||||
name = name.substring(0, 15);
|
||||
}
|
||||
this.name = name.toUpperCase();
|
||||
this.name = (name.length() > 15 ? name.substring(0, 15) : name).toUpperCase();
|
||||
}
|
||||
|
||||
public abstract void doDeallocate();
|
||||
@@ -251,6 +271,8 @@ public abstract class DiskNode {
|
||||
public abstract void doRefresh();
|
||||
|
||||
public abstract void readBlock(int sequence, byte[] buffer) throws IOException;
|
||||
|
||||
public abstract int getLength();
|
||||
|
||||
public void readBlock(byte[] buffer) throws IOException {
|
||||
checkFile();
|
||||
|
||||
@@ -26,10 +26,15 @@ import java.io.IOException;
|
||||
* Representation of a prodos file with a known file type and having a known
|
||||
* size (either seedling, sapling or tree)
|
||||
*
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public class FileNode extends DiskNode {
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return (int) getPhysicalFile().length();
|
||||
}
|
||||
|
||||
public enum FileType {
|
||||
|
||||
UNKNOWN(0x00, 0x0000),
|
||||
@@ -66,6 +71,15 @@ public class FileNode extends DiskNode {
|
||||
this.code = code;
|
||||
this.defaultLoadAddress = addr;
|
||||
}
|
||||
|
||||
public static FileType findByCode(int code) {
|
||||
for (FileType t : FileType.values()) {
|
||||
if (t.code == code) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
public int fileType = 0x00;
|
||||
public int loadAddress = 0x00;
|
||||
@@ -81,38 +95,44 @@ public class FileNode extends DiskNode {
|
||||
} else if (fileSize <= SAPLING_MAX_SIZE) {
|
||||
setType(EntryType.SAPLING);
|
||||
return EntryType.SAPLING;
|
||||
} else {
|
||||
setType(EntryType.TREE);
|
||||
return EntryType.TREE;
|
||||
}
|
||||
setType(EntryType.TREE);
|
||||
return EntryType.TREE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
String[] parts = name.split("\\.");
|
||||
FileType t = null;
|
||||
FileType t = FileType.UNKNOWN;
|
||||
int offset = 0;
|
||||
if (parts.length > 1) {
|
||||
String extension = parts[parts.length - 1].toUpperCase();
|
||||
String[] extParts = extension.split("#");
|
||||
if (extParts.length == 2) {
|
||||
offset = Integer.parseInt(extParts[1], 16);
|
||||
extension = extParts[0];
|
||||
String prodosName = name;
|
||||
if (name.matches("^.*?#[0-9A-Fa-f]{6}$")) {
|
||||
int type = Integer.parseInt(name.substring(name.length() - 6, name.length() - 4), 16);
|
||||
offset = Integer.parseInt(name.substring(name.length() - 4), 16);
|
||||
t = FileType.findByCode(type);
|
||||
prodosName = name.substring(0, name.length()-7).replaceAll("[^A-Za-z0-9#]", ".").toUpperCase();
|
||||
} else {
|
||||
String[] parts = name.replaceAll("[^A-Za-z0-9#]", ".").split("\\.");
|
||||
if (parts.length > 1) {
|
||||
String extension = parts[parts.length - 1].toUpperCase();
|
||||
String[] extParts = extension.split("\\#");
|
||||
if (extParts.length == 2) {
|
||||
offset = Integer.parseInt(extParts[1], 16);
|
||||
extension = extParts[0];
|
||||
}
|
||||
try {
|
||||
t = FileType.valueOf(extension);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
System.out.println("Not sure what extension " + extension + " is!");
|
||||
}
|
||||
prodosName = "";
|
||||
for (int i = 0; i < parts.length - 1; i++) {
|
||||
prodosName += (i > 0 ? "." + parts[i] : parts[i]);
|
||||
}
|
||||
if (extParts[extParts.length - 1].equals("SYSTEM")) {
|
||||
prodosName += ".SYSTEM";
|
||||
}
|
||||
}
|
||||
try {
|
||||
t = FileType.valueOf(extension);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
System.out.println("Not sure what extension " + extension + " is!");
|
||||
}
|
||||
name = "";
|
||||
for (int i = 0; i < parts.length - 1; i++) {
|
||||
name += (i > 0 ? "." + parts[i] : parts[i]);
|
||||
}
|
||||
if (extParts[extParts.length - 1].equals("SYSTEM")) {
|
||||
name += ".SYSTEM";
|
||||
}
|
||||
}
|
||||
if (t == null) {
|
||||
t = FileType.UNKNOWN;
|
||||
}
|
||||
if (offset == 0) {
|
||||
offset = t.defaultLoadAddress;
|
||||
@@ -121,12 +141,14 @@ public class FileNode extends DiskNode {
|
||||
loadAddress = offset;
|
||||
|
||||
// Pass usable name (stripped of file extension and other type info) as name
|
||||
super.setName(name);
|
||||
super.setName(prodosName);
|
||||
}
|
||||
|
||||
public FileNode(ProdosVirtualDisk ownerFilesystem, File file) throws IOException {
|
||||
setOwnerFilesystem(ownerFilesystem);
|
||||
super(ownerFilesystem);
|
||||
setPhysicalFile(file);
|
||||
setName(file.getName());
|
||||
allocate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,15 +157,13 @@ public class FileNode extends DiskNode {
|
||||
|
||||
@Override
|
||||
public void doAllocate() throws IOException {
|
||||
int dataBlocks = (int) ((getPhysicalFile().length() / ProdosVirtualDisk.BLOCK_SIZE) + 1);
|
||||
int treeBlocks;
|
||||
if (dataBlocks > 1 && dataBlocks < 257) {
|
||||
treeBlocks = 1;
|
||||
} else {
|
||||
treeBlocks = 1 + (dataBlocks / 256);
|
||||
int dataBlocks = (int) ((getPhysicalFile().length() + ProdosVirtualDisk.BLOCK_SIZE - 1) / ProdosVirtualDisk.BLOCK_SIZE);
|
||||
int treeBlocks = (((dataBlocks * 2) + (ProdosVirtualDisk.BLOCK_SIZE - 2)) / ProdosVirtualDisk.BLOCK_SIZE);
|
||||
if (treeBlocks > 1) {
|
||||
treeBlocks++;
|
||||
}
|
||||
for (int i = 1; i < dataBlocks + treeBlocks; i++) {
|
||||
SubNode subNode = new SubNode(i, this);
|
||||
new SubNode(i, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +173,12 @@ public class FileNode extends DiskNode {
|
||||
|
||||
@Override
|
||||
public void readBlock(int block, byte[] buffer) throws IOException {
|
||||
// System.out.println("Read block "+block+" of file "+getName());
|
||||
allocate();
|
||||
int dataBlocks = (int) ((getPhysicalFile().length() + ProdosVirtualDisk.BLOCK_SIZE - 1) / ProdosVirtualDisk.BLOCK_SIZE);
|
||||
int treeBlocks = (((dataBlocks * 2) + (ProdosVirtualDisk.BLOCK_SIZE - 2)) / ProdosVirtualDisk.BLOCK_SIZE);
|
||||
if (treeBlocks > 1) {
|
||||
treeBlocks++;
|
||||
}
|
||||
switch (this.getType()) {
|
||||
case SEEDLING:
|
||||
readFile(buffer, 0);
|
||||
@@ -163,20 +188,18 @@ public class FileNode extends DiskNode {
|
||||
readFile(buffer, (block - 1));
|
||||
} else {
|
||||
// Generate seedling index block
|
||||
generateIndex(buffer, 0, 256);
|
||||
generateIndex(buffer, 1, dataBlocks + 1);
|
||||
}
|
||||
break;
|
||||
case TREE:
|
||||
int dataBlocks = (int) ((getPhysicalFile().length() / ProdosVirtualDisk.BLOCK_SIZE) + 1);
|
||||
int treeBlocks = (dataBlocks / 256);
|
||||
if (block == 0) {
|
||||
generateIndex(buffer, 0, treeBlocks);
|
||||
} else if (block < treeBlocks) {
|
||||
int start = treeBlocks + (block - 1 * 256);
|
||||
int end = Math.min(start + 256, treeBlocks);
|
||||
generateIndex(buffer, treeBlocks, end);
|
||||
generateIndex(buffer, 1, treeBlocks);
|
||||
} else if (block <= treeBlocks) {
|
||||
int start = treeBlocks + ((block - 1) * 256);
|
||||
int end = treeBlocks + dataBlocks;
|
||||
generateIndex(buffer, start, end);
|
||||
} else {
|
||||
readFile(buffer, (block - treeBlocks));
|
||||
readFile(buffer, (block - treeBlocks - 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -190,10 +213,10 @@ public class FileNode extends DiskNode {
|
||||
}
|
||||
|
||||
private void generateIndex(byte[] buffer, int indexStart, int indexLimit) {
|
||||
int pos = 0;
|
||||
for (int i = indexStart; pos < 256 && i < indexLimit && i < additionalNodes.size(); i++, pos++) {
|
||||
buffer[pos] = (byte) (additionalNodes.get(i).baseBlock & 0x0ff);
|
||||
buffer[pos + 256] = (byte) ((additionalNodes.get(i).baseBlock >> 8) & 0x0ff);
|
||||
for (int i = indexStart, count = 0; count < 256 && i < indexLimit && i <= additionalNodes.size(); i++, count++) {
|
||||
int base = getNodeSequence(i).getBaseBlock();
|
||||
buffer[count] = (byte) (base & 0x0ff);
|
||||
buffer[count + 256] = (byte) (base >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,26 +22,28 @@ import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Maintain freespace and node allocation
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public class FreespaceBitmap extends DiskNode {
|
||||
int size = (ProdosVirtualDisk.MAX_BLOCK + 1) / 8 / ProdosVirtualDisk.BLOCK_SIZE;
|
||||
public FreespaceBitmap(ProdosVirtualDisk fs, int start) throws IOException {
|
||||
setBaseBlock(start);
|
||||
setOwnerFilesystem(fs);
|
||||
|
||||
for (int i=1; i < size; i++) {
|
||||
SubNode subNode = new SubNode(i, this, start+i);
|
||||
}
|
||||
int size = (ProdosVirtualDisk.MAX_BLOCK + 1) / 8 / ProdosVirtualDisk.BLOCK_SIZE;
|
||||
|
||||
public FreespaceBitmap(ProdosVirtualDisk fs, int start) throws IOException {
|
||||
super(fs, start);
|
||||
allocate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDeallocate() {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAllocate() {
|
||||
///
|
||||
public void doAllocate() throws IOException {
|
||||
for (int i = 1; i < size; i++) {
|
||||
SubNode subNode = new SubNode(i, this, getBaseBlock());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,22 +54,18 @@ public class FreespaceBitmap extends DiskNode {
|
||||
@Override
|
||||
public void readBlock(int sequence, byte[] buffer) throws IOException {
|
||||
int startBlock = sequence * ProdosVirtualDisk.BLOCK_SIZE * 8;
|
||||
int endBlock = (sequence+1)* ProdosVirtualDisk.BLOCK_SIZE * 8;
|
||||
int bitCounter=0;
|
||||
int pos=0;
|
||||
int value=0;
|
||||
for (int i=startBlock; i < endBlock; i++) {
|
||||
if (!getOwnerFilesystem().isAllocated(i)) {
|
||||
value++;
|
||||
}
|
||||
bitCounter++;
|
||||
if (bitCounter < 8) {
|
||||
value *= 2;
|
||||
} else {
|
||||
bitCounter = 0;
|
||||
buffer[pos++]=(byte) value;
|
||||
value = 0;
|
||||
int endBlock = (sequence + 1) * ProdosVirtualDisk.BLOCK_SIZE * 8;
|
||||
for (int i = startBlock; i < endBlock; i++) {
|
||||
if (!getOwnerFilesystem().isBlockAllocated(i)) {
|
||||
int pos = (i - startBlock) / 8;
|
||||
int bit = 1 << (i % 8);
|
||||
buffer[pos] |= bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return (1 + getChildren().size()) * IDisk.BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.io.IOException;
|
||||
*/
|
||||
public interface IDisk {
|
||||
public static int BLOCK_SIZE = 512;
|
||||
public static int MAX_BLOCK = 65535;
|
||||
public static int MAX_BLOCK = 0x07fff;
|
||||
|
||||
public void mliFormat() throws IOException;
|
||||
public void mliRead(int block, int bufferAddress, RAM memory) throws IOException;
|
||||
|
||||
@@ -23,6 +23,7 @@ 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;
|
||||
@@ -33,10 +34,10 @@ import javafx.scene.control.Label;
|
||||
*/
|
||||
public class MassStorageDrive implements MediaConsumer {
|
||||
IDisk disk = null;
|
||||
Label icon = null;
|
||||
Optional<Label> icon = null;
|
||||
|
||||
@Override
|
||||
public Label getIcon() {
|
||||
public Optional<Label> getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@@ -45,7 +46,7 @@ public class MassStorageDrive implements MediaConsumer {
|
||||
* @param i
|
||||
*/
|
||||
@Override
|
||||
public void setIcon(Label i) {
|
||||
public void setIcon(Optional<Label> i) {
|
||||
icon = i;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package jace.hardware.massStorage;
|
||||
|
||||
import jace.Emulator;
|
||||
import jace.EmulatorUILogic;
|
||||
import jace.apple2e.MOS65C02;
|
||||
import jace.core.Computer;
|
||||
@@ -27,8 +28,6 @@ import jace.hardware.ProdosDriver.MLI_COMMAND_TYPE;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -38,49 +37,50 @@ import java.util.logging.Logger;
|
||||
* is a folder and not a disk image. FreespaceBitmap and the various Node
|
||||
* classes are used to represent the filesystem structure.
|
||||
*
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public class ProdosVirtualDisk implements IDisk {
|
||||
|
||||
public static int VOLUME_START = 2;
|
||||
public static int FREESPACE_BITMAP_START = 6;
|
||||
public static final int VOLUME_START = 2;
|
||||
public static final int FREESPACE_BITMAP_START = 6;
|
||||
byte[] ioBuffer;
|
||||
File physicalRoot;
|
||||
Map<Integer, DiskNode> physicalMap;
|
||||
private final DiskNode[] physicalMap;
|
||||
DirectoryNode rootDirectory;
|
||||
FreespaceBitmap freespaceBitmap;
|
||||
|
||||
public ProdosVirtualDisk(File rootPath) throws IOException {
|
||||
ioBuffer = new byte[BLOCK_SIZE];
|
||||
physicalMap = new DiskNode[MAX_BLOCK];
|
||||
initDiskStructure();
|
||||
setPhysicalPath(rootPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mliRead(int block, int bufferAddress, RAM memory) throws IOException {
|
||||
// System.out.println("Read block " + block + " to " + Integer.toHexString(bufferAddress));
|
||||
DiskNode node = physicalMap.get(block);
|
||||
Arrays.fill(ioBuffer, (byte) (block & 0x0ff));
|
||||
DiskNode node = physicalMap[block];
|
||||
Arrays.fill(ioBuffer, (byte) 0);
|
||||
if (node == null) {
|
||||
System.out.println("Reading unknown block?!");
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
memory.write(bufferAddress + i, (byte) 0, false, false);
|
||||
}
|
||||
System.out.println("Unknown block " + Integer.toHexString(block));
|
||||
} else {
|
||||
// if (node.getPhysicalFile() == null) {
|
||||
// System.out.println("reading block "+block+ " from directory structure to "+Integer.toHexString(bufferAddress));
|
||||
// } else {
|
||||
// System.out.println("reading block "+block+ " from "+node.getPhysicalFile().getName()+" to "+Integer.toHexString(bufferAddress));
|
||||
// }
|
||||
node.readBlock(ioBuffer);
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
memory.write(bufferAddress + i, ioBuffer[i], false, false);
|
||||
}
|
||||
}
|
||||
// for (int i=0; i < 512; i++) {
|
||||
// if (i % 32 == 0 && i > 0) System.out.println();
|
||||
// System.out.print(((ioBuffer[i]&0x0ff)<16 ? "0" : "") + Integer.toHexString(ioBuffer[i] & 0x0ff) + " ");
|
||||
for (int i = 0; i < ioBuffer.length; i++) {
|
||||
memory.write(bufferAddress + i, ioBuffer[i], false, false);
|
||||
}
|
||||
// System.out.println("Block " + Integer.toHexString(block));
|
||||
// for (int i = 0; i < 32; i++) {
|
||||
// String hex = "";
|
||||
// String text = "";
|
||||
// for (int j = 0; j < 16; j++) {
|
||||
// int val = 0x0ff & memory.readRaw(bufferAddress + i * 16 + j);
|
||||
// char show = (char) (((val & 0x7f) < ' ') ? '.' : val & 0x7f);
|
||||
// hex += (val < 16 ? "0" : "") + Integer.toString(val, 16) + " ";
|
||||
// text += show;
|
||||
// }
|
||||
// System.out.println(hex + " " + text);
|
||||
// }
|
||||
// System.out.println();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,41 +120,51 @@ public class ProdosVirtualDisk implements IDisk {
|
||||
return mostLikelyMatch;
|
||||
}
|
||||
|
||||
public int getNextFreeBlock() throws IOException {
|
||||
public int getNextFreeBlock(int start) throws IOException {
|
||||
// Don't allocate Zero block for anything!
|
||||
// for (int i = 0; i < MAX_BLOCK; i++) {
|
||||
for (int i = 2; i < MAX_BLOCK; i++) {
|
||||
if (!physicalMap.containsKey(i)) {
|
||||
for (int i = start; i < MAX_BLOCK; i++) {
|
||||
if (physicalMap[i] == null) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new IOException("Virtual Disk Full!");
|
||||
}
|
||||
|
||||
// Mark space occupied by node
|
||||
public void allocateEntry(DiskNode node) {
|
||||
physicalMap.put(node.baseBlock, node);
|
||||
node.additionalNodes.stream().forEach((sub) -> {
|
||||
physicalMap.put(sub.getBaseBlock(), sub);
|
||||
});
|
||||
public int allocateEntry(DiskNode node) throws IOException {
|
||||
return allocateEntryNear(node, FREESPACE_BITMAP_START);
|
||||
}
|
||||
|
||||
public int allocateEntryNear(DiskNode node, int start) throws IOException {
|
||||
if (isNodeAllocated(node)) {
|
||||
return node.getBaseBlock();
|
||||
}
|
||||
int block = getNextFreeBlock(start);
|
||||
node.setBaseBlock(block);
|
||||
physicalMap[block] = node;
|
||||
return block;
|
||||
}
|
||||
|
||||
public boolean isNodeAllocated(DiskNode node) {
|
||||
return node.getBaseBlock() >= 0 && physicalMap[node.getBaseBlock()] == node;
|
||||
}
|
||||
|
||||
// Mark space occupied by nodes as free (remove allocation mapping)
|
||||
public void deallocateEntry(DiskNode node) {
|
||||
// Only de-map nodes if the allocation table is actually pointing to the nodes!
|
||||
if (physicalMap.get(node.baseBlock) != null && physicalMap.get(node.baseBlock).equals(node)) {
|
||||
physicalMap.remove(node.baseBlock);
|
||||
if (physicalMap[node.getBaseBlock()] != null && physicalMap[node.getBaseBlock()].equals(node)) {
|
||||
physicalMap[node.getBaseBlock()] = null;
|
||||
}
|
||||
node.additionalNodes.stream().filter((sub) ->
|
||||
(physicalMap.get(sub.getBaseBlock()) != null && physicalMap.get(sub.baseBlock).equals(sub))).
|
||||
node.additionalNodes.stream().filter((sub)
|
||||
-> (physicalMap[sub.getBaseBlock()] != null && physicalMap[sub.getBaseBlock()].equals(sub))).
|
||||
forEach((sub) -> {
|
||||
physicalMap.remove(sub.getBaseBlock());
|
||||
});
|
||||
physicalMap[sub.getBaseBlock()] = null;
|
||||
});
|
||||
}
|
||||
|
||||
// Is the specified block in use?
|
||||
public boolean isAllocated(int i) {
|
||||
return (physicalMap.containsKey(i));
|
||||
public boolean isBlockAllocated(int i) {
|
||||
return (i >= physicalMap.length || physicalMap[i] != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -180,12 +190,15 @@ public class ProdosVirtualDisk implements IDisk {
|
||||
return physicalRoot;
|
||||
}
|
||||
|
||||
public void setPhysicalPath(File f) throws IOException {
|
||||
private void initDiskStructure() throws IOException {
|
||||
freespaceBitmap = new FreespaceBitmap(this, FREESPACE_BITMAP_START);
|
||||
}
|
||||
|
||||
private void setPhysicalPath(File f) throws IOException {
|
||||
if (physicalRoot != null && physicalRoot.equals(f)) {
|
||||
return;
|
||||
}
|
||||
physicalRoot = f;
|
||||
physicalMap = new HashMap<>();
|
||||
if (!physicalRoot.exists() || !physicalRoot.isDirectory()) {
|
||||
try {
|
||||
throw new IOException("Root path must be a directory that exists!");
|
||||
@@ -194,13 +207,8 @@ public class ProdosVirtualDisk implements IDisk {
|
||||
}
|
||||
}
|
||||
// Root directory ALWAYS starts on block 2!
|
||||
rootDirectory = new DirectoryNode(this, physicalRoot, VOLUME_START);
|
||||
rootDirectory = new DirectoryNode(this, physicalRoot, VOLUME_START, true);
|
||||
rootDirectory.setName("VIRTUAL");
|
||||
allocateEntry(rootDirectory);
|
||||
freespaceBitmap = new FreespaceBitmap(this, FREESPACE_BITMAP_START);
|
||||
allocateEntry(freespaceBitmap);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -215,6 +223,6 @@ public class ProdosVirtualDisk implements IDisk {
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return 0x0ffff;
|
||||
return MAX_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,24 +31,28 @@ import java.io.IOException;
|
||||
public class SubNode extends DiskNode {
|
||||
|
||||
int sequenceNumber;
|
||||
private int seq;
|
||||
|
||||
public SubNode(int seq, DiskNode parent) throws IOException {
|
||||
super(parent.getOwnerFilesystem());
|
||||
init(seq, parent);
|
||||
}
|
||||
|
||||
public SubNode(int seq, DiskNode parent, int baseBlock) throws IOException {
|
||||
setBaseBlock(baseBlock);
|
||||
super(parent.getOwnerFilesystem(), baseBlock);
|
||||
init(seq, parent);
|
||||
}
|
||||
|
||||
private void init(int seq, DiskNode parent) throws IOException {
|
||||
sequenceNumber = seq;
|
||||
setParent(parent);
|
||||
setOwnerFilesystem(parent.getOwnerFilesystem());
|
||||
parent.additionalNodes.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return parent.getName() + "; block "+sequenceNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDeallocate() {
|
||||
}
|
||||
@@ -65,4 +69,9 @@ public class SubNode extends DiskNode {
|
||||
public void readBlock(int sequence, byte[] buffer) throws IOException {
|
||||
parent.readBlock(sequenceNumber, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return IDisk.BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,6 @@ public class SoundGenerator extends TimedGenerator {
|
||||
double amp = stateChanges == 0 ? 1 : 1.0 / Math.max(stateChanges-1, 1);
|
||||
int vol = useEnvGen ? envGen.getEffectiveAmplitude() : amplitude;
|
||||
boolean on = noiseActive && noiseGen.isOn() || (active && inverted);
|
||||
// return invert ? -CardMockingboard.VolTable[vol] : CardMockingboard.VolTable[vol];
|
||||
return on ? (int) (CardMockingboard.VolTable[vol] * amp) : 0;
|
||||
}
|
||||
|
||||
|
||||
50
src/main/java/jace/ide/HeadlessProgram.java
Normal file
50
src/main/java/jace/ide/HeadlessProgram.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ 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;
|
||||
@@ -96,31 +98,42 @@ public class IdeController {
|
||||
|
||||
@FXML
|
||||
void newApplesoftBasicClicked(ActionEvent event) {
|
||||
Program tab = createTab(DocumentType.applesoft, null);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void newAssemblyListingClicked(ActionEvent event) {
|
||||
createTab(DocumentType.assembly, null);
|
||||
createTab(DocumentType.assembly, null, false);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void newHexdataClicked(ActionEvent event) {
|
||||
createTab(DocumentType.hex, null);
|
||||
createTab(DocumentType.hex, null, false);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void newPlainTextClicked(ActionEvent event) {
|
||||
createTab(DocumentType.plain, null);
|
||||
createTab(DocumentType.plain, null, false);
|
||||
}
|
||||
|
||||
Map<Tab, Program> openDocuments = new HashMap<>();
|
||||
Map<Option, Object> globalOptions = new EnumMap<>(Option.class);
|
||||
|
||||
private Program createTab(DocumentType type, File document) {
|
||||
private Program createTab(DocumentType type, File document, boolean isBlank) {
|
||||
WebView editor = new WebView();
|
||||
Program proxy = new Program(type, globalOptions);
|
||||
proxy.initEditor(editor, document);
|
||||
proxy.initEditor(editor, document, isBlank);
|
||||
Tab t = new Tab(proxy.getName(), editor);
|
||||
tabPane.getTabs().add(t);
|
||||
openDocuments.put(t, proxy);
|
||||
@@ -153,7 +166,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);
|
||||
createTab(type, file, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.application.Platform;
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.scene.control.TextInputDialog;
|
||||
import javafx.scene.web.PromptData;
|
||||
@@ -104,7 +105,7 @@ public class Program {
|
||||
return Optional.ofNullable(targetFile);
|
||||
}
|
||||
|
||||
public void initEditor(WebView editor, File sourceFile) {
|
||||
public void initEditor(WebView editor, File sourceFile, boolean isBlank) {
|
||||
this.editor = editor;
|
||||
targetFile = sourceFile;
|
||||
if (targetFile != null) {
|
||||
@@ -116,7 +117,7 @@ public class Program {
|
||||
if (newState == Worker.State.SUCCEEDED) {
|
||||
JSObject document = (JSObject) editor.getEngine().executeScript("window");
|
||||
document.setMember("java", this);
|
||||
createEditor();
|
||||
Platform.runLater(()->createEditor(isBlank));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -131,12 +132,13 @@ public class Program {
|
||||
editor.getEngine().load(getClass().getResource(CODEMIRROR_EDITOR).toExternalForm());
|
||||
}
|
||||
|
||||
public void createEditor() {
|
||||
String document = targetFile == null ? getHandler().getNewDocumentContent() : getFileContents(targetFile);
|
||||
public void createEditor(boolean isBlank) {
|
||||
String document = targetFile == null ? isBlank ? "" : getHandler().getNewDocumentContent() : getFileContents(targetFile);
|
||||
String optionString = buildOptions();
|
||||
editor.getEngine().executeScript("var codeMirror = CodeMirror(document.body, " + optionString + ");");
|
||||
codeMirror = (JSObject) editor.getEngine().executeScript("codeMirror");
|
||||
setValue(document);
|
||||
Platform.runLater(() -> setValue(document));
|
||||
// setValue(document);
|
||||
}
|
||||
|
||||
public String getFileContents(File sourceFile) {
|
||||
@@ -165,7 +167,6 @@ public class Program {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public void save(File newTarget) {
|
||||
FileWriter writer = null;
|
||||
if (newTarget == null && targetFile == null) {
|
||||
@@ -220,6 +221,7 @@ public class Program {
|
||||
if (lastResult.isSuccessful()) {
|
||||
getHandler().execute(lastResult);
|
||||
} else {
|
||||
lastResult.getOtherMessages().forEach(System.err::println);
|
||||
getHandler().clean(lastResult);
|
||||
}
|
||||
}
|
||||
@@ -230,20 +232,20 @@ public class Program {
|
||||
getHandler().clean(lastResult);
|
||||
}
|
||||
|
||||
private void manageCompileResult(CompileResult lastResult) {
|
||||
protected void manageCompileResult(CompileResult lastResult) {
|
||||
editor.getEngine().executeScript("clearHighlights()");
|
||||
lastResult.getWarnings().forEach((line,message) ->
|
||||
editor.getEngine().executeScript("highlightLine("+line+",false,\""+escapeString(message)+"\");")
|
||||
lastResult.getWarnings().forEach((line, message)
|
||||
-> editor.getEngine().executeScript("highlightLine(" + line + ",false,\"" + escapeString(message) + "\");")
|
||||
);
|
||||
lastResult.getErrors().forEach((line,message) ->
|
||||
editor.getEngine().executeScript("highlightLine("+line+",true,\""+escapeString(message)+"\");")
|
||||
lastResult.getErrors().forEach((line, message)
|
||||
-> editor.getEngine().executeScript("highlightLine(" + line + ",true,\"" + escapeString(message) + "\");")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private String escapeString(Object message) {
|
||||
return String.valueOf(message).replaceAll("\\\"", """);
|
||||
}
|
||||
|
||||
|
||||
public void log(String message) {
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ package jace.library;
|
||||
import jace.core.Utility;
|
||||
import jace.hardware.FloppyDisk;
|
||||
import java.io.File;
|
||||
import java.util.Optional;
|
||||
import javafx.scene.image.Image;
|
||||
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ public enum DiskType {
|
||||
public boolean isProdosOrdered = false;
|
||||
public boolean is140kb = false;
|
||||
public String description;
|
||||
public Image diskIcon;
|
||||
public Optional<Image> diskIcon;
|
||||
DiskType(String desc, boolean is140, boolean po, String iconPath) {
|
||||
description = desc;
|
||||
is140kb = is140;
|
||||
@@ -49,7 +50,7 @@ public enum DiskType {
|
||||
}
|
||||
|
||||
static public DiskType determineType(File file) {
|
||||
if (!file.exists()) return null;
|
||||
if (file == null || !file.exists()) return null;
|
||||
if (file.isDirectory()) return VIRTUAL;
|
||||
if (file.getName().toLowerCase().endsWith("hdv")) {
|
||||
return LARGE;
|
||||
|
||||
@@ -20,6 +20,7 @@ package jace.library;
|
||||
|
||||
import jace.library.MediaEntry.MediaFile;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
/**
|
||||
@@ -27,8 +28,8 @@ import javafx.scene.control.Label;
|
||||
* @author brobert
|
||||
*/
|
||||
public interface MediaConsumer {
|
||||
public Label getIcon();
|
||||
public void setIcon(Label i);
|
||||
public Optional<Label> getIcon();
|
||||
public void setIcon(Optional<Label> i);
|
||||
public void insertMedia(MediaEntry e, MediaFile f) throws IOException;
|
||||
public MediaEntry getMediaEntry();
|
||||
public MediaFile getMediaFile();
|
||||
|
||||
@@ -21,6 +21,7 @@ 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;
|
||||
|
||||
@@ -78,7 +79,7 @@ public class State extends HashMap<ObjectGraphNode, StateValue> implements Seria
|
||||
}
|
||||
|
||||
public void apply() {
|
||||
Set<ObjectGraphNode> applied = new HashSet<>();
|
||||
Set<ObjectGraphNode> applied = new LinkedHashSet<>();
|
||||
State current = this;
|
||||
while (current != null) {
|
||||
for (StateValue val : current.values()) {
|
||||
|
||||
@@ -21,14 +21,17 @@ 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;
|
||||
@@ -37,6 +40,7 @@ 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;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -64,12 +68,13 @@ public class StateManager implements Reconfigurable {
|
||||
private ObjectGraphNode<BufferedImage> imageGraphNode;
|
||||
|
||||
Computer computer;
|
||||
|
||||
private StateManager(Computer computer) {
|
||||
this.computer = computer;
|
||||
}
|
||||
|
||||
private void buildStateMap() {
|
||||
allStateVariables = new HashSet<>();
|
||||
allStateVariables = new LinkedHashSet<>();
|
||||
objectLookup = new WeakHashMap<>();
|
||||
ObjectGraphNode emulator = new ObjectGraphNode(Emulator.instance);
|
||||
emulator.name = "Emulator";
|
||||
@@ -295,7 +300,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;
|
||||
@@ -402,6 +407,17 @@ 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;
|
||||
@@ -412,7 +428,7 @@ public class StateManager implements Reconfigurable {
|
||||
state.apply();
|
||||
alphaState.tail = state;
|
||||
state.nextState = null;
|
||||
computer.getVideo().forceRefresh();
|
||||
Video.forceRefresh();
|
||||
System.gc();
|
||||
if (resume) {
|
||||
computer.resume();
|
||||
|
||||
@@ -1,787 +0,0 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// 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
@@ -1,480 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
// 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");
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
// 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();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
// 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(); }
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,8 @@
|
||||
</accelerator>
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#newAssemblyListingClicked" text="Assembly Listing" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#newApplesoftBasicClicked" text="Applesoft Basic 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="#newPlainTextClicked" text="Plain Text" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#newHexdataClicked" text="Data (Hex)" />
|
||||
</items>
|
||||
|
||||
115
src/test/java/jace/cpu/Basic6502FuncationalityTest.java
Normal file
115
src/test/java/jace/cpu/Basic6502FuncationalityTest.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -54,14 +54,14 @@ public class ApplesoftTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeBinary() {
|
||||
public void deserializeBinaryTest() {
|
||||
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
|
||||
assertNotNull(program);
|
||||
assertNotSame("", program.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripStringComparison() {
|
||||
public void roundTripStringComparisonTest() {
|
||||
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
|
||||
String serialized = program.toString();
|
||||
ApplesoftProgram deserialized = ApplesoftProgram.fromString(serialized);
|
||||
|
||||
Reference in New Issue
Block a user