diff --git a/Platform/Apple/tools/jace/.vscode/launch.json b/Platform/Apple/tools/jace/.vscode/launch.json
new file mode 100644
index 00000000..fd194888
--- /dev/null
+++ b/Platform/Apple/tools/jace/.vscode/launch.json
@@ -0,0 +1,55 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "java",
+ "request": "attach",
+ "name": "Attach by Process ID",
+ "processId": "${command:PickJavaProcess}"
+ },
+ {
+ "type": "java",
+ "name": "Current File",
+ "request": "launch",
+ "mainClass": "${file}"
+ },
+ {
+ "type": "java",
+ "name": "ConvertDiskImage",
+ "request": "launch",
+ "mainClass": "lawlesslegends/jace.ConvertDiskImage",
+ "projectName": "lawlesslegends"
+ },
+ {
+ "type": "java",
+ "name": "LawlessLegends",
+ "request": "launch",
+ "mainClass": "lawlesslegends/jace.LawlessLegends",
+ "projectName": "lawlesslegends"
+ },
+ {
+ "type": "java",
+ "name": "ApplesoftProgram",
+ "request": "launch",
+ "mainClass": "lawlesslegends/jace.applesoft.ApplesoftProgram",
+ "projectName": "lawlesslegends"
+ },
+ {
+ "type": "java",
+ "name": "AcmeCrossAssembler",
+ "request": "launch",
+ "mainClass": "lawlesslegends/jace.assembly.AcmeCrossAssembler",
+ "projectName": "lawlesslegends"
+ },
+ {
+ "type": "java",
+ "name": "Library",
+ "request": "launch",
+ "mainClass": "lawlesslegends/jace.ui.Library",
+ "projectName": "lawlesslegends"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Platform/Apple/tools/jace/.vscode/settings.json b/Platform/Apple/tools/jace/.vscode/settings.json
new file mode 100644
index 00000000..e0f15db2
--- /dev/null
+++ b/Platform/Apple/tools/jace/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "java.configuration.updateBuildConfiguration": "automatic"
+}
\ No newline at end of file
diff --git a/Platform/Apple/tools/jace/pom.xml b/Platform/Apple/tools/jace/pom.xml
index ee9ce579..d79876a9 100644
--- a/Platform/Apple/tools/jace/pom.xml
+++ b/Platform/Apple/tools/jace/pom.xml
@@ -1,5 +1,7 @@
-
+
4.0.0
org.8bitbunch
@@ -13,6 +15,7 @@
UTF-8
jace.LawlessLegends
apache20
+ 3.3.3
@@ -43,7 +46,7 @@
com.gluonhq
gluonfx-maven-plugin
- 1.0.19
+ 1.0.21
jace.LawlessLegends
@@ -55,44 +58,44 @@
lawlesslegends/jace.LawlessLegends
-
-
-
- default-cli
-
-
-
-
- debug
-
-
-
-
-
-
-
-
- ide-debug
-
-
-
-
-
-
-
-
- ide-profile
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ default-cli
+
+
+
+
+ debug
+
+
+
+
+
+
+
+
+ ide-debug
+
+
+
+
+
+
+
+
+ ide-profile
+
+
+
+
+
+
+
+
+
+
+
@@ -116,8 +119,8 @@
module nestedvm {
- exports org.ibex.nestedvm;
- exports org.ibex.nestedvm.util;
+ exports org.ibex.nestedvm;
+ exports org.ibex.nestedvm.util;
}
@@ -130,13 +133,25 @@
+
+
+
+ org.lwjgl
+ lwjgl-bom
+ ${lwjgl.version}
+ import
+ pom
+
+
+
+
junit
junit
4.13.2
test
-
+
org.xerial.thirdparty
nestedvm
@@ -175,18 +190,69 @@
org.openjfx
javafx-swing
- 18
+ 20
jar
org.lwjgl
- lwjgl-openal
- 3.3.2
+ lwjgl
- javazoom
+ org.lwjgl
+ lwjgl-openal
+
+
+ org.lwjgl
+ lwjgl
+ ${lwjgl.natives}
+
+
+ org.lwjgl
+ lwjgl-openal
+ ${lwjgl.natives}
+
+
+ com.badlogicgames.jlayer
jlayer
- 1.0.1
+ 1.0.2-gdx
-
+
+
+ lwjgl-natives-linux-amd64
+
+
+ unix
+ amd64
+
+
+
+ natives-linux
+
+
+
+ lwjgl-natives-macos-x86_64
+
+
+ mac
+ x86_64
+
+
+
+ natives-macos
+
+
+
+ lwjgl-natives-windows-amd64
+
+
+ windows
+ amd64
+
+
+
+ natives-windows
+
+
+
+
\ No newline at end of file
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/Emulator.java b/Platform/Apple/tools/jace/src/main/java/jace/Emulator.java
index 5bf4ff07..c5220d1c 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/Emulator.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/Emulator.java
@@ -35,7 +35,7 @@ import jace.lawless.LawlessComputer;
public class Emulator {
public static Emulator instance;
- public static EmulatorUILogic logic = new EmulatorUILogic();
+ private static EmulatorUILogic logic;
public static Thread mainThread;
// public static void main(String... args) {
@@ -44,6 +44,13 @@ public class Emulator {
// }
private final LawlessComputer computer;
+
+ public static EmulatorUILogic getUILogic() {
+ if (logic == null) {
+ logic = new EmulatorUILogic();
+ }
+ return logic;
+ }
public static Emulator getInstance(List args) {
Emulator i = getInstance();
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/EmulatorUILogic.java b/Platform/Apple/tools/jace/src/main/java/jace/EmulatorUILogic.java
index 57944d3d..f83ef650 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/EmulatorUILogic.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/EmulatorUILogic.java
@@ -18,6 +18,8 @@
*/
package jace;
+import static jace.core.Utility.gripe;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -44,7 +46,6 @@ import jace.config.Reconfigurable;
import jace.core.Debugger;
import jace.core.RAM;
import jace.core.RAMListener;
-import static jace.core.Utility.gripe;
import jace.ide.IdeController;
import javafx.application.Platform;
import javafx.event.EventHandler;
@@ -90,7 +91,7 @@ public class EmulatorUILogic implements Reconfigurable {
category = "General",
name = "Show Drives"
)
- public boolean showDrives = false;
+ public boolean showDrives = Emulator.withComputer(c->!c.PRODUCTION_MODE, false);
public static void updateCPURegisters(MOS65C02 cpu) {
// DebuggerPanel debuggerPanel = Emulator.getFrame().getDebuggerPanel();
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/JaceUIController.java b/Platform/Apple/tools/jace/src/main/java/jace/JaceUIController.java
index a14b28d6..ce188004 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/JaceUIController.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/JaceUIController.java
@@ -246,9 +246,9 @@ public class JaceUIController {
});
speedSlider.valueProperty().addListener((val, oldValue, newValue) -> setSpeed(newValue.doubleValue()));
Platform.runLater(() -> {
- speedSlider.setValue(Emulator.logic.speedSetting);
+ speedSlider.setValue(Emulator.getUILogic().speedSetting);
// Kind of redundant but make sure speed is properly set as if the user did it
- setSpeed(Emulator.logic.speedSetting);
+ setSpeed(Emulator.getUILogic().speedSetting);
});
musicSelection.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) ->
Emulator.withComputer(computer ->
@@ -268,7 +268,7 @@ public class JaceUIController {
}
public void setSpeed(double speed) {
- Emulator.logic.speedSetting = (int) speed;
+ Emulator.getUILogic().speedSetting = (int) speed;
double speedRatio = convertSpeedToRatio(speed);
if (speedSlider.getValue() != speed) {
Platform.runLater(()->speedSlider.setValue(speed));
@@ -404,7 +404,7 @@ public class JaceUIController {
private List getMediaConsumers() {
List consumers = new ArrayList<>();
Emulator.withComputer(c -> consumers.add(((LawlessComputer) c).getUpgradeHandler()));
- if (Emulator.logic.showDrives) {
+ if (Emulator.getUILogic().showDrives) {
Emulator.withMemory(m -> {
for (Optional card : m.getAllCards()) {
card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent ->
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/LawlessLegends.java b/Platform/Apple/tools/jace/src/main/java/jace/LawlessLegends.java
index de3d4289..28952342 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/LawlessLegends.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/LawlessLegends.java
@@ -138,7 +138,7 @@ public class LawlessLegends extends Application {
new Thread(()->{
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Booting with watchdog");
RAMListener startListener = c.getMemory().
- observe(RAMEvent.TYPE.EXECUTE, 0x2000, (e) -> {
+ observe("Lawless Legends watchdog", RAMEvent.TYPE.EXECUTE, 0x2000, (e) -> {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Boot was detected, watchdog terminated.");
romStarted = true;
});
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Apple2e.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Apple2e.java
index 98b7917b..6758f149 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Apple2e.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Apple2e.java
@@ -76,9 +76,9 @@ public class Apple2e extends Computer {
@ConfigurableField(name = "Slot 5", shortName = "s5card")
public DeviceSelection card5 = new DeviceSelection<>(Cards.class, null);
@ConfigurableField(name = "Slot 6", shortName = "s6card")
- public DeviceSelection card6 = new DeviceSelection<>(Cards.class, Cards.DiskIIDrive);
+ public DeviceSelection card6 = new DeviceSelection<>(Cards.class, Cards.DiskIIDrive, true);
@ConfigurableField(name = "Slot 7", shortName = "s7card")
- public DeviceSelection card7 = new DeviceSelection<>(Cards.class, Cards.MassStorage);
+ public DeviceSelection card7 = new DeviceSelection<>(Cards.class, Cards.MassStorage, true);
@ConfigurableField(name = "Debug rom", shortName = "debugRom", description = "Use debugger //e rom")
public boolean useDebugRom = false;
@ConfigurableField(name = "Helpful hints", shortName = "hints")
@@ -434,7 +434,7 @@ public class Apple2e extends Computer {
private void enableHints() {
if (hints.isEmpty()) {
- hints.add(getMemory().observe(RAMEvent.TYPE.EXECUTE, 0x0FB63, (e)->{
+ hints.add(getMemory().observe("Helpful hints", RAMEvent.TYPE.EXECUTE, 0x0FB63, (e)->{
animationTimer.schedule(drawHints, 1, TimeUnit.SECONDS);
animationSchedule =
animationTimer.scheduleAtFixedRate(doAnimation, 1250, 100, TimeUnit.MILLISECONDS);
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/RAM128k.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/RAM128k.java
index 940b1b44..9c01dafb 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/RAM128k.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/RAM128k.java
@@ -53,9 +53,9 @@ abstract public class RAM128k extends RAM {
Function factory;
String name;
- Class clazz;
+ Class extends RAM128k> clazz;
- RamCards(String name, Class clazz, Function factory) {
+ RamCards(String name, Class extends RAM128k> clazz, Function factory) {
this.factory = factory;
this.name = name;
this.clazz = clazz;
@@ -409,7 +409,7 @@ abstract public class RAM128k extends RAM {
*
*/
@Override
- public synchronized void configureActiveMemory() {
+ public void configureActiveMemory() {
String auxZpConfiguration = getAuxZPConfiguration();
String readConfiguration = getReadConfiguration() + auxZpConfiguration;
String writeConfiguration = getWriteConfiguration() + auxZpConfiguration;
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java
index d78f6c77..eb70f6c4 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java
@@ -24,6 +24,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Timer;
+import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -37,6 +38,7 @@ import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.core.SoundGeneratorDevice;
import jace.core.SoundMixer;
+import jace.core.SoundMixer.SoundBuffer;
import javafx.stage.FileChooser;
/**
@@ -108,23 +110,15 @@ public class Speaker extends SoundGeneratorDevice {
* Manifestation of the apple speaker softswitch
*/
private boolean speakerBit = false;
- //
- /**
- * Locking semaphore to prevent race conditions when working with buffer or
- * related variables
- */
- private final Object bufferLock = new Object();
/**
* Double-buffer used for playing processed sound -- as one is played the
* other fills up.
*/
- private byte[] primaryBuffer;
- private byte[] secondaryBuffer;
- private int bufferPos = 0;
private Timer playbackTimer;
private double TICKS_PER_SAMPLE = ((double) Motherboard.DEFAULT_SPEED) / SoundMixer.RATE;
private double TICKS_PER_SAMPLE_FLOOR = Math.floor(TICKS_PER_SAMPLE);
private RAMListener listener = null;
+ private SoundBuffer buffer = null;
/**
* Creates a new instance of Speaker
@@ -148,11 +142,14 @@ public class Speaker extends SoundGeneratorDevice {
playbackTimer = null;
}
speakerBit = false;
- if (sdl != null && sdl.isOpen()) {
- sdl.stop();
- sdl.close();
+ if (buffer != null) {
+ try {
+ buffer.shutdown();
+ } catch (InterruptedException | ExecutionException e) {
+ // Ignore
+ }
}
- sdl = null;
+ buffer = null;
return result;
}
@@ -162,19 +159,17 @@ public class Speaker extends SoundGeneratorDevice {
*/
@Override
public void resume() {
- if (sdl == null || !sdl.isOpen()) {
- sdl = computer.mixer.getLine();
- if (sdl != null) {
- sdl.start();
- counter = 0;
- idleCycles = 0;
- level = 0;
- bufferPos = 0;
- } else {
- Logger.getLogger(getClass().getName()).severe("Unable to get audio line for speaker!");
- detach();
- return;
- }
+ if (buffer == null || !buffer.isAlive()) {
+ buffer = SoundMixer.createBuffer(false);
+ }
+ if (buffer != null) {
+ counter = 0;
+ idleCycles = 0;
+ level = 0;
+ } else {
+ Logger.getLogger(getClass().getName()).severe("Unable to get audio buffer for speaker!");
+ detach();
+ return;
}
if (force1mhz) {
@@ -187,27 +182,13 @@ public class Speaker extends SoundGeneratorDevice {
setRun(true);
}
- public void playCurrentBuffer() {
- byte[] buffer;
- int len;
- synchronized (bufferLock) {
- len = bufferPos;
- buffer = primaryBuffer;
- primaryBuffer = secondaryBuffer;
- bufferPos = 0;
- }
- secondaryBuffer = buffer;
- if (sdl != null && len > 0) {
- sdl.write(buffer, 0, len);
- }
- }
-
/**
* Reset idle counter whenever sound playback occurs
*/
public void resetIdle() {
idleCycles = 0;
if (!isRunning()) {
+ speakerBit = false;
resume();
}
}
@@ -237,28 +218,32 @@ public class Speaker extends SoundGeneratorDevice {
}
private void toggleSpeaker(RAMEvent e) {
- if (e.getType() == RAMEvent.TYPE.WRITE) {
- level += 2;
- }
+ // if (e.getType() == RAMEvent.TYPE.WRITE) {
+ // level += 2;
+ // }
speakerBit = !speakerBit;
resetIdle();
}
private void playSample(int sample) {
- if (sdl == null || !sdl.isOpen()) {
- resume();
+ if (buffer == null || !buffer.isAlive()) {
+ Logger.getLogger(getClass().getName()).severe("Audio buffer not initalized properly!");
+ buffer = SoundMixer.createBuffer(false);
+ }
+ try {
+ buffer.playSample((short) sample);
+ } catch (InterruptedException | ExecutionException e) {
+ // TODO: Do we need to really worry about this?
+ e.printStackTrace();
}
- int bytes = SoundMixer.BITS >> 3;
- // Prepare sound output in little endian format
- for (int i = 0; i < bytes; i++) {
- primaryBuffer[i] = primaryBuffer[i+bytes] = (byte) (sample & 0x0ff);
- sample >>= 8;
- }
- sdl.write(primaryBuffer, 0, bytes*2);
if (fileOutputActive) {
+ byte[] bytes = new byte[2];
+ bytes[0] = (byte) (sample & 0x0ff);
+ bytes[1] = (byte) ((sample >> 8) & 0x0ff);
+
try {
- out.write(primaryBuffer, 0, bytes*2);
+ out.write(bytes, 0, 2);
} catch (IOException ex) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Error recording sound", ex);
toggleFileOutput();
@@ -271,7 +256,7 @@ public class Speaker extends SoundGeneratorDevice {
* Add a memory event listener for C03x for capturing speaker events
*/
private void configureListener() {
- listener = computer.getMemory().observe(RAMEvent.TYPE.ANY, 0x0c030, 0x0c03f, this::toggleSpeaker);
+ listener = computer.getMemory().observe("Speaker", RAMEvent.TYPE.ANY, 0x0c030, 0x0c03f, this::toggleSpeaker);
}
private void removeListener() {
@@ -295,14 +280,7 @@ public class Speaker extends SoundGeneratorDevice {
@Override
public final void reconfigure() {
- super.reconfigure();
-
- if (primaryBuffer != null && secondaryBuffer != null) {
- return;
- }
- BUFFER_SIZE = 20000 * (SoundMixer.BITS >> 3);
- primaryBuffer = new byte[BUFFER_SIZE];
- secondaryBuffer = new byte[BUFFER_SIZE];
+ super.reconfigure();
}
@Override
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java
index 367107ed..29fb722a 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java
@@ -719,8 +719,8 @@ public class VideoDHGR extends Video {
}
private void registerDirtyFlagChecks() {
- computer.getMemory().observe(RAMEvent.TYPE.WRITE, 0x0400, 0x0bff, this::registerTextDirtyFlag);
- computer.getMemory().observe(RAMEvent.TYPE.WRITE, 0x02000, 0x05fff, this::registerHiresDirtyFlag);
+ computer.getMemory().observe("Check for text changes", RAMEvent.TYPE.WRITE, 0x0400, 0x0bff, this::registerTextDirtyFlag);
+ computer.getMemory().observe("Check for graphics changes", RAMEvent.TYPE.WRITE, 0x02000, 0x05fff, this::registerHiresDirtyFlag);
}
@Override
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java
index 18a5c225..c2701a40 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java
@@ -423,11 +423,11 @@ public class VideoNTSC extends VideoDHGR {
return;
}
RAM memory = computer.getMemory();
- rgbStateListeners.add(memory.observe(RAMEvent.TYPE.ANY, 0x0c05e, (e) -> {
+ rgbStateListeners.add(memory.observe("NTSC: AN3 state change", RAMEvent.TYPE.ANY, 0x0c05e, (e) -> {
an3 = false;
rgbStateChange();
}));
- rgbStateListeners.add(memory.observe(RAMEvent.TYPE.ANY, 0x0c05f, (e) -> {
+ rgbStateListeners.add(memory.observe("NTSC: 80COL state change", RAMEvent.TYPE.ANY, 0x0c05f, (e) -> {
if (!an3) {
f2 = f1;
f1 = SoftSwitches._80COL.getState();
@@ -435,7 +435,7 @@ public class VideoNTSC extends VideoDHGR {
an3 = true;
rgbStateChange();
}));
- rgbStateListeners.add(memory.observe(RAMEvent.TYPE.EXECUTE, 0x0fa62, (e) -> {
+ rgbStateListeners.add(memory.observe("NTSC: Reset hook for reverting RGB mode", RAMEvent.TYPE.EXECUTE, 0x0fa62, (e) -> {
// When reset hook is called, reset the graphics mode
// This is useful in case a program is running that
// is totally clueless how to set the RGB state correctly.
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/IntC8SoftSwitch.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/IntC8SoftSwitch.java
index edf81787..63a4e026 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/IntC8SoftSwitch.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/softswitch/IntC8SoftSwitch.java
@@ -40,7 +40,7 @@ public class IntC8SoftSwitch extends SoftSwitch {
super("InternalC8Rom", false);
// INTC8Rom should activate whenever C3xx memory is accessed and SLOTC3ROM is off
addListener(
- new RAMListener(RAMEvent.TYPE.ANY, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
+ new RAMListener("Softswitch " + getName() + " on", RAMEvent.TYPE.ANY, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(0x0C300);
@@ -57,7 +57,7 @@ public class IntC8SoftSwitch extends SoftSwitch {
// INTCXRom shoud deactivate whenever CFFF is accessed
addListener(
- new RAMListener(RAMEvent.TYPE.ANY, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
+ new RAMListener("Softswitch " + getName() + " off", RAMEvent.TYPE.ANY, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(0x0CFFF);
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftProgram.java b/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftProgram.java
index 5303385d..6e74982d 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftProgram.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/applesoft/ApplesoftProgram.java
@@ -202,7 +202,7 @@ public class ApplesoftProgram {
*/
private void whenReady(Runnable r) {
Emulator.withMemory(memory->{
- memory.addListener(new RAMListener(RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
+ memory.addListener(new RAMListener("Applesoft: Trap GOTO command", RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(GOTO_CMD);
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/cheat/Cheats.java b/Platform/Apple/tools/jace/src/main/java/jace/cheat/Cheats.java
index 3dedc143..ab908dbd 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/cheat/Cheats.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/cheat/Cheats.java
@@ -87,36 +87,36 @@ public abstract class Cheats extends Device {
}
}
- public RAMListener bypassCode(int address, int addressEnd) {
+ public RAMListener bypassCode(String name, int address, int addressEnd) {
int noOperation = MOS65C02.COMMAND.NOP.ordinal();
- return addCheat(RAMEvent.TYPE.READ, (e) -> e.setNewValue(noOperation), address, addressEnd);
+ return addCheat(name, RAMEvent.TYPE.READ, (e) -> e.setNewValue(noOperation), address, addressEnd);
}
- public RAMListener forceValue(int value, int... address) {
- return addCheat(RAMEvent.TYPE.ANY, (e) -> e.setNewValue(value), address);
+ public RAMListener forceValue(String name, int value, int... address) {
+ return addCheat(name, RAMEvent.TYPE.ANY, (e) -> e.setNewValue(value), address);
}
- public RAMListener forceValue(int value, Boolean auxFlag, int... address) {
- return addCheat(RAMEvent.TYPE.ANY, auxFlag, (e) -> e.setNewValue(value), address);
+ public RAMListener forceValue(String name, int value, Boolean auxFlag, int... address) {
+ return addCheat(name, RAMEvent.TYPE.ANY, auxFlag, (e) -> e.setNewValue(value), address);
}
- public RAMListener addCheat(RAMEvent.TYPE type, RAMEvent.RAMEventHandler handler, int... address) {
+ public RAMListener addCheat(String name, RAMEvent.TYPE type, RAMEvent.RAMEventHandler handler, int... address) {
RAMListener listener;
if (address.length == 1) {
- listener = computer.getMemory().observe(type, address[0], handler);
+ listener = computer.getMemory().observe(getName() + ": " + name, type, address[0], handler);
} else {
- listener = computer.getMemory().observe(type, address[0], address[1], handler);
+ listener = computer.getMemory().observe(getName() + ": " + name, type, address[0], address[1], handler);
}
listeners.add(listener);
return listener;
}
- public RAMListener addCheat(RAMEvent.TYPE type, Boolean auxFlag, RAMEvent.RAMEventHandler handler, int... address) {
+ public RAMListener addCheat(String name, RAMEvent.TYPE type, Boolean auxFlag, RAMEvent.RAMEventHandler handler, int... address) {
RAMListener listener;
if (address.length == 1) {
- listener = computer.getMemory().observe(type, address[0], auxFlag, handler);
+ listener = computer.getMemory().observe(getName() + ": " + name, type, address[0], auxFlag, handler);
} else {
- listener = computer.getMemory().observe(type, address[0], address[1], auxFlag, handler);
+ listener = computer.getMemory().observe(getName() + ": " + name, type, address[0], address[1], auxFlag, handler);
}
listeners.add(listener);
return listener;
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/cheat/DynamicCheat.java b/Platform/Apple/tools/jace/src/main/java/jace/cheat/DynamicCheat.java
index 1f1a1be3..68910800 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/cheat/DynamicCheat.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/cheat/DynamicCheat.java
@@ -1,5 +1,7 @@
package jace.cheat;
+import javax.script.ScriptException;
+
import jace.core.RAMEvent;
import jace.core.RAMListener;
import javafx.beans.property.BooleanProperty;
@@ -9,7 +11,6 @@ import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.util.Callback;
-import javax.script.ScriptException;
/**
*
@@ -21,10 +22,11 @@ public class DynamicCheat extends RAMListener {
StringProperty expression;
BooleanProperty active;
StringProperty name;
+ String cheatName;
Callback expressionCallback;
- public DynamicCheat(int address, String expr) {
- super(RAMEvent.TYPE.ANY, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY);
+ public DynamicCheat(String cheatName, int address, String expr) {
+ super(cheatName, RAMEvent.TYPE.ANY, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY);
id = (int) (Math.random() * 10000000);
addr = new SimpleIntegerProperty(address);
expression = new SimpleStringProperty(expr);
@@ -106,18 +108,19 @@ public class DynamicCheat extends RAMListener {
public static final String DELIMITER = ";";
public String serialize() {
- return escape(name.get()) + DELIMITER
+ return escape(cheatName) + DELIMITER + escape(name.get()) + DELIMITER
+ escape("$"+Integer.toHexString(addr.get())) + DELIMITER
+ escape(expression.get());
}
static public DynamicCheat deserialize(String in) {
String[] parts = in.split(DELIMITER);
- String name = unescape(parts[0]);
- Integer addr = Integer.parseInt(parts[1].substring(1), 16);
- String expr = unescape(parts[2]);
+ String cheatName = unescape(parts[0]);
+ String name = unescape(parts[1]);
+ Integer addr = Integer.parseInt(parts[2].substring(1), 16);
+ String expr = unescape(parts[3]);
- DynamicCheat out = new DynamicCheat(addr, expr);
+ DynamicCheat out = new DynamicCheat(cheatName, addr, expr);
out.name.set(name);
return out;
}
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/cheat/MetaCheat.java b/Platform/Apple/tools/jace/src/main/java/jace/cheat/MetaCheat.java
index cc201efc..b353944d 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/cheat/MetaCheat.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/cheat/MetaCheat.java
@@ -302,7 +302,7 @@ public class MetaCheat extends Cheats {
}
}
if (memoryViewListener == null) {
- memoryViewListener = memory.observe(RAMEvent.TYPE.ANY, startAddress, endAddress, this::processMemoryEvent);
+ memoryViewListener = memory.observe("Metacheat memory viewer", RAMEvent.TYPE.ANY, startAddress, endAddress, this::processMemoryEvent);
listeners.add(memoryViewListener);
}
});
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/cheat/MontezumasRevengeCheats.java b/Platform/Apple/tools/jace/src/main/java/jace/cheat/MontezumasRevengeCheats.java
index b30e4d5e..2dd0e3c0 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/cheat/MontezumasRevengeCheats.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/cheat/MontezumasRevengeCheats.java
@@ -71,21 +71,21 @@ public class MontezumasRevengeCheats extends Cheats {
@Override
public void registerListeners() {
if (repulsiveHack) {
- addCheat(RAMEvent.TYPE.WRITE, this::repulsiveBehavior, 0x1508, 0x1518);
+ addCheat("Repulsive", RAMEvent.TYPE.WRITE, this::repulsiveBehavior, 0x1508, 0x1518);
}
if (featherFall) {
- addCheat(RAMEvent.TYPE.WRITE, this::featherFallBehavior, PLAYER_Y);
+ addCheat("Feather fall", RAMEvent.TYPE.WRITE, this::featherFallBehavior, PLAYER_Y);
// Bypass the part that realizes you should die when you hit the floor
- bypassCode(0x6bb3, 0x6bb4);
+ bypassCode("Feather fall code hack", 0x6bb3, 0x6bb4);
}
if (moonJump) {
- addCheat(RAMEvent.TYPE.WRITE, this::moonJumpBehavior, Y_VELOCITY);
+ addCheat("Moon jump", RAMEvent.TYPE.WRITE, this::moonJumpBehavior, Y_VELOCITY);
}
if (infiniteLives) {
- forceValue(11, LIVES);
+ forceValue("Infinite lives", 11, LIVES);
}
if (safePassage) {
@@ -103,22 +103,22 @@ public class MontezumasRevengeCheats extends Cheats {
memory.write(0x0f51, (byte) 0b00001000, false, false);
memory.write(0x0f52, (byte) 0b10000100, false, false);
memory.write(0x0f53, (byte) 0b11010101, false, false);
- forceValue(32, FLOOR_TIMER);
- forceValue(32, HAZARD_TIMER);
- forceValue(1, HAZARD_FLAG);
+ forceValue("Hack floor timer", 32, FLOOR_TIMER);
+ forceValue("Hack hazard timer", 32, HAZARD_TIMER);
+ forceValue("Hack hazard flag", 1, HAZARD_FLAG);
});
}
if (scoreHack) {
// Score: 900913
- forceValue(0x90, SCORE);
- forceValue(0x09, SCORE + 1);
- forceValue(0x13, SCORE + 2);
+ forceValue("Hack score 1", 0x90, SCORE);
+ forceValue("Hack score 2", 0x09, SCORE + 1);
+ forceValue("Hack score 3", 0x13, SCORE + 2);
}
if (snakeCharmer) {
// Skip the code that determines you're touching an enemy
- bypassCode(0x07963, 0x07964);
+ bypassCode("Snake charmer", 0x07963, 0x07964);
}
if (mouseHack) {
EmulatorUILogic.addMouseListener(listener);
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/cheat/PrinceOfPersiaCheats.java b/Platform/Apple/tools/jace/src/main/java/jace/cheat/PrinceOfPersiaCheats.java
index d7c4f408..fd7b164e 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/cheat/PrinceOfPersiaCheats.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/cheat/PrinceOfPersiaCheats.java
@@ -172,19 +172,19 @@ public class PrinceOfPersiaCheats extends Cheats {
@Override
public void registerListeners() {
if (velocityHack) {
- addCheat(RAMEvent.TYPE.READ_DATA, true, this::velocityHackBehavior, CharYVel);
+ addCheat("Hack velocity", RAMEvent.TYPE.READ_DATA, true, this::velocityHackBehavior, CharYVel);
}
if (invincibilityHack) {
- forceValue(3, true, KidStrength);
+ forceValue("Hack invincibility", 3, true, KidStrength);
}
if (sleepHack) {
- forceValue(0, true, EnemyAlert);
+ forceValue("Go to sleep!", 0, true, EnemyAlert);
}
if (swordHack) {
- forceValue(1, true, hasSword);
+ forceValue("Can haz sword", 1, true, hasSword);
}
if (timeHack) {
- forceValue(0x69, true, MinLeft);
+ forceValue("Hack time", 0x69, true, MinLeft);
}
if (mouseHack) {
EmulatorUILogic.addMouseListener(listener);
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/config/Configuration.java b/Platform/Apple/tools/jace/src/main/java/jace/config/Configuration.java
index d86b7561..03de6947 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/config/Configuration.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/config/Configuration.java
@@ -62,6 +62,11 @@ import javafx.scene.image.ImageView;
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class Configuration implements Reconfigurable {
+ public EmulatorUILogic ui;
+
+ public Configuration() {
+ ui = Emulator.getUILogic();
+ }
static ConfigurableField getConfigurableFieldInfo(Reconfigurable subject, String settingName) {
Field f;
@@ -262,13 +267,12 @@ public class Configuration implements Reconfigurable {
}
}
public static ConfigNode BASE;
- public static EmulatorUILogic ui = Emulator.logic;
@ConfigurableField(name = "Autosave Changes", description = "If unchecked, changes are only saved when the Save button is pressed.")
public static boolean saveAutomatically = false;
public static void buildTree() {
BASE = new ConfigNode(new Configuration());
- Set visited = new LinkedHashSet();
+ Set visited = new LinkedHashSet<>();
buildTree(BASE, visited);
Emulator.withComputer(c->{
ConfigNode computer = new ConfigNode(BASE, c);
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/Card.java b/Platform/Apple/tools/jace/src/main/java/jace/core/Card.java
index 80b5cb13..d0c1cc7a 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/core/Card.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/core/Card.java
@@ -18,7 +18,6 @@
*/
package jace.core;
-import jace.Emulator;
import jace.apple2e.SoftSwitches;
/**
@@ -103,10 +102,10 @@ public abstract class Card extends Device {
@Override
public void reconfigure() {
//super.reconfigure();
- Emulator.whileSuspended(c-> {
+ // Emulator.whileSuspended(c-> {
unregisterListeners();
registerListeners();
- });
+ // });
}
public void notifyVBLStateChanged(boolean state) {
@@ -121,12 +120,12 @@ public abstract class Card extends Device {
RAM memory = computer.getMemory();
int baseIO = 0x0c080 + slot * 16;
int baseRom = 0x0c000 + slot * 256;
- ioListener = memory.observe(RAMEvent.TYPE.ANY, baseIO, baseIO + 15, (e) -> {
+ ioListener = memory.observe("Slot " + getSlot() + " " + getDeviceName() + " IO access", RAMEvent.TYPE.ANY, baseIO, baseIO + 15, (e) -> {
int address = e.getAddress() & 0x0f;
handleIOAccess(address, e.getType(), e.getNewValue(), e);
});
- firmwareListener = memory.observe(RAMEvent.TYPE.ANY, baseRom, baseRom + 255, (e) -> {
+ firmwareListener = memory.observe("Slot " + getSlot() + " " + getDeviceName() + " CX Firmware access", RAMEvent.TYPE.ANY, baseRom, baseRom + 255, (e) -> {
computer.getMemory().setActiveCard(slot);
// Sather 6-4: Writes will still go through even when CXROM inhibits slot ROM
if (SoftSwitches.CXROM.isOff() || !e.getType().isRead()) {
@@ -134,7 +133,7 @@ public abstract class Card extends Device {
}
});
- c8firmwareListener = memory.observe(RAMEvent.TYPE.ANY, 0xc800, 0xcfff, (e) -> {
+ c8firmwareListener = memory.observe("Slot " + getSlot() + " " + getDeviceName() + " C8 Firmware access", RAMEvent.TYPE.ANY, 0xc800, 0xcfff, (e) -> {
if (SoftSwitches.CXROM.isOff() && SoftSwitches.INTC8ROM.isOff()
&& computer.getMemory().getActiveSlot() == slot) {
handleC8FirmwareAccess(e.getAddress() - 0x0c800, e.getType(), e.getNewValue(), e);
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/Motherboard.java b/Platform/Apple/tools/jace/src/main/java/jace/core/Motherboard.java
index dc087741..73f50852 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/core/Motherboard.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/core/Motherboard.java
@@ -135,6 +135,7 @@ public class Motherboard extends TimedDevice {
addChildDevice(speaker);
} catch (Throwable t) {
System.out.println("Unable to initalize sound -- deactivating speaker out");
+ t.printStackTrace();
}
} else {
System.out.println("Speaker not enabled, leaving it off.");
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/RAM.java b/Platform/Apple/tools/jace/src/main/java/jace/core/RAM.java
index a5542dd7..3fc14e20 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/core/RAM.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/core/RAM.java
@@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentSkipListSet;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import jace.Emulator;
import jace.apple2e.SoftSwitches;
import jace.config.Reconfigurable;
@@ -56,6 +57,7 @@ public abstract class RAM implements Reconfigurable {
*
* @param computer
*/
+ @SuppressWarnings("unchecked")
public RAM(Computer computer) {
this.computer = computer;
listeners = new ConcurrentSkipListSet<>();
@@ -95,11 +97,10 @@ public abstract class RAM implements Reconfigurable {
cards[slot] = Optional.of(c);
c.setSlot(slot);
c.attach();
+ configureActiveMemory();
}
public void removeCard(Card c) {
- c.suspend();
- c.detach();
removeCard(c.getSlot());
}
@@ -198,14 +199,15 @@ public abstract class RAM implements Reconfigurable {
}
}
+ @SuppressWarnings("unchecked")
private void refreshListenerMap() {
listenerMap = (Set[]) new Set[256];
ioListenerMap = (Set[]) new Set[256];
listeners.forEach(this::addListenerRange);
- }
+ }
- public RAMListener observe(RAMEvent.TYPE type, int address, RAMEvent.RAMEventHandler handler) {
- return addListener(new RAMListener(type, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
+ public RAMListener observe(String observerationName, RAMEvent.TYPE type, int address, RAMEvent.RAMEventHandler handler) {
+ return addListener(new RAMListener(observerationName, type, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(address);
@@ -218,8 +220,8 @@ public abstract class RAM implements Reconfigurable {
});
}
- public RAMListener observe(RAMEvent.TYPE type, int address, Boolean auxFlag, RAMEvent.RAMEventHandler handler) {
- return addListener(new RAMListener(type, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
+ public RAMListener observe(String observerationName, RAMEvent.TYPE type, int address, Boolean auxFlag, RAMEvent.RAMEventHandler handler) {
+ return addListener(new RAMListener(observerationName, type, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(address);
@@ -234,8 +236,8 @@ public abstract class RAM implements Reconfigurable {
});
}
- public RAMListener observe(RAMEvent.TYPE type, int addressStart, int addressEnd, RAMEvent.RAMEventHandler handler) {
- return addListener(new RAMListener(type, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
+ public RAMListener observe(String observerationName, RAMEvent.TYPE type, int addressStart, int addressEnd, RAMEvent.RAMEventHandler handler) {
+ return addListener(new RAMListener(observerationName, type, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(addressStart);
@@ -249,8 +251,8 @@ public abstract class RAM implements Reconfigurable {
});
}
- public RAMListener observe(RAMEvent.TYPE type, int addressStart, int addressEnd, Boolean auxFlag, RAMEvent.RAMEventHandler handler) {
- return addListener(new RAMListener(type, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
+ public RAMListener observe(String observerationName, RAMEvent.TYPE type, int addressStart, int addressEnd, Boolean auxFlag, RAMEvent.RAMEventHandler handler) {
+ return addListener(new RAMListener(observerationName, type, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(addressStart);
@@ -280,14 +282,12 @@ public abstract class RAM implements Reconfigurable {
return l;
}
listeners.add(l);
- computer.cpu.whileSuspended(()->{
- addListenerRange(l);
- });
+ Emulator.whileSuspended((c)->addListenerRange(l));
return l;
}
- public RAMListener addExecutionTrap(int address, Consumer handler) {
- RAMListener listener = new RAMListener(RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
+ public RAMListener addExecutionTrap(String observerationName, int address, Consumer handler) {
+ RAMListener listener = new RAMListener(observerationName, RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(address);
@@ -307,9 +307,7 @@ public abstract class RAM implements Reconfigurable {
return;
}
listeners.remove(l);
- computer.cpu.whileSuspended(()->{
- refreshListenerMap();
- });
+ Emulator.whileSuspended(c->refreshListenerMap());
}
private byte _callListener(RAMEvent.TYPE t, int address, int oldValue, int newValue) {
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/RAMListener.java b/Platform/Apple/tools/jace/src/main/java/jace/core/RAMListener.java
index 5eb7c3f1..ad239dd4 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/core/RAMListener.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/core/RAMListener.java
@@ -39,14 +39,17 @@ public abstract class RAMListener implements RAMEvent.RAMEventHandler, Comparabl
private int valueStart;
private int valueEnd;
private int valueAmount;
+ private String name;
/**
* Creates a new instance of RAMListener
+ * @param name
* @param t
* @param s
* @param v
*/
- public RAMListener(RAMEvent.TYPE t, RAMEvent.SCOPE s, RAMEvent.VALUE v) {
+ public RAMListener(String name, RAMEvent.TYPE t, RAMEvent.SCOPE s, RAMEvent.VALUE v) {
+ setName(name);
setType(t);
setScope(s);
setValue(v);
@@ -77,6 +80,14 @@ public abstract class RAMListener implements RAMEvent.RAMEventHandler, Comparabl
this.value = value;
}
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
public int getScopeStart() {
return scopeStart;
}
@@ -161,18 +172,23 @@ public abstract class RAMListener implements RAMEvent.RAMEventHandler, Comparabl
@Override
public int compareTo(RAMListener o) {
- if (o.scopeStart == scopeStart) {
- if (o.scopeEnd == scopeEnd) {
- if (o.type == type) {
- return Integer.compare(o.hashCode(), hashCode());
+ if (o.name == name) {
+ if (o.scopeStart == scopeStart) {
+ if (o.scopeEnd == scopeEnd) {
+ if (o.type == type) {
+ // Ignore hash codes -- combination of name, address range and type should identify similar listeners.
+ return (int) 0;
+ } else {
+ return Integer.compare(o.type.ordinal(), type.ordinal());
+ }
} else {
- return Integer.compare(o.type.ordinal(), type.ordinal());
+ return Integer.compare(o.scopeEnd, scopeEnd);
}
} else {
- return Integer.compare(o.scopeEnd, scopeEnd);
+ return Integer.compare(o.scopeStart, scopeStart);
}
} else {
- return Integer.compare(o.scopeStart, scopeStart);
+ return o.name.compareTo(name);
}
}
}
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/SoftSwitch.java b/Platform/Apple/tools/jace/src/main/java/jace/core/SoftSwitch.java
index aba74d74..f289e7f2 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/core/SoftSwitch.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/core/SoftSwitch.java
@@ -106,7 +106,7 @@ public abstract class SoftSwitch {
exclusionActivate.add(i);
}
}
- RAMListener l = new RAMListener(changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
+ RAMListener l = new RAMListener("Softswitch toggle " + name, changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(beginAddr);
@@ -136,7 +136,7 @@ public abstract class SoftSwitch {
exclusionActivate.add(i);
}
}
- RAMListener l = new RAMListener(changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
+ RAMListener l = new RAMListener("Softswitch on " + name, changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(beginAddr);
@@ -169,7 +169,7 @@ public abstract class SoftSwitch {
exclusionDeactivate.add(i);
}
}
- RAMListener l = new RAMListener(changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
+ RAMListener l = new RAMListener("Softswitch off " + name, changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(beginAddr);
@@ -201,7 +201,7 @@ public abstract class SoftSwitch {
}
}
// RAMListener l = new RAMListener(changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
- RAMListener l = new RAMListener(RAMEvent.TYPE.READ, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
+ RAMListener l = new RAMListener("Softswitch read state " + name, RAMEvent.TYPE.READ, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(beginAddr);
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/SoundMixer.java b/Platform/Apple/tools/jace/src/main/java/jace/core/SoundMixer.java
index 6823347d..e45cac3f 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/core/SoundMixer.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/core/SoundMixer.java
@@ -18,14 +18,25 @@
*/
package jace.core;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.logging.Logger;
+import org.lwjgl.BufferUtils;
import org.lwjgl.openal.AL;
+import org.lwjgl.openal.AL10;
import org.lwjgl.openal.ALC;
import org.lwjgl.openal.ALC10;
import org.lwjgl.openal.ALCCapabilities;
import org.lwjgl.openal.ALCapabilities;
+import jace.Emulator;
import jace.config.ConfigurableField;
/**
@@ -41,8 +52,9 @@ public class SoundMixer extends Device {
/**
* Bits per sample
+ * Making this configurable requires too much effort and not a lot of benefit
*/
- @ConfigurableField(name = "Bits per sample", shortName = "bits")
+ // @ConfigurableField(name = "Bits per sample", shortName = "bits")
public static int BITS = 16;
/**
* Sample playback rate
@@ -51,38 +63,206 @@ public class SoundMixer extends Device {
public static int RATE = 48000;
@ConfigurableField(name = "Mute", shortName = "mute")
public static boolean MUTE = false;
+
+ @ConfigurableField(name = "Buffer size", shortName = "buffer")
+ public static int BUFFER_SIZE = 512;
+
+ public static boolean PLAYBACK_ENABLED = false;
+ // Innocent until proven guilty by a failed initialization
+ public static boolean PLAYBACK_DRIVER_DETECTED = true;
+ public static boolean PLAYBACK_INITIALIZED = false;
- private final String defaultDeviceName;
- private long audioDevice;
- private long audioContext;
- private ALCCapabilities audioCapabilities;
- private ALCapabilities audioLibCapabilities;
+ private static String defaultDeviceName;
+ private static long audioDevice = -1;
+ private static long audioContext = -1;
+ private static ALCCapabilities audioCapabilities;
+ private static ALCapabilities audioLibCapabilities;
+ // In case the OpenAL implementation wants to be run in a single thread, use a single thread executor
+ protected static ExecutorService soundThreadExecutor = Executors.newSingleThreadExecutor();
public SoundMixer(Computer computer) {
super(computer);
- defaultDeviceName = ALC10.alcGetString(0, ALC10.ALC_DEFAULT_DEVICE_SPECIFIER);
+ defaultDeviceName = ALC10.alcGetString(0, ALC10.ALC_DEFAULT_DEVICE_SPECIFIER);
+ }
+
+ public static T performSoundFunction(Callable operation) {
+ return performSoundFunction(operation, false);
+ }
+
+ public static T performSoundFunction(Callable operation, boolean ignoreError) {
+ Future result = soundThreadExecutor.submit(operation);
+ try {
+ if (!ignoreError) {
+ Future error = soundThreadExecutor.submit(AL10::alGetError);
+ int err;
+ err = error.get();
+ if (err != AL10.AL_NO_ERROR) {
+ throw new RuntimeException(AL10.alGetString(err));
+ }
+ }
+ return result.get();
+ } catch (InterruptedException | ExecutionException e) {
+ System.out.println("Error when executing sound action: " + e.getMessage());
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static void performSoundOperation(Runnable operation) {
+ performSoundOperation(operation, false);
+ }
+
+ public static void performSoundOperation(Runnable operation, boolean ignoreError) {
+ performSoundFunction(()->{
+ operation.run();
+ return null;
+ }, ignoreError);
+ }
+
+ public static void performSoundOperationAsync(Runnable operation) {
+ soundThreadExecutor.submit(operation);
+ }
+
+ protected static void initSound() {
+ performSoundOperation(()->{
+ if (!PLAYBACK_INITIALIZED) {
+ audioDevice = ALC10.alcOpenDevice(defaultDeviceName);
+ audioContext = ALC10.alcCreateContext(audioDevice, new int[]{0});
+ ALC10.alcMakeContextCurrent(audioContext);
+ audioCapabilities = ALC.createCapabilities(audioDevice);
+ audioLibCapabilities = AL.createCapabilities(audioCapabilities);
+ if (!audioLibCapabilities.OpenAL10) {
+ PLAYBACK_DRIVER_DETECTED = false;
+ Logger.getLogger(SoundMixer.class.getName()).warning("OpenAL 1.0 not supported");
+ Emulator.withComputer(c->c.mixer.detach());
+ }
+ PLAYBACK_INITIALIZED = true;
+ } else {
+ ALC10.alcMakeContextCurrent(audioContext);
+ }
+ });
}
// Lots of inspiration from https://www.youtube.com/watch?v=dLrqBTeipwg
@Override
public void attach() {
+ if (!PLAYBACK_DRIVER_DETECTED) {
+ Logger.getLogger(SoundMixer.class.getName()).warning("Sound driver not detected");
+ return;
+ }
super.attach();
- audioDevice = ALC10.alcOpenDevice(defaultDeviceName);
- // TODO: Other attributes?
- audioContext = ALC10.alcCreateContext(audioDevice, new int[]{0});
- ALC10.alcMakeContextCurrent(audioContext);
- audioCapabilities = ALC.createCapabilities(audioDevice);
- audioLibCapabilities = AL.createCapabilities(audioCapabilities);
- if (!audioLibCapabilities.OpenAL10) {
- Logger.getLogger(SoundMixer.class.getName()).warning("OpenAL 1.0 not supported");
- detach();
+ initSound();
+ PLAYBACK_ENABLED = true;
+ }
+
+ private static List buffers = new ArrayList<>();
+ public static SoundBuffer createBuffer(boolean stereo) {
+ if (!PLAYBACK_ENABLED) {
+ return null;
+ }
+ SoundBuffer buffer = new SoundBuffer(stereo);
+ buffers.add(buffer);
+ return buffer;
+ }
+
+ public static class SoundBuffer {
+ public static int MAX_BUFFER_ID;
+ private ShortBuffer currentBuffer;
+ private ShortBuffer alternateBuffer;
+ private int audioFormat;
+ private int currentBufferId;
+ private int alternateBufferId;
+ private int sourceId;
+ private boolean isAlive;
+ private int buffersGenerated = 0;
+
+ public SoundBuffer(boolean stereo) {
+ initSound();
+ currentBuffer = BufferUtils.createShortBuffer(BUFFER_SIZE * (stereo ? 2 : 1));
+ alternateBuffer = BufferUtils.createShortBuffer(BUFFER_SIZE * (stereo ? 2 : 1));
+ currentBufferId = performSoundFunction(AL10::alGenBuffers);
+ alternateBufferId = performSoundFunction(AL10::alGenBuffers);
+ sourceId = performSoundFunction(AL10::alGenSources);
+ audioFormat = stereo ? AL10.AL_FORMAT_STEREO16 : AL10.AL_FORMAT_MONO16;
+ isAlive = true;
+ }
+
+ public boolean isAlive() {
+ return isAlive;
+ }
+
+ /* If stereo, call this once for left and then again for right sample */
+ public void playSample(short sample) throws InterruptedException, ExecutionException {
+ if (!isAlive) {
+ Logger.getLogger(SoundMixer.class.getName()).warning("Playback attempted on stopped buffer!");
+ return;
+ }
+ currentBuffer.put(sample);
+ if (!currentBuffer.hasRemaining()) {
+ buffersGenerated++;
+ currentBuffer.flip();
+ if (buffersGenerated > 2) {
+ int[] unqueueBuffers = new int[]{currentBufferId};
+ performSoundOperation(()->{
+ int buffersProcessed = AL10.alGetSourcei(sourceId, AL10.AL_BUFFERS_PROCESSED);
+ while (buffersProcessed < 1) {
+ Thread.onSpinWait();
+ buffersProcessed = AL10.alGetSourcei(sourceId, AL10.AL_BUFFERS_PROCESSED);
+ }
+ });
+ performSoundOperation(()->{
+ AL10.alSourceUnqueueBuffers(sourceId, unqueueBuffers);
+ });
+ }
+ performSoundOperation(()->AL10.alBufferData(currentBufferId, audioFormat, currentBuffer, RATE));
+ performSoundOperation(()->AL10.alSourceQueueBuffers(sourceId, currentBufferId));
+ performSoundOperationAsync(()->{
+ if (AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING) {
+ AL10.alSourcePlay(sourceId);
+ }
+ });
+
+ // Swap AL buffers
+ int tempId = currentBufferId;
+ currentBufferId = alternateBufferId;
+ alternateBufferId = tempId;
+ // Swap Java buffers
+ ShortBuffer tempBuffer = currentBuffer;
+ currentBuffer = alternateBuffer;
+ alternateBuffer = tempBuffer;
+ }
+ }
+
+ public void shutdown() throws InterruptedException, ExecutionException {
+ if (!isAlive) {
+ return;
+ }
+ isAlive = false;
+ performSoundOperation(()->AL10.alSourceStop(sourceId));
+ performSoundOperation(()->AL10.alDeleteSources(sourceId));
+ performSoundOperation(()->AL10.alDeleteBuffers(alternateBufferId));
+ performSoundOperation(()->AL10.alDeleteBuffers(currentBufferId));
}
}
@Override
public void detach() {
- ALC10.alcDestroyContext(audioContext);
- ALC10.alcCloseDevice(audioDevice);
+ if (!PLAYBACK_ENABLED) {
+ return;
+ }
MUTE = true;
+ PLAYBACK_ENABLED = false;
+
+ for (SoundBuffer buffer : buffers) {
+ try {
+ buffer.shutdown();
+ } catch (InterruptedException | ExecutionException e) {
+ Logger.getLogger(SoundMixer.class.getName()).warning("Error when detaching sound mixer: " + e.getMessage());
+ }
+ }
+ buffers.clear();
+ PLAYBACK_INITIALIZED = false;
+ performSoundOperation(()->ALC10.alcDestroyContext(audioContext), true);
+ performSoundOperation(()->ALC10.alcCloseDevice(audioDevice), true);
super.detach();
}
@@ -100,10 +280,11 @@ public class SoundMixer extends Device {
@Override
public synchronized void reconfigure() {
- if (MUTE) {
- detach();
- } else {
+ PLAYBACK_ENABLED = PLAYBACK_DRIVER_DETECTED && !MUTE;
+ if (PLAYBACK_ENABLED) {
attach();
+ } else {
+ detach();
}
}
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardDiskII.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardDiskII.java
index fe9dbb9a..ffd4c983 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardDiskII.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardDiskII.java
@@ -27,7 +27,6 @@ import java.util.logging.Logger;
import jace.EmulatorUILogic;
import jace.config.ConfigurableField;
import jace.config.Name;
-import jace.config.Reconfigurable;
import jace.core.Card;
import jace.core.Computer;
import jace.core.RAMEvent;
@@ -46,7 +45,7 @@ import jace.library.MediaConsumerParent;
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
@Name("Disk ][ Controller")
-public class CardDiskII extends Card implements Reconfigurable, MediaConsumerParent {
+public class CardDiskII extends Card implements MediaConsumerParent {
DiskIIDrive currentDrive;
DiskIIDrive drive1 = new DiskIIDrive(computer);
@@ -128,7 +127,8 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
case 0xC:
// read/write latch
currentDrive.write();
- e.setNewValue(currentDrive.readLatch());
+ int latch = currentDrive.readLatch();
+ e.setNewValue(latch);
break;
case 0xF:
// write mode
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardMockingboard.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardMockingboard.java
index 24297d64..bb5cff7c 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardMockingboard.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardMockingboard.java
@@ -348,7 +348,7 @@ public class CardMockingboard extends Card implements Runnable {
public void run() {
SourceDataLine out = null;
try {
- out = computer.mixer.getLine();
+ // out = computer.mixer.getLine();
if (out == null) {
setRun(false);
return;
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardRamworks.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardRamworks.java
index 0ca31752..9227cf1d 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardRamworks.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardRamworks.java
@@ -140,7 +140,7 @@ public class CardRamworks extends RAM128k {
private RAMListener bankSelectListener;
@Override
public void attach() {
- bankSelectListener = observe(RAMEvent.TYPE.WRITE, BANK_SELECT, (e) -> {
+ bankSelectListener = observe("Ramworks bank select", RAMEvent.TYPE.WRITE, BANK_SELECT, (e) -> {
currentBank = e.getNewValue();
configureActiveMemory();
});
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/DiskIIDrive.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/DiskIIDrive.java
index f461100c..5f8dbfd9 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/DiskIIDrive.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/DiskIIDrive.java
@@ -18,6 +18,14 @@
*/
package jace.hardware;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.LockSupport;
+
import jace.EmulatorUILogic;
import jace.core.Computer;
import jace.library.MediaConsumer;
@@ -25,12 +33,6 @@ import jace.library.MediaEntry;
import jace.library.MediaEntry.MediaFile;
import jace.state.StateManager;
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;
/**
@@ -50,11 +52,13 @@ public class DiskIIDrive implements MediaConsumer {
this.computer = computer;
}
+ public boolean DEBUG = true;
+
FloppyDisk disk;
// Number of milliseconds to wait between last write and update to disk image
public static long WRITE_UPDATE_DELAY = 1000;
// Flag to halt if any writes to floopy occur when updating physical disk image
- boolean diskUpdatePending = false;
+ AtomicBoolean diskUpdatePending = new AtomicBoolean();
// Last time of write operation
long lastWriteTime;
// Managed thread to update disk image in background
@@ -86,7 +90,7 @@ public class DiskIIDrive implements MediaConsumer {
driveOn = false;
magnets = 0;
dirtyTracks = new HashSet<>();
- diskUpdatePending = false;
+ diskUpdatePending.set(false);
}
void step(int register) {
@@ -113,13 +117,18 @@ public class DiskIIDrive implements MediaConsumer {
}
nibbleOffset = 0;
- //System.out.printf("new half track %d\n", currentHalfTrack);
+ if (DEBUG) {
+ System.out.printf("step %d, new half track %d\n", register, halfTrack);
+ }
}
}
}
}
void setOn(boolean b) {
+ if (DEBUG) {
+ System.out.println("Drive setOn: "+b);
+ }
driveOn = b;
}
@@ -155,17 +164,20 @@ public class DiskIIDrive implements MediaConsumer {
void write() {
if (writeMode) {
- while (diskUpdatePending) {
+ while (diskUpdatePending.get()) {
// If another thread requested writes to block (e.g. because of disk activity), wait for it to finish!
- LockSupport.parkNanos(1000);
+ Thread.onSpinWait();
}
- if (disk != null) {
- // Do nothing if write-protection is enabled!
- if (getMediaEntry() == null || !getMediaEntry().writeProtected) {
- dirtyTracks.add(trackStartOffset / FloppyDisk.TRACK_NIBBLE_LENGTH);
- disk.nibbles[trackStartOffset + nibbleOffset++] = latch;
- triggerDiskUpdate();
- StateManager.markDirtyValue(disk.nibbles, computer);
+ // Holding the lock should block any other threads from writing to disk
+ synchronized (diskUpdatePending) {
+ if (disk != null) {
+ // Do nothing if write-protection is enabled!
+ if (getMediaEntry() == null || !getMediaEntry().writeProtected) {
+ dirtyTracks.add(trackStartOffset / FloppyDisk.TRACK_NIBBLE_LENGTH);
+ disk.nibbles[trackStartOffset + nibbleOffset++] = latch;
+ triggerDiskUpdate();
+ StateManager.markDirtyValue(disk.nibbles, computer);
+ }
}
}
@@ -192,19 +204,20 @@ public class DiskIIDrive implements MediaConsumer {
}
private void updateDisk() {
-
// Signal disk update is underway
- diskUpdatePending = true;
- // Update all tracks as necessary
- if (disk != null) {
- dirtyTracks.stream().forEach((track) -> {
- disk.updateTrack(track);
- });
+ synchronized (diskUpdatePending) {
+ diskUpdatePending.set(true);
+ // Update all tracks as necessary
+ if (disk != null) {
+ dirtyTracks.stream().forEach((track) -> {
+ disk.updateTrack(track);
+ });
+ }
+ // Empty out dirty list
+ dirtyTracks.clear();
}
- // Empty out dirty list
- dirtyTracks.clear();
// Signal disk update is completed
- diskUpdatePending = false;
+ diskUpdatePending.set(false);
}
private void triggerDiskUpdate() {
@@ -226,6 +239,9 @@ public class DiskIIDrive implements MediaConsumer {
}
void insertDisk(File diskPath) throws IOException {
+ if (DEBUG) {
+ System.out.println("inserting disk " + diskPath.getAbsolutePath() + " into drive");
+ }
disk = new FloppyDisk(diskPath, computer);
dirtyTracks = new HashSet<>();
// Emulator state has changed significantly, reset state manager
@@ -300,14 +316,16 @@ public class DiskIIDrive implements MediaConsumer {
@Override
public boolean isAccepted(MediaEntry e, MediaFile f) {
if (f == null) return false;
-// System.out.println("Type is accepted: "+f.path+"; "+e.type.toString()+": "+e.type.is140kb);
+ if (DEBUG) {
+ System.out.println("Type is accepted: "+f.path+"; "+e.type.toString()+": "+e.type.is140kb);
+ }
return e.type.is140kb;
}
private void waitForPendingWrites() {
- while (diskUpdatePending || !dirtyTracks.isEmpty()) {
+ while (diskUpdatePending.get()) {
// If the current disk has unsaved changes, wait!!!
- LockSupport.parkNanos(1000);
+ Thread.onSpinWait();
}
}
}
\ No newline at end of file
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/FloppyDisk.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/FloppyDisk.java
index aa43ed62..f921bde1 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/FloppyDisk.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/FloppyDisk.java
@@ -18,9 +18,6 @@
*/
package jace.hardware;
-import jace.core.Computer;
-import jace.state.StateManager;
-import jace.state.Stateful;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -32,6 +29,10 @@ import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
+import jace.core.Computer;
+import jace.state.StateManager;
+import jace.state.Stateful;
+
/**
* Representation of a 140kb floppy disk image. This also performs conversions
* as needed. Internally, the emulator will always use a "nibblized" disk
@@ -180,6 +181,17 @@ public class FloppyDisk {
writeJunkBytes(output, 38 - gap2);
}
}
+ // Write output to stdout for debugging purposes
+ if (DEBUG) {
+ System.out.println("Nibblized disk:");
+ for (int i = 0; i < output.size(); i++) {
+ System.out.print(Integer.toString(output.toByteArray()[i] & 0x0ff, 16) + " ");
+ if (i % 16 == 255) {
+ System.out.println();
+ }
+ }
+ System.out.println();
+ }
return output.toByteArray();
}
@@ -298,17 +310,23 @@ public class FloppyDisk {
byte[] trackNibbles = new byte[TRACK_NIBBLE_LENGTH];
byte[] trackData = new byte[SECTOR_COUNT * 256];
// Copy track into temporary buffer
-// System.out.println("Nibblized track "+track);
-// System.out.printf("%04d:",0);
- for (int i = 0, pos = track * TRACK_NIBBLE_LENGTH; i < TRACK_NIBBLE_LENGTH; i++, pos++) {
- trackNibbles[i] = nibbles[pos];
-// System.out.print(Integer.toString(nibbles[pos] & 0x0ff, 16)+" ");
-// if (i % 16 == 15) {
-// System.out.println();
-// System.out.printf("%04d:",i+1);
-// }
+ if (DEBUG) {
+ System.out.println("Nibblized track "+track);
+ System.out.printf("%04d:",0);
+ }
+ for (int i = 0, pos = track * TRACK_NIBBLE_LENGTH; i < TRACK_NIBBLE_LENGTH; i++, pos++) {
+ trackNibbles[i] = nibbles[pos];
+ if (DEBUG) {
+ System.out.print(Integer.toString(nibbles[pos] & 0x0ff, 16)+" ");
+ if (i % 16 == 15) {
+ System.out.println();
+ System.out.printf("%04d:",i+1);
+ }
+ }
+ }
+ if (DEBUG) {
+ System.out.println();
}
-// System.out.println();
int pos = 0;
for (int i = 0; i < SECTOR_COUNT; i++) {
@@ -318,7 +336,9 @@ public class FloppyDisk {
int trackVerify = decodeOddEven(trackNibbles[pos + 5], trackNibbles[pos + 6]);
// Locate sector number
int sector = decodeOddEven(trackNibbles[pos + 7], trackNibbles[pos + 8]);
-// System.out.println("Writing track " + track + ", getting address block for T" + trackVerify + ".S" + sector + " found at NIB offset "+pos);
+ if (DEBUG) {
+ System.out.println("Writing track " + track + ", getting address block for T" + trackVerify + ".S" + sector + " found at NIB offset "+pos);
+ }
// Skip to end of address block
pos = locatePattern(pos, trackNibbles, 0x0de, 0x0aa /*, 0x0eb this is sometimes being written as FF??*/);
// Locate start of sector data
@@ -326,7 +346,9 @@ public class FloppyDisk {
// Determine offset in output data for sector
//int offset = reverseLoopkup(currentSectorOrder, sector) * 256;
int offset = currentSectorOrder[sector] * 256;
-// System.out.println("Sector "+sector+" maps to physical sector "+reverseLoopkup(currentSectorOrder, sector));
+ if (DEBUG) {
+ System.out.println("Sector "+sector+" maps to physical sector "+reverseLoopkup(currentSectorOrder, sector));
+ }
// Decode sector data
denibblizeSector(trackNibbles, pos + 3, trackData, offset);
// Skip to end of sector
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/Joystick.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/Joystick.java
index aabf9356..d9dc60a3 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/Joystick.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/Joystick.java
@@ -201,7 +201,7 @@ public class Joystick extends Device {
RAMListener listener;
private void registerListeners() {
- listener = computer.getMemory().observe(RAMEvent.TYPE.ANY, 0x0c070, 0x0c07f, this::initJoystickRead);
+ listener = computer.getMemory().observe("Joystick I/O", RAMEvent.TYPE.ANY, 0x0c070, 0x0c07f, this::initJoystickRead);
}
private void removeListeners() {
diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/NoSlotClock.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/NoSlotClock.java
index 111c492c..bfb907b7 100644
--- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/NoSlotClock.java
+++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/NoSlotClock.java
@@ -1,5 +1,8 @@
package jace.hardware;
+import java.util.Calendar;
+import java.util.Optional;
+
import jace.EmulatorUILogic;
import jace.apple2e.SoftSwitches;
import jace.config.ConfigurableField;
@@ -8,8 +11,6 @@ import jace.core.Device;
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;
/**
@@ -31,7 +32,7 @@ public class NoSlotClock extends Device {
public boolean patchProdosClock = false;
Optional