Compare commits

...

103 Commits

Author SHA1 Message Date
Brendan Robert 9674e59f1e
Merge pull request #39 from badvision/dependabot/maven/junit-junit-4.13.1
Bump junit from 4.10 to 4.13.1
2021-09-28 18:16:52 -05:00
dependabot[bot] 774f706f68
Bump junit from 4.10 to 4.13.1
Bumps [junit](https://github.com/junit-team/junit4) from 4.10 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.10.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.10...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-13 01:37:17 +00:00
Brendan Robert cc0cead894 Added better standalone testing for command line arguments. Got video mode to be a first-class startup parameter 2019-06-19 00:47:04 -05:00
Brendan Robert 553d439ff8
Merge pull request #36 from Michaelangel007/master
Fix spelling, and detect if maven isn't installed.
2018-11-29 12:52:49 -06:00
Brendan Robert 15be6e3436 Added accelerator support, also refined how child devices are tracked to better support a new abstraction for sound devices 2018-06-02 13:50:13 -05:00
Brendan Robert 8bd9ec1781 Wolfenstein cheats: Added "Day in the office" hack mode for cheap laughs 2018-05-26 01:46:31 -05:00
Brendan Robert f7ca7c198c Wolfenstein cheat module updated to keep the big boss alive so you can drop him yourself, if you so choose. 2018-05-26 01:00:48 -05:00
Brendan Robert 1f2eff2e42 Added Beyond Wolfenstein cheats 2018-05-26 00:47:57 -05:00
Brendan Robert d4073b9096 UI and Metacheat improvements, also new Wolfenstein trainer module! 2018-05-24 01:53:24 -05:00
Brendan Robert 9cee0cece9 Upstream fixes from Lawless Legends for keyboard focus and speed handling 2018-05-23 00:50:56 -05:00
Brendan Robert 142ee2df2a BEHOLD! The Resurrection of MetaCheat! 2018-05-23 00:38:22 -05:00
Brendan Robert 9118b83a43 Remove dead code 2018-05-23 00:38:02 -05:00
Michaelangel007 8525330d53 Test and Display if maven isn't installed 2018-05-22 22:24:48 -06:00
Michaelangel007 ddc41ec84e Fix spelling 2018-05-22 22:24:07 -06:00
Brendan Robert eb776d44af Committing upstream changes from Lawless Legends to address mockingboard init issues. 2018-05-22 22:30:33 -05:00
Brendan Robert ad9da99cb8
Update README.md 2018-01-15 15:20:50 -06:00
Brendan Robert 79c1ee825c
Update README.md 2018-01-15 15:19:19 -06:00
Brendan Robert 0d07d65b82
Update README.md 2018-01-15 15:17:42 -06:00
Brendan Robert 769d7f4302
Update README.md 2018-01-15 15:16:03 -06:00
Brendan Robert d8ab357d84
Update README.md 2018-01-15 15:14:36 -06:00
Brendan Robert ce9027cb6b
Update README.md 2018-01-15 15:05:32 -06:00
Brendan Robert ede44af6d1
Update README.md 2018-01-15 15:04:24 -06:00
Brendan Robert 4425e8884d
Shaking the can...
(putting kids through college is a bigger challenge than I anticipated...)
2018-01-15 15:03:48 -06:00
Brendan Robert 0c0b2c107c Backported changes from Lawless Legends app experience; namely better boot behavior and also UI Controls Overlay. :) 2018-01-12 23:27:56 -06:00
Brendan Robert dba57e6e89 Created application logo 2017-12-26 16:46:12 -06:00
Brendan Robert 09c1d78832 Fixed font missing issue when loading a packaged version of the application on OSX 2017-12-26 14:35:13 -06:00
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
112 changed files with 4061 additions and 8935 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,10 +1,31 @@
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`
Support JACE:
JACE will always be free, but it does take considerable time to refine and add new features. If you would like to show your support and encourage the author to keep maintaining this emulator, why not throw him some change to buy him a drink? (The emulator was named for the Jack and Cokes consumed during its inception.)
<a href="bitcoin:1TmP94jrEtJNqz7wrCpViA6musGsiTXEq?amount=0.000721&label=Jace%20Donations"><img src="https://sites.google.com/site/brendanrobert/projects/jace/donate.png" height="150px" width="150px"></a>
* <a href="bitcoin:1TmP94jrEtJNqz7wrCpViA6musGsiTXEq?amount=0.000721&label=Jace%20Donations">Donate to support JACE! (BTC address: 1TmP94jrEtJNqz7wrCpViA6musGsiTXEq)</a>
* <a href="https://www.paypal.me/BrendanRobert">Donate to support JACE! (Paypal)</a>
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.
* NEW: UI Control Overlay makes common actions more conveniently available at the click of a button!
* Built-in IDE for writing basic and assembly programs, using ACME to compile and execute directly without leaving the emulator.
* Disk and Mass-storage (hard drive, 3.5 floppy) images
* Joystick and Mouse are fully supported (Joystick can be emulated with either keyboard or mouse, Java doesn't have native joystick support
* All graphics modes are supported, including Apple RGB "Mixed" and B&W modes.

117
build.sh Executable file
View File

@ -0,0 +1,117 @@
#!/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 COMPILE Java programs.
#
# Solution:
#
# 1. Check which verison of Java that Maven is using
#
# mvn -version
#
# NOTE: If this command returns:
#
# -bash: mvn: command not found
#
# You either didn't install maven or it isn't in your path
# i.e.
# which mvn
# /usr/local/bin/mvn
#
# 2. Check which version of the Java JRE is installed:
#
# java -version
#
# 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 "$(which mvn)" ]]; then
echo "ERROR: Maven isn't installed"
echo "Install via:"
echo " brew install maven"
exit
fi
if [[ -z "$JAVA_HOME" ]]; then
echo "WARNING: JAVA_HOME was not set"
echo "... Defaulting to Java 1.8..."
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>

83
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>
@ -144,8 +92,13 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<version>4.13.1</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

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

View File

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

View File

@ -47,20 +47,22 @@ 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);
}
primaryStage.show();
Emulator emulator = new Emulator(getParameters().getRaw());
javafx.application.Platform.runLater(() -> {
new Thread(() -> {
new Emulator(getParameters().getRaw());
reconnectUIHooks();
EmulatorUILogic.scaleIntegerRatio();
while (Emulator.computer.getVideo() == null || Emulator.computer.getVideo().getFrameBuffer() == null) {
Thread.yield();
}
controller.connectComputer(Emulator.computer);
bootWatchdog();
});
}).start();
primaryStage.setOnCloseRequest(event -> {
Emulator.computer.deactivate();
Platform.exit();
@ -68,6 +70,10 @@ public class JaceApplication extends Application {
});
}
public void reconnectUIHooks() {
controller.connectComputer(Emulator.computer, primaryStage);
}
public static JaceApplication getApplication() {
return singleton;
}
@ -86,7 +92,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.Motherboard;
import jace.core.Utility;
import jace.library.MediaCache;
import jace.library.MediaConsumer;
import jace.library.MediaConsumerParent;
@ -30,19 +30,39 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.animation.FadeTransition;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.binding.NumberBinding;
import javafx.beans.binding.When;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.ImageView;
import javafx.scene.input.DragEvent;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.util.StringConverter;
/**
*
@ -55,7 +75,7 @@ public class JaceUIController {
@FXML
private AnchorPane rootPane;
@FXML
private StackPane stackPane;
@ -65,30 +85,215 @@ public class JaceUIController {
@FXML
private ImageView appleScreen;
@FXML
private BorderPane controlOverlay;
@FXML
private Slider speedSlider;
@FXML
private AnchorPane menuButtonPane;
@FXML
private Button menuButton;
Computer computer;
private final BooleanProperty aspectRatioCorrectionEnabled = new SimpleBooleanProperty(false);
@FXML
void initialize() {
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
assert stackPane != null : "fx:id=\"stackPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
assert notificationBox != null : "fx:id=\"notificationBox\" was not injected: check your FXML file 'JaceUI.fxml'.";
assert appleScreen != null : "fx:id=\"appleScreen\" was not injected: check your FXML file 'JaceUI.fxml'.";
appleScreen.fitWidthProperty().bind(rootPane.widthProperty());
speedSlider.setValue(1.0);
controlOverlay.setVisible(false);
menuButtonPane.setVisible(false);
controlOverlay.setFocusTraversable(false);
menuButtonPane.setFocusTraversable(false);
NumberBinding aspectCorrectedWidth = rootPane.heightProperty().multiply(3.0).divide(2.0);
NumberBinding width = new When(
aspectRatioCorrectionEnabled.and(aspectCorrectedWidth.lessThan(rootPane.widthProperty()))
).then(aspectCorrectedWidth).otherwise(rootPane.widthProperty());
appleScreen.fitWidthProperty().bind(width);
appleScreen.fitHeightProperty().bind(rootPane.heightProperty());
appleScreen.setVisible(false);
menuButtonPane.setPickOnBounds(false);
rootPane.setOnDragEntered(this::processDragEnteredEvent);
rootPane.setOnDragExited(this::processDragExitedEvent);
rootPane.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
rootPane.setOnMouseMoved(this::showMenuButton);
rootPane.setOnMouseExited(this::hideControlOverlay);
menuButton.setOnMouseClicked(this::showControlOverlay);
controlOverlay.setOnMouseClicked(this::hideControlOverlay);
delayTimer.getKeyFrames().add(new KeyFrame(Duration.millis(3000), evt -> {
hideControlOverlay(null);
rootPane.requestFocus();
}));
}
public void connectComputer(Computer computer) {
this.computer = computer;
appleScreen.setImage(computer.getVideo().getFrameBuffer());
EventHandler<KeyEvent> keyboardHandler = computer.getKeyboard().getListener();
rootPane.setFocusTraversable(true);
rootPane.setOnKeyPressed(keyboardHandler);
rootPane.setOnKeyReleased(keyboardHandler);
private void showMenuButton(MouseEvent evt) {
if (!evt.isPrimaryButtonDown() && !evt.isSecondaryButtonDown() && !controlOverlay.isVisible()) {
resetMenuButtonTimer();
if (!menuButtonPane.isVisible()) {
menuButtonPane.setVisible(true);
FadeTransition ft = new FadeTransition(Duration.millis(500), menuButtonPane);
ft.setFromValue(0.0);
ft.setToValue(1.0);
ft.play();
}
}
rootPane.requestFocus();
}
Timeline delayTimer = new Timeline();
private void resetMenuButtonTimer() {
delayTimer.playFromStart();
}
private void showControlOverlay(MouseEvent evt) {
if (!evt.isPrimaryButtonDown() && !evt.isSecondaryButtonDown()) {
delayTimer.stop();
menuButtonPane.setVisible(false);
controlOverlay.setVisible(true);
FadeTransition ft = new FadeTransition(Duration.millis(500), controlOverlay);
ft.setFromValue(0.0);
ft.setToValue(1.0);
ft.play();
rootPane.requestFocus();
}
}
private void hideControlOverlay(MouseEvent evt) {
if (menuButtonPane.isVisible()) {
FadeTransition ft1 = new FadeTransition(Duration.millis(500), menuButtonPane);
ft1.setFromValue(1.0);
ft1.setToValue(0.0);
ft1.setOnFinished(evt1 -> menuButtonPane.setVisible(false));
ft1.play();
}
if (controlOverlay.isVisible()) {
FadeTransition ft2 = new FadeTransition(Duration.millis(500), controlOverlay);
ft2.setFromValue(1.0);
ft2.setToValue(0.0);
ft2.setOnFinished(evt1 -> controlOverlay.setVisible(false));
ft2.play();
}
}
protected double convertSpeedToRatio(Double setting) {
if (setting < 1.0) {
return 0.5;
} else if (setting == 1.0) {
return 1.0;
} else if (setting >= 10) {
return Double.MAX_VALUE;
} else {
double val = Math.pow(2.0, (setting - 1.0) / 1.5);
val = Math.floor(val * 2.0) / 2.0;
if (val > 2.0) {
val = Math.floor(val);
}
return val;
}
}
private void connectControls(Stage primaryStage) {
connectButtons(controlOverlay);
if (computer.getKeyboard() != null) {
EventHandler<KeyEvent> keyboardHandler = computer.getKeyboard().getListener();
primaryStage.setOnShowing(evt -> computer.getKeyboard().resetState());
rootPane.setOnKeyPressed(keyboardHandler);
rootPane.setOnKeyReleased(keyboardHandler);
rootPane.setFocusTraversable(true);
}
speedSlider.setMinorTickCount(0);
speedSlider.setMajorTickUnit(1);
speedSlider.setLabelFormatter(new StringConverter<Double>() {
@Override
public String toString(Double val) {
if (val < 1.0) {
return "Half";
} else if (val >= 10.0) {
return "";
}
double v = convertSpeedToRatio(val);
if (v != Math.floor(v)) {
return String.valueOf(v) + "x";
} else {
return String.valueOf((int) v) + "x";
}
}
@Override
public Double fromString(String string) {
return 1.0;
}
});
speedSlider.valueProperty().addListener((val, oldValue, newValue) -> setSpeed(newValue.doubleValue()));
Platform.runLater(() -> {
speedSlider.setValue(Emulator.logic.speedSetting);
// Kind of redundant but make sure speed is properly set as if the user did it
setSpeed(Emulator.logic.speedSetting);
});
}
private void connectButtons(Node n) {
if (n instanceof Button) {
Button button = (Button) n;
Runnable action = Utility.getNamedInvokableAction(button.getText());
button.setOnMouseClicked(evt -> action.run());
} else if (n instanceof Parent) {
for (Node child : ((Parent) n).getChildrenUnmodifiable()) {
connectButtons(child);
}
}
}
protected void setSpeed(double speed) {
Emulator.logic.speedSetting = (int) speed;
double speedRatio = convertSpeedToRatio(speed);
if (speedSlider.getValue() != speed) {
Platform.runLater(() -> speedSlider.setValue(speed));
}
if (speedRatio > 100.0) {
Emulator.computer.getMotherboard().setMaxSpeed(true);
Motherboard.cpuPerClock = 3;
} else {
if (speedRatio > 25) {
Motherboard.cpuPerClock = 2;
} else {
Motherboard.cpuPerClock = 1;
}
Emulator.computer.getMotherboard().setMaxSpeed(false);
Emulator.computer.getMotherboard().setSpeedInPercentage((int) (speedRatio * 100));
}
Emulator.computer.getMotherboard().reconfigure();
}
public void toggleAspectRatio() {
setAspectRatioEnabled(aspectRatioCorrectionEnabled.not().get());
}
public void setAspectRatioEnabled(boolean enabled) {
aspectRatioCorrectionEnabled.set(enabled);
}
public void connectComputer(Computer computer, Stage primaryStage) {
if (computer == null) {
return;
}
this.computer = computer;
Platform.runLater(() -> {
connectControls(primaryStage);
appleScreen.setImage(computer.getVideo().getFrameBuffer());
appleScreen.setVisible(true);
rootPane.requestFocus();
});
}
private void processDragEnteredEvent(DragEvent evt) {
MediaEntry media = null;
if (evt.getDragboard().hasFiles()) {
@ -112,80 +317,89 @@ 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()) {
card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent -> {
consumers.addAll(Arrays.asList(((MediaConsumerParent) parent).getConsumers()));
});
if (Emulator.logic.showDrives) {
for (Optional<Card> card : computer.memory.getAllCards()) {
card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent -> {
consumers.addAll(Arrays.asList(((MediaConsumerParent) parent).getConsumers()));
});
}
}
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 +409,62 @@ public class JaceUIController {
}
void removeIndicator(Label icon) {
Application.invokeLater(()->{
Application.invokeLater(() -> {
notificationBox.getChildren().remove(icon);
iconTTL.remove(icon);
});
}
}
ScheduledExecutorService notificationExecutor = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture ttlCleanupTask = null;
private void trackTTL(Label icon, long TTL) {
iconTTL.put(icon, System.currentTimeMillis()+TTL);
if (ttlCleanupTask == null || ttlCleanupTask.isCancelled()) {
iconTTL.put(icon, System.currentTimeMillis() + TTL);
if (ttlCleanupTask == null || ttlCleanupTask.isCancelled()) {
ttlCleanupTask = notificationExecutor.scheduleWithFixedDelay(this::processTTL, 1, 100, TimeUnit.MILLISECONDS);
}
}
private void processTTL() {
Long now = System.currentTimeMillis();
iconTTL.keySet().stream()
.filter((icon) -> (iconTTL.get(icon) <= now))
.forEach((icon) -> {
removeIndicator(icon);
});
.filter((icon) -> (iconTTL.get(icon) <= now))
.forEach((icon) -> {
removeIndicator(icon);
});
if (iconTTL.isEmpty()) {
ttlCleanupTask.cancel(true);
ttlCleanupTask = null;
}
}
public void addMouseListener(EventHandler<MouseEvent> handler) {
appleScreen.addEventHandler(MouseEvent.ANY, handler);
}
public void removeMouseListener(EventHandler<MouseEvent> handler) {
appleScreen.removeEventHandler(MouseEvent.ANY, handler);
}
Label currentNotification = null;
public void displayNotification(String message) {
Label oldNotification = currentNotification;
Label notification = new Label(message);
currentNotification = notification;
notification.setEffect(new DropShadow(2.0, Color.BLACK));
notification.setTextFill(Color.WHITE);
notification.setBackground(new Background(new BackgroundFill(Color.rgb(0, 0, 80, 0.7), new CornerRadii(5.0), new Insets(-5.0))));
Application.invokeLater(() -> {
stackPane.getChildren().remove(oldNotification);
stackPane.getChildren().add(notification);
});
notificationExecutor.schedule(() -> {
Application.invokeLater(() -> {
stackPane.getChildren().remove(notification);
});
}, 4, TimeUnit.SECONDS);
}
}

View File

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

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

View File

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

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

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

View File

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

View File

@ -1,445 +1,445 @@
package jace.cheat;
import jace.Emulator;
import jace.JaceApplication;
import jace.core.CPU;
import jace.core.Computer;
import jace.core.RAM;
import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.state.State;
import jace.ui.MetacheatUI;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class MetaCheat extends Cheats {
static final ScriptEngine NASHORN_ENGINE = new ScriptEngineManager().getEngineByName("nashorn");
static Invocable NASHORN_INVOCABLE = (Invocable) NASHORN_ENGINE;
public static enum SearchType {
VALUE, TEXT, CHANGE
}
public static enum SearchChangeType {
NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT
}
public static class SearchResult {
int address;
int lastObservedValue = 0;
private SearchResult(int address, int val) {
this.address = address;
lastObservedValue = val;
}
@Override
public String toString() {
return Integer.toHexString(address) + ": " + lastObservedValue + " (" + Integer.toHexString(lastObservedValue) + ")";
}
public int getAddress() {
return address;
}
}
MetacheatUI ui;
public int fadeRate = 1;
public int lightRate = 30;
public int historyLength = 10;
private int startAddress = 0;
private int endAddress = 0x0ffff;
private final StringProperty startAddressProperty = new SimpleStringProperty(Integer.toHexString(startAddress));
private final StringProperty endAddressProperty = new SimpleStringProperty(Integer.toHexString(endAddress));
private boolean byteSized = true;
private SearchType searchType = SearchType.VALUE;
private SearchChangeType searchChangeType = SearchChangeType.NO_CHANGE;
private final BooleanProperty signedProperty = new SimpleBooleanProperty(false);
private final StringProperty searchValueProperty = new SimpleStringProperty("0");
private final StringProperty changeByProperty = new SimpleStringProperty("0");
private final ObservableList<DynamicCheat> cheatList = FXCollections.observableArrayList();
private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList();
private final ObservableList<State> snapshotList = FXCollections.observableArrayList();
public MetaCheat(Computer computer) {
super(computer);
addNumericValidator(startAddressProperty);
addNumericValidator(endAddressProperty);
addNumericValidator(searchValueProperty);
addNumericValidator(changeByProperty);
startAddressProperty.addListener((prop, oldVal, newVal) -> {
startAddress = Math.max(0, Math.min(65535, parseInt(newVal)));
});
endAddressProperty.addListener((prop, oldVal, newVal) -> {
endAddress = Math.max(0, Math.min(65535, parseInt(newVal)));
});
}
private void addNumericValidator(StringProperty stringProperty) {
stringProperty.addListener((ObservableValue<? extends String> prop, String oldVal, String newVal) -> {
if (newVal == null || newVal.isEmpty()) {
return;
}
if (!newVal.matches("(\\+|-)?(x|$)?[0-9a-fA-F]*")) {
stringProperty.set("");
}
});
}
public int parseInt(String s) throws NumberFormatException {
if (s == null || s.isEmpty()) {
return 0;
}
if (s.matches("(\\+|-)?[0-9]+")) {
return Integer.parseInt(s);
} else {
String upper = s.toUpperCase();
boolean positive = !upper.startsWith("-");
for (int i = 0; i < upper.length(); i++) {
char c = upper.charAt(i);
if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) {
int value = Integer.parseInt(s.substring(i), 16);
if (!positive) {
value *= -1;
}
return value;
}
}
}
throw new NumberFormatException("Could not interpret int value " + s);
}
@Override
void registerListeners() {
}
public void addCheat(DynamicCheat cheat) {
cheatList.add(cheat);
computer.getMemory().addListener(cheat);
cheat.addressProperty().addListener((prop, oldVal, newVal) -> {
computer.getMemory().removeListener(cheat);
cheat.doConfig();
computer.getMemory().addListener(cheat);
});
}
public void removeCheat(DynamicCheat cheat) {
cheat.active.set(false);
computer.getMemory().removeListener(cheat);
cheatList.remove(cheat);
}
@Override
protected void unregisterListeners() {
super.unregisterListeners();
cheatList.stream().forEach(computer.getMemory()::removeListener);
}
@Override
protected String getDeviceName() {
return "MetaCheat";
}
@Override
public void detach() {
super.detach();
ui.detach();
}
@Override
public void attach() {
ui = JaceApplication.getApplication().showMetacheat();
ui.registerMetacheatEngine(this);
super.attach();
}
public int getStartAddress() {
return startAddress;
}
public int getEndAddress() {
return endAddress;
}
public void setByteSized(boolean b) {
byteSized = b;
}
public void setSearchType(SearchType searchType) {
this.searchType = searchType;
}
public void setSearchChangeType(SearchChangeType searchChangeType) {
this.searchChangeType = searchChangeType;
}
public Property<Boolean> signedProperty() {
return signedProperty;
}
public Property<String> searchValueProperty() {
return searchValueProperty;
}
public Property<String> searchChangeByProperty() {
return changeByProperty;
}
public ObservableList<DynamicCheat> getCheats() {
return cheatList;
}
public ObservableList<SearchResult> getSearchResults() {
return resultList;
}
public ObservableList<State> getSnapshots() {
return snapshotList;
}
public Property<String> startAddressProperty() {
return startAddressProperty;
}
public Property<String> endAddressProperty() {
return endAddressProperty;
}
public void newSearch() {
RAM memory = Emulator.computer.getMemory();
resultList.clear();
int compare = parseInt(searchValueProperty.get());
for (int i = 0; i < 0x10000; i++) {
boolean signed = signedProperty.get();
int val
= byteSized
? signed ? memory.readRaw(i) : memory.readRaw(i) & 0x0ff
: signed ? memory.readWordRaw(i) : memory.readWordRaw(i) & 0x0ffff;
if (!searchType.equals(SearchType.VALUE) || val == compare) {
SearchResult result = new SearchResult(i, val);
resultList.add(result);
}
}
}
public void performSearch() {
RAM memory = Emulator.computer.getMemory();
boolean signed = signedProperty.get();
resultList.removeIf((SearchResult result) -> {
int val = byteSized
? signed ? memory.readRaw(result.address) : memory.readRaw(result.address) & 0x0ff
: signed ? memory.readWordRaw(result.address) : memory.readWordRaw(result.address) & 0x0ffff;
int last = result.lastObservedValue;
result.lastObservedValue = val;
switch (searchType) {
case VALUE:
int compare = parseInt(searchValueProperty.get());
return compare != val;
case CHANGE:
switch (searchChangeType) {
case AMOUNT:
int amount = parseInt(searchChangeByProperty().getValue());
return (val - last) != amount;
case GREATER:
return val <= last;
case ANY_CHANGE:
return val == last;
case LESS:
return val >= last;
case NO_CHANGE:
return val != last;
}
break;
case TEXT:
break;
}
return false;
});
}
RAMListener memoryViewListener = null;
private final Map<Integer, MemoryCell> memoryCells = new ConcurrentHashMap<>();
public MemoryCell getMemoryCell(int address) {
return memoryCells.get(address);
}
public void initMemoryView() {
RAM memory = Emulator.computer.getMemory();
for (int addr = getStartAddress(); addr <= getEndAddress(); addr++) {
if (getMemoryCell(addr) == null) {
MemoryCell cell = new MemoryCell();
cell.address = addr;
cell.value.set(memory.readRaw(addr));
memoryCells.put(addr, cell);
}
}
if (memoryViewListener == null) {
memoryViewListener = memory.observe(RAMEvent.TYPE.ANY, startAddress, endAddress, this::processMemoryEvent);
listeners.add(memoryViewListener);
}
}
int fadeCounter = 0;
int FADE_TIMER_VALUE = (int) (Emulator.computer.getMotherboard().cyclesPerSecond / 60);
@Override
public void tick() {
computer.cpu.performSingleTrace();
if (fadeCounter-- <= 0) {
fadeCounter = FADE_TIMER_VALUE;
memoryCells.values().stream()
.filter((cell) -> cell.hasCounts())
.forEach((cell) -> {
if (cell.execCount.get() > 0) {
cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate));
}
if (cell.readCount.get() > 0) {
cell.readCount.set(Math.max(0, cell.readCount.get() - fadeRate));
}
if (cell.writeCount.get() > 0) {
cell.writeCount.set(Math.max(0, cell.writeCount.get() - fadeRate));
}
if (MemoryCell.listener != null) {
MemoryCell.listener.changed(null, cell, cell);
}
});
}
}
AtomicInteger pendingInspectorUpdates = new AtomicInteger(0);
public void onInspectorChanged() {
pendingInspectorUpdates.set(0);
}
private void processMemoryEvent(RAMEvent e) {
MemoryCell cell = getMemoryCell(e.getAddress());
if (cell != null) {
CPU cpu = Emulator.computer.getCpu();
int pc = cpu.getProgramCounter();
String trace = cpu.getLastTrace();
switch (e.getType()) {
case EXECUTE:
cell.execInstructionsDisassembly.add(trace);
if (cell.execInstructionsDisassembly.size() > historyLength) {
cell.execInstructionsDisassembly.remove(0);
}
case READ_OPERAND:
cell.execCount.set(Math.min(255, cell.execCount.get() + lightRate));
break;
case WRITE:
cell.writeCount.set(Math.min(255, cell.writeCount.get() + lightRate));
if (ui.isInspecting(cell.address)) {
if (pendingInspectorUpdates.incrementAndGet() < 5) {
Platform.runLater(() -> {
pendingInspectorUpdates.decrementAndGet();
cell.writeInstructions.add(pc);
cell.writeInstructionsDisassembly.add(trace);
if (cell.writeInstructions.size() > historyLength) {
cell.writeInstructions.remove(0);
cell.writeInstructionsDisassembly.remove(0);
}
});
}
} else {
cell.writeInstructions.add(cpu.getProgramCounter());
cell.writeInstructionsDisassembly.add(cpu.getLastTrace());
if (cell.writeInstructions.size() > historyLength) {
cell.writeInstructions.remove(0);
cell.writeInstructionsDisassembly.remove(0);
}
}
break;
default:
cell.readCount.set(Math.min(255, cell.readCount.get() + lightRate));
if (ui.isInspecting(cell.address)) {
if (pendingInspectorUpdates.incrementAndGet() < 5) {
Platform.runLater(() -> {
pendingInspectorUpdates.decrementAndGet();
cell.readInstructions.add(pc);
cell.readInstructionsDisassembly.add(trace);
if (cell.readInstructions.size() > historyLength) {
cell.readInstructions.remove(0);
cell.readInstructionsDisassembly.remove(0);
}
});
}
} else {
cell.readInstructions.add(cpu.getProgramCounter());
cell.readInstructionsDisassembly.add(cpu.getLastTrace());
if (cell.readInstructions.size() > historyLength) {
cell.readInstructions.remove(0);
cell.readInstructionsDisassembly.remove(0);
}
}
}
cell.value.set(e.getNewValue());
}
}
public void saveCheats(File saveFile) {
FileWriter writer = null;
try {
writer = new FileWriter(saveFile);
for (DynamicCheat cheat : cheatList) {
writer.write(cheat.serialize());
writer.write("\n");
}
writer.close();
} catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
writer.close();
} catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public void loadCheats(File saveFile) {
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(saveFile));
StringBuilder guts = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
DynamicCheat cheat = DynamicCheat.deserialize(line);
addCheat(cheat);
}
in.close();
} catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
in.close();
} catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
package jace.cheat;
import jace.Emulator;
import jace.JaceApplication;
import jace.core.CPU;
import jace.core.Computer;
import jace.core.RAM;
import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.state.State;
import jace.ui.MetacheatUI;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class MetaCheat extends Cheats {
static final ScriptEngine NASHORN_ENGINE = new ScriptEngineManager().getEngineByName("nashorn");
static Invocable NASHORN_INVOCABLE = (Invocable) NASHORN_ENGINE;
public static enum SearchType {
VALUE, TEXT, CHANGE
}
public static enum SearchChangeType {
NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT
}
public static class SearchResult {
int address;
int lastObservedValue = 0;
private SearchResult(int address, int val) {
this.address = address;
lastObservedValue = val;
}
@Override
public String toString() {
return Integer.toHexString(address) + ": " + lastObservedValue + " (" + Integer.toHexString(lastObservedValue) + ")";
}
public int getAddress() {
return address;
}
}
MetacheatUI ui;
public int fadeRate = 1;
public int lightRate = 30;
public int historyLength = 10;
private int startAddress = 0;
private int endAddress = 0x0AFFF;
private final StringProperty startAddressProperty = new SimpleStringProperty(Integer.toHexString(startAddress));
private final StringProperty endAddressProperty = new SimpleStringProperty(Integer.toHexString(endAddress));
private boolean byteSized = true;
private SearchType searchType = SearchType.VALUE;
private SearchChangeType searchChangeType = SearchChangeType.NO_CHANGE;
private final BooleanProperty signedProperty = new SimpleBooleanProperty(false);
private final StringProperty searchValueProperty = new SimpleStringProperty("0");
private final StringProperty changeByProperty = new SimpleStringProperty("0");
private final ObservableList<DynamicCheat> cheatList = FXCollections.observableArrayList();
private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList();
private final ObservableList<State> snapshotList = FXCollections.observableArrayList();
public MetaCheat(Computer computer) {
super(computer);
addNumericValidator(startAddressProperty);
addNumericValidator(endAddressProperty);
addNumericValidator(searchValueProperty);
addNumericValidator(changeByProperty);
startAddressProperty.addListener((prop, oldVal, newVal) -> {
startAddress = Math.max(0, Math.min(65535, Integer.parseInt(newVal, 16)));
});
endAddressProperty.addListener((prop, oldVal, newVal) -> {
endAddress = Math.max(0, Math.min(65535, Integer.parseInt(newVal, 16)));
});
}
private void addNumericValidator(StringProperty stringProperty) {
stringProperty.addListener((ObservableValue<? extends String> prop, String oldVal, String newVal) -> {
if (newVal == null || newVal.isEmpty()) {
return;
}
if (!newVal.matches("(\\+|-)?(x|$)?[0-9a-fA-F]*")) {
stringProperty.set("");
}
});
}
public int parseInt(String s) throws NumberFormatException {
if (s == null || s.isEmpty()) {
return 0;
}
if (s.matches("(\\+|-)?[0-9]+")) {
return Integer.parseInt(s);
} else {
String upper = s.toUpperCase();
boolean positive = !upper.startsWith("-");
for (int i = 0; i < upper.length(); i++) {
char c = upper.charAt(i);
if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) {
int value = Integer.parseInt(s.substring(i), 16);
if (!positive) {
value *= -1;
}
return value;
}
}
}
throw new NumberFormatException("Could not interpret int value " + s);
}
@Override
public void registerListeners() {
}
public void addCheat(DynamicCheat cheat) {
cheatList.add(cheat);
computer.getMemory().addListener(cheat);
cheat.addressProperty().addListener((prop, oldVal, newVal) -> {
computer.getMemory().removeListener(cheat);
cheat.doConfig();
computer.getMemory().addListener(cheat);
});
}
public void removeCheat(DynamicCheat cheat) {
cheat.active.set(false);
computer.getMemory().removeListener(cheat);
cheatList.remove(cheat);
}
@Override
protected void unregisterListeners() {
super.unregisterListeners();
cheatList.stream().forEach(computer.getMemory()::removeListener);
}
@Override
protected String getDeviceName() {
return "MetaCheat";
}
@Override
public void detach() {
super.detach();
ui.detach();
}
@Override
public void attach() {
ui = JaceApplication.getApplication().showMetacheat();
ui.registerMetacheatEngine(this);
super.attach();
}
public int getStartAddress() {
return startAddress;
}
public int getEndAddress() {
return endAddress;
}
public void setByteSized(boolean b) {
byteSized = b;
}
public void setSearchType(SearchType searchType) {
this.searchType = searchType;
}
public void setSearchChangeType(SearchChangeType searchChangeType) {
this.searchChangeType = searchChangeType;
}
public Property<Boolean> signedProperty() {
return signedProperty;
}
public Property<String> searchValueProperty() {
return searchValueProperty;
}
public Property<String> searchChangeByProperty() {
return changeByProperty;
}
public ObservableList<DynamicCheat> getCheats() {
return cheatList;
}
public ObservableList<SearchResult> getSearchResults() {
return resultList;
}
public ObservableList<State> getSnapshots() {
return snapshotList;
}
public Property<String> startAddressProperty() {
return startAddressProperty;
}
public Property<String> endAddressProperty() {
return endAddressProperty;
}
public void newSearch() {
RAM memory = Emulator.computer.getMemory();
resultList.clear();
int compare = parseInt(searchValueProperty.get());
for (int i = 0; i < 0x10000; i++) {
boolean signed = signedProperty.get();
int val
= byteSized
? signed ? memory.readRaw(i) : memory.readRaw(i) & 0x0ff
: signed ? memory.readWordRaw(i) : memory.readWordRaw(i) & 0x0ffff;
if (!searchType.equals(SearchType.VALUE) || val == compare) {
SearchResult result = new SearchResult(i, val);
resultList.add(result);
}
}
}
public void performSearch() {
RAM memory = Emulator.computer.getMemory();
boolean signed = signedProperty.get();
resultList.removeIf((SearchResult result) -> {
int val = byteSized
? signed ? memory.readRaw(result.address) : memory.readRaw(result.address) & 0x0ff
: signed ? memory.readWordRaw(result.address) : memory.readWordRaw(result.address) & 0x0ffff;
int last = result.lastObservedValue;
result.lastObservedValue = val;
switch (searchType) {
case VALUE:
int compare = parseInt(searchValueProperty.get());
return compare != val;
case CHANGE:
switch (searchChangeType) {
case AMOUNT:
int amount = parseInt(searchChangeByProperty().getValue());
return (val - last) != amount;
case GREATER:
return val <= last;
case ANY_CHANGE:
return val == last;
case LESS:
return val >= last;
case NO_CHANGE:
return val != last;
}
break;
case TEXT:
break;
}
return false;
});
}
RAMListener memoryViewListener = null;
private final Map<Integer, MemoryCell> memoryCells = new ConcurrentHashMap<>();
public MemoryCell getMemoryCell(int address) {
return memoryCells.get(address);
}
public void initMemoryView() {
RAM memory = Emulator.computer.getMemory();
for (int addr = getStartAddress(); addr <= getEndAddress(); addr++) {
if (getMemoryCell(addr) == null) {
MemoryCell cell = new MemoryCell();
cell.address = addr;
cell.value.set(memory.readRaw(addr));
memoryCells.put(addr, cell);
}
}
if (memoryViewListener == null) {
memoryViewListener = memory.observe(RAMEvent.TYPE.ANY, startAddress, endAddress, this::processMemoryEvent);
listeners.add(memoryViewListener);
}
}
int fadeCounter = 0;
int FADE_TIMER_VALUE = (int) (Emulator.computer.getMotherboard().getSpeedInHz() / 60);
@Override
public void tick() {
computer.cpu.performSingleTrace();
if (fadeCounter-- <= 0) {
fadeCounter = FADE_TIMER_VALUE;
memoryCells.values().stream()
.filter((cell) -> cell.hasCounts())
.forEach((cell) -> {
if (cell.execCount.get() > 0) {
cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate));
}
if (cell.readCount.get() > 0) {
cell.readCount.set(Math.max(0, cell.readCount.get() - fadeRate));
}
if (cell.writeCount.get() > 0) {
cell.writeCount.set(Math.max(0, cell.writeCount.get() - fadeRate));
}
if (MemoryCell.listener != null) {
MemoryCell.listener.changed(null, cell, cell);
}
});
}
}
AtomicInteger pendingInspectorUpdates = new AtomicInteger(0);
public void onInspectorChanged() {
pendingInspectorUpdates.set(0);
}
private void processMemoryEvent(RAMEvent e) {
MemoryCell cell = getMemoryCell(e.getAddress());
if (cell != null) {
CPU cpu = Emulator.computer.getCpu();
int pc = cpu.getProgramCounter();
String trace = cpu.getLastTrace();
switch (e.getType()) {
case EXECUTE:
cell.execInstructionsDisassembly.add(trace);
if (cell.execInstructionsDisassembly.size() > historyLength) {
cell.execInstructionsDisassembly.remove(0);
}
case READ_OPERAND:
cell.execCount.set(Math.min(255, cell.execCount.get() + lightRate));
break;
case WRITE:
cell.writeCount.set(Math.min(255, cell.writeCount.get() + lightRate));
if (ui.isInspecting(cell.address)) {
if (pendingInspectorUpdates.incrementAndGet() < 5) {
Platform.runLater(() -> {
pendingInspectorUpdates.decrementAndGet();
cell.writeInstructions.add(pc);
cell.writeInstructionsDisassembly.add(trace);
if (cell.writeInstructions.size() > historyLength) {
cell.writeInstructions.remove(0);
cell.writeInstructionsDisassembly.remove(0);
}
});
}
} else {
cell.writeInstructions.add(cpu.getProgramCounter());
cell.writeInstructionsDisassembly.add(cpu.getLastTrace());
if (cell.writeInstructions.size() > historyLength) {
cell.writeInstructions.remove(0);
cell.writeInstructionsDisassembly.remove(0);
}
}
break;
default:
cell.readCount.set(Math.min(255, cell.readCount.get() + lightRate));
if (ui.isInspecting(cell.address)) {
if (pendingInspectorUpdates.incrementAndGet() < 5) {
Platform.runLater(() -> {
pendingInspectorUpdates.decrementAndGet();
cell.readInstructions.add(pc);
cell.readInstructionsDisassembly.add(trace);
if (cell.readInstructions.size() > historyLength) {
cell.readInstructions.remove(0);
cell.readInstructionsDisassembly.remove(0);
}
});
}
} else {
cell.readInstructions.add(cpu.getProgramCounter());
cell.readInstructionsDisassembly.add(cpu.getLastTrace());
if (cell.readInstructions.size() > historyLength) {
cell.readInstructions.remove(0);
cell.readInstructionsDisassembly.remove(0);
}
}
}
cell.value.set(e.getNewValue());
}
}
public void saveCheats(File saveFile) {
FileWriter writer = null;
try {
writer = new FileWriter(saveFile);
for (DynamicCheat cheat : cheatList) {
writer.write(cheat.serialize());
writer.write("\n");
}
writer.close();
} catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
writer.close();
} catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public void loadCheats(File saveFile) {
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(saveFile));
StringBuilder guts = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
DynamicCheat cheat = DynamicCheat.deserialize(line);
addCheat(cheat);
}
in.close();
} catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
in.close();
} catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}

View File

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

View File

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

View File

@ -49,6 +49,7 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import javafx.collections.ObservableList;
import javafx.scene.control.TreeItem;
import javafx.scene.image.ImageView;
@ -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
@ -188,16 +189,17 @@ public class Configuration implements Reconfigurable {
}
public void setFieldValue(String field, Serializable value) {
setChanged(true);
if (value != null) {
if (value.equals(getFieldValue(field))) {
return;
}
} else {
if (getFieldValue(field) == null) {
setChanged(false);
return;
}
}
setChanged(true);
setRawFieldValue(field, value);
}
@ -251,9 +253,15 @@ public class Configuration implements Reconfigurable {
if (!changed) {
setGraphic(null);
} else {
setGraphic(getChangedIcon());
getChangedIcon().ifPresent(this::setGraphic);
}
}
public Stream<ConfigNode> getTreeAsStream() {
return Stream.concat(
Stream.of(this),
children.stream().flatMap(ConfigNode::getTreeAsStream));
}
}
public static ConfigNode BASE;
public static EmulatorUILogic ui = Emulator.logic;
@ -283,7 +291,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);
@ -300,7 +308,7 @@ public class Configuration implements Reconfigurable {
node.setRawFieldValue(f.getName(), (Serializable) o);
}
continue;
}
}
if (o == null) {
continue;
}
@ -493,7 +501,9 @@ public class Configuration implements Reconfigurable {
newRoot.getChildren().stream().forEach((child) -> {
String childName = child.toString();
ConfigNode oldChild = oldRoot.findChild(childName);
if (oldChild == null) {oldChild = oldRoot.findChild(child.id);}
if (oldChild == null) {
oldChild = oldRoot.findChild(child.id);
}
// System.out.println("Applying settings for " + childName);
applyConfigTree(child, oldChild);
});
@ -597,7 +607,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

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

View File

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

View File

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

View File

@ -18,6 +18,7 @@
*/
package jace.core;
import jace.Emulator;
import jace.apple2e.SoftSwitches;
import jace.config.InvokableAction;
import jace.config.Reconfigurable;
@ -39,6 +40,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 +52,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) {
@ -61,6 +69,7 @@ public class Keyboard implements Reconfigurable {
return "kbd";
}
static byte currentKey = 0;
public boolean shiftPressed = false;
public static void clearStrobe() {
currentKey = (byte) (currentKey & 0x07f);
@ -95,6 +104,7 @@ public class Keyboard implements Reconfigurable {
registerKeyHandler(new KeyHandler(code) {
@Override
public boolean handleKeyUp(KeyEvent e) {
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
if (action == null || !action.notifyOnRelease()) {
return false;
}
@ -118,6 +128,7 @@ public class Keyboard implements Reconfigurable {
@Override
public boolean handleKeyDown(KeyEvent e) {
// System.out.println("Key down: "+method.toString());
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
Object returnValue = null;
try {
if (method.getParameterCount() > 0) {
@ -241,11 +252,15 @@ public class Keyboard implements Reconfigurable {
default:
}
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
if (e.isShiftDown()) {
c = fixShiftedChar(c);
}
if (e.isControlDown()) {
if (c == 255) {
return;
}
c = (char) (c & 0x01f);
}

View File

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

View File

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

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

View File

@ -18,12 +18,14 @@
*/
package jace.core;
import jace.Emulator;
import jace.config.Configuration;
import jace.config.InvokableAction;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.reflections.Reflections;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -31,18 +33,23 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import org.reflections.Reflections;
/**
* This is a set of helper functions which do not belong anywhere else.
* Functions vary from introspection, discovery, and string/pattern matching.
@ -51,151 +58,11 @@ import javafx.scene.paint.Color;
*/
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
/**
@ -205,11 +72,11 @@ public class Utility {
*
* @param s
* @param t
* @return Distance (higher is better)
* @return Distance (lower means a closer match, zero is identical)
*/
public static int levenshteinDistance(String s, String t) {
if (s == null || t == null || s.length() == 0 || t.length() == 0) {
return -1;
return Integer.MAX_VALUE;
}
s = s.toLowerCase().replaceAll("[^a-zA-Z0-9\\s]", "");
@ -235,9 +102,20 @@ public class Utility {
}
}
}
return Math.max(m, n) - dist[m][n];
return dist[m][n];
}
/**
* Normalize distance based on longest string
* @param s
* @param t
* @return Similarity ranking, higher is better
*/
public static int adjustedLevenshteinDistance(String s, String t) {
return Math.max(s.length(), t.length()) - levenshteinDistance(s, t);
}
/**
* Compare strings based on a tally of similar patterns found, using a fixed
* search window The resulting score is heavily penalized if the strings
@ -247,7 +125,7 @@ public class Utility {
* @param c1
* @param c2
* @param width Search window size
* @return Overall similarity score (higher is beter)
* @return Overall similarity score (higher is better)
*/
public static double rankMatch(String c1, String c2, int width) {
double score = 0;
@ -270,23 +148,36 @@ 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;
if (Emulator.instance == null && headless) {
Emulator.instance = new Emulator(Collections.emptyList());
}
return result;
}
public static Image loadIcon(String filename) {
public static boolean isHeadlessMode() {
return isHeadless;
}
public static Optional<Image> loadIcon(String filename) {
if (isHeadless) {
return Optional.empty();
}
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 +200,21 @@ 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 confirm(String title, String message, Runnable accept) {
Platform.runLater(() -> {
Alert confirm = new Alert(Alert.AlertType.CONFIRMATION);
confirm.setContentText(message);
confirm.setTitle(title);
Optional<ButtonType> response = confirm.showAndWait();
response.ifPresent(b -> {
if (b.getButtonData().isDefaultButton()) {
(new Thread(accept)).start();
}
});
});
}
// public static void runModalProcess(String title, final Runnable runnable) {
@ -332,7 +237,6 @@ public class Utility {
// frame.dispose();
// }).start();
// }
public static class RankingComparator implements Comparator<String> {
String match;
@ -346,8 +250,8 @@ public class Utility {
@Override
public int compare(String o1, String o2) {
double s1 = levenshteinDistance(match, o1);
double s2 = levenshteinDistance(match, o2);
double s1 = adjustedLevenshteinDistance(match, o1);
double s2 = adjustedLevenshteinDistance(match, o2);
if (s2 == s1) {
s1 = rankMatch(o1, match, 3) + rankMatch(o1, match, 2);
s2 = rankMatch(o2, match, 3) + rankMatch(o2, match, 2);
@ -387,7 +291,7 @@ public class Utility {
// System.out.println(match + "->" + c + ":" + l + " -- "+ m2 + "," + m3 + "," + "(" + (m2 + m3) + ")");
// }
// double score = rankMatch(match, candidates.get(0), 2);
double score = levenshteinDistance(match, candidates.get(0));
double score = adjustedLevenshteinDistance(match, candidates.get(0));
if (score > 1) {
return candidates.get(0);
}
@ -581,4 +485,47 @@ public class Utility {
}
return setChild(object, paths[paths.length - 1], value, hex);
}
static Map<InvokableAction, Runnable> allActions = null;
public static Map<InvokableAction, Runnable> getAllInvokableActions() {
if (allActions == null) {
allActions = new HashMap<>();
Configuration.BASE.getTreeAsStream().forEach((Configuration.ConfigNode node) -> {
for (Method m : node.subject.getClass().getMethods()) {
if (m.isAnnotationPresent(InvokableAction.class)) {
allActions.put(m.getAnnotation(InvokableAction.class), () -> {
try {
m.invoke(node.subject);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(Utility.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
}
});
}
return allActions;
}
public static Runnable getNamedInvokableAction(String action) {
Map<InvokableAction, Runnable> actions = getAllInvokableActions();
List<InvokableAction> actionsList = new ArrayList(actions.keySet());
actionsList.sort((a,b) -> Integer.compare(getActionNameMatch(action, a), getActionNameMatch(action, b)));
// for (InvokableAction a : actionsList) {
// String actionName = a.alternatives() == null ? a.name() : (a.name() + ";" + a.alternatives());
// System.out.println("Score for " + action + " evaluating " + a.name() + ": " + getActionNameMatch(action, a));
// }
return actions.get(actionsList.get(0));
}
private static int getActionNameMatch(String str, InvokableAction action) {
int nameMatch = levenshteinDistance(str, action.name());
if (action.alternatives() != null) {
for (String alt : action.alternatives().split(";")) {
nameMatch = Math.min(nameMatch, levenshteinDistance(str, alt));
}
}
return nameMatch;
}
}

View File

@ -18,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;
}
@ -131,12 +132,15 @@ public abstract class Video extends Device {
public static int MIN_SCREEN_REFRESH = 15;
Runnable redrawScreen = () -> {
if (computer.getRunningProperty().get()) {
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
if (visible != null && video != null) {
// if (computer.getRunningProperty().get()) {
screenDirty = false;
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
// }
}
};
public void redraw() {
screenDirty = false;
javafx.application.Platform.runLater(redrawScreen);
}
@ -156,18 +160,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 +179,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 +212,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 +274,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

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

View File

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

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

View File

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

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

View File

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

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

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

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

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

View File

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

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>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

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