77 Commits

Author SHA1 Message Date
Brendan Robert
9ce60ea00e Giving up on the multi-line idea for now 2017-12-26 11:48:27 -06:00
Brendan Robert
3bed89e530 Still trying to get that multiline command working for travis 2017-12-26 11:36:20 -06:00
Brendan Robert
c773fda3ff Travis CI issue, packr downloaded to different file name 2017-12-26 11:26:18 -06:00
Brendan Robert
3678f294df Ignore tmp files and IDE hint to prefer Java 1.8 2017-12-26 11:25:53 -06:00
Brendan Robert
90d13dfde0 Test to see if Travis will run Packr to produce a mac build 2017-12-26 11:13:05 -06:00
Brendan Robert
f19b0847ad Merge pull request #34 from Michaelangel007/master
Cleanup DHGR code for readability
2017-10-24 10:12:23 -05:00
Brendan Robert
d4e6724bf0 Applying suggestion from issue #32 - Thanks Lutepatious! 2017-05-03 02:45:06 -05:00
badvision
3b9d6a232e When switching midi devices, first send all notes off, all sound off on every channel just so it doesn't leave phantom notes playing. 2017-02-19 22:26:06 -06:00
badvision
b57d3e06e0 Added configurable midi output device selection; will still default to the Java Sound Synth if no selection has been made. 2017-02-19 22:12:37 -06:00
badvision
8a0e633a63 Fixed bug in handling mixer device names 2017-02-12 00:21:29 -06:00
badvision
3e676cd3c5 Added additional debugging output to the FC NOP special opcodes 2017-02-06 23:12:42 -06:00
badvision
29161bcf7e Live Coding support for Applesoft basic! :D 2016-11-22 01:12:12 -06:00
badvision
0b696a2379 Added updates to PROG END pointer at $AF to better support save commands 2016-11-19 22:04:39 -06:00
badvision
fe062e2d15 Fix applesoft execution problem (needed to clear variable pointers!) 2016-11-05 10:46:58 -05:00
badvision
ab5d3472c0 Fixed assembler issues on Windows platform. 2016-11-04 22:41:24 -05:00
badvision
a544cae02c Removed JavaFX packager dependency (it's built-in now) and also switched to using a nestedVM dependency instead of including the classes directly. 2016-11-04 22:40:46 -05:00
Brendan Robert
aef9c086b4 Update README.md 2016-08-02 12:44:55 -05:00
Michaelangel007
271dd70f22 Cleanup DHGR code for readability 2016-06-07 13:26:31 -07:00
Brendan Robert
e6594514ef Fixed recursive death bug for new commandline feature -- confirmed that HDV/2MG disks can be inserted via CMD options now. 2016-05-22 16:25:42 -05:00
Brendan Robert
7668e723ec Mass storage supports command-line settings now. For example -s7.d1 myDisk.2mg will insert the 800k or hard drive image into Slot 7, Drive 1. 2016-05-20 23:50:38 -05:00
Brendan Robert
b4ee1fafd8 Merge pull request #29 from Michaelangel007/master
Added 3x zoom for retina displays (From MichaelAngel007)
2016-02-26 00:19:07 -06:00
Michaelangel007
8833f80138 Added 3x zoom for retina displays 2016-02-25 11:57:22 -07:00
Brendan Robert
eb21dfaec1 Issue #14: Notification message is now formatted properly and multiple notifications no longer overlap 2016-02-25 01:40:27 -06:00
Brendan Robert
ad819c5678 Issue #27: Accounted for titlebar size and fixed 2x aspect incorrect width 2016-02-25 01:27:59 -06:00
Brendan Robert
deee9a2a72 Issue #28: Fixed detection of configurable variables that are string types. 2016-02-25 01:14:30 -06:00
Brendan Robert
4c308f997e Issue #28: Optional usage on Disk Drive indicator was causing massive lurch in UI performance. 2016-02-25 00:55:48 -06:00
Brendan Robert
6515c90856 Issue #22: Minor progress hooking up state manager; still doesn't work and crashes the emulator when triggered. :/ 2016-02-23 01:45:11 -06:00
Brendan Robert
3f115624e6 Issue #13: Added monochrome modes and ctrl+shift+g switches between various display modes. 2016-02-21 02:22:47 -06:00
Brendan Robert
3123ee1eec Issue #27: Updated recent changes to only change window within the UI thread. 2016-02-20 22:33:16 -06:00
Brendan Robert
abbe5d007e Issue #27: Added ctrl+shift+f to toggle fullscreen and updated startup to use aspect ratio 1x method for consistency. 2016-02-20 21:30:23 -06:00
Brendan Robert
ce6a8ca873 Issue #27: Ctrl+Shift+A now toggles between 1x/1.5x/2x display modes 2016-02-20 18:35:26 -06:00
Brendan Robert
a0fb1a2149 Issue #26: Changing mousetext mode should force a screen refresh. 2016-02-20 11:10:54 -06:00
Brendan Robert
ef9f7019e5 Issue #24: Additional unit tests added, bugs in BBR/BBS opcodes fixed (not that there's any software that uses those...) 2016-02-15 23:53:24 -06:00
Brendan Robert
7edbbd9c8d Headless mode disables joystick to avoid init issues. 2016-02-14 23:04:54 -06:00
Brendan Robert
5542bb3425 Fixed issue cased when there are no valid selections, default should be null then. 2016-02-14 23:00:20 -06:00
Brendan Robert
609ad08bfd Issue #24: Mute prevents any sound mixer from being created; error in sound init fixed; fixed incorrect assumption in CPU addition test 2016-02-14 22:59:40 -06:00
Brendan Robert
4a371ceec3 Issue #24: Getting new junit test working on travis-ci 2016-02-14 21:56:06 -06:00
Brendan Robert
d845a10de4 Issue #24: Nearly-hedless mode is confirmed and it is possible to JUnit test the actual emulator in headless mode! Right now sound doesn't respect headless mode though so it might cause issues on travis-ci. 2016-02-14 11:13:03 -06:00
Brendan Robert
921ce3a0b0 Issue #12: Added option to create empty applesoft program and added warning before trying to load program from memory. 2016-02-12 23:53:16 -06:00
Brendan Robert
58199776a5 Update run.sh 2016-02-06 16:27:03 -06:00
Brendan Robert
4769e6c5aa Update README.md 2016-02-06 16:26:09 -06:00
Brendan Robert
5982151cf8 Update README.md 2016-02-06 16:25:38 -06:00
Brendan Robert
756a0c6bce Goofed package path -- should deploy correctly now from travis-ci 2016-02-06 16:16:45 -06:00
Brendan Robert
76c4ff4cce Changing travis-ci deployment to use unified jar 2016-02-06 16:07:49 -06:00
Brendan Robert
bfca447b4a jar builds will come from travis-ci.org now in releases. 2016-02-06 15:59:27 -06:00
Brendan Robert
b28a39a059 Issue #18: Updating packaging to work more reliably. 2016-02-06 15:58:45 -06:00
Brendan Robert
af1bdb6b35 Fixed issue #10 reported by Michael -- Looks like introspection works a little differently in 8.0.66 when there are multiple methods with the same name! 2016-01-14 21:29:40 -06:00
Brendan Robert
97b367ccce Updated binaries 2016-01-13 23:49:48 -06:00
Brendan Robert
d5d2424915 Adding a little delay to prevent the issue #8 reported by lifepillar. 2016-01-13 22:44:59 -06:00
Brendan Robert
61356782b6 Removed debug line 2016-01-13 22:25:35 -06:00
Brendan Robert
b9fcc6d82d Fixed ctrl sending key events by itself to the emulator. This fixes the ctrl-c issue reported by Michael as well. 2016-01-13 22:25:14 -06:00
Brendan Robert
8064a804eb Fixed outstanding bugs in VideoDHGR class. It still sucks compared to VideoNTSC but that's a known fact and not a defect. :D 2016-01-13 01:11:59 -06:00
Brendan Robert
fa7cab9866 Merge pull request #3 from Michaelangel007/master
Fix broken download link
2016-01-12 17:07:50 -06:00
Michaelangel007
9a6737e8f0 Updated REAME and run.sh 2016-01-10 11:39:18 -08:00
Michaelangel007
07c71f0117 Added binary jar 2016-01-10 11:35:25 -08:00
Michaelangel007
24f6f7e23f Update Maven project to include dependencies 2016-01-10 11:24:44 -08:00
Michaelangel007
bffe4eba22 Cleaned up build instructions 2016-01-10 11:19:59 -08:00
Michaelangel007
34ef28be1f Updated 2016-01-10 10:54:53 -08:00
Michaelangel007
b5c7418b84 Fixed and Verified script works for OSX 2016-01-10 10:51:56 -08:00
Michaelangel007
1548c1b327 Updated build shell script with troubleshooting steps 2016-01-10 10:12:16 -08:00
Michaelangel007
4c8e809b46 Added build shell script since some freaking instructions on how to compile would be nice 2016-01-10 08:55:04 -08:00
Michaelangel007
676256b6fc Download for 2.0 is source tarball only 2016-01-10 08:36:13 -08:00
Michaelangel007
292b84d1e9 Fix broken download link 2016-01-10 08:28:52 -08:00
Brendan Robert
c261f0f103 Cleared up funky behavior on lo-res graphics, and some improvement on rendering for Crazy Cycles demo 2016-01-03 16:44:09 -06:00
Brendan Robert
ca6a831020 Reset keyboard state when window is deactivated/reactivated 2016-01-03 16:43:31 -06:00
Brendan Robert
732f4768a6 Fixed subdirectory bugs, added support for A2GameServer data files suffix (#TTAAAA where TT is the hex code and AAAA is the aux data/load address). This should be very servicable now. 2015-12-31 21:48:48 -06:00
Brendan Robert
5f9352abb3 Fixed gremlin in directory node handling -- now there are no missing entries and no garbage files listed in Copy ][+ 2015-12-28 21:28:11 -06:00
Brendan Robert
8bcf3a922a Got all the bugs out of the directory handling and also removed a lot of bugs and side-effects through code refactoring 2015-12-28 20:58:48 -06:00
Brendan Robert
59ab31f433 Fixed extended filename parsing and broken sapling file issues 2015-12-28 14:44:24 -06:00
Brendan Robert
6c4136841e Changed packaging instructions in POM 2015-12-20 22:44:23 -06:00
Brendan Robert
17b8183a31 Miscellaneous changes 2015-12-20 22:44:01 -06:00
Brendan Robert
d03c1d9333 Fixed occasional NPE 2015-12-20 22:43:30 -06:00
Brendan Robert
893052b004 Added debugging NOP opcode support. 2015-12-20 22:43:00 -06:00
Brendan Robert
9f838d11f1 Allow writes to C100-C8FF go to cards even if reads are going to the ROM. 2015-12-20 22:42:37 -06:00
Brendan Robert
633b514b38 Removed debug statements from recent changes 2015-12-20 14:18:50 -06:00
Brendan Robert
4021af3ac6 Fixed bugs in the mass-storage implementation. Directories can be mounted via drag/drop, file information is now property reported including dates, and now large files >128k work correctly. 2015-12-20 12:45:56 -06:00
Brendan Robert
cf87f30e35 Added randomized gaps in NIB image generation code just to shake things up a little. :) 2015-11-10 13:52:15 -06:00
73 changed files with 1600 additions and 7257 deletions

3
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

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

4
run.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
java -jar target/Jace.jar

View File

@@ -46,6 +46,7 @@ public class Emulator {
* @param args
*/
public Emulator(List<String> args) {
instance = this;
computer = new Apple2e();
Configuration.buildTree();
Configuration.loadSettings();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -297,23 +297,23 @@ public class VideoDHGR extends Video {
if ((xOffset & 0x01) == 1) {
return;
}
int b1 = ((RAM128k) computer.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset);
int b2 = ((RAM128k) computer.getMemory()).getMainMemory().readByte(rowAddress + xOffset);
int b1 = ((RAM128k) computer.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset );
int b2 = ((RAM128k) computer.getMemory()).getMainMemory() .readByte(rowAddress + xOffset );
int b3 = ((RAM128k) computer.getMemory()).getAuxVideoMemory().readByte(rowAddress + xOffset + 1);
int b4 = ((RAM128k) computer.getMemory()).getMainMemory().readByte(rowAddress + xOffset + 1);
int b4 = ((RAM128k) computer.getMemory()).getMainMemory() .readByte(rowAddress + xOffset + 1);
int useColOffset = xOffset << 1;
// This shouldn't be necessary but prevents an index bounds exception when graphics modes are flipped (Race condition?)
if (useColOffset >= 77) {
useColOffset = 76;
}
useColor[useColOffset] = (b1 & 0x80) != 0;
useColor[useColOffset ] = (b1 & 0x80) != 0;
useColor[useColOffset + 1] = (b2 & 0x80) != 0;
useColor[useColOffset + 2] = (b3 & 0x80) != 0;
useColor[useColOffset + 3] = (b4 & 0x80) != 0;
int dhgrWord = 0x07f & b1;
dhgrWord |= (0x07f & b2) << 7;
dhgrWord |= (0x07f & b3) << 14;
dhgrWord |= (0x07f & b4) << 21;
int dhgrWord = (0x07f & b1) ;
dhgrWord |= (0x07f & b2) << 7;
dhgrWord |= (0x07f & b3) << 14;
dhgrWord |= (0x07f & b4) << 21;
showDhgr(screen, TIMES_14[xOffset], y, dhgrWord);
}
boolean extraHalfBit = false;
@@ -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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -335,4 +335,6 @@ public abstract class RAM implements Reconfigurable {
abstract public void attach();
abstract public void detach();
abstract public void performExtendedCommand(int i);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -56,7 +56,7 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
static public boolean USE_MAX_SPEED = true;
@ConfigurableField(category = "Disk", defaultValue = "", shortName = "d1", name = "Drive 1 disk image", description = "Path of disk 1")
public String disk1;
@ConfigurableField(category = "Disk", defaultValue = "", shortName = "d2", name = "Drive 2 disk image", description = "Path of disk 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@@ -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("\\\"", "&quot;");
}
public void log(String message) {
System.out.println(message);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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