mirror of
https://github.com/badvision/lawless-legends.git
synced 2024-06-09 23:29:37 +00:00
Large code cleanup/refactoring, mostly addressing code fragility and emulator structure that results in poor startup behavior.
This commit is contained in:
parent
f2c9e83eb7
commit
9f4952cabd
1
Platform/Apple/tools/jace/.gitignore
vendored
1
Platform/Apple/tools/jace/.gitignore
vendored
|
@ -4,3 +4,4 @@
|
|||
/.jace.conf
|
||||
*.classpath
|
||||
*.project
|
||||
/src/main/resources/jace/data/sound/*.mp3
|
|
@ -39,48 +39,61 @@ public class Emulator {
|
|||
// instance = new Emulator(args);
|
||||
// }
|
||||
|
||||
public static LawlessComputer computer;
|
||||
private final LawlessComputer computer;
|
||||
|
||||
public static Emulator getInstance(List<String> args) {
|
||||
Emulator i = getInstance();
|
||||
i.processCmdlineArgs(args);
|
||||
return i;
|
||||
}
|
||||
|
||||
public static Emulator getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new Emulator();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static LawlessComputer getComputer() {
|
||||
return getInstance().computer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of Emulator
|
||||
* @param args
|
||||
*/
|
||||
public Emulator(List<String> args) {
|
||||
private Emulator() {
|
||||
instance = this;
|
||||
computer = new LawlessComputer();
|
||||
Configuration.buildTree();
|
||||
Configuration.loadSettings();
|
||||
mainThread = Thread.currentThread();
|
||||
Map<String, String> settings = new LinkedHashMap<>();
|
||||
if (args != null) {
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
if (args.get(i).startsWith("-")) {
|
||||
String key = args.get(i).substring(1);
|
||||
if ((i + 1) < args.size()) {
|
||||
String val = args.get(i + 1);
|
||||
if (!val.startsWith("-")) {
|
||||
settings.put(key, val);
|
||||
i++;
|
||||
} else {
|
||||
settings.put(key, "true");
|
||||
}
|
||||
} else {
|
||||
settings.put(key, "true");
|
||||
}
|
||||
} else {
|
||||
System.err.println("Did not understand parameter " + args.get(i) + ", skipping.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Configuration.applySettings(settings);
|
||||
// EmulatorUILogic.registerDebugger();
|
||||
// computer.coldStart();
|
||||
}
|
||||
|
||||
public static void resizeVideo() {
|
||||
// AbstractEmulatorFrame window = getFrame();
|
||||
// if (window != null) {
|
||||
// window.resizeVideo();
|
||||
// }
|
||||
private void processCmdlineArgs(List<String> args) {
|
||||
if (args == null || args.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, String> settings = new LinkedHashMap<>();
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
if (args.get(i).startsWith("-")) {
|
||||
String key = args.get(i).substring(1);
|
||||
if ((i + 1) < args.size()) {
|
||||
String val = args.get(i + 1);
|
||||
if (!val.startsWith("-")) {
|
||||
settings.put(key, val);
|
||||
i++;
|
||||
} else {
|
||||
settings.put(key, "true");
|
||||
}
|
||||
} else {
|
||||
settings.put(key, "true");
|
||||
}
|
||||
} else {
|
||||
System.err.println("Did not understand parameter " + args.get(i) + ", skipping.");
|
||||
}
|
||||
}
|
||||
Configuration.applySettings(settings);
|
||||
}
|
||||
}
|
|
@ -34,7 +34,6 @@ import static jace.core.Utility.*;
|
|||
import jace.ide.IdeController;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
@ -76,7 +75,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
@Override
|
||||
public void updateStatus() {
|
||||
enableDebug(true);
|
||||
MOS65C02 cpu = (MOS65C02) Emulator.computer.getCpu();
|
||||
MOS65C02 cpu = (MOS65C02) Emulator.getComputer().getCpu();
|
||||
updateCPURegisters(cpu);
|
||||
}
|
||||
};
|
||||
|
@ -114,7 +113,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
}
|
||||
|
||||
public static void enableTrace(boolean b) {
|
||||
Emulator.computer.getCpu().setTraceEnabled(b);
|
||||
Emulator.getComputer().getCpu().setTraceEnabled(b);
|
||||
}
|
||||
|
||||
public static void stepForward() {
|
||||
|
@ -122,7 +121,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
}
|
||||
|
||||
static void registerDebugger() {
|
||||
Emulator.computer.getCpu().setDebug(debugger);
|
||||
Emulator.getComputer().getCpu().setDebug(debugger);
|
||||
}
|
||||
|
||||
public static Integer getValidAddress(String s) {
|
||||
|
@ -141,7 +140,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
// public static void updateWatchList(final DebuggerPanel panel) {
|
||||
// java.awt.EventQueue.invokeLater(() -> {
|
||||
// watches.stream().forEach((oldWatch) -> {
|
||||
// Emulator.computer.getMemory().removeListener(oldWatch);
|
||||
// Emulator.getComputer().getMemory().removeListener(oldWatch);
|
||||
// });
|
||||
// if (panel == null) {
|
||||
// return;
|
||||
|
@ -168,10 +167,10 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
// watchValue.setText(Integer.toHexString(e.getNewValue() & 0x0FF));
|
||||
// }
|
||||
// };
|
||||
// Emulator.computer.getMemory().addListener(newListener);
|
||||
// Emulator.getComputer().getMemory().addListener(newListener);
|
||||
// watches.add(newListener);
|
||||
// // Print out the current value right away
|
||||
// byte b = Emulator.computer.getMemory().readRaw(address);
|
||||
// byte b = Emulator.getComputer().getMemory().readRaw(address);
|
||||
// watchValue.setText(Integer.toString(b & 0x0ff, 16));
|
||||
// } else {
|
||||
// watchValue.setText("00");
|
||||
|
@ -211,11 +210,11 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
alternatives = "Execute program;Load binary;Load program;Load rom;Play single-load game",
|
||||
defaultKeyMapping = "ctrl+shift+b")
|
||||
public static void runFile() {
|
||||
Emulator.computer.pause();
|
||||
Emulator.getComputer().pause();
|
||||
FileChooser select = new FileChooser();
|
||||
File binary = select.showOpenDialog(LawlessLegends.getApplication().primaryStage);
|
||||
if (binary == null) {
|
||||
Emulator.computer.resume();
|
||||
Emulator.getComputer().resume();
|
||||
return;
|
||||
}
|
||||
runFileNamed(binary);
|
||||
|
@ -233,25 +232,24 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
}
|
||||
} catch (NumberFormatException | IOException ex) {
|
||||
}
|
||||
Emulator.computer.getCpu().resume();
|
||||
Emulator.getComputer().getCpu().resume();
|
||||
}
|
||||
|
||||
public static void brun(File binary, int address) throws FileNotFoundException, IOException {
|
||||
public static void brun(File binary, int address) throws IOException {
|
||||
// If it was halted already, then it was initiated outside of an opcode execution
|
||||
// If it was not yet halted, then it is the case that the CPU is processing another opcode
|
||||
// So if that is the case, the program counter will need to be decremented here to compensate
|
||||
// TODO: Find a better mousetrap for this one -- it's an ugly hack
|
||||
Emulator.computer.pause();
|
||||
Emulator.getComputer().pause();
|
||||
FileInputStream in = new FileInputStream(binary);
|
||||
byte[] data = new byte[in.available()];
|
||||
in.read(data);
|
||||
RAM ram = Emulator.computer.getMemory();
|
||||
RAM ram = Emulator.getComputer().getMemory();
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
ram.write(address + i, data[i], false, true);
|
||||
}
|
||||
CPU cpu = Emulator.computer.getCpu();
|
||||
Emulator.computer.getCpu().setProgramCounter(address);
|
||||
Emulator.computer.resume();
|
||||
Emulator.getComputer().getCpu().setProgramCounter(address);
|
||||
Emulator.getComputer().resume();
|
||||
}
|
||||
|
||||
@InvokableAction(
|
||||
|
@ -291,11 +289,11 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
description = "Save raw (RAM) format of visible screen",
|
||||
alternatives = "screendump;raw screenshot",
|
||||
defaultKeyMapping = "ctrl+shift+z")
|
||||
public static void saveScreenshotRaw() throws FileNotFoundException, IOException {
|
||||
public static void saveScreenshotRaw() throws IOException {
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss");
|
||||
String timestamp = df.format(new Date());
|
||||
String type;
|
||||
int start = Emulator.computer.getVideo().getCurrentWriter().actualWriter().getYOffset(0);
|
||||
int start = Emulator.getComputer().getVideo().getCurrentWriter().actualWriter().getYOffset(0);
|
||||
int len;
|
||||
if (start < 0x02000) {
|
||||
// Lo-res or double-lores
|
||||
|
@ -312,8 +310,8 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
}
|
||||
File outFile = new File("screen_" + type + "_a" + Integer.toHexString(start) + "_" + timestamp);
|
||||
try (FileOutputStream out = new FileOutputStream(outFile)) {
|
||||
RAM128k ram = (RAM128k) Emulator.computer.memory;
|
||||
Emulator.computer.pause();
|
||||
RAM128k ram = (RAM128k) Emulator.getComputer().memory;
|
||||
Emulator.getComputer().pause();
|
||||
if (dres) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
out.write(ram.getAuxVideoMemory().readByte(start + i));
|
||||
|
@ -334,8 +332,8 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
defaultKeyMapping = "ctrl+shift+s")
|
||||
public static void saveScreenshot() throws IOException {
|
||||
FileChooser select = new FileChooser();
|
||||
Emulator.computer.pause();
|
||||
Image i = Emulator.computer.getVideo().getFrameBuffer();
|
||||
Emulator.getComputer().pause();
|
||||
Image i = Emulator.getComputer().getVideo().getFrameBuffer();
|
||||
// BufferedImage bufImageARGB = SwingFXUtils.fromFXImage(i, null);
|
||||
File targetFile = select.showSaveDialog(LawlessLegends.getApplication().primaryStage);
|
||||
if (targetFile == null) {
|
||||
|
@ -366,7 +364,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
fxmlLoader.setResources(null);
|
||||
try {
|
||||
Stage configWindow = new Stage();
|
||||
AnchorPane node = (AnchorPane) fxmlLoader.load();
|
||||
AnchorPane node = fxmlLoader.load();
|
||||
ConfigurationUIController controller = fxmlLoader.getController();
|
||||
controller.initialize();
|
||||
Scene s = new Scene(node);
|
||||
|
@ -388,7 +386,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
fxmlLoader.setResources(null);
|
||||
try {
|
||||
Stage editorWindow = new Stage();
|
||||
AnchorPane node = (AnchorPane) fxmlLoader.load();
|
||||
AnchorPane node = fxmlLoader.load();
|
||||
IdeController controller = fxmlLoader.getController();
|
||||
controller.initialize();
|
||||
Scene s = new Scene(node);
|
||||
|
@ -421,7 +419,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||
if (stage.isFullScreen()) {
|
||||
LawlessLegends.getApplication().controller.toggleAspectRatio();
|
||||
} else {
|
||||
int width = 0, height = 0;
|
||||
int width, height;
|
||||
switch (size) {
|
||||
case 0: // 1x
|
||||
width = 560;
|
||||
|
|
|
@ -219,9 +219,9 @@ public class JaceUIController {
|
|||
}
|
||||
double v = convertSpeedToRatio(val);
|
||||
if (v != Math.floor(v)) {
|
||||
return String.valueOf(v) + "x";
|
||||
return v + "x";
|
||||
} else {
|
||||
return String.valueOf((int) v) + "x";
|
||||
return (int) v + "x";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,9 +236,9 @@ public class JaceUIController {
|
|||
// Kind of redundant but make sure speed is properly set as if the user did it
|
||||
setSpeed(Emulator.logic.speedSetting);
|
||||
});
|
||||
musicSelection.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
|
||||
((LawlessHacks) ((LawlessComputer) computer).activeCheatEngine).changeMusicScore(String.valueOf(newValue));
|
||||
});
|
||||
musicSelection.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) ->
|
||||
((LawlessHacks) ((LawlessComputer) computer).activeCheatEngine).changeMusicScore(String.valueOf(newValue))
|
||||
);
|
||||
}
|
||||
|
||||
private void connectButtons(Node n) {
|
||||
|
@ -247,9 +247,7 @@ public class JaceUIController {
|
|||
Runnable action = Utility.getNamedInvokableAction(button.getText());
|
||||
button.setOnMouseClicked(evt -> action.run());
|
||||
} else if (n instanceof Parent) {
|
||||
for (Node child : ((Parent) n).getChildrenUnmodifiable()) {
|
||||
connectButtons(child);
|
||||
}
|
||||
((Parent) n).getChildrenUnmodifiable().forEach(child -> connectButtons(child));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,19 +257,19 @@ public class JaceUIController {
|
|||
if (speedSlider.getValue() != speed) {
|
||||
Platform.runLater(()->speedSlider.setValue(speed));
|
||||
}
|
||||
if (speedRatio > 100.0) {
|
||||
Emulator.computer.getMotherboard().setMaxSpeed(true);
|
||||
Motherboard.cpuPerClock = 3;
|
||||
if (speedRatio >= 100.0) {
|
||||
Emulator.getComputer().getMotherboard().setMaxSpeed(true);
|
||||
Motherboard.cpuPerClock = 10;
|
||||
} else {
|
||||
if (speedRatio > 25) {
|
||||
Motherboard.cpuPerClock = 2;
|
||||
} else {
|
||||
Motherboard.cpuPerClock = 1;
|
||||
}
|
||||
Emulator.computer.getMotherboard().setMaxSpeed(false);
|
||||
Emulator.computer.getMotherboard().setSpeedInPercentage((int) (speedRatio * 100));
|
||||
Emulator.getComputer().getMotherboard().setMaxSpeed(false);
|
||||
Emulator.getComputer().getMotherboard().setSpeedInPercentage((int) (speedRatio * 100));
|
||||
}
|
||||
Emulator.computer.getMotherboard().reconfigure();
|
||||
Emulator.getComputer().getMotherboard().reconfigure();
|
||||
}
|
||||
|
||||
public void toggleAspectRatio() {
|
||||
|
@ -375,19 +373,17 @@ public class JaceUIController {
|
|||
|
||||
private void endDragEvent() {
|
||||
stackPane.getChildren().remove(drivePanel);
|
||||
drivePanel.getChildren().stream().forEach((n) -> {
|
||||
n.setOnDragDropped(null);
|
||||
});
|
||||
drivePanel.getChildren().forEach((n) -> n.setOnDragDropped(null));
|
||||
}
|
||||
|
||||
private List<MediaConsumer> getMediaConsumers() {
|
||||
List<MediaConsumer> consumers = new ArrayList<>();
|
||||
consumers.add(Emulator.computer.getUpgradeHandler());
|
||||
consumers.add(Emulator.getComputer().getUpgradeHandler());
|
||||
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()));
|
||||
});
|
||||
card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent ->
|
||||
consumers.addAll(Arrays.asList(((MediaConsumerParent) parent).getConsumers()))
|
||||
);
|
||||
}
|
||||
}
|
||||
return consumers;
|
||||
|
@ -432,9 +428,7 @@ public class JaceUIController {
|
|||
Long now = System.currentTimeMillis();
|
||||
iconTTL.keySet().stream()
|
||||
.filter((icon) -> (iconTTL.get(icon) <= now))
|
||||
.forEach((icon) -> {
|
||||
removeIndicator(icon);
|
||||
});
|
||||
.forEach(this::removeIndicator);
|
||||
if (iconTTL.isEmpty()) {
|
||||
ttlCleanupTask.cancel(true);
|
||||
ttlCleanupTask = null;
|
||||
|
@ -463,10 +457,8 @@ public class JaceUIController {
|
|||
stackPane.getChildren().add(notification);
|
||||
});
|
||||
|
||||
notificationExecutor.schedule(() -> {
|
||||
Application.invokeLater(() -> {
|
||||
stackPane.getChildren().remove(notification);
|
||||
});
|
||||
}, 4, TimeUnit.SECONDS);
|
||||
notificationExecutor.schedule(
|
||||
() -> Application.invokeLater(() -> stackPane.getChildren().remove(notification)),
|
||||
4, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package jace;
|
||||
|
||||
import jace.apple2e.SoftSwitches;
|
||||
|
@ -45,7 +40,6 @@ public class LawlessLegends extends Application {
|
|||
public JaceUIController controller;
|
||||
|
||||
static boolean romStarted = false;
|
||||
static public boolean PRODUCTION_MODE = true;
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) throws Exception {
|
||||
|
@ -54,7 +48,7 @@ public class LawlessLegends extends Application {
|
|||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/JaceUI.fxml"));
|
||||
fxmlLoader.setResources(null);
|
||||
try {
|
||||
AnchorPane node = (AnchorPane) fxmlLoader.load();
|
||||
AnchorPane node = fxmlLoader.load();
|
||||
controller = fxmlLoader.getController();
|
||||
controller.initialize();
|
||||
Scene s = new Scene(node);
|
||||
|
@ -67,25 +61,25 @@ public class LawlessLegends extends Application {
|
|||
}
|
||||
|
||||
primaryStage.show();
|
||||
new Thread(() -> {
|
||||
new Emulator(getParameters().getRaw());
|
||||
Platform.runLater(() -> new Thread(() -> {
|
||||
Emulator.getInstance(getParameters().getRaw());
|
||||
configureEmulatorForGame();
|
||||
reconnectUIHooks();
|
||||
EmulatorUILogic.scaleIntegerRatio();
|
||||
while (Emulator.computer.getVideo() == null || Emulator.computer.getVideo().getFrameBuffer() == null) {
|
||||
while (Emulator.getComputer().getVideo() == null || Emulator.getComputer().getVideo().getFrameBuffer() == null) {
|
||||
Thread.yield();
|
||||
}
|
||||
bootWatchdog();
|
||||
}).start();
|
||||
}).start());
|
||||
primaryStage.setOnCloseRequest(event -> {
|
||||
Emulator.computer.deactivate();
|
||||
Emulator.getComputer().deactivate();
|
||||
Platform.exit();
|
||||
System.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
public void reconnectUIHooks() {
|
||||
controller.connectComputer(Emulator.computer, primaryStage);
|
||||
controller.connectComputer(Emulator.getComputer(), primaryStage);
|
||||
}
|
||||
|
||||
public static LawlessLegends getApplication() {
|
||||
|
@ -129,49 +123,47 @@ public class LawlessLegends extends Application {
|
|||
*/
|
||||
private void bootWatchdog() {
|
||||
romStarted = false;
|
||||
if (PRODUCTION_MODE) {
|
||||
RAMListener startListener = Emulator.computer.getMemory().
|
||||
observe(RAMEvent.TYPE.EXECUTE, 0x0c700, (e) -> {
|
||||
romStarted = true;
|
||||
});
|
||||
Emulator.computer.invokeColdStart();
|
||||
if (Emulator.getComputer().PRODUCTION_MODE) {
|
||||
RAMListener startListener = Emulator.getComputer().getMemory().
|
||||
observe(RAMEvent.TYPE.EXECUTE, 0x0c700, (e) -> romStarted = true);
|
||||
Emulator.getComputer().invokeColdStart();
|
||||
try {
|
||||
Thread.sleep(7500);
|
||||
if (!romStarted) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Boot not detected, performing a cold start");
|
||||
Emulator.computer.invokeColdStart();
|
||||
Emulator.getComputer().invokeColdStart();
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(LawlessLegends.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
Emulator.computer.getMemory().removeListener(startListener);
|
||||
Emulator.getComputer().getMemory().removeListener(startListener);
|
||||
} else {
|
||||
romStarted = true;
|
||||
Emulator.computer.invokeColdStart();
|
||||
Emulator.getComputer().invokeColdStart();
|
||||
}
|
||||
}
|
||||
|
||||
private void configureEmulatorForGame() {
|
||||
Emulator.computer.enableHints = false;
|
||||
Emulator.computer.clockEnabled = true;
|
||||
Emulator.computer.joy1enabled = false;
|
||||
Emulator.computer.joy2enabled = false;
|
||||
Emulator.computer.enableStateManager = false;
|
||||
Emulator.computer.ramCard.setValue(CardRamworks.class);
|
||||
Emulator.computer.videoRenderer.setValue(LawlessVideo.class);
|
||||
if (PRODUCTION_MODE) {
|
||||
Emulator.computer.card7.setValue(CardMassStorage.class);
|
||||
Emulator.computer.card6.setValue(CardDiskII.class);
|
||||
Emulator.computer.card5.setValue(CardRamFactor.class);
|
||||
Emulator.computer.card4.setValue(null);
|
||||
Emulator.computer.card2.setValue(null);
|
||||
Emulator.getComputer().enableHints = false;
|
||||
Emulator.getComputer().clockEnabled = true;
|
||||
Emulator.getComputer().joy1enabled = false;
|
||||
Emulator.getComputer().joy2enabled = false;
|
||||
Emulator.getComputer().enableStateManager = false;
|
||||
Emulator.getComputer().ramCard.setValue(CardRamworks.class);
|
||||
Emulator.getComputer().videoRenderer.setValue(LawlessVideo.class);
|
||||
if (Emulator.getComputer().PRODUCTION_MODE) {
|
||||
Emulator.getComputer().card7.setValue(CardMassStorage.class);
|
||||
Emulator.getComputer().card6.setValue(CardDiskII.class);
|
||||
Emulator.getComputer().card5.setValue(CardRamFactor.class);
|
||||
Emulator.getComputer().card4.setValue(null);
|
||||
Emulator.getComputer().card2.setValue(null);
|
||||
}
|
||||
Emulator.computer.cheatEngine.setValue(LawlessHacks.class);
|
||||
Emulator.getComputer().cheatEngine.setValue(LawlessHacks.class);
|
||||
Configuration.buildTree();
|
||||
Emulator.computer.reconfigure();
|
||||
Emulator.getComputer().reconfigure();
|
||||
VideoNTSC.setVideoMode(VideoNTSC.VideoMode.TextFriendly, false);
|
||||
if (PRODUCTION_MODE) {
|
||||
((LawlessImageTool) Emulator.computer.getUpgradeHandler()).loadGame();
|
||||
if (Emulator.getComputer().PRODUCTION_MODE) {
|
||||
((LawlessImageTool) Emulator.getComputer().getUpgradeHandler()).loadGame();
|
||||
} else {
|
||||
for (SoftSwitches s : SoftSwitches.values()) {
|
||||
s.getSwitch().reset();
|
||||
|
|
|
@ -18,33 +18,20 @@
|
|||
*/
|
||||
package jace.apple2e;
|
||||
|
||||
import jace.Emulator;
|
||||
import jace.LawlessLegends;
|
||||
import jace.apple2e.softswitch.VideoSoftSwitch;
|
||||
import jace.cheat.Cheats;
|
||||
import jace.config.ClassSelection;
|
||||
import jace.config.ConfigurableField;
|
||||
import jace.core.Card;
|
||||
import jace.core.Computer;
|
||||
import jace.core.Motherboard;
|
||||
import jace.core.RAM;
|
||||
import jace.core.RAMEvent;
|
||||
import jace.core.RAMListener;
|
||||
import jace.core.Utility;
|
||||
import jace.core.Video;
|
||||
import jace.hardware.CardDiskII;
|
||||
import jace.hardware.CardExt80Col;
|
||||
import jace.hardware.ConsoleProbe;
|
||||
import jace.hardware.Joystick;
|
||||
import jace.hardware.NoSlotClock;
|
||||
import jace.hardware.ZipWarpAccelerator;
|
||||
import jace.core.*;
|
||||
import jace.hardware.*;
|
||||
import jace.hardware.massStorage.CardMassStorage;
|
||||
import jace.lawless.LawlessVideo;
|
||||
import jace.state.Stateful;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
|
@ -66,30 +53,30 @@ public class Apple2e extends Computer {
|
|||
|
||||
static int IRQ_VECTOR = 0x003F2;
|
||||
@ConfigurableField(name = "Slot 1", shortName = "s1card")
|
||||
public ClassSelection card1 = new ClassSelection(Card.class, null);
|
||||
public ClassSelection<Card> card1 = new ClassSelection<>(Card.class, null);
|
||||
@ConfigurableField(name = "Slot 2", shortName = "s2card")
|
||||
public ClassSelection card2 = new ClassSelection(Card.class, null);
|
||||
public ClassSelection<Card> card2 = new ClassSelection<>(Card.class, null);
|
||||
@ConfigurableField(name = "Slot 3", shortName = "s3card")
|
||||
public ClassSelection card3 = new ClassSelection(Card.class, null);
|
||||
public ClassSelection<Card> card3 = new ClassSelection<>(Card.class, null);
|
||||
@ConfigurableField(name = "Slot 4", shortName = "s4card")
|
||||
public ClassSelection card4 = new ClassSelection(Card.class, null);
|
||||
public ClassSelection<Card> card4 = new ClassSelection<>(Card.class, null);
|
||||
@ConfigurableField(name = "Slot 5", shortName = "s5card")
|
||||
public ClassSelection card5 = new ClassSelection(Card.class, null);
|
||||
public ClassSelection<Card> card5 = new ClassSelection<>(Card.class, null);
|
||||
@ConfigurableField(name = "Slot 6", shortName = "s6card")
|
||||
public ClassSelection card6 = new ClassSelection(Card.class, CardDiskII.class);
|
||||
public ClassSelection<Card> card6 = new ClassSelection<>(Card.class, CardDiskII.class);
|
||||
@ConfigurableField(name = "Slot 7", shortName = "s7card")
|
||||
public ClassSelection card7 = new ClassSelection(Card.class, CardMassStorage.class);
|
||||
public ClassSelection<Card> card7 = new ClassSelection<>(Card.class, CardMassStorage.class);
|
||||
@ConfigurableField(name = "Debug rom", shortName = "debugRom", description = "Use debugger //e rom")
|
||||
public boolean useDebugRom = false;
|
||||
@ConfigurableField(name = "Console probe", description = "Enable console redirection (experimental!)")
|
||||
public boolean useConsoleProbe = false;
|
||||
private ConsoleProbe probe = new ConsoleProbe();
|
||||
private final ConsoleProbe probe = new ConsoleProbe();
|
||||
@ConfigurableField(name = "Helpful hints", shortName = "hints")
|
||||
public boolean enableHints = true;
|
||||
@ConfigurableField(name = "Renderer", shortName = "video", description = "Video rendering implementation")
|
||||
public ClassSelection videoRenderer = new ClassSelection(Video.class, VideoNTSC.class);
|
||||
public ClassSelection<Video> videoRenderer = new ClassSelection<>(Video.class, LawlessVideo.class);
|
||||
@ConfigurableField(name = "Aux Ram", shortName = "ram", description = "Aux ram card")
|
||||
public ClassSelection ramCard = new ClassSelection(RAM128k.class, CardExt80Col.class);
|
||||
public ClassSelection<RAM128k> ramCard = new ClassSelection<>(RAM128k.class, CardRamworks.class);
|
||||
@ConfigurableField(name = "Joystick 1 Enabled", shortName = "joy1", description = "If unchecked, then there is no joystick support.", enablesDevice = true)
|
||||
public boolean joy1enabled = true;
|
||||
@ConfigurableField(name = "Joystick 2 Enabled", shortName = "joy2", description = "If unchecked, then there is no joystick support.", enablesDevice = true)
|
||||
|
@ -98,11 +85,13 @@ public class Apple2e extends Computer {
|
|||
public boolean clockEnabled = true;
|
||||
@ConfigurableField(name = "Accelerator Enabled", shortName = "zip", description = "If checked, add support for Zip/Transwarp", enablesDevice = true)
|
||||
public boolean acceleratorEnabled = true;
|
||||
|
||||
@ConfigurableField(name = "Production mode", shortName = "production")
|
||||
public boolean PRODUCTION_MODE = true;
|
||||
|
||||
public Joystick joystick1;
|
||||
public Joystick joystick2;
|
||||
@ConfigurableField(name = "Activate Cheats", shortName = "cheat", defaultValue = "")
|
||||
public ClassSelection cheatEngine = new ClassSelection(Cheats.class, null);
|
||||
@ConfigurableField(name = "Activate Cheats", shortName = "cheat")
|
||||
public ClassSelection<Cheats> cheatEngine = new ClassSelection<>(Cheats.class, null);
|
||||
public Cheats activeCheatEngine = null;
|
||||
public NoSlotClock clock;
|
||||
public ZipWarpAccelerator accelerator;
|
||||
|
@ -113,11 +102,10 @@ public class Apple2e extends Computer {
|
|||
public Apple2e() {
|
||||
super();
|
||||
try {
|
||||
reconfigure();
|
||||
setCpu(new MOS65C02(this));
|
||||
reinitMotherboard();
|
||||
setMotherboard(new Motherboard(this, null));
|
||||
} catch (Throwable t) {
|
||||
System.err.println("Unable to initalize virtual machine");
|
||||
System.err.println("Unable to initialize virtual machine");
|
||||
t.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
@ -127,29 +115,21 @@ public class Apple2e extends Computer {
|
|||
return "Computer (Apple //e)";
|
||||
}
|
||||
|
||||
protected void reinitMotherboard() {
|
||||
if (motherboard != null && motherboard.isRunning()) {
|
||||
motherboard.suspend();
|
||||
}
|
||||
setMotherboard(new Motherboard(this, motherboard));
|
||||
reconfigure();
|
||||
motherboard.reconfigure();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void coldStart() {
|
||||
pause();
|
||||
reinitMotherboard();
|
||||
for (SoftSwitches s : SoftSwitches.values()) {
|
||||
s.getSwitch().reset();
|
||||
}
|
||||
getMemory().configureActiveMemory();
|
||||
getVideo().configureVideoMode();
|
||||
for (Optional<Card> c : getMemory().getAllCards()) {
|
||||
c.ifPresent(Card::reset);
|
||||
}
|
||||
motherboard.whileSuspended(()->{
|
||||
for (SoftSwitches s : SoftSwitches.values()) {
|
||||
s.getSwitch().reset();
|
||||
}
|
||||
reconfigure();
|
||||
getMemory().configureActiveMemory();
|
||||
getVideo().configureVideoMode();
|
||||
for (Optional<Card> c : getMemory().getAllCards()) {
|
||||
c.ifPresent(Card::reset);
|
||||
}
|
||||
});
|
||||
reboot();
|
||||
resume();
|
||||
}
|
||||
|
||||
public void reboot() {
|
||||
|
@ -162,21 +142,21 @@ public class Apple2e extends Computer {
|
|||
|
||||
@Override
|
||||
public void warmStart() {
|
||||
boolean restart = pause();
|
||||
// This isn't really authentic behavior but sometimes games like memory to have a consistent state when booting.
|
||||
((RAM128k)getMemory()).zeroAllRam();
|
||||
for (SoftSwitches s : SoftSwitches.values()) {
|
||||
if (! (s.getSwitch() instanceof VideoSoftSwitch)) {
|
||||
s.getSwitch().reset();
|
||||
motherboard.whileSuspended(()->{
|
||||
// This isn't really authentic behavior but sometimes games like memory to have a consistent state when booting.
|
||||
for (SoftSwitches s : SoftSwitches.values()) {
|
||||
if (! (s.getSwitch() instanceof VideoSoftSwitch)) {
|
||||
s.getSwitch().reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
getMemory().configureActiveMemory();
|
||||
getVideo().configureVideoMode();
|
||||
getCpu().reset();
|
||||
for (Optional<Card> c : getMemory().getAllCards()) {
|
||||
c.ifPresent(Card::reset);
|
||||
}
|
||||
getCpu().resume();
|
||||
((RAM128k)getMemory()).zeroAllRam();
|
||||
getMemory().configureActiveMemory();
|
||||
getVideo().configureVideoMode();
|
||||
getCpu().reset();
|
||||
for (Optional<Card> c : getMemory().getAllCards()) {
|
||||
c.ifPresent(Card::reset);
|
||||
}
|
||||
});
|
||||
resume();
|
||||
}
|
||||
|
||||
|
@ -201,118 +181,126 @@ public class Apple2e extends Computer {
|
|||
}
|
||||
}
|
||||
|
||||
private Class<? extends RAM128k> getDesiredMemoryConfiguration() {
|
||||
if (ramCard.getValue() == null) {
|
||||
return CardExt80Col.class;
|
||||
} else {
|
||||
return ramCard.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMemoryConfigurationCorrect() {
|
||||
if (getMemory() == null) {
|
||||
return false;
|
||||
}
|
||||
return getMemory().getClass().equals(getDesiredMemoryConfiguration());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void reconfigure() {
|
||||
boolean restart = pause();
|
||||
super.reconfigure();
|
||||
|
||||
if (Utility.isHeadlessMode()) {
|
||||
joy1enabled = false;
|
||||
joy2enabled = false;
|
||||
|
||||
}
|
||||
|
||||
RAM128k currentMemory = (RAM128k) getMemory();
|
||||
if (currentMemory != null && ramCard.getValue() != null && !(currentMemory.getClass().equals(ramCard.getValue()))) {
|
||||
try {
|
||||
RAM128k newMemory = (RAM128k) ramCard.getValue().getConstructor(Computer.class).newInstance(this);
|
||||
newMemory.copyFrom(currentMemory);
|
||||
setMemory(newMemory);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ex) {
|
||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
if (motherboard == null) {
|
||||
return;
|
||||
}
|
||||
if (getMemory() == null) {
|
||||
try {
|
||||
currentMemory = (RAM128k) ramCard.getValue().getConstructor(Computer.class).newInstance(this);
|
||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
try {
|
||||
setMemory(currentMemory);
|
||||
motherboard.whileSuspended(()-> {
|
||||
if (getMemory() != null) {
|
||||
for (SoftSwitches s : SoftSwitches.values()) {
|
||||
s.getSwitch().register(this);
|
||||
s.getSwitch().unregister();
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
}
|
||||
}
|
||||
currentMemory.reconfigure();
|
||||
if (!isMemoryConfigurationCorrect()) {
|
||||
try {
|
||||
System.out.println("Creating new ram using " + getDesiredMemoryConfiguration().getName());
|
||||
RAM128k newMemory = getDesiredMemoryConfiguration().getConstructor(Computer.class).newInstance(this);
|
||||
|
||||
if (motherboard != null) {
|
||||
if (accelerator == null) {
|
||||
accelerator = new ZipWarpAccelerator(this);
|
||||
if (getMemory() != null) {
|
||||
newMemory.copyFrom((RAM128k) getMemory());
|
||||
}
|
||||
setMemory(newMemory);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ex) {
|
||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
for (SoftSwitches s : SoftSwitches.values()) {
|
||||
s.getSwitch().register(this);
|
||||
}
|
||||
|
||||
try {
|
||||
if (useDebugRom) {
|
||||
loadRom("jace/data/apple2e_debug.rom");
|
||||
} else {
|
||||
loadRom("jace/data/apple2e.rom");
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
getMemory().configureActiveMemory();
|
||||
|
||||
Set<Device> newDeviceSet = new HashSet<>();
|
||||
|
||||
if (acceleratorEnabled) {
|
||||
motherboard.addChildDevice(accelerator);
|
||||
} else {
|
||||
motherboard.removeChildDevice(accelerator);
|
||||
if (accelerator == null) {
|
||||
accelerator = new ZipWarpAccelerator(this);
|
||||
}
|
||||
newDeviceSet.add(accelerator);
|
||||
}
|
||||
|
||||
if (joy1enabled) {
|
||||
if (joystick1 == null) {
|
||||
joystick1 = new Joystick(0, this);
|
||||
motherboard.addChildDevice(joystick1);
|
||||
}
|
||||
} else if (joystick1 != null) {
|
||||
motherboard.removeChildDevice(joystick1);
|
||||
newDeviceSet.add(joystick1);
|
||||
} else {
|
||||
joystick1 = null;
|
||||
}
|
||||
|
||||
if (joy2enabled) {
|
||||
if (joystick2 == null) {
|
||||
joystick2 = new Joystick(1, this);
|
||||
motherboard.addChildDevice(joystick2);
|
||||
}
|
||||
} else if (joystick2 != null) {
|
||||
motherboard.removeChildDevice(joystick2);
|
||||
newDeviceSet.add(joystick2);
|
||||
} else {
|
||||
joystick2 = null;
|
||||
}
|
||||
|
||||
if (clockEnabled) {
|
||||
if (clock == null) {
|
||||
clock = new NoSlotClock(this);
|
||||
motherboard.addChildDevice(clock);
|
||||
}
|
||||
} else if (clock != null) {
|
||||
motherboard.removeChildDevice(clock);
|
||||
newDeviceSet.add(clock);
|
||||
} else {
|
||||
clock = null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (useConsoleProbe) {
|
||||
probe.init(this);
|
||||
} else {
|
||||
probe.shutdown();
|
||||
}
|
||||
|
||||
if (useDebugRom) {
|
||||
loadRom("jace/data/apple2e_debug.rom");
|
||||
} else {
|
||||
loadRom("jace/data/apple2e.rom");
|
||||
}
|
||||
RAM128k ram = (RAM128k) getMemory();
|
||||
if (LawlessLegends.PRODUCTION_MODE) {
|
||||
// Force Slot 7 boot
|
||||
ram.activeRead.writeByte(0x0fffc, (byte) 0x000);
|
||||
ram.activeRead.writeByte(0x0fffd, (byte) 0x0c7);
|
||||
}
|
||||
|
||||
if (getVideo() == null || getVideo().getClass() != videoRenderer.getValue()) {
|
||||
boolean resumeVideo = false;
|
||||
if (getVideo() != null) {
|
||||
getVideo().suspend();
|
||||
resumeVideo = getVideo().suspend();
|
||||
}
|
||||
try {
|
||||
setVideo((Video) videoRenderer.getValue().getConstructor(Computer.class).newInstance(this));
|
||||
setVideo(videoRenderer.getValue().getConstructor(Computer.class).newInstance(this));
|
||||
getVideo().configureVideoMode();
|
||||
getVideo().reconfigure();
|
||||
Emulator.resizeVideo();
|
||||
if (LawlessLegends.getApplication() != null) {
|
||||
LawlessLegends.getApplication().reconnectUIHooks();
|
||||
}
|
||||
getVideo().resume();
|
||||
} catch (InstantiationException | IllegalAccessException ex) {
|
||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
if (resumeVideo) {
|
||||
getVideo().resume();
|
||||
}
|
||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
@ -334,12 +322,11 @@ public class Apple2e extends Computer {
|
|||
} else {
|
||||
disableHints();
|
||||
}
|
||||
getMemory().configureActiveMemory();
|
||||
|
||||
if (cheatEngine.getValue() == null) {
|
||||
if (activeCheatEngine != null) {
|
||||
activeCheatEngine.detach();
|
||||
motherboard.addChildDevice(activeCheatEngine);
|
||||
newDeviceSet.add(activeCheatEngine);
|
||||
}
|
||||
activeCheatEngine = null;
|
||||
} else {
|
||||
|
@ -348,28 +335,27 @@ public class Apple2e extends Computer {
|
|||
if (activeCheatEngine.getClass().equals(cheatEngine.getValue())) {
|
||||
startCheats = false;
|
||||
} else {
|
||||
motherboard.removeChildDevice(activeCheatEngine);
|
||||
activeCheatEngine = null;
|
||||
}
|
||||
}
|
||||
if (startCheats) {
|
||||
try {
|
||||
activeCheatEngine = (Cheats) cheatEngine.getValue().getConstructor(Computer.class).newInstance(this);
|
||||
activeCheatEngine = cheatEngine.getValue().getConstructor(Computer.class).newInstance(this);
|
||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
motherboard.addChildDevice(activeCheatEngine);
|
||||
newDeviceSet.add(activeCheatEngine);
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
||||
super.reconfigure();
|
||||
|
||||
if (restart) {
|
||||
resume();
|
||||
}
|
||||
newDeviceSet.add(cpu);
|
||||
newDeviceSet.add(video);
|
||||
for (Optional<Card> c : getMemory().getAllCards()) {
|
||||
c.ifPresent(newDeviceSet::add);
|
||||
}
|
||||
motherboard.setAllDevices(newDeviceSet);
|
||||
motherboard.reconfigure();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -394,7 +380,7 @@ public class Apple2e extends Computer {
|
|||
// }
|
||||
// return motherboard.isRunning() && !motherboard.isPaused;
|
||||
// }
|
||||
private List<RAMListener> hints = new ArrayList<>();
|
||||
private final List<RAMListener> hints = new ArrayList<>();
|
||||
|
||||
ScheduledExecutorService animationTimer = new ScheduledThreadPoolExecutor(1);
|
||||
Runnable drawHints = () -> {
|
||||
|
@ -411,7 +397,7 @@ public class Apple2e extends Computer {
|
|||
" Java Apple Computer Emulator",
|
||||
"",
|
||||
" Presented by BLuRry",
|
||||
" http://goo.gl/SnzqG",
|
||||
" https://goo.gl/SnzqG",
|
||||
"",
|
||||
"To insert a disk, please drag it over",
|
||||
"this window and drop on the desired",
|
||||
|
@ -485,9 +471,7 @@ public class Apple2e extends Computer {
|
|||
}
|
||||
|
||||
private void disableHints() {
|
||||
hints.stream().forEach((hint) -> {
|
||||
getMemory().removeListener(hint);
|
||||
});
|
||||
hints.forEach((hint) -> getMemory().removeListener(hint));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
package jace.apple2e;
|
||||
|
||||
import jace.Emulator;
|
||||
import jace.config.ConfigurableField;
|
||||
import jace.core.CPU;
|
||||
import jace.core.Computer;
|
||||
|
@ -77,6 +78,7 @@ public class MOS65C02 extends CPU {
|
|||
|
||||
public MOS65C02(Computer computer) {
|
||||
super(computer);
|
||||
initOpcodes();
|
||||
clearState();
|
||||
}
|
||||
|
||||
|
@ -313,23 +315,23 @@ public class MOS65C02 extends CPU {
|
|||
TXS(0x009A, COMMAND.TXS, MODE.IMPLIED, 2),
|
||||
TYA(0x0098, COMMAND.TYA, MODE.IMPLIED, 2),
|
||||
WAI(0x00CB, COMMAND.WAI, MODE.IMPLIED, 3, true);
|
||||
private int code;
|
||||
private boolean isExtendedOpcode;
|
||||
private final int code;
|
||||
private final boolean isExtendedOpcode;
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
private int waitCycles;
|
||||
private final int waitCycles;
|
||||
|
||||
public int getWaitCycles() {
|
||||
return waitCycles;
|
||||
}
|
||||
private COMMAND command;
|
||||
private final COMMAND command;
|
||||
|
||||
public COMMAND getCommand() {
|
||||
return command;
|
||||
}
|
||||
private MODE addressingMode;
|
||||
private final MODE addressingMode;
|
||||
|
||||
public MODE getMode() {
|
||||
return addressingMode;
|
||||
|
@ -347,15 +349,15 @@ public class MOS65C02 extends CPU {
|
|||
command.getProcessor().processCommand(address, value, addressingMode, cpu);
|
||||
}
|
||||
|
||||
private OPCODE(int val, COMMAND c, MODE m, int wait) {
|
||||
OPCODE(int val, COMMAND c, MODE m, int wait) {
|
||||
this(val, c, m, wait, m.fetchValue, false);
|
||||
}
|
||||
|
||||
private OPCODE(int val, COMMAND c, MODE m, int wait, boolean extended) {
|
||||
OPCODE(int val, COMMAND c, MODE m, int wait, boolean extended) {
|
||||
this(val, c, m, wait, m.fetchValue, extended);
|
||||
}
|
||||
|
||||
private OPCODE(int val, COMMAND c, MODE m, int wait, boolean fetch, boolean extended) {
|
||||
OPCODE(int val, COMMAND c, MODE m, int wait, boolean fetch, boolean extended) {
|
||||
code = val;
|
||||
waitCycles = wait - 1;
|
||||
command = c;
|
||||
|
@ -365,9 +367,9 @@ public class MOS65C02 extends CPU {
|
|||
}
|
||||
}
|
||||
|
||||
public static interface AddressCalculator {
|
||||
public interface AddressCalculator {
|
||||
|
||||
abstract int calculateAddress(MOS65C02 cpu);
|
||||
int calculateAddress(MOS65C02 cpu);
|
||||
|
||||
default int getValue(boolean generateEvent, MOS65C02 cpu) {
|
||||
int address = calculateAddress(cpu);
|
||||
|
@ -450,7 +452,7 @@ public class MOS65C02 extends CPU {
|
|||
return cpu.getMemory().read(address, TYPE.READ_DATA, true, false);
|
||||
}
|
||||
});
|
||||
private int size;
|
||||
private final int size;
|
||||
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
|
@ -460,27 +462,20 @@ public class MOS65C02 extends CPU {
|
|||
// public String getFormat() {
|
||||
// return this.format;
|
||||
// }
|
||||
private AddressCalculator calculator;
|
||||
private final AddressCalculator calculator;
|
||||
|
||||
public int calcAddress(MOS65C02 cpu) {
|
||||
return calculator.calculateAddress(cpu);
|
||||
}
|
||||
private boolean indirect;
|
||||
|
||||
public boolean isIndirect() {
|
||||
return indirect;
|
||||
}
|
||||
String f1;
|
||||
String f2;
|
||||
boolean twoByte = false;
|
||||
boolean relative = false;
|
||||
boolean implied = true;
|
||||
boolean fetchValue = true;
|
||||
boolean fetchValue;
|
||||
|
||||
private MODE(int size, String fmt, AddressCalculator calc) {
|
||||
MODE(int size, String fmt, AddressCalculator calc) {
|
||||
this(size, fmt, calc, true);
|
||||
}
|
||||
private MODE(int size, String fmt, AddressCalculator calc, boolean fetch) {
|
||||
|
||||
MODE(int size, String fmt, AddressCalculator calc, boolean fetch) {
|
||||
this.fetchValue = fetch;
|
||||
this.size = size;
|
||||
if (fmt.contains("~")) {
|
||||
|
@ -499,11 +494,6 @@ public class MOS65C02 extends CPU {
|
|||
// this.format = fmt;
|
||||
|
||||
this.calculator = calc;
|
||||
this.indirect = toString().startsWith("INDIRECT");
|
||||
}
|
||||
|
||||
public MOS65C02.AddressCalculator getCalculator() {
|
||||
return calculator;
|
||||
}
|
||||
|
||||
public String formatMode(int pc, MOS65C02 cpu) {
|
||||
|
@ -524,9 +514,8 @@ public class MOS65C02 extends CPU {
|
|||
}
|
||||
}
|
||||
|
||||
public static interface CommandProcessor {
|
||||
|
||||
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu);
|
||||
public interface CommandProcessor {
|
||||
void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu);
|
||||
}
|
||||
|
||||
private static class BBRCommand implements CommandProcessor {
|
||||
|
@ -1023,30 +1012,29 @@ public class MOS65C02 extends CPU {
|
|||
WAI((address, value, addressMode, cpu) -> {
|
||||
cpu.waitForInterrupt();
|
||||
});
|
||||
private CommandProcessor processor;
|
||||
private final CommandProcessor processor;
|
||||
|
||||
public CommandProcessor getProcessor() {
|
||||
return processor;
|
||||
}
|
||||
private boolean storeOnly;
|
||||
private final boolean storeOnly;
|
||||
|
||||
public boolean isStoreOnly() {
|
||||
return storeOnly;
|
||||
}
|
||||
|
||||
private COMMAND(CommandProcessor processor) {
|
||||
COMMAND(CommandProcessor processor) {
|
||||
this(false, processor);
|
||||
}
|
||||
|
||||
private COMMAND(boolean storeOnly, CommandProcessor processor) {
|
||||
COMMAND(boolean storeOnly, CommandProcessor processor) {
|
||||
this.storeOnly = storeOnly;
|
||||
this.processor = processor;
|
||||
}
|
||||
}
|
||||
static private OPCODE[] opcodes;
|
||||
private final OPCODE[] opcodes = new OPCODE[256];
|
||||
|
||||
static {
|
||||
opcodes = new OPCODE[256];
|
||||
private void initOpcodes() {
|
||||
for (OPCODE o : OPCODE.values()) {
|
||||
opcodes[o.getCode()] = o;
|
||||
}
|
||||
|
@ -1084,10 +1072,11 @@ public class MOS65C02 extends CPU {
|
|||
if (opcode == null) {
|
||||
// handle bad opcode as a NOP
|
||||
int wait = 0;
|
||||
int bytes = 2;
|
||||
int bytes;
|
||||
int n = op & 0x0f;
|
||||
switch (n) {
|
||||
case 2:
|
||||
bytes = 2;
|
||||
wait = 2;
|
||||
break;
|
||||
case 3:
|
||||
|
@ -1112,6 +1101,7 @@ public class MOS65C02 extends CPU {
|
|||
wait = 4;
|
||||
} break;
|
||||
default:
|
||||
bytes = 2;
|
||||
}
|
||||
incrementProgramCounter(bytes);
|
||||
addWaitCycles(wait);
|
||||
|
@ -1151,8 +1141,7 @@ public class MOS65C02 extends CPU {
|
|||
|
||||
public byte pop() {
|
||||
STACK = (STACK + 1) & 0x0FF;
|
||||
byte val = getMemory().read(0x0100 + STACK, TYPE.READ_DATA, true, false);
|
||||
return val;
|
||||
return getMemory().read(0x0100 + STACK, TYPE.READ_DATA, true, false);
|
||||
}
|
||||
|
||||
private byte getStatus() {
|
||||
|
@ -1227,10 +1216,6 @@ public class MOS65C02 extends CPU {
|
|||
}
|
||||
}
|
||||
|
||||
public int getSTACK() {
|
||||
return STACK;
|
||||
}
|
||||
|
||||
// Cold/Warm boot procedure
|
||||
@Override
|
||||
public void reset() {
|
||||
|
@ -1246,6 +1231,9 @@ public class MOS65C02 extends CPU {
|
|||
// V = true;
|
||||
// Z = true;
|
||||
int newPC = getMemory().readWord(RESET_VECTOR, TYPE.READ_DATA, true, false);
|
||||
if (Emulator.getComputer().PRODUCTION_MODE) {
|
||||
newPC = 0x0C700;
|
||||
}
|
||||
LOG.log(Level.WARNING, "Reset called, setting PC to ({0}) = {1}", new Object[]{Integer.toString(RESET_VECTOR, 16), Integer.toString(newPC, 16)});
|
||||
setProgramCounter(newPC);
|
||||
}
|
||||
|
@ -1278,27 +1266,25 @@ public class MOS65C02 extends CPU {
|
|||
}
|
||||
|
||||
public String getState() {
|
||||
StringBuilder out = new StringBuilder();
|
||||
out.append(byte2(A)).append(" ");
|
||||
out.append(byte2(X)).append(" ");
|
||||
out.append(byte2(Y)).append(" ");
|
||||
// out += "PC:"+wordString(getProgramCounter())+" ";
|
||||
out.append("01").append(byte2(STACK)).append(" ");
|
||||
out.append(getFlags());
|
||||
return out.toString();
|
||||
return String.format("%s %s %s 01%s %s",
|
||||
byte2(A),
|
||||
byte2(X),
|
||||
byte2(Y),
|
||||
byte2(STACK),
|
||||
getFlags()
|
||||
);
|
||||
}
|
||||
|
||||
public String getFlags() {
|
||||
StringBuilder out = new StringBuilder();
|
||||
out.append(N ? "N" : ".");
|
||||
out.append(V ? "V" : ".");
|
||||
out.append("R");
|
||||
out.append(B ? "B" : ".");
|
||||
out.append(D ? "D" : ".");
|
||||
out.append(I ? "I" : ".");
|
||||
out.append(Z ? "Z" : ".");
|
||||
out.append((C != 0) ? "C" : ".");
|
||||
return out.toString();
|
||||
return String.format("%s%sR%s%s%s%s%s",
|
||||
N ? "N" : ".",
|
||||
V ? "V" : ".",
|
||||
B ? "B" : ".",
|
||||
D ? "D" : ".",
|
||||
I ? "I" : ".",
|
||||
Z ? "Z" : ".",
|
||||
(C != 0) ? "C" : "."
|
||||
);
|
||||
}
|
||||
|
||||
public String disassemble() {
|
||||
|
@ -1320,9 +1306,7 @@ public class MOS65C02 extends CPU {
|
|||
((o.getMode().getSize() > 2) ?
|
||||
byte2(b2) : " " ) + " ";
|
||||
*/
|
||||
StringBuilder out = new StringBuilder(o.getCommand().toString());
|
||||
out.append(" ").append(format);
|
||||
return out.toString();
|
||||
return String.format("%s %s", o.getCommand().toString(), format);
|
||||
}
|
||||
private boolean pageBoundaryPenalty = false;
|
||||
|
||||
|
@ -1382,6 +1366,6 @@ public class MOS65C02 extends CPU {
|
|||
|
||||
@Override
|
||||
public void reconfigure() {
|
||||
// Currently do nothing
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,9 @@
|
|||
*/
|
||||
package jace.apple2e;
|
||||
|
||||
import jace.core.CPU;
|
||||
import jace.core.Card;
|
||||
import jace.core.Computer;
|
||||
import jace.core.PagedMemory;
|
||||
import jace.core.RAM;
|
||||
import jace.core.*;
|
||||
import jace.state.Stateful;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
|
@ -31,6 +28,8 @@ import java.util.Map;
|
|||
import java.util.concurrent.Semaphore;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Implementation of a 128k memory space and the MMU found in an Apple //e. The
|
||||
|
@ -41,7 +40,7 @@ import java.util.logging.Logger;
|
|||
@Stateful
|
||||
abstract public class RAM128k extends RAM {
|
||||
|
||||
Logger LOG = Logger.getLogger(RAM128k.class.getName());
|
||||
static final Logger LOG = Logger.getLogger(RAM128k.class.getName());
|
||||
|
||||
Map<String, PagedMemory> banks;
|
||||
Map<String, PagedMemory> memoryConfigurations = new HashMap<>();
|
||||
|
@ -86,34 +85,31 @@ abstract public class RAM128k extends RAM {
|
|||
|
||||
@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 = this.activeRead.get(i);
|
||||
byte[] write = this.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;
|
||||
}
|
||||
if (param == 0xda) {// 64 da : Dump all memory mappings
|
||||
System.out.println("Active banks");
|
||||
for (int i = 0; i < 256; i++) {
|
||||
byte[] read = this.activeRead.get(i);
|
||||
byte[] write = this.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");
|
||||
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:
|
||||
}
|
||||
return false;
|
||||
}).findFirst().orElse("unknown");
|
||||
LOG.log(Level.INFO, "Bank {0}\t{1}\t{2}", new Object[]{Integer.toHexString(i), readBank, writeBank});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,7 +157,7 @@ abstract public class RAM128k extends RAM {
|
|||
}
|
||||
}
|
||||
|
||||
private final Semaphore configurationSemaphone = new Semaphore(1, true);
|
||||
private final Semaphore configurationSemaphore = new Semaphore(1, true);
|
||||
|
||||
public String getReadConfiguration() {
|
||||
String rstate = "";
|
||||
|
@ -391,7 +387,7 @@ abstract public class RAM128k extends RAM {
|
|||
|
||||
try {
|
||||
log("MMU Switches");
|
||||
configurationSemaphone.acquire();
|
||||
configurationSemaphore.acquire();
|
||||
|
||||
if (memoryConfigurations.containsKey(readConfiguration)) {
|
||||
activeRead = memoryConfigurations.get(readConfiguration);
|
||||
|
@ -407,7 +403,7 @@ abstract public class RAM128k extends RAM {
|
|||
memoryConfigurations.put(writeConfiguration, activeWrite);
|
||||
}
|
||||
|
||||
configurationSemaphone.release();
|
||||
configurationSemaphore.release();
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(RAM128k.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
@ -416,12 +412,17 @@ abstract public class RAM128k extends RAM {
|
|||
public void log(String message) {
|
||||
CPU cpu = computer.getCpu();
|
||||
if (cpu != null && cpu.isLogEnabled()) {
|
||||
String stack = "";
|
||||
StringBuilder stack = new StringBuilder();
|
||||
for (StackTraceElement e : Thread.currentThread().getStackTrace()) {
|
||||
stack += e.getClassName() + "." + e.getMethodName() + "(" + e.getLineNumber() + ");";
|
||||
stack.append(String.format("%s.%s(%s);",e.getClassName(), e.getMethodName(), e.getLineNumber()));
|
||||
}
|
||||
cpu.log(stack);
|
||||
cpu.log(message + ";" + SoftSwitches.RAMRD + ";" + SoftSwitches.RAMWRT + ";" + SoftSwitches.AUXZP + ";" + SoftSwitches._80STORE + ";" + SoftSwitches.HIRES + ";" + SoftSwitches.PAGE2 + ";" + SoftSwitches.LCBANK1 + ";" + SoftSwitches.LCRAM + ";" + SoftSwitches.LCWRITE);
|
||||
cpu.log(stack.toString());
|
||||
String switches = Stream.of(
|
||||
SoftSwitches.RAMRD, SoftSwitches.RAMWRT, SoftSwitches.AUXZP,
|
||||
SoftSwitches._80STORE, SoftSwitches.HIRES, SoftSwitches.PAGE2,
|
||||
SoftSwitches.LCBANK1, SoftSwitches.LCRAM, SoftSwitches.LCWRITE
|
||||
).map(Object::toString).collect(Collectors.joining(";"));
|
||||
cpu.log(String.join(";", message, switches));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,14 +435,19 @@ abstract public class RAM128k extends RAM {
|
|||
protected void loadRom(String path) throws IOException {
|
||||
// Remap writable ram to reflect rom file structure
|
||||
byte[] ignore = new byte[256];
|
||||
activeWrite.set(0, ignore); // Ignore first bank of data
|
||||
for (int i = 1; i < 17; i++) {
|
||||
byte[][] restore = new byte[18][];
|
||||
for (int i = 0; i < 17; i++) {
|
||||
restore[i] = activeWrite.get(i);
|
||||
activeWrite.set(i, ignore);
|
||||
}
|
||||
activeWrite.setBanks(0, cPageRom.getMemory().length, 0x011, cPageRom);
|
||||
activeWrite.setBanks(0, rom.getMemory().length, 0x020, rom);
|
||||
//----------------------
|
||||
InputStream inputRom = getClass().getClassLoader().getResourceAsStream(path);
|
||||
if (inputRom == null) {
|
||||
LOG.log(Level.SEVERE, "Rom not found: " + path);
|
||||
return;
|
||||
}
|
||||
int read = 0;
|
||||
int addr = 0;
|
||||
byte[] in = new byte[1024];
|
||||
|
@ -453,7 +459,10 @@ abstract public class RAM128k extends RAM {
|
|||
// System.out.println("Finished reading rom with " + inputRom.available() + " bytes left unread!");
|
||||
//dump();
|
||||
// Clear cached configurations as we might have outdated references now
|
||||
memoryConfigurations.clear();
|
||||
for (int i = 0; i < 17; i++) {
|
||||
activeWrite.set(i, restore[i]);
|
||||
}
|
||||
memoryConfigurations.clear();
|
||||
configureActiveMemory();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import jace.apple2e.softswitch.MemorySoftSwitch;
|
|||
import jace.apple2e.softswitch.VideoSoftSwitch;
|
||||
import jace.core.RAMEvent;
|
||||
import jace.core.SoftSwitch;
|
||||
import jace.core.Video;
|
||||
|
||||
/**
|
||||
* Softswitches reside in the addresses C000-C07f and control everything from
|
||||
|
@ -62,7 +63,7 @@ public enum SoftSwitches {
|
|||
@Override
|
||||
public void stateChanged() {
|
||||
super.stateChanged();
|
||||
computer.getVideo().forceRefresh();
|
||||
Video.forceRefresh();
|
||||
}
|
||||
}),
|
||||
TEXT(new VideoSoftSwitch("Text", 0x0c050, 0x0c051, 0x0c01a, RAMEvent.TYPE.ANY, true)),
|
||||
|
@ -167,7 +168,7 @@ public enum SoftSwitches {
|
|||
/**
|
||||
* Creates a new instance of SoftSwitches
|
||||
*/
|
||||
private SoftSwitches(SoftSwitch softswitch) {
|
||||
SoftSwitches(SoftSwitch softswitch) {
|
||||
this.softswitch = softswitch;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,20 +18,13 @@
|
|||
*/
|
||||
package jace.apple2e;
|
||||
|
||||
import jace.core.Computer;
|
||||
import jace.core.Font;
|
||||
import jace.core.Palette;
|
||||
import jace.core.RAMEvent;
|
||||
import jace.core.Video;
|
||||
import static jace.core.Video.hiresOffset;
|
||||
import static jace.core.Video.hiresRowLookup;
|
||||
import static jace.core.Video.textRowLookup;
|
||||
import jace.core.VideoWriter;
|
||||
import java.util.logging.Logger;
|
||||
import jace.core.*;
|
||||
import javafx.scene.image.PixelWriter;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This is the primary video rendering class, which provides all necessary video
|
||||
* writers for every display mode as well as managing the display mode (via
|
||||
|
@ -51,19 +44,19 @@ public class VideoDHGR extends Video {
|
|||
9, 11, 13, 15
|
||||
};
|
||||
private static final boolean USE_GS_MOUSETEXT = false;
|
||||
private VideoWriter textPage1;
|
||||
private VideoWriter textPage2;
|
||||
private VideoWriter loresPage1;
|
||||
private VideoWriter loresPage2;
|
||||
private VideoWriter hiresPage1;
|
||||
private VideoWriter hiresPage2;
|
||||
private final VideoWriter textPage1;
|
||||
private final VideoWriter textPage2;
|
||||
private final VideoWriter loresPage1;
|
||||
private final VideoWriter loresPage2;
|
||||
private final VideoWriter hiresPage1;
|
||||
private final VideoWriter hiresPage2;
|
||||
// Special 80-column modes
|
||||
private VideoWriter text80Page1;
|
||||
private VideoWriter text80Page2;
|
||||
private VideoWriter dloresPage1;
|
||||
private VideoWriter dloresPage2;
|
||||
private VideoWriter dhiresPage1;
|
||||
private VideoWriter dhiresPage2;
|
||||
private final VideoWriter text80Page1;
|
||||
private final VideoWriter text80Page2;
|
||||
private final VideoWriter dloresPage1;
|
||||
private final VideoWriter dloresPage2;
|
||||
private final VideoWriter dhiresPage1;
|
||||
private final VideoWriter dhiresPage2;
|
||||
// Mixed mode
|
||||
private final VideoWriter mixed;
|
||||
private VideoWriter currentGraphicsWriter = null;
|
||||
|
@ -76,6 +69,10 @@ public class VideoDHGR extends Video {
|
|||
*/
|
||||
public VideoDHGR(Computer computer) {
|
||||
super(computer);
|
||||
|
||||
initCharMap();
|
||||
initLookupTables();
|
||||
|
||||
hiresPage1 = new VideoWriter() {
|
||||
@Override
|
||||
public int getYOffset(int y) {
|
||||
|
@ -334,26 +331,23 @@ public class VideoDHGR extends Video {
|
|||
// Take two consecutive bytes and double them, taking hi-bit into account
|
||||
// This should yield a 28-bit word of 7 color dhgr pixels
|
||||
// This looks like crap on text...
|
||||
static final int[][] HGR_TO_DHGR;
|
||||
static final int[][] HGR_TO_DHGR = new int[512][256];
|
||||
// Take two consecutive bytes and double them, disregarding hi-bit
|
||||
// Useful for text mode
|
||||
static final int[][] HGR_TO_DHGR_BW;
|
||||
static final int[] TIMES_14;
|
||||
static final int[] FLIP_BITS;
|
||||
|
||||
static {
|
||||
static final int[][] HGR_TO_DHGR_BW = new int[256][256];
|
||||
static final int[] TIMES_14 = new int[40];
|
||||
static final int[] FLIP_BITS = new int[256];
|
||||
|
||||
static void initLookupTables() {
|
||||
// complete reverse of 8 bits
|
||||
FLIP_BITS = new int[256];
|
||||
for (int i = 0; i < 256; i++) {
|
||||
FLIP_BITS[i] = (((i * 0x0802 & 0x22110) | (i * 0x8020 & 0x88440)) * 0x10101 >> 16) & 0x0ff;
|
||||
}
|
||||
|
||||
TIMES_14 = new int[40];
|
||||
for (int i = 0; i < 40; i++) {
|
||||
TIMES_14[i] = i * 14;
|
||||
}
|
||||
HGR_TO_DHGR = new int[512][256];
|
||||
HGR_TO_DHGR_BW = new int[256][256];
|
||||
|
||||
for (int bb1 = 0; bb1 < 512; bb1++) {
|
||||
for (int bb2 = 0; bb2 < 256; bb2++) {
|
||||
int value = ((bb1 & 0x0181) >= 0x0101) ? 1 : 0;
|
||||
|
@ -403,7 +397,7 @@ public class VideoDHGR extends Video {
|
|||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx, y, color);
|
||||
}
|
||||
|
||||
protected void displayDoubleLores(WritableImage screen, int xOffset, int y, int rowAddress) {
|
||||
|
@ -435,17 +429,17 @@ public class VideoDHGR extends Video {
|
|||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx++, y, color);
|
||||
writer.setColor(xx, y, color);
|
||||
}
|
||||
boolean flashInverse = false;
|
||||
int flashTimer = 0;
|
||||
int FLASH_SPEED = 16; // UTAIIe:8-13,P7 - FLASH toggles every 16 scans
|
||||
int[] currentCharMap = CHAR_MAP1;
|
||||
static final int[] CHAR_MAP1;
|
||||
static final int[] CHAR_MAP2;
|
||||
static final int[] CHAR_MAP3;
|
||||
static final int[] CHAR_MAP1 = new int[256];
|
||||
static final int[] CHAR_MAP2 = new int[256];
|
||||
static final int[] CHAR_MAP3 = new int[256];
|
||||
|
||||
static {
|
||||
static void initCharMap() {
|
||||
// Generate screen text lookup maps ahead of time
|
||||
// ALTCHR clear
|
||||
// 00-3F - Inverse characters (uppercase only) "@P 0"
|
||||
|
@ -462,11 +456,8 @@ public class VideoDHGR extends Video {
|
|||
// C0-DF - Normal characters (repeat 80-9F)
|
||||
// E0-FF - Normal characters (lowercase)
|
||||
// MAP1: Normal map, flash inverse = false
|
||||
CHAR_MAP1 = new int[256];
|
||||
// MAP2: Normal map, flash inverse = true
|
||||
CHAR_MAP2 = new int[256];
|
||||
// MAP3: Alt map, mousetext mode
|
||||
CHAR_MAP3 = new int[256];
|
||||
for (int b = 0; b < 256; b++) {
|
||||
int mod = b % 0x020;
|
||||
// Inverse
|
||||
|
|
|
@ -26,14 +26,13 @@ import jace.core.Computer;
|
|||
import jace.core.RAM;
|
||||
import jace.core.RAMEvent;
|
||||
import jace.core.RAMListener;
|
||||
import java.util.Arrays;
|
||||
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;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides a clean color monitor simulation, complete with text-friendly
|
||||
|
@ -53,27 +52,32 @@ public class VideoNTSC extends VideoDHGR {
|
|||
|
||||
@ConfigurableField(name = "Text palette", shortName = "textPalette", defaultValue = "false", description = "Use text-friendly color palette")
|
||||
public boolean useTextPalette = false;
|
||||
int activePalette[][] = SOLID_PALETTE;
|
||||
final int[][] SOLID_PALETTE = new int[4][128];
|
||||
final int[][] TEXT_PALETTE = new int[4][128];
|
||||
int[][] activePalette = SOLID_PALETTE;
|
||||
@ConfigurableField(name = "Video 7", shortName = "video7", defaultValue = "true", description = "Enable Video 7 RGB rendering support")
|
||||
public boolean enableVideo7 = true;
|
||||
// Scanline represents 560 bits, divided up into 28-bit words
|
||||
int[] scanline = new int[20];
|
||||
static public int[] divBy28 = new int[560];
|
||||
final int[] scanline = new int[20];
|
||||
final public int[] divBy28 = new int[560];
|
||||
|
||||
static {
|
||||
public final void initDivideTables() {
|
||||
for (int i = 0; i < 560; i++) {
|
||||
divBy28[i] = i / 28;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean[] colorActive = new boolean[80];
|
||||
int rowStart = 0;
|
||||
|
||||
public VideoNTSC(Computer computer) {
|
||||
super(computer);
|
||||
initDivideTables();
|
||||
initNtscPalette();
|
||||
registerStateListeners();
|
||||
}
|
||||
|
||||
public static enum VideoMode {
|
||||
public enum VideoMode {
|
||||
TextFriendly("Text-friendly color"),
|
||||
Color("Color"),
|
||||
Mode7TextFriendly("Mode7 with Text-friendly palette"),
|
||||
|
@ -96,11 +100,15 @@ public class VideoNTSC extends VideoDHGR {
|
|||
defaultKeyMapping = {"ctrl+shift+g"})
|
||||
public static void changeVideoMode() {
|
||||
currentMode = (currentMode + 1) % VideoMode.values().length;
|
||||
setVideoMode(VideoMode.values()[currentMode], true);
|
||||
((VideoNTSC) Emulator.getComputer().getVideo())._setVideoMode(VideoMode.values()[currentMode], true);
|
||||
}
|
||||
|
||||
public static void setVideoMode(VideoMode newMode, boolean showNotification) {
|
||||
VideoNTSC thiss = (VideoNTSC) Emulator.computer.video;
|
||||
((VideoNTSC) Emulator.getComputer().getVideo())._setVideoMode(newMode, showNotification);
|
||||
}
|
||||
|
||||
private void _setVideoMode(VideoMode newMode, boolean showNotification) {
|
||||
VideoNTSC thiss = (VideoNTSC) Emulator.getComputer().video;
|
||||
thiss.monochomeMode = false;
|
||||
WHITE = Color.WHITE;
|
||||
switch (newMode) {
|
||||
|
@ -208,16 +216,16 @@ public class VideoNTSC extends VideoDHGR {
|
|||
} else {
|
||||
c2 >>= 4;
|
||||
}
|
||||
int pat;
|
||||
if ((xOffset & 0x01) == 0) {
|
||||
int pat = c1 | (c1 & 7) << 4;
|
||||
pat = c1 | (c1 & 7) << 4;
|
||||
pat |= c2 << 7 | (c2 & 7) << 11;
|
||||
scanline[pos] = pat;
|
||||
} else {
|
||||
int pat = scanline[pos];
|
||||
pat = scanline[pos];
|
||||
pat |= (c1 & 12) << 12 | c1 << 16 | (c1 & 1) << 20;
|
||||
pat |= (c2 & 12) << 19 | c2 << 23 | (c2 & 1) << 27;
|
||||
scanline[pos] = pat;
|
||||
}
|
||||
scanline[pos] = pat;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -229,7 +237,7 @@ public class VideoNTSC extends VideoDHGR {
|
|||
// Offset is based on location in graphics buffer that corresponds with the row and
|
||||
// a number (0-20) that represents how much of the scanline was rendered
|
||||
// This is based off the xyOffset but is different because of P
|
||||
static int pyOffset[][];
|
||||
static int[][] pyOffset;
|
||||
|
||||
static {
|
||||
pyOffset = new int[192][21];
|
||||
|
@ -308,8 +316,6 @@ public class VideoNTSC extends VideoDHGR {
|
|||
public static final double MAX_I = 0.5957;
|
||||
// q Range [-0.5226, 0.5226]
|
||||
public static final double MAX_Q = 0.5226;
|
||||
static final int SOLID_PALETTE[][] = new int[4][128];
|
||||
static final int[][] TEXT_PALETTE = new int[4][128];
|
||||
static final double[][] YIQ_VALUES = {
|
||||
{0.0, 0.0, 0.0}, //0000 0
|
||||
{0.25, 0.5, 0.5}, //0001 1
|
||||
|
@ -329,7 +335,7 @@ public class VideoNTSC extends VideoDHGR {
|
|||
{1.0, 0.0, 0.0}, //1111 f
|
||||
};
|
||||
|
||||
static {
|
||||
private void initNtscPalette() {
|
||||
int maxLevel = 10;
|
||||
for (int offset = 0; offset < 4; offset++) {
|
||||
for (int pattern = 0; pattern < 128; pattern++) {
|
||||
|
@ -363,10 +369,7 @@ public class VideoNTSC extends VideoDHGR {
|
|||
if (x < minX) {
|
||||
return minX;
|
||||
}
|
||||
if (x > maxX) {
|
||||
return maxX;
|
||||
}
|
||||
return x;
|
||||
return Math.min(x, maxX);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -381,7 +384,7 @@ public class VideoNTSC extends VideoDHGR {
|
|||
// http://apple2.info/download/Ext80ColumnAppleColorCardHR.pdf
|
||||
rgbMode graphicsMode = rgbMode.MIX;
|
||||
|
||||
public static enum rgbMode {
|
||||
public enum rgbMode {
|
||||
|
||||
COLOR(true), MIX(true), BW(false), COL_160(false);
|
||||
boolean colorMode = false;
|
||||
|
@ -395,10 +398,6 @@ public class VideoNTSC extends VideoDHGR {
|
|||
}
|
||||
}
|
||||
|
||||
public static enum ModeStateChanges {
|
||||
|
||||
SET_AN3, CLEAR_AN3, SET_80, CLEAR_80;
|
||||
}
|
||||
boolean f1 = true;
|
||||
boolean f2 = true;
|
||||
boolean an3 = false;
|
||||
|
|
|
@ -17,7 +17,7 @@ public class ApplesoftHandler implements LanguageHandler<ApplesoftProgram> {
|
|||
|
||||
@Override
|
||||
public String getNewDocumentContent() {
|
||||
return ApplesoftProgram.fromMemory(Emulator.computer.getMemory()).toString();
|
||||
return ApplesoftProgram.fromMemory(Emulator.getComputer().getMemory()).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -69,7 +69,7 @@ public class ApplesoftProgram {
|
|||
Logger.getLogger(ApplesoftProgram.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
ApplesoftProgram test = ApplesoftProgram.fromBinary(Arrays.asList(toObjects(source)));
|
||||
System.out.println(test.toString());
|
||||
System.out.println(test);
|
||||
}
|
||||
|
||||
public static Byte[] toObjects(byte[] bytesPrim) {
|
||||
|
@ -149,8 +149,8 @@ public class ApplesoftProgram {
|
|||
}
|
||||
|
||||
public void run() {
|
||||
RAM memory = Emulator.computer.memory;
|
||||
Emulator.computer.pause();
|
||||
RAM memory = Emulator.getComputer().memory;
|
||||
Emulator.getComputer().pause();
|
||||
int programStart = memory.readWordRaw(START_OF_PROG_POINTER);
|
||||
int programEnd = programStart + getProgramSize();
|
||||
if (isProgramRunning()) {
|
||||
|
@ -162,11 +162,11 @@ public class ApplesoftProgram {
|
|||
injectProgram();
|
||||
clearVariables(programEnd);
|
||||
}
|
||||
Emulator.computer.resume();
|
||||
Emulator.getComputer().resume();
|
||||
}
|
||||
|
||||
private void injectProgram() {
|
||||
RAM memory = Emulator.computer.memory;
|
||||
RAM memory = Emulator.getComputer().memory;
|
||||
int pos = memory.readWordRaw(START_OF_PROG_POINTER);
|
||||
for (Line line : lines) {
|
||||
int nextPos = pos + line.getLength();
|
||||
|
@ -193,7 +193,7 @@ public class ApplesoftProgram {
|
|||
}
|
||||
|
||||
private boolean isProgramRunning() {
|
||||
RAM memory = Emulator.computer.memory;
|
||||
RAM memory = Emulator.getComputer().memory;
|
||||
return (memory.readRaw(RUNNING_FLAG) & 0x0FF) != NOT_RUNNING;
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,7 @@ public class ApplesoftProgram {
|
|||
* If the program is running, wait until it advances to the next line
|
||||
*/
|
||||
private void whenReady(Runnable r) {
|
||||
RAM memory = Emulator.computer.memory;
|
||||
RAM memory = Emulator.getComputer().memory;
|
||||
memory.addListener(new RAMListener(RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
|
||||
@Override
|
||||
protected void doConfig() {
|
||||
|
@ -222,7 +222,7 @@ public class ApplesoftProgram {
|
|||
* @param programEnd Program ending address
|
||||
*/
|
||||
private void clearVariables(int programEnd) {
|
||||
RAM memory = Emulator.computer.memory;
|
||||
RAM memory = Emulator.getComputer().memory;
|
||||
memory.writeWord(ARRAY_TABLE, programEnd, false, true);
|
||||
memory.writeWord(VARIABLE_TABLE, programEnd, false, true);
|
||||
memory.writeWord(VARIABLE_TABLE_END, programEnd, false, true);
|
||||
|
@ -234,7 +234,7 @@ public class ApplesoftProgram {
|
|||
* @param programEnd Program ending address
|
||||
*/
|
||||
private void relocateVariables(int programEnd) {
|
||||
RAM memory = Emulator.computer.memory;
|
||||
RAM memory = Emulator.getComputer().memory;
|
||||
int currentEnd = memory.readWordRaw(END_OF_PROG_POINTER);
|
||||
memory.writeWord(END_OF_PROG_POINTER, programEnd, false, true);
|
||||
if (programEnd > currentEnd) {
|
||||
|
@ -254,4 +254,8 @@ public class ApplesoftProgram {
|
|||
int size = lines.stream().collect(Collectors.summingInt(Line::getLength)) + 4;
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return lines.size();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import java.util.stream.Collectors;
|
|||
*/
|
||||
public class Command {
|
||||
|
||||
public static enum TOKEN {
|
||||
public enum TOKEN {
|
||||
|
||||
END((byte) 0x080, "END"),
|
||||
FOR((byte) 0x081, "FOR"),
|
||||
|
@ -160,7 +160,7 @@ public class Command {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
private String str;
|
||||
private final String str;
|
||||
public byte code;
|
||||
|
||||
TOKEN(byte b, String str) {
|
||||
|
|
|
@ -31,28 +31,28 @@ public class AssemblyHandler implements LanguageHandler<File> {
|
|||
@Override
|
||||
public void execute(CompileResult<File> lastResult) {
|
||||
if (lastResult.isSuccessful()) {
|
||||
try {
|
||||
boolean resume = false;
|
||||
if (Emulator.computer.isRunning()) {
|
||||
resume = true;
|
||||
Emulator.computer.pause();
|
||||
RAM memory = Emulator.getComputer().getMemory();
|
||||
try {
|
||||
FileInputStream input = new FileInputStream(lastResult.getCompiledAsset());
|
||||
int startLSB = input.read();
|
||||
int startMSB = input.read();
|
||||
int start = startLSB + startMSB << 8;
|
||||
System.out.printf("Issuing JSR to $%s%n", Integer.toHexString(start));
|
||||
Emulator.getComputer().getCpu().whileSuspended(() -> {
|
||||
try {
|
||||
int pos = start;
|
||||
int next;
|
||||
while ((next=input.read()) != -1) {
|
||||
memory.write(pos++, (byte) next, false, true);
|
||||
}
|
||||
Emulator.getComputer().getCpu().JSR(start);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
});
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
FileInputStream input = new FileInputStream(lastResult.getCompiledAsset());
|
||||
int startLSB = input.read();
|
||||
int startMSB = input.read();
|
||||
int pos = startLSB + startMSB << 8;
|
||||
Emulator.computer.getCpu().JSR(pos);
|
||||
int next;
|
||||
while ((next=input.read()) != -1) {
|
||||
memory.write(pos++, (byte) next, false, true);
|
||||
}
|
||||
if (resume) {
|
||||
Emulator.computer.resume();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
clean(lastResult);
|
||||
}
|
||||
|
|
|
@ -2,46 +2,35 @@ package jace.cheat;
|
|||
|
||||
import jace.Emulator;
|
||||
import jace.LawlessLegends;
|
||||
import jace.core.CPU;
|
||||
import jace.core.Computer;
|
||||
import jace.core.RAM;
|
||||
import jace.core.RAMEvent;
|
||||
import jace.core.RAMListener;
|
||||
import jace.core.*;
|
||||
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 javafx.application.Platform;
|
||||
import javafx.beans.property.*;
|
||||
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;
|
||||
import java.io.*;
|
||||
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 {
|
||||
public enum SearchType {
|
||||
VALUE, TEXT, CHANGE
|
||||
}
|
||||
|
||||
public static enum SearchChangeType {
|
||||
public enum SearchChangeType {
|
||||
NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT
|
||||
}
|
||||
|
||||
|
@ -156,7 +145,7 @@ public class MetaCheat extends Cheats {
|
|||
@Override
|
||||
protected void unregisterListeners() {
|
||||
super.unregisterListeners();
|
||||
cheatList.stream().forEach(computer.getMemory()::removeListener);
|
||||
cheatList.forEach(computer.getMemory()::removeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -230,7 +219,7 @@ public class MetaCheat extends Cheats {
|
|||
}
|
||||
|
||||
public void newSearch() {
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
RAM memory = Emulator.getComputer().getMemory();
|
||||
resultList.clear();
|
||||
int compare = parseInt(searchValueProperty.get());
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
|
@ -247,7 +236,7 @@ public class MetaCheat extends Cheats {
|
|||
}
|
||||
|
||||
public void performSearch() {
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
RAM memory = Emulator.getComputer().getMemory();
|
||||
boolean signed = signedProperty.get();
|
||||
resultList.removeIf((SearchResult result) -> {
|
||||
int val = byteSized
|
||||
|
@ -289,7 +278,7 @@ public class MetaCheat extends Cheats {
|
|||
}
|
||||
|
||||
public void initMemoryView() {
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
RAM memory = Emulator.getComputer().getMemory();
|
||||
for (int addr = getStartAddress(); addr <= getEndAddress(); addr++) {
|
||||
if (getMemoryCell(addr) == null) {
|
||||
MemoryCell cell = new MemoryCell();
|
||||
|
@ -305,7 +294,7 @@ public class MetaCheat extends Cheats {
|
|||
}
|
||||
|
||||
int fadeCounter = 0;
|
||||
int FADE_TIMER_VALUE = (int) (Emulator.computer.getMotherboard().getSpeedInHz() / 60);
|
||||
int FADE_TIMER_VALUE = (int) (Emulator.getComputer().getMotherboard().getSpeedInHz() / 60);
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
|
@ -313,7 +302,7 @@ public class MetaCheat extends Cheats {
|
|||
if (fadeCounter-- <= 0) {
|
||||
fadeCounter = FADE_TIMER_VALUE;
|
||||
memoryCells.values().stream()
|
||||
.filter((cell) -> cell.hasCounts())
|
||||
.filter(MemoryCell::hasCounts)
|
||||
.forEach((cell) -> {
|
||||
if (cell.execCount.get() > 0) {
|
||||
cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate));
|
||||
|
@ -339,7 +328,7 @@ public class MetaCheat extends Cheats {
|
|||
private void processMemoryEvent(RAMEvent e) {
|
||||
MemoryCell cell = getMemoryCell(e.getAddress());
|
||||
if (cell != null) {
|
||||
CPU cpu = Emulator.computer.getCpu();
|
||||
CPU cpu = Emulator.getComputer().getCpu();
|
||||
int pc = cpu.getProgramCounter();
|
||||
String trace = cpu.getLastTrace();
|
||||
switch (e.getType()) {
|
||||
|
|
|
@ -71,7 +71,7 @@ public class MontezumasRevengeCheats extends Cheats {
|
|||
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
RAM memory = Emulator.computer.memory;
|
||||
RAM memory = Emulator.getComputer().memory;
|
||||
if (repulsiveHack) {
|
||||
addCheat(RAMEvent.TYPE.WRITE, this::repulsiveBehavior, 0x1508, 0x1518);
|
||||
}
|
||||
|
|
|
@ -32,28 +32,26 @@ import java.util.Set;
|
|||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
|
||||
public class ClassSelection extends DynamicSelection<Class> {
|
||||
public class ClassSelection<C> extends DynamicSelection<Class<? extends C>> {
|
||||
|
||||
Class template = null;
|
||||
Class<C> template;
|
||||
|
||||
public ClassSelection(Class supertype, Class defaultValue) {
|
||||
public ClassSelection(Class<C> supertype, Class<? extends C> defaultValue) {
|
||||
super(defaultValue);
|
||||
template = supertype;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkedHashMap<Class, String> getSelections() {
|
||||
LinkedHashMap<Class, String> selections = new LinkedHashMap<>();
|
||||
Set<? extends Class> allClasses = Utility.findAllSubclasses(template);
|
||||
if (!allClasses.contains(null)) {
|
||||
allClasses.add(null);
|
||||
}
|
||||
List<Entry<Class, String>> values = new ArrayList<>();
|
||||
public LinkedHashMap<Class<C>, String> getSelections() {
|
||||
LinkedHashMap<Class<C>, String> selections = new LinkedHashMap<>();
|
||||
Set<Class<? extends C>> allClasses = Utility.findAllSubclasses(template);
|
||||
allClasses.add(null);
|
||||
List<Entry<Class<? extends C>, String>> values = new ArrayList<>();
|
||||
if (allowNull()) {
|
||||
values.add(new Entry<Class, String>() {
|
||||
values.add(new Entry<Class<? extends C>, String>() {
|
||||
|
||||
@Override
|
||||
public Class getKey() {
|
||||
public Class<? extends C> getKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -68,11 +66,11 @@ public class ClassSelection extends DynamicSelection<Class> {
|
|||
}
|
||||
});
|
||||
}
|
||||
for (final Class c : allClasses) {
|
||||
Entry<Class, String> entry = new Map.Entry<Class, String>() {
|
||||
for (final Class<? extends C> c : allClasses) {
|
||||
Entry<Class<? extends C>, String> entry = new Map.Entry<Class<? extends C>, String>() {
|
||||
|
||||
@Override
|
||||
public Class getKey() {
|
||||
public Class<? extends C> getKey() {
|
||||
return c;
|
||||
}
|
||||
|
||||
|
@ -82,7 +80,7 @@ public class ClassSelection extends DynamicSelection<Class> {
|
|||
return "**Empty**";
|
||||
}
|
||||
if (c.isAnnotationPresent(Name.class)) {
|
||||
return ((Name) c.getAnnotation(Name.class)).value();
|
||||
return c.getAnnotation(Name.class).value();
|
||||
}
|
||||
return c.getSimpleName();
|
||||
}
|
||||
|
@ -114,7 +112,7 @@ public class ClassSelection extends DynamicSelection<Class> {
|
|||
return (o1.getValue().compareTo(o2.getValue()));
|
||||
}
|
||||
});
|
||||
values.stream().forEach((entry) -> {
|
||||
values.forEach((entry) -> {
|
||||
Class key = entry.getKey();
|
||||
selections.put(key, entry.getValue());
|
||||
});
|
||||
|
|
|
@ -39,10 +39,10 @@ import java.lang.annotation.Target;
|
|||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ConfigurableField {
|
||||
public String name();
|
||||
public String shortName() default "";
|
||||
public String defaultValue() default "";
|
||||
public String description() default "";
|
||||
public String category() default "General";
|
||||
public boolean enablesDevice() default false;
|
||||
String name();
|
||||
String shortName() default "";
|
||||
String defaultValue() default "";
|
||||
String description() default "";
|
||||
String category() default "General";
|
||||
boolean enablesDevice() default false;
|
||||
}
|
|
@ -23,37 +23,16 @@ import jace.EmulatorUILogic;
|
|||
import jace.core.Computer;
|
||||
import jace.core.Keyboard;
|
||||
import jace.core.Utility;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.TreeItem;
|
||||
import javafx.scene.image.ImageView;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
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.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
|
||||
/**
|
||||
* Manages the configuration state of the emulator components.
|
||||
|
@ -125,7 +104,7 @@ public class Configuration implements Reconfigurable {
|
|||
|
||||
public transient ConfigNode root;
|
||||
public transient ConfigNode parent;
|
||||
private transient ObservableList<ConfigNode> children;
|
||||
private transient final ObservableList<ConfigNode> children;
|
||||
public transient Reconfigurable subject;
|
||||
private transient boolean changed = true;
|
||||
|
||||
|
@ -145,7 +124,7 @@ public class Configuration implements Reconfigurable {
|
|||
|
||||
private void readObject(java.io.ObjectInputStream in)
|
||||
throws IOException, ClassNotFoundException {
|
||||
children = super.getChildren();
|
||||
children.setAll(super.getChildren());
|
||||
id = (String) in.readObject();
|
||||
name = (String) in.readObject();
|
||||
settings = (Map) in.readObject();
|
||||
|
@ -218,7 +197,7 @@ public class Configuration implements Reconfigurable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<ConfigNode> getChildren() {
|
||||
final public ObservableList<ConfigNode> getChildren() {
|
||||
return super.getChildren();
|
||||
}
|
||||
|
||||
|
@ -275,7 +254,7 @@ public class Configuration implements Reconfigurable {
|
|||
}
|
||||
public static ConfigNode BASE;
|
||||
public static EmulatorUILogic ui = Emulator.logic;
|
||||
public static Computer emulator = Emulator.computer;
|
||||
public static Computer emulator = Emulator.getComputer();
|
||||
@ConfigurableField(name = "Autosave Changes", description = "If unchecked, changes are only saved when the Save button is pressed.")
|
||||
public static boolean saveAutomatically = false;
|
||||
|
||||
|
@ -301,7 +280,7 @@ public class Configuration implements Reconfigurable {
|
|||
// System.out.println("Evaluating field " + f.getName());
|
||||
try {
|
||||
Object o = f.get(node.subject);
|
||||
if (!f.getType().isPrimitive() && f.getType() != String.class && visited.contains(o)) {
|
||||
if (o == null || !f.getType().isPrimitive() && f.getType() != String.class && visited.contains(o)) {
|
||||
continue;
|
||||
}
|
||||
visited.add(o);
|
||||
|
@ -311,7 +290,7 @@ public class Configuration implements Reconfigurable {
|
|||
// if (o.getClass().isAssignableFrom(Reconfigurable.class)) {
|
||||
// if (Reconfigurable.class.isAssignableFrom(o.getClass())) {
|
||||
if (f.isAnnotationPresent(ConfigurableField.class)) {
|
||||
if (o != null && ISelection.class.isAssignableFrom(o.getClass())) {
|
||||
if (ISelection.class.isAssignableFrom(o.getClass())) {
|
||||
ISelection selection = (ISelection) o;
|
||||
node.setRawFieldValue(f.getName(), (Serializable) selection.getSelections().get(selection.getValue()));
|
||||
} else {
|
||||
|
@ -319,9 +298,6 @@ public class Configuration implements Reconfigurable {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
if (o == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (o instanceof Reconfigurable) {
|
||||
Reconfigurable r = (Reconfigurable) o;
|
||||
|
@ -398,7 +374,6 @@ public class Configuration implements Reconfigurable {
|
|||
defaultKeyMapping = "meta+ctrl+s"
|
||||
)
|
||||
public static void saveSettings() {
|
||||
FileOutputStream fos = null;
|
||||
{
|
||||
ObjectOutputStream oos = null;
|
||||
try {
|
||||
|
@ -429,29 +404,29 @@ public class Configuration implements Reconfigurable {
|
|||
defaultKeyMapping = "meta+ctrl+r"
|
||||
)
|
||||
public static void loadSettings() {
|
||||
{
|
||||
boolean successful = false;
|
||||
ObjectInputStream ois = null;
|
||||
boolean successful = false;
|
||||
ObjectInputStream ois = null;
|
||||
try {
|
||||
ois = new ObjectInputStream(new FileInputStream(getSettingsFile()));
|
||||
ConfigNode newRoot = (ConfigNode) ois.readObject();
|
||||
applyConfigTree(newRoot, BASE);
|
||||
successful = true;
|
||||
} catch (FileNotFoundException ex) {
|
||||
// This just means there are no settings to be saved -- just ignore it.
|
||||
} catch (InvalidClassException ex) {
|
||||
Logger.getLogger(Configuration.class.getName()).log(Level.WARNING, "Unable to load settings, Jace version is newer and incompatible with old settings.");
|
||||
} catch (ClassNotFoundException | IOException ex) {
|
||||
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
ois = new ObjectInputStream(new FileInputStream(getSettingsFile()));
|
||||
ConfigNode newRoot = (ConfigNode) ois.readObject();
|
||||
applyConfigTree(newRoot, BASE);
|
||||
successful = true;
|
||||
} catch (FileNotFoundException ex) {
|
||||
// This just means there are no settings to be saved -- just ignore it.
|
||||
} catch (ClassNotFoundException | IOException ex) {
|
||||
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
if (ois != null) {
|
||||
ois.close();
|
||||
}
|
||||
if (!successful) {
|
||||
applySettings(BASE);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
|
||||
if (ois != null) {
|
||||
ois.close();
|
||||
}
|
||||
if (!successful) {
|
||||
applySettings(BASE);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -475,7 +450,7 @@ public class Configuration implements Reconfigurable {
|
|||
public static boolean applySettings(ConfigNode node) {
|
||||
boolean resume = false;
|
||||
if (node == BASE) {
|
||||
resume = Emulator.computer.pause();
|
||||
resume = Emulator.getComputer().pause();
|
||||
}
|
||||
boolean hasChanged = false;
|
||||
if (node.changed) {
|
||||
|
@ -494,7 +469,7 @@ public class Configuration implements Reconfigurable {
|
|||
}
|
||||
|
||||
if (resume) {
|
||||
Emulator.computer.resume();
|
||||
Emulator.getComputer().resume();
|
||||
}
|
||||
|
||||
return hasChanged;
|
||||
|
@ -649,7 +624,7 @@ public class Configuration implements Reconfigurable {
|
|||
System.out.println(prefix + ">>" + setting + " (" + n.subject.getShortName() + "." + sn + ")");
|
||||
});
|
||||
n.getChildren().stream().forEach((c) -> {
|
||||
printTree(c, prefix + "." + c.toString(), i + 1);
|
||||
printTree(c, prefix + "." + c, i + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +1,29 @@
|
|||
package jace.config;
|
||||
|
||||
import jace.config.Configuration.ConfigNode;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.SplitPane;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.TreeItem;
|
||||
import javafx.scene.control.TreeView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
public class ConfigurationUIController {
|
||||
public static final String DELIMITER = "~!~";
|
||||
|
@ -104,8 +96,8 @@ public class ConfigurationUIController {
|
|||
|
||||
private void getExpandedNodes(String prefix, TreeItem<ConfigNode> root, Set<String> expanded) {
|
||||
if (root == null) return;
|
||||
root.getChildren().stream().filter((item) -> (item.isExpanded())).forEach((item) -> {
|
||||
String name = prefix+item.toString();
|
||||
root.getChildren().stream().filter(TreeItem::isExpanded).forEach((item) -> {
|
||||
String name = prefix+ item;
|
||||
expanded.add(name);
|
||||
getExpandedNodes(name+DELIMITER, item, expanded);
|
||||
});
|
||||
|
@ -113,7 +105,7 @@ public class ConfigurationUIController {
|
|||
|
||||
private void setExpandedNodes(String prefix, TreeItem<ConfigNode> root, Set<String> expanded) {
|
||||
if (root == null) return;
|
||||
root.getChildren().stream().forEach((item) -> {
|
||||
root.getChildren().forEach((item) -> {
|
||||
String name = prefix+item.toString();
|
||||
if (expanded.contains(name)) {
|
||||
item.setExpanded(true);
|
||||
|
@ -163,12 +155,8 @@ public class ConfigurationUIController {
|
|||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
node.hotkeys.forEach((name, values) -> {
|
||||
settingsVbox.getChildren().add(buildKeyShortcutRow(node, name, values));
|
||||
});
|
||||
node.settings.forEach((name, value) -> {
|
||||
settingsVbox.getChildren().add(buildSettingRow(node, name, value));
|
||||
});
|
||||
node.hotkeys.forEach((name, values) -> settingsVbox.getChildren().add(buildKeyShortcutRow(node, name, values)));
|
||||
node.settings.forEach((name, value) -> settingsVbox.getChildren().add(buildSettingRow(node, name, value)));
|
||||
}
|
||||
|
||||
private Node buildSettingRow(ConfigNode node, String settingName, Serializable value) {
|
||||
|
@ -198,13 +186,11 @@ public class ConfigurationUIController {
|
|||
Label label = new Label(actionInfo.name());
|
||||
label.getStyleClass().add("setting-keyboard-shortcut");
|
||||
label.setMinWidth(150.0);
|
||||
String value = Arrays.stream(values).collect(Collectors.joining(" or "));
|
||||
String value = String.join(" or ", values);
|
||||
Text widget = new Text(value);
|
||||
widget.setWrappingWidth(180.0);
|
||||
widget.getStyleClass().add("setting-keyboard-value");
|
||||
widget.setOnMouseClicked((event) -> {
|
||||
editKeyboardShortcut(node, actionName, widget);
|
||||
});
|
||||
widget.setOnMouseClicked((event) -> editKeyboardShortcut(node, actionName, widget));
|
||||
label.setLabelFor(widget);
|
||||
row.getChildren().add(label);
|
||||
row.getChildren().add(widget);
|
||||
|
@ -247,18 +233,14 @@ public class ConfigurationUIController {
|
|||
|
||||
private Node buildTextField(ConfigNode node, String settingName, Serializable value, String validationPattern) {
|
||||
TextField widget = new TextField(String.valueOf(value));
|
||||
widget.textProperty().addListener((e) -> {
|
||||
node.setFieldValue(settingName, widget.getText());
|
||||
});
|
||||
widget.textProperty().addListener((e) -> node.setFieldValue(settingName, widget.getText()));
|
||||
return widget;
|
||||
}
|
||||
|
||||
private Node buildBooleanField(ConfigNode node, String settingName, Serializable value) {
|
||||
CheckBox widget = new CheckBox();
|
||||
widget.setSelected(value.equals(Boolean.TRUE));
|
||||
widget.selectedProperty().addListener((e) -> {
|
||||
node.setFieldValue(settingName, widget.isSelected());
|
||||
});
|
||||
widget.selectedProperty().addListener((e) -> node.setFieldValue(settingName, widget.isSelected()));
|
||||
return widget;
|
||||
}
|
||||
|
||||
|
@ -284,9 +266,9 @@ public class ConfigurationUIController {
|
|||
} else {
|
||||
widget.setValue(selected);
|
||||
}
|
||||
widget.valueProperty().addListener((Observable e) -> {
|
||||
node.setFieldValue(settingName, widget.getConverter().toString(widget.getValue()));
|
||||
});
|
||||
widget.valueProperty().addListener((Observable e) ->
|
||||
node.setFieldValue(settingName, widget.getConverter().toString(widget.getValue()))
|
||||
);
|
||||
return widget;
|
||||
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
|
||||
Logger.getLogger(ConfigurationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
|
|
|
@ -27,11 +27,11 @@ import java.util.LinkedHashMap;
|
|||
*/
|
||||
public interface ISelection<T> extends Serializable {
|
||||
|
||||
public LinkedHashMap<? extends T, String> getSelections();
|
||||
LinkedHashMap<? extends T, String> getSelections();
|
||||
|
||||
public T getValue();
|
||||
T getValue();
|
||||
|
||||
public void setValue(T value);
|
||||
void setValue(T value);
|
||||
|
||||
public void setValueByMatch(String value);
|
||||
void setValueByMatch(String value);
|
||||
}
|
||||
|
|
|
@ -51,37 +51,37 @@ public @interface InvokableAction {
|
|||
/*
|
||||
* Should be short and meaningful name for action being invoked, e.g. "Insert disk"
|
||||
*/
|
||||
public String name();
|
||||
String name();
|
||||
/*
|
||||
* Can be used to group actions by overall topic, for example an automated table of contents
|
||||
* To be determined...
|
||||
*/
|
||||
public String category() default "General";
|
||||
String category() default "General";
|
||||
/*
|
||||
* More descriptive text which provides additional clarity, e.g.
|
||||
* "This will present you with a file selection dialog to pick a floppy disk image.
|
||||
* Currently, dos-ordered (DSK, DO), Prodos-ordered (PO), and Nibble (NIB) formats are supported."
|
||||
*/
|
||||
public String description() default "";
|
||||
String description() default "";
|
||||
/*
|
||||
* Alternatives should be delimited by semicolons) can provide more powerful search
|
||||
* For "insert disk", alternatives might be "change disk;switch disk" and
|
||||
* reboot might have alternatives as "warm start;cold start;boot;restart".
|
||||
*/
|
||||
public String alternatives() default "";
|
||||
String alternatives() default "";
|
||||
/*
|
||||
* If true, the key event will be consumed and not processed by any other event handlers
|
||||
* If the corresponding method returns a boolean, that value will be used instead.
|
||||
* True = consume (stop processing keystroke), false = pass-through to other handlers
|
||||
*/
|
||||
public boolean consumeKeyEvent() default true;
|
||||
boolean consumeKeyEvent() default true;
|
||||
/*
|
||||
* If false (default) event is only triggered on press, not release. If true,
|
||||
* method is notified on press and on release
|
||||
*/
|
||||
public boolean notifyOnRelease() default false;
|
||||
boolean notifyOnRelease() default false;
|
||||
/*
|
||||
* Standard keyboard mapping
|
||||
*/
|
||||
public String[] defaultKeyMapping();
|
||||
String[] defaultKeyMapping();
|
||||
}
|
|
@ -30,6 +30,6 @@ import java.lang.annotation.Target;
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Name {
|
||||
public String value();
|
||||
public String description() default "";
|
||||
String value();
|
||||
String description() default "";
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ package jace.config;
|
|||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public interface Reconfigurable {
|
||||
public String getName();
|
||||
public String getShortName();
|
||||
public void reconfigure();
|
||||
String getName();
|
||||
String getShortName();
|
||||
void reconfigure();
|
||||
}
|
||||
|
|
|
@ -73,14 +73,14 @@ public abstract class CPU extends Device {
|
|||
}
|
||||
|
||||
public void dumpTrace() {
|
||||
computer.pause();
|
||||
ArrayList<String> newLog = new ArrayList<>();
|
||||
ArrayList<String> oldLog = traceLog;
|
||||
traceLog = newLog;
|
||||
computer.resume();
|
||||
LOG.log(Level.INFO, "Most recent {0} instructions:", traceLength);
|
||||
oldLog.stream().forEach(LOG::info);
|
||||
oldLog.clear();
|
||||
whileSuspended(()->{
|
||||
ArrayList<String> newLog = new ArrayList<>();
|
||||
ArrayList<String> oldLog = traceLog;
|
||||
traceLog = newLog;
|
||||
LOG.log(Level.INFO, "Most recent {0} instructions:", traceLength);
|
||||
oldLog.forEach(LOG::info);
|
||||
oldLog.clear();
|
||||
});
|
||||
}
|
||||
|
||||
public void setDebug(Debugger d) {
|
||||
|
@ -117,9 +117,9 @@ public abstract class CPU extends Device {
|
|||
try {
|
||||
if (debugger != null) {
|
||||
if (!debugger.isActive() && debugger.hasBreakpoints()) {
|
||||
debugger.getBreakpoints().stream().filter((i) -> (i == getProgramCounter())).forEach((_item) -> {
|
||||
if (debugger.getBreakpoints().contains(getProgramCounter())){
|
||||
debugger.setActive(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (debugger.isActive()) {
|
||||
debugger.updateStatus();
|
||||
|
|
|
@ -102,12 +102,10 @@ public abstract class Card extends Device {
|
|||
@Override
|
||||
public void reconfigure() {
|
||||
//super.reconfigure();
|
||||
boolean restart = suspend();
|
||||
unregisterListeners();
|
||||
if (restart) {
|
||||
resume();
|
||||
}
|
||||
registerListeners();
|
||||
computer.motherboard.whileSuspended(() -> {
|
||||
unregisterListeners();
|
||||
registerListeners();
|
||||
});
|
||||
}
|
||||
|
||||
public void notifyVBLStateChanged(boolean state) {
|
||||
|
|
|
@ -22,13 +22,17 @@ import jace.config.ConfigurableField;
|
|||
import jace.config.InvokableAction;
|
||||
import jace.config.Reconfigurable;
|
||||
import jace.state.StateManager;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This is a very generic stub of a Computer and provides a generic set of
|
||||
* overall functionality, namely boot, pause and resume features. What sort of
|
||||
|
@ -45,7 +49,7 @@ public abstract class Computer implements Reconfigurable {
|
|||
public Keyboard keyboard;
|
||||
public StateManager stateManager;
|
||||
public Motherboard motherboard;
|
||||
public AtomicBoolean romLoaded = new AtomicBoolean(false);
|
||||
public final CompletableFuture<Boolean> romLoaded;
|
||||
@ConfigurableField(category = "advanced", name = "State management", shortName = "rewind", description = "This enables rewind support, but consumes a lot of memory when active.")
|
||||
public boolean enableStateManager;
|
||||
public final SoundMixer mixer;
|
||||
|
@ -57,7 +61,7 @@ public abstract class Computer implements Reconfigurable {
|
|||
public Computer() {
|
||||
keyboard = new Keyboard(this);
|
||||
mixer = new SoundMixer(this);
|
||||
romLoaded.set(false);
|
||||
romLoaded = new CompletableFuture<>();
|
||||
}
|
||||
|
||||
public RAM getMemory() {
|
||||
|
@ -125,7 +129,7 @@ public abstract class Computer implements Reconfigurable {
|
|||
|
||||
public void loadRom(String path) throws IOException {
|
||||
memory.loadRom(path);
|
||||
romLoaded.set(true);
|
||||
romLoaded.complete(true);
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
|
@ -148,15 +152,16 @@ public abstract class Computer implements Reconfigurable {
|
|||
description = "Process startup sequence from power-up",
|
||||
category = "general",
|
||||
alternatives = "Full reset;reset emulator",
|
||||
consumeKeyEvent = true,
|
||||
defaultKeyMapping = {"Ctrl+Shift+Backspace", "Ctrl+Shift+Delete"})
|
||||
public void invokeColdStart() {
|
||||
if (!romLoaded.get()) {
|
||||
if (!romLoaded.isDone()) {
|
||||
Thread delayedStart = new Thread(() -> {
|
||||
while (!romLoaded.get()) {
|
||||
Thread.yield();
|
||||
try {
|
||||
romLoaded.get();
|
||||
coldStart();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
Logger.getLogger(Computer.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
coldStart();
|
||||
});
|
||||
delayedStart.start();
|
||||
} else {
|
||||
|
|
|
@ -20,6 +20,7 @@ package jace.core;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A debugger has the ability to track a list of breakpoints and step a CPU one
|
||||
|
@ -48,19 +49,9 @@ public abstract class Debugger {
|
|||
public List<Integer> getBreakpoints() {
|
||||
return breakpoints;
|
||||
}
|
||||
private boolean hasBreakpoints = false;
|
||||
|
||||
boolean hasBreakpoints() {
|
||||
return hasBreakpoints;
|
||||
}
|
||||
|
||||
public void updateBreakpoints() {
|
||||
hasBreakpoints = false;
|
||||
for (Integer i : breakpoints) {
|
||||
if (i != null) {
|
||||
hasBreakpoints = true;
|
||||
}
|
||||
}
|
||||
return !breakpoints.isEmpty() && breakpoints.stream().anyMatch(Objects::nonNull);
|
||||
}
|
||||
|
||||
boolean takeStep() {
|
||||
|
|
|
@ -18,10 +18,12 @@
|
|||
*/
|
||||
package jace.core;
|
||||
|
||||
import jace.state.Stateful;
|
||||
import jace.config.Reconfigurable;
|
||||
import org.eclipse.collections.api.list.MutableList;
|
||||
import org.eclipse.collections.impl.factory.Lists;
|
||||
import jace.state.Stateful;
|
||||
import org.eclipse.collections.api.factory.Sets;
|
||||
import org.eclipse.collections.api.set.MutableSet;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Device is a very simple abstraction of any emulation component. A device
|
||||
|
@ -39,11 +41,10 @@ import org.eclipse.collections.impl.factory.Lists;
|
|||
public abstract class Device implements Reconfigurable {
|
||||
|
||||
protected Computer computer;
|
||||
private MutableList<Device> children;
|
||||
private final MutableSet<Device> children;
|
||||
|
||||
private Device() {
|
||||
// TODO: Previously this was synchronized -- confirm this is safe to leave unsynchronized
|
||||
children = Lists.mutable.<Device>empty();
|
||||
children = Sets.mutable.empty();
|
||||
}
|
||||
|
||||
public Device(Computer computer) {
|
||||
|
@ -55,13 +56,16 @@ public abstract class Device implements Reconfigurable {
|
|||
@Stateful
|
||||
private int waitCycles = 0;
|
||||
@Stateful
|
||||
private boolean run = true;
|
||||
private boolean run = false;
|
||||
@Stateful
|
||||
public boolean isPaused = false;
|
||||
@Stateful
|
||||
public boolean isAttached = false;
|
||||
|
||||
public void addChildDevice(Device d) {
|
||||
if (d == null || children.contains(d) || d.equals(this)) {
|
||||
return;
|
||||
}
|
||||
children.add(d);
|
||||
if (isAttached) {
|
||||
d.attach();
|
||||
|
@ -69,6 +73,9 @@ public abstract class Device implements Reconfigurable {
|
|||
}
|
||||
|
||||
public void removeChildDevice(Device d) {
|
||||
if (d == null) {
|
||||
return;
|
||||
}
|
||||
children.remove(d);
|
||||
d.suspend();
|
||||
if (isAttached) {
|
||||
|
@ -83,6 +90,11 @@ public abstract class Device implements Reconfigurable {
|
|||
public Iterable<Device> getChildren() {
|
||||
return children.asUnmodifiable();
|
||||
}
|
||||
|
||||
public void setAllDevices(Collection<Device> newDevices) {
|
||||
children.stream().filter(d-> !newDevices.contains(d)).forEach(this::removeChildDevice);
|
||||
newDevices.stream().filter(d-> !children.contains(d)).forEach(this::addChildDevice);
|
||||
}
|
||||
|
||||
public boolean getRunningProperty() {
|
||||
return run;
|
||||
|
@ -96,14 +108,14 @@ public abstract class Device implements Reconfigurable {
|
|||
waitCycles = wait;
|
||||
}
|
||||
|
||||
public void doTick() {
|
||||
if (run) {
|
||||
children.forEach(Device::tick);
|
||||
if (waitCycles <= 0) {
|
||||
tick();
|
||||
public void doTick() {
|
||||
if (isRunning()) {
|
||||
children.forEach(Device::doTick);
|
||||
if (waitCycles > 0) {
|
||||
waitCycles--;
|
||||
return;
|
||||
}
|
||||
waitCycles--;
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +124,6 @@ public abstract class Device implements Reconfigurable {
|
|||
}
|
||||
|
||||
public synchronized void setRun(boolean run) {
|
||||
// System.out.println(Thread.currentThread().getName() + (run ? " resuming " : " suspending ")+ getDeviceName());
|
||||
isPaused = false;
|
||||
this.run = run;
|
||||
}
|
||||
|
@ -126,19 +137,45 @@ public abstract class Device implements Reconfigurable {
|
|||
|
||||
public abstract void tick();
|
||||
|
||||
private Runnable buildSuspendedRunnable(Runnable r) {
|
||||
Runnable finalProcess = () -> {
|
||||
if (isRunning()) {
|
||||
suspend();
|
||||
r.run();
|
||||
resume();
|
||||
} else {
|
||||
r.run();
|
||||
}
|
||||
};
|
||||
for (Device child : getChildren()) {
|
||||
finalProcess = child.buildSuspendedRunnable(finalProcess);
|
||||
}
|
||||
return finalProcess;
|
||||
}
|
||||
|
||||
public void whileSuspended(Runnable r) {
|
||||
buildSuspendedRunnable(r).run();
|
||||
}
|
||||
|
||||
public boolean suspend() {
|
||||
children.forEach(Device::suspend);
|
||||
if (isRunning()) {
|
||||
// System.out.println(getName() + " Suspended");
|
||||
// Utility.printStackTrace();
|
||||
setRun(false);
|
||||
return true;
|
||||
}
|
||||
children.forEach(Device::suspend);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
setRun(true);
|
||||
waitCycles = 0;
|
||||
children.forEach(Device::resume);
|
||||
if (!isRunning()) {
|
||||
// System.out.println(getName() + " Resumed");
|
||||
// Utility.printStackTrace();
|
||||
setRun(true);
|
||||
waitCycles = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void attach() {
|
||||
|
|
|
@ -22,7 +22,11 @@ import jace.Emulator;
|
|||
import jace.apple2e.SoftSwitches;
|
||||
import jace.config.InvokableAction;
|
||||
import jace.config.Reconfigurable;
|
||||
import java.awt.Toolkit;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
|
@ -37,10 +41,6 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
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
|
||||
|
@ -96,15 +96,15 @@ public class Keyboard implements Reconfigurable {
|
|||
*/
|
||||
public Keyboard() {
|
||||
}
|
||||
private static Map<KeyCode, Set<KeyHandler>> keyHandlersByKey = new HashMap<>();
|
||||
private static Map<Object, Set<KeyHandler>> keyHandlersByOwner = new HashMap<>();
|
||||
private static final Map<KeyCode, Set<KeyHandler>> keyHandlersByKey = new HashMap<>();
|
||||
private static final Map<Object, Set<KeyHandler>> keyHandlersByOwner = new HashMap<>();
|
||||
|
||||
public static void registerInvokableAction(InvokableAction action, Object owner, Method method, String code) {
|
||||
boolean isStatic = Modifier.isStatic(method.getModifiers());
|
||||
registerKeyHandler(new KeyHandler(code) {
|
||||
@Override
|
||||
public boolean handleKeyUp(KeyEvent e) {
|
||||
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
|
||||
Emulator.getComputer().getKeyboard().shiftPressed = e.isShiftDown();
|
||||
if (action == null || !action.notifyOnRelease()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -128,7 +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();
|
||||
Emulator.getComputer().getKeyboard().shiftPressed = e.isShiftDown();
|
||||
Object returnValue = null;
|
||||
try {
|
||||
if (method.getParameterCount() > 0) {
|
||||
|
@ -163,9 +163,8 @@ public class Keyboard implements Reconfigurable {
|
|||
if (!keyHandlersByOwner.containsKey(owner)) {
|
||||
return;
|
||||
}
|
||||
keyHandlersByOwner.get(owner).stream().filter((handler) -> !(!keyHandlersByKey.containsKey(handler.key))).forEach((handler) -> {
|
||||
keyHandlersByKey.get(handler.key).remove(handler);
|
||||
});
|
||||
keyHandlersByOwner.get(owner).stream().filter((handler) -> keyHandlersByKey.containsKey(handler.key)).forEach(
|
||||
(handler) -> keyHandlersByKey.get(handler.key).remove(handler));
|
||||
keyHandlersByOwner.remove(owner);
|
||||
}
|
||||
|
||||
|
@ -252,7 +251,7 @@ public class Keyboard implements Reconfigurable {
|
|||
default:
|
||||
}
|
||||
|
||||
Emulator.computer.getKeyboard().shiftPressed = e.isShiftDown();
|
||||
Emulator.getComputer().getKeyboard().shiftPressed = e.isShiftDown();
|
||||
if (e.isShiftDown()) {
|
||||
c = fixShiftedChar(c);
|
||||
}
|
||||
|
@ -312,16 +311,20 @@ public class Keyboard implements Reconfigurable {
|
|||
|
||||
@InvokableAction(name = "Open Apple Key", alternatives = "OA", category = "Keyboard", notifyOnRelease = true, defaultKeyMapping = "Alt", consumeKeyEvent = false)
|
||||
public void openApple(boolean pressed) {
|
||||
computer.pause();
|
||||
boolean isRunning = computer.pause();
|
||||
SoftSwitches.PB0.getSwitch().setState(pressed);
|
||||
computer.resume();
|
||||
if (isRunning) {
|
||||
computer.resume();
|
||||
}
|
||||
}
|
||||
|
||||
@InvokableAction(name = "Closed Apple Key", alternatives = "CA", category = "Keyboard", notifyOnRelease = true, defaultKeyMapping = {"Shortcut","Meta","Command"}, consumeKeyEvent = false)
|
||||
public void solidApple(boolean pressed) {
|
||||
computer.pause();
|
||||
boolean isRunning = computer.pause();
|
||||
SoftSwitches.PB1.getSwitch().setState(pressed);
|
||||
computer.resume();
|
||||
if (isRunning) {
|
||||
computer.resume();
|
||||
}
|
||||
}
|
||||
|
||||
public static void pasteFromString(String text) {
|
||||
|
|
|
@ -21,9 +21,9 @@ package jace.core;
|
|||
import jace.apple2e.SoftSwitches;
|
||||
import jace.apple2e.Speaker;
|
||||
import jace.config.ConfigurableField;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -78,13 +78,12 @@ public class Motherboard extends TimedDevice {
|
|||
public String getShortName() {
|
||||
return "mb";
|
||||
}
|
||||
@ConfigurableField(category = "advanced", name = "CPU per clock", defaultValue = "1", description = "Number of CPU cycles per clock cycle (normal = 1)")
|
||||
public static int cpuPerClock = 1;
|
||||
@ConfigurableField(category = "advanced", shortName = "cpuPerClock", name = "CPU per clock", defaultValue = "1", description = "Number of CPU cycles per clock cycle (normal = 1)")
|
||||
public static int cpuPerClock = 4;
|
||||
public int clockCounter = 1;
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
Optional<Card>[] cards = computer.getMemory().getAllCards();
|
||||
try {
|
||||
clockCounter--;
|
||||
computer.getCpu().doTick();
|
||||
|
@ -93,10 +92,12 @@ public class Motherboard extends TimedDevice {
|
|||
}
|
||||
clockCounter = cpuPerClock;
|
||||
computer.getVideo().doTick();
|
||||
Optional<Card>[] cards = computer.getMemory().getAllCards();
|
||||
for (Optional<Card> card : cards) {
|
||||
card.ifPresent(Card::doTick);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
System.out.print("!");
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, t);
|
||||
}
|
||||
}
|
||||
|
@ -112,36 +113,30 @@ public class Motherboard extends TimedDevice {
|
|||
|
||||
@Override
|
||||
public synchronized void reconfigure() {
|
||||
boolean startAgain = pause();
|
||||
accelorationRequestors.clear();
|
||||
super.reconfigure();
|
||||
// Now create devices as needed, e.g. sound
|
||||
whileSuspended(() -> {
|
||||
accelorationRequestors.clear();
|
||||
super.reconfigure();
|
||||
|
||||
if (enableSpeaker) {
|
||||
try {
|
||||
if (speaker == null) {
|
||||
speaker = new Speaker(computer);
|
||||
if (computer.mixer.lineAvailable) {
|
||||
speaker.attach();
|
||||
addChildDevice(speaker);
|
||||
} else {
|
||||
System.out.print("No lines available! Speaker not running.");
|
||||
// Now create devices as needed, e.g. sound
|
||||
|
||||
if (enableSpeaker) {
|
||||
try {
|
||||
if (speaker == null) {
|
||||
speaker = new Speaker(computer);
|
||||
if (computer.mixer.lineAvailable) {
|
||||
speaker.attach();
|
||||
} else {
|
||||
System.out.print("No lines available! Speaker not running.");
|
||||
}
|
||||
}
|
||||
speaker.reconfigure();
|
||||
} catch (Throwable t) {
|
||||
System.out.println("Unable to initalize sound -- deactivating speaker out");
|
||||
}
|
||||
speaker.reconfigure();
|
||||
} catch (Throwable t) {
|
||||
System.out.println("Unable to initalize sound -- deactivating speaker out");
|
||||
removeChildDevice(speaker);
|
||||
} else {
|
||||
System.out.println("Speaker not enabled, leaving it off.");
|
||||
}
|
||||
} else {
|
||||
System.out.println("Speaker not enabled, leaving it off.");
|
||||
if (speaker != null) {
|
||||
removeChildDevice(speaker);
|
||||
}
|
||||
}
|
||||
if (startAgain && computer.getMemory() != null) {
|
||||
resume();
|
||||
}
|
||||
});
|
||||
}
|
||||
HashSet<Object> accelorationRequestors = new HashSet<>();
|
||||
|
||||
|
@ -156,45 +151,4 @@ public class Motherboard extends TimedDevice {
|
|||
disableTempMaxSpeed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attach() {
|
||||
}
|
||||
final Set<Card> resume = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public boolean suspend() {
|
||||
synchronized (resume) {
|
||||
resume.clear();
|
||||
for (Optional<Card> c : computer.getMemory().getAllCards()) {
|
||||
if (!c.isPresent()) {
|
||||
continue;
|
||||
}
|
||||
if (!c.get().suspendWithCPU() || !c.get().isRunning()) {
|
||||
continue;
|
||||
}
|
||||
if (c.get().suspend()) {
|
||||
resume.add(c.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.suspend();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
super.resume();
|
||||
synchronized (resume) {
|
||||
resume.stream().forEach((c) -> {
|
||||
c.resume();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
System.out.println("Detaching motherboard");
|
||||
// halt();
|
||||
super.detach();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,9 +41,9 @@ public class PagedMemory {
|
|||
FIRMWARE_80COL(0x0c300),
|
||||
SLOW_ROM(0x0c100),
|
||||
RAM(0x0000);
|
||||
protected int baseAddress;
|
||||
int baseAddress;
|
||||
|
||||
private Type(int newBase) {
|
||||
Type(int newBase) {
|
||||
baseAddress = newBase;
|
||||
}
|
||||
|
||||
|
@ -80,9 +80,7 @@ public class PagedMemory {
|
|||
public void loadData(byte[] romData) {
|
||||
for (int i = 0; i < romData.length; i += 256) {
|
||||
byte[] b = new byte[256];
|
||||
for (int j = 0; j < 256; j++) {
|
||||
b[j] = romData[i + j];
|
||||
}
|
||||
System.arraycopy(romData, i, b, 0, 256);
|
||||
internalMemory[i >> 8] = b;
|
||||
}
|
||||
}
|
||||
|
@ -137,10 +135,10 @@ public class PagedMemory {
|
|||
byte[][] sourceMemory = source.getMemory();
|
||||
int sourceBase = source.type.getBaseAddress() >> 8;
|
||||
int thisBase = type.getBaseAddress() >> 8;
|
||||
int start = sourceBase > thisBase ? sourceBase : thisBase;
|
||||
int start = Math.max(sourceBase, thisBase);
|
||||
int sourceEnd = sourceBase + source.getMemory().length;
|
||||
int thisEnd = thisBase + getMemory().length;
|
||||
int end = sourceEnd < thisEnd ? sourceEnd : thisEnd;
|
||||
int end = Math.min(sourceEnd, thisEnd);
|
||||
for (int i = start; i < end; i++) {
|
||||
set(i - thisBase, sourceMemory[i - sourceBase]);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ public abstract class RAM implements Reconfigurable {
|
|||
public RAM(Computer computer) {
|
||||
this.computer = computer;
|
||||
listeners = new HashSet<>();
|
||||
cards = new Optional[8];
|
||||
cards = (Optional<Card>[]) new Optional[8];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
cards[i] = Optional.empty();
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ public abstract class RAM implements Reconfigurable {
|
|||
|
||||
public Optional<Card> getCard(int slot) {
|
||||
if (slot >= 1 && slot <= 7) {
|
||||
return cards[slot].flatMap(card->card == null ? Optional.empty() : Optional.of(card));
|
||||
return cards[slot];
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
@ -152,8 +152,7 @@ public abstract class RAM implements Reconfigurable {
|
|||
public int readWord(int address, RAMEvent.TYPE eventType, boolean triggerEvent, boolean requireSynchronization) {
|
||||
int lsb = 0x00ff & read(address, eventType, triggerEvent, requireSynchronization);
|
||||
int msb = (0x00ff & read(address + 1, eventType, triggerEvent, requireSynchronization)) << 8;
|
||||
int value = msb + lsb;
|
||||
return value;
|
||||
return msb + lsb;
|
||||
}
|
||||
|
||||
private void mapListener(RAMListener l, int address) {
|
||||
|
@ -193,11 +192,9 @@ public abstract class RAM implements Reconfigurable {
|
|||
}
|
||||
|
||||
private void refreshListenerMap() {
|
||||
listenerMap = new Set[256];
|
||||
ioListenerMap = new Set[256];
|
||||
listeners.stream().forEach((l) -> {
|
||||
addListenerRange(l);
|
||||
});
|
||||
listenerMap = (Set<RAMListener>[]) new Set[256];
|
||||
ioListenerMap = (Set<RAMListener>[]) new Set[256];
|
||||
listeners.forEach(this::addListenerRange);
|
||||
}
|
||||
|
||||
public RAMListener observe(RAMEvent.TYPE type, int address, RAMEvent.RAMEventHandler handler) {
|
||||
|
@ -264,16 +261,11 @@ public abstract class RAM implements Reconfigurable {
|
|||
|
||||
private boolean isAuxFlagCorrect(RAMEvent e, Boolean auxFlag) {
|
||||
if (e.getAddress() < 0x0100) {
|
||||
if (SoftSwitches.AUXZP.getState() != auxFlag) {
|
||||
return false;
|
||||
}
|
||||
return SoftSwitches.AUXZP.getState() == auxFlag;
|
||||
} else if (e.getAddress() >= 0x0C000 && e.getAddress() <= 0x0CFFF) {
|
||||
// I/O page doesn't care about the aux flag
|
||||
return true;
|
||||
} else if (auxFlag != null && SoftSwitches.RAMRD.getState() != auxFlag) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else return auxFlag == null || SoftSwitches.RAMRD.getState() == auxFlag;
|
||||
}
|
||||
|
||||
public RAMListener addListener(final RAMListener l) {
|
||||
|
@ -300,13 +292,14 @@ public abstract class RAM implements Reconfigurable {
|
|||
|
||||
public byte callListener(RAMEvent.TYPE t, int address, int oldValue, int newValue, boolean requireSyncronization) {
|
||||
Set<RAMListener> activeListeners;
|
||||
boolean resume = false;
|
||||
if (requireSyncronization) {
|
||||
computer.getCpu().suspend();
|
||||
resume = computer.getCpu().suspend();
|
||||
}
|
||||
if ((address & 0x0FF00) == 0x0C000) {
|
||||
activeListeners = ioListenerMap[address & 0x0FF];
|
||||
if (activeListeners == null && t.isRead()) {
|
||||
if (requireSyncronization) {
|
||||
if (resume) {
|
||||
computer.getCpu().resume();
|
||||
}
|
||||
return computer.getVideo().getFloatingBus();
|
||||
|
@ -314,17 +307,15 @@ public abstract class RAM implements Reconfigurable {
|
|||
} else {
|
||||
activeListeners = listenerMap[(address >> 8) & 0x0ff];
|
||||
}
|
||||
if (activeListeners != null) {
|
||||
if (activeListeners != null && !activeListeners.isEmpty()) {
|
||||
RAMEvent e = new RAMEvent(t, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY, address, oldValue, newValue);
|
||||
activeListeners.stream().forEach((l) -> {
|
||||
l.handleEvent(e);
|
||||
});
|
||||
if (requireSyncronization) {
|
||||
activeListeners.forEach((l) -> l.handleEvent(e));
|
||||
if (resume) {
|
||||
computer.getCpu().resume();
|
||||
}
|
||||
return (byte) e.getNewValue();
|
||||
}
|
||||
if (requireSyncronization) {
|
||||
if (resume) {
|
||||
computer.getCpu().resume();
|
||||
}
|
||||
return (byte) newValue;
|
||||
|
|
|
@ -31,8 +31,8 @@ package jace.core;
|
|||
*/
|
||||
public class RAMEvent {
|
||||
|
||||
static public interface RAMEventHandler {
|
||||
public void handleEvent(RAMEvent e);
|
||||
public interface RAMEventHandler {
|
||||
void handleEvent(RAMEvent e);
|
||||
}
|
||||
|
||||
public enum TYPE {
|
||||
|
@ -43,7 +43,7 @@ public class RAMEvent {
|
|||
READ_OPERAND(true),
|
||||
WRITE(false),
|
||||
ANY(false);
|
||||
boolean read = false;
|
||||
boolean read;
|
||||
|
||||
TYPE(boolean r) {
|
||||
this.read = r;
|
||||
|
@ -52,14 +52,14 @@ public class RAMEvent {
|
|||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public enum SCOPE {
|
||||
|
||||
ADDRESS,
|
||||
RANGE,
|
||||
ANY
|
||||
};
|
||||
}
|
||||
|
||||
public enum VALUE {
|
||||
|
||||
|
@ -68,7 +68,8 @@ public class RAMEvent {
|
|||
EQUALS,
|
||||
NOT_EQUALS,
|
||||
CHANGE_BY
|
||||
};
|
||||
}
|
||||
|
||||
private TYPE type;
|
||||
private SCOPE scope;
|
||||
private VALUE value;
|
||||
|
|
|
@ -145,9 +145,7 @@ public abstract class RAMListener implements RAMEvent.RAMEventHandler {
|
|||
return false;
|
||||
} else if (value == RAMEvent.VALUE.NOT_EQUALS && e.getNewValue() == valueAmount) {
|
||||
return false;
|
||||
} else if (value == RAMEvent.VALUE.RANGE && (e.getNewValue() < valueStart || e.getNewValue() > valueEnd)) {
|
||||
return false;
|
||||
}
|
||||
} else return value != RAMEvent.VALUE.RANGE || (e.getNewValue() >= valueStart && e.getNewValue() <= valueEnd);
|
||||
}
|
||||
|
||||
// Ok, so we've filtered out the uninteresting stuff
|
||||
|
|
|
@ -40,12 +40,12 @@ public abstract class SoftSwitch {
|
|||
|
||||
@Stateful
|
||||
public Boolean state;
|
||||
private Boolean initalState;
|
||||
private List<RAMListener> listeners;
|
||||
private final Boolean initalState;
|
||||
private final List<RAMListener> listeners;
|
||||
private final List<Integer> exclusionActivate = new ArrayList<>();
|
||||
private final List<Integer> exclusionDeactivate = new ArrayList<>();
|
||||
private final List<Integer> exclusionQuery = new ArrayList<>();
|
||||
private String name;
|
||||
private final String name;
|
||||
private boolean toggleType = false;
|
||||
protected Computer computer;
|
||||
|
||||
|
@ -241,17 +241,13 @@ public abstract class SoftSwitch {
|
|||
|
||||
public void register(Computer computer) {
|
||||
this.computer = computer;
|
||||
RAM m = computer.getMemory();
|
||||
listeners.stream().forEach((l) -> {
|
||||
m.addListener(l);
|
||||
});
|
||||
listeners.forEach(computer.getMemory()::addListener);
|
||||
}
|
||||
|
||||
public void unregister() {
|
||||
RAM m = computer.getMemory();
|
||||
listeners.stream().forEach((l) -> {
|
||||
m.removeListener(l);
|
||||
});
|
||||
if (computer != null && computer.getMemory() != null) {
|
||||
listeners.forEach(computer.getMemory()::removeListener);
|
||||
}
|
||||
this.computer = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ package jace.core;
|
|||
|
||||
import jace.config.ConfigurableField;
|
||||
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -69,8 +71,6 @@ public abstract class TimedDevice extends Device {
|
|||
worker = null;
|
||||
return result;
|
||||
}
|
||||
Thread timerThread;
|
||||
|
||||
public boolean pause() {
|
||||
if (!isRunning()) {
|
||||
return false;
|
||||
|
@ -92,20 +92,18 @@ public abstract class TimedDevice extends Device {
|
|||
return;
|
||||
}
|
||||
worker = new Thread(() -> {
|
||||
System.out.println("Worker thread for " + getDeviceName() + " starting");
|
||||
while (isRunning()) {
|
||||
hasStopped = false;
|
||||
doTick();
|
||||
while (isPaused) {
|
||||
hasStopped = true;
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
return;
|
||||
}
|
||||
LockSupport.parkNanos(1000);
|
||||
}
|
||||
resync();
|
||||
}
|
||||
hasStopped = true;
|
||||
System.out.println("Worker thread for " + getDeviceName() + " stopped");
|
||||
});
|
||||
worker.setDaemon(false);
|
||||
worker.setPriority(Thread.MAX_PRIORITY);
|
||||
|
|
|
@ -20,23 +20,6 @@ package jace.core;
|
|||
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import javafx.application.Platform;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Alert;
|
||||
|
@ -50,6 +33,15 @@ import javafx.scene.image.ImageView;
|
|||
import javafx.scene.paint.Color;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This is a set of helper functions which do not belong anywhere else.
|
||||
* Functions vary from introspection, discovery, and string/pattern matching.
|
||||
|
@ -60,7 +52,7 @@ public class Utility {
|
|||
|
||||
static Reflections reflections = new Reflections("jace");
|
||||
|
||||
public static Set<Class> findAllSubclasses(Class clazz) {
|
||||
public static <T> Set<Class<? extends T>> findAllSubclasses(Class<T> clazz) {
|
||||
return reflections.getSubTypesOf(clazz);
|
||||
}
|
||||
|
||||
|
@ -148,8 +140,9 @@ public class Utility {
|
|||
return score * adjustment * adjustment;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String join(Collection<String> c, String d) {
|
||||
return c.stream().collect(Collectors.joining(d));
|
||||
return String.join(d, c);
|
||||
}
|
||||
|
||||
private static boolean isHeadless = false;
|
||||
|
@ -315,9 +308,16 @@ public class Utility {
|
|||
}
|
||||
|
||||
public static void printStackTrace() {
|
||||
System.out.println("CURRENT STACK TRACE:");
|
||||
System.out.println("START OF STACK TRACE:");
|
||||
int skip = 2;
|
||||
for (StackTraceElement s : Thread.currentThread().getStackTrace()) {
|
||||
System.out.println(s.getClassName() + "." + s.getMethodName() + " (line " + s.getLineNumber() + ") " + (s.isNativeMethod() ? "NATIVE" : ""));
|
||||
if (skip-- > 0) {
|
||||
continue;
|
||||
}
|
||||
if (s.getClassName().startsWith("com.sun.javafx.event")) {
|
||||
break;
|
||||
}
|
||||
System.out.println(" " + s.getClassName() + "." + s.getMethodName() + " (line " + s.getLineNumber() + ") " + (s.isNativeMethod() ? "NATIVE" : ""));
|
||||
}
|
||||
System.out.println("END OF STACK TRACE");
|
||||
}
|
||||
|
@ -372,7 +372,7 @@ public class Utility {
|
|||
for (Method m : object.getClass().getMethods()) {
|
||||
if (m.getName().equalsIgnoreCase("get" + fieldName) && m.getParameterTypes().length == 0) {
|
||||
try {
|
||||
return m.invoke(object, new Object[0]);
|
||||
return m.invoke(object);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex1) {
|
||||
}
|
||||
}
|
||||
|
@ -543,5 +543,5 @@ public class Utility {
|
|||
}
|
||||
}
|
||||
return nameMatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,20 +59,16 @@ public abstract class Video extends Device {
|
|||
static final public int APPLE_SCREEN_LINES = 192;
|
||||
static final public int HBLANK = CYCLES_PER_LINE - APPLE_CYCLES_PER_LINE;
|
||||
static final public int VBLANK = (TOTAL_LINES - APPLE_SCREEN_LINES) * CYCLES_PER_LINE;
|
||||
static public int[] textOffset;
|
||||
static public int[] hiresOffset;
|
||||
static public int[] textRowLookup;
|
||||
static public int[] hiresRowLookup;
|
||||
private boolean screenDirty;
|
||||
private boolean lineDirty;
|
||||
static final public int[] textOffset = new int[192];
|
||||
static final public int[] hiresOffset = new int[192];
|
||||
static final public int[] textRowLookup = new int[0x0400];
|
||||
static final public int[] hiresRowLookup = new int[0x02000];
|
||||
private boolean screenDirty = true;
|
||||
private boolean lineDirty = true;
|
||||
private boolean isVblank = false;
|
||||
static VideoWriter[][] writerCheck = new VideoWriter[40][192];
|
||||
static final VideoWriter[][] writerCheck = new VideoWriter[40][192];
|
||||
|
||||
static {
|
||||
textOffset = new int[192];
|
||||
hiresOffset = new int[192];
|
||||
textRowLookup = new int[0x0400];
|
||||
hiresRowLookup = new int[0x02000];
|
||||
static void initLookupTables() {
|
||||
for (int i = 0; i < 192; i++) {
|
||||
textOffset[i] = calculateTextOffset(i >> 3);
|
||||
hiresOffset[i] = calculateHiresOffset(i);
|
||||
|
@ -94,7 +90,7 @@ public abstract class Video extends Device {
|
|||
*/
|
||||
public Video(Computer computer) {
|
||||
super(computer);
|
||||
suspend();
|
||||
initLookupTables();
|
||||
video = new WritableImage(560, 192);
|
||||
visible = new WritableImage(560, 192);
|
||||
vPeriod = 0;
|
||||
|
@ -278,9 +274,9 @@ public abstract class Video extends Device {
|
|||
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();
|
||||
public static void forceRefresh() {
|
||||
if (Emulator.getComputer().video != null) {
|
||||
Emulator.getComputer().video._forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ public class CardMockingboard extends Card implements Runnable {
|
|||
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;
|
||||
private final int MAX_IDLE_SAMPLES = SAMPLE_RATE;
|
||||
|
||||
@Override
|
||||
public String getDeviceName() {
|
||||
|
@ -92,7 +92,7 @@ public class CardMockingboard extends Card implements Runnable {
|
|||
// has to be final to be used inside of anonymous class below
|
||||
final int j = i;
|
||||
controllers[i] = new R6522(computer) {
|
||||
int controller = j;
|
||||
final int controller = j;
|
||||
|
||||
@Override
|
||||
public void sendOutputA(int value) {
|
||||
|
|
|
@ -60,9 +60,9 @@ public class CardRamworks extends RAM128k {
|
|||
return memoryBank;
|
||||
}
|
||||
|
||||
public static enum BankType {
|
||||
public enum BankType {
|
||||
MAIN_MEMORY, LANGUAGE_CARD_1, LANGUAGE_CARD_2
|
||||
};
|
||||
}
|
||||
|
||||
public CardRamworks(Computer computer) {
|
||||
super(computer);
|
||||
|
@ -139,7 +139,7 @@ public class CardRamworks extends RAM128k {
|
|||
private RAMListener bankSelectListener;
|
||||
@Override
|
||||
public void attach() {
|
||||
bankSelectListener = computer.getMemory().observe(RAMEvent.TYPE.WRITE, BANK_SELECT, (e) -> {
|
||||
bankSelectListener = observe(RAMEvent.TYPE.WRITE, BANK_SELECT, (e) -> {
|
||||
currentBank = e.getNewValue();
|
||||
configureActiveMemory();
|
||||
});
|
||||
|
|
|
@ -99,7 +99,7 @@ public class FloppyDisk {
|
|||
NIBBLE_62_REVERSE[NIBBLE_62[i] & 0x0ff] = 0x0ff & i;
|
||||
}
|
||||
}
|
||||
private static boolean DEBUG = false;
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
public FloppyDisk() throws IOException {
|
||||
// This constructor is only used for disk conversion...
|
||||
|
@ -220,8 +220,7 @@ public class FloppyDisk {
|
|||
|
||||
private int decodeOddEven(byte b1, byte b2) {
|
||||
// return (((b1 ^ 0x0AA) << 1) & 0x0ff) | ((b2 ^ 0x0AA) & 0x0ff);
|
||||
int result = ((((b1 << 1) | 1) & b2) & 0x0ff);
|
||||
return result;
|
||||
return ((((b1 << 1) | 1) & b2) & 0x0ff);
|
||||
}
|
||||
|
||||
private void nibblizeBlock(ByteArrayOutputStream output, int track, int sector, byte[] nibbles) {
|
||||
|
|
|
@ -201,7 +201,6 @@ public class Joystick extends Device {
|
|||
return hogKeyboard;
|
||||
}
|
||||
|
||||
;
|
||||
@InvokableAction(name = "Right", category = "joystick", defaultKeyMapping = "right", notifyOnRelease = true)
|
||||
public boolean joystickRight(boolean pressed) {
|
||||
if (!useKeyboard) {
|
||||
|
@ -214,7 +213,6 @@ public class Joystick extends Device {
|
|||
return hogKeyboard;
|
||||
}
|
||||
|
||||
;
|
||||
@InvokableAction(name = "Up", category = "joystick", defaultKeyMapping = "up", notifyOnRelease = true)
|
||||
public boolean joystickUp(boolean pressed) {
|
||||
if (!useKeyboard) {
|
||||
|
@ -227,7 +225,6 @@ public class Joystick extends Device {
|
|||
return hogKeyboard;
|
||||
}
|
||||
|
||||
;
|
||||
@InvokableAction(name = "Down", category = "joystick", defaultKeyMapping = "down", notifyOnRelease = true)
|
||||
public boolean joystickDown(boolean pressed) {
|
||||
if (!useKeyboard) {
|
||||
|
|
|
@ -59,11 +59,11 @@ public class PassportMidiInterface extends Card {
|
|||
}
|
||||
|
||||
// MIDI timing: 31250 BPS, 8-N-1 (roughly 3472k per second)
|
||||
public static enum TIMER_MODE {
|
||||
public enum TIMER_MODE {
|
||||
|
||||
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
|
||||
|
|
|
@ -40,7 +40,7 @@ public abstract class ProdosDriver {
|
|||
public static int MLI_BUFFER_ADDRESS = 0x044;
|
||||
public static int MLI_BLOCK_NUMBER = 0x046;
|
||||
|
||||
public static enum MLI_RETURN {
|
||||
public enum MLI_RETURN {
|
||||
NO_ERROR(0), IO_ERROR(0x027), NO_DEVICE(0x028), WRITE_PROTECTED(0x02B);
|
||||
public int intValue;
|
||||
|
||||
|
@ -49,7 +49,7 @@ public abstract class ProdosDriver {
|
|||
}
|
||||
}
|
||||
|
||||
public static enum MLI_COMMAND_TYPE {
|
||||
public enum MLI_COMMAND_TYPE {
|
||||
STATUS(0x0), READ(0x01), WRITE(0x02), FORMAT(0x03);
|
||||
public int intValue;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public abstract class SmartportDriver {
|
|||
this.computer = computer;
|
||||
}
|
||||
|
||||
public static enum ERROR_CODE {
|
||||
public enum ERROR_CODE {
|
||||
NO_ERROR(0), INVALID_COMMAND(0x01), BAD_PARAM_COUNT(0x04), INVALID_UNIT(0x011), INVALID_CODE(0x021), BAD_BLOCK_NUMBER(0x02d);
|
||||
int intValue;
|
||||
ERROR_CODE(int c) {
|
||||
|
@ -50,7 +50,7 @@ public abstract class SmartportDriver {
|
|||
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) ? 0 : 1;
|
||||
}
|
||||
|
||||
private ERROR_CODE callSmartport() {
|
||||
|
|
|
@ -132,7 +132,7 @@ public class ZipWarpAccelerator extends Device {
|
|||
return "ZipChip Accelerator";
|
||||
}
|
||||
|
||||
public static enum SPEED {
|
||||
public enum SPEED {
|
||||
MAX(4.0, 0b000000000, 0b011111100),
|
||||
_2_667(2.6667, 0b000000100, 0b011111100),
|
||||
_3(3.0, 0b000001000, 0b011111000),
|
||||
|
@ -178,14 +178,14 @@ public class ZipWarpAccelerator extends Device {
|
|||
private void setSpeed(SPEED speed) {
|
||||
speedValue = speed.val;
|
||||
if (speed.max) {
|
||||
Emulator.computer.getMotherboard().setMaxSpeed(true);
|
||||
Emulator.getComputer().getMotherboard().setMaxSpeed(true);
|
||||
Motherboard.cpuPerClock = 3;
|
||||
} else {
|
||||
Emulator.computer.getMotherboard().setMaxSpeed(false);
|
||||
Emulator.computer.getMotherboard().setSpeedInPercentage((int) (speed.ratio * 100));
|
||||
Emulator.getComputer().getMotherboard().setMaxSpeed(false);
|
||||
Emulator.getComputer().getMotherboard().setSpeedInPercentage((int) (speed.ratio * 100));
|
||||
Motherboard.cpuPerClock = 1;
|
||||
}
|
||||
Emulator.computer.getMotherboard().reconfigure();
|
||||
Emulator.getComputer().getMotherboard().reconfigure();
|
||||
}
|
||||
|
||||
private void turnOffAcceleration() {
|
||||
|
|
|
@ -47,9 +47,9 @@ 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")
|
||||
@ConfigurableField(category = "Disk", shortName = "d1", name = "Drive 1 disk image", description = "Path of disk 1")
|
||||
public String disk1;
|
||||
@ConfigurableField(category = "Disk", defaultValue = "", shortName = "d2", name = "Drive 2 disk image", description = "Path of disk 2")
|
||||
@ConfigurableField(category = "Disk", shortName = "d2", name = "Drive 2 disk image", description = "Path of disk 2")
|
||||
public String disk2;
|
||||
MassStorageDrive drive1;
|
||||
MassStorageDrive drive2;
|
||||
|
@ -109,7 +109,7 @@ public class CardMassStorage extends Card implements MediaConsumerParent {
|
|||
|
||||
@Override
|
||||
public boolean isWriteProtected() {
|
||||
return getCurrentDisk() != null ? getCurrentDisk().isWriteProtected() : true;
|
||||
return getCurrentDisk() == null || getCurrentDisk().isWriteProtected();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -194,6 +194,8 @@ public class CardMassStorage extends Card implements MediaConsumerParent {
|
|||
try {
|
||||
if (drive1.getCurrentDisk() != null) {
|
||||
currentDrive = drive1;
|
||||
// Reset stack pointer on boot helps prevent random crashes!
|
||||
((MOS65C02) computer.getCpu()).STACK = 0x0ff;
|
||||
getCurrentDisk().boot0(getSlot(), computer);
|
||||
} else {
|
||||
// Patch for crash on start when no image is mounted
|
||||
|
|
|
@ -27,18 +27,18 @@ import java.io.IOException;
|
|||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public interface IDisk {
|
||||
public static int BLOCK_SIZE = 512;
|
||||
public static int MAX_BLOCK = 0x07fff;
|
||||
int BLOCK_SIZE = 512;
|
||||
int MAX_BLOCK = 0x07fff;
|
||||
|
||||
public void mliFormat() throws IOException;
|
||||
public void mliRead(int block, int bufferAddress, RAM memory) throws IOException;
|
||||
public void mliWrite(int block, int bufferAddress, RAM memory) throws IOException;
|
||||
public void boot0(int slot, Computer computer) throws IOException;
|
||||
void mliFormat() throws IOException;
|
||||
void mliRead(int block, int bufferAddress, RAM memory) throws IOException;
|
||||
void mliWrite(int block, int bufferAddress, RAM memory) throws IOException;
|
||||
void boot0(int slot, Computer computer) throws IOException;
|
||||
|
||||
// Return size in 512k blocks
|
||||
public int getSize();
|
||||
int getSize();
|
||||
|
||||
public void eject();
|
||||
public boolean isWriteProtected();
|
||||
void eject();
|
||||
boolean isWriteProtected();
|
||||
|
||||
}
|
||||
|
|
|
@ -21,16 +21,15 @@ package jace.hardware.massStorage;
|
|||
import jace.apple2e.MOS65C02;
|
||||
import jace.core.Computer;
|
||||
import jace.core.RAM;
|
||||
import static jace.hardware.ProdosDriver.*;
|
||||
import jace.hardware.ProdosDriver.MLI_COMMAND_TYPE;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static jace.hardware.ProdosDriver.MLI_COMMAND;
|
||||
import static jace.hardware.ProdosDriver.MLI_UNITNUMBER;
|
||||
|
||||
/**
|
||||
* Representation of a hard drive or 800k disk image used by CardMassStorage
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
|
@ -119,8 +118,6 @@ public class LargeDisk implements IDisk {
|
|||
logicalBlocks = physicalBlocks;
|
||||
result = true;
|
||||
}
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(LargeDisk.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(LargeDisk.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
|
@ -142,7 +139,7 @@ public class LargeDisk implements IDisk {
|
|||
logicalBlocks = physicalBlocks;
|
||||
}
|
||||
|
||||
private void readDiskImage(File f) throws FileNotFoundException, IOException {
|
||||
private void readDiskImage(File f) throws IOException {
|
||||
eject();
|
||||
setPhysicalPath(f);
|
||||
if (!read2mg(f)) {
|
||||
|
|
|
@ -111,7 +111,7 @@ public class AY8910_old {
|
|||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
private List<PSG> chips;
|
||||
private final List<PSG> chips;
|
||||
|
||||
static int[] VolTable;
|
||||
static {
|
||||
|
@ -558,8 +558,8 @@ buf3[index] = (volc * VolC) / STEP;
|
|||
length--;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public void writeReg(int chipNumber, int register, int value) {
|
||||
Reg r = Reg.get(register);
|
||||
writeReg(chipNumber, r, value);
|
||||
|
|
|
@ -277,4 +277,4 @@ public class PSG {
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public abstract class R6522 extends TimedDevice {
|
|||
// 6522 VIA
|
||||
// http://www.applevault.com/twiki/Main/Mockingboard/6522.pdf
|
||||
// I/O registers
|
||||
public static enum Register {
|
||||
public enum Register {
|
||||
ORB(0), // Output Register B
|
||||
ORA(1), // Output Register A
|
||||
DDRB(2),// Data direction reg B
|
||||
|
|
|
@ -73,4 +73,4 @@ public class SoundGenerator extends TimedGenerator {
|
|||
noiseActive = false;
|
||||
inverted = false;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -78,4 +78,4 @@ public class TimedGenerator {
|
|||
counter = 0;
|
||||
period = 1;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -9,15 +9,15 @@ import java.util.Map;
|
|||
*/
|
||||
public interface CompileResult<T> {
|
||||
|
||||
public boolean isSuccessful();
|
||||
boolean isSuccessful();
|
||||
|
||||
public T getCompiledAsset();
|
||||
T getCompiledAsset();
|
||||
|
||||
public Map<Integer, String> getErrors();
|
||||
Map<Integer, String> getErrors();
|
||||
|
||||
public Map<Integer, String> getWarnings();
|
||||
Map<Integer, String> getWarnings();
|
||||
|
||||
public List<String> getOtherMessages();
|
||||
List<String> getOtherMessages();
|
||||
|
||||
public List<String> getRawOutput();
|
||||
List<String> getRawOutput();
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ package jace.ide;
|
|||
|
||||
public interface LanguageHandler<T> {
|
||||
|
||||
public String getNewDocumentContent();
|
||||
String getNewDocumentContent();
|
||||
|
||||
public CompileResult<T> compile(Program program);
|
||||
CompileResult<T> compile(Program program);
|
||||
|
||||
public void execute(CompileResult<T> lastResult);
|
||||
void execute(CompileResult<T> lastResult);
|
||||
|
||||
public void clean(CompileResult<T> lastResult);
|
||||
void clean(CompileResult<T> lastResult);
|
||||
}
|
|
@ -2,18 +2,6 @@ package jace.ide;
|
|||
|
||||
import jace.applesoft.ApplesoftHandler;
|
||||
import jace.assembly.AssemblyHandler;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
@ -21,6 +9,11 @@ import javafx.scene.web.PromptData;
|
|||
import javafx.scene.web.WebView;
|
||||
import netscape.javascript.JSObject;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author blurry
|
||||
|
@ -29,7 +22,7 @@ public class Program {
|
|||
|
||||
public static String CODEMIRROR_EDITOR = "/codemirror/editor.html";
|
||||
|
||||
public static enum DocumentType {
|
||||
public enum DocumentType {
|
||||
|
||||
applesoft(new ApplesoftHandler(), "textfile", "*.bas"), assembly(new AssemblyHandler(), "textfile", "*.a", "*.s", "*.asm"), plain(new TextHandler(), "textfile", "*.txt"), hex(new TextHandler(), "textfile", "*.bin", "*.raw");
|
||||
|
||||
|
@ -55,7 +48,7 @@ public class Program {
|
|||
}
|
||||
}
|
||||
|
||||
public static enum Option {
|
||||
public enum Option {
|
||||
|
||||
mode, value, theme, indentUnit, smartIndent, tabSize, indentWithTabs, electricChars, specialChars,
|
||||
specialCharPlaceHolder, rtlMoveVisually, keyMap, extraKeys, lineWrapping,
|
||||
|
@ -206,9 +199,9 @@ public class Program {
|
|||
}
|
||||
builder.append(o.name()).append(":");
|
||||
if (v instanceof String) {
|
||||
builder.append('"').append(String.valueOf(v)).append('"');
|
||||
builder.append('"').append(v).append('"');
|
||||
} else {
|
||||
builder.append(String.valueOf(v));
|
||||
builder.append(v);
|
||||
}
|
||||
});
|
||||
builder.append("}");
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package jace.lawless;
|
||||
|
||||
import jace.LawlessLegends;
|
||||
import jace.apple2e.Apple2e;
|
||||
import jace.apple2e.RAM128k;
|
||||
import jace.apple2e.SoftSwitches;
|
||||
|
@ -32,12 +31,18 @@ public class LawlessComputer extends Apple2e {
|
|||
|
||||
public LawlessComputer() {
|
||||
super();
|
||||
motherboard.whileSuspended(this::initLawlessLegendsConfiguration);
|
||||
}
|
||||
|
||||
private void initLawlessLegendsConfiguration() {
|
||||
reconfigure(); // Required before anything so that memory is initialized
|
||||
this.cheatEngine.setValue(LawlessHacks.class);
|
||||
this.activeCheatEngine = new LawlessHacks(this);
|
||||
this.activeCheatEngine.attach();
|
||||
blankTextPage1();
|
||||
reconfigure();
|
||||
}
|
||||
|
||||
|
||||
private void blankTextPage1() {
|
||||
// Fill text page 1 with spaces
|
||||
for (int i = 0x0400; i < 0x07FF; i++) {
|
||||
|
@ -47,15 +52,15 @@ public class LawlessComputer extends Apple2e {
|
|||
|
||||
@Override
|
||||
public void coldStart() {
|
||||
pause();
|
||||
reinitMotherboard();
|
||||
RAM128k ram = (RAM128k) getMemory();
|
||||
ram.zeroAllRam();
|
||||
blankTextPage1();
|
||||
for (SoftSwitches s : SoftSwitches.values()) {
|
||||
s.getSwitch().reset();
|
||||
}
|
||||
if (showBootAnimation && LawlessLegends.PRODUCTION_MODE) {
|
||||
motherboard.whileSuspended(()->{
|
||||
RAM128k ram = (RAM128k) getMemory();
|
||||
ram.zeroAllRam();
|
||||
blankTextPage1();
|
||||
for (SoftSwitches s : SoftSwitches.values()) {
|
||||
s.getSwitch().reset();
|
||||
}
|
||||
});
|
||||
if (showBootAnimation && PRODUCTION_MODE) {
|
||||
(new Thread(this::startAnimation)).start();
|
||||
} else {
|
||||
finishColdStart();
|
||||
|
|
|
@ -88,7 +88,7 @@ public class LawlessImageTool implements MediaConsumer {
|
|||
}
|
||||
|
||||
private void insertHardDisk(int drive, MediaEntry entry, MediaFile file) {
|
||||
RAM memory = Emulator.computer.memory;
|
||||
RAM memory = Emulator.getComputer().memory;
|
||||
|
||||
memory.getCard(7).ifPresent(card -> {
|
||||
try {
|
||||
|
@ -106,7 +106,7 @@ public class LawlessImageTool implements MediaConsumer {
|
|||
}
|
||||
|
||||
private void readCurrentDisk(int drive) {
|
||||
RAM memory = Emulator.computer.memory;
|
||||
RAM memory = Emulator.getComputer().memory;
|
||||
|
||||
memory.getCard(7).ifPresent(card -> {
|
||||
gameMediaEntry = ((CardMassStorage) card).getConsumers()[drive].getMediaEntry();
|
||||
|
@ -115,7 +115,7 @@ public class LawlessImageTool implements MediaConsumer {
|
|||
}
|
||||
|
||||
private void ejectHardDisk(int drive) {
|
||||
RAM memory = Emulator.computer.memory;
|
||||
RAM memory = Emulator.getComputer().memory;
|
||||
|
||||
memory.getCard(7).ifPresent(card -> {
|
||||
((CardMassStorage) card).getConsumers()[drive].eject();
|
||||
|
@ -194,7 +194,7 @@ public class LawlessImageTool implements MediaConsumer {
|
|||
java.nio.file.Files.copy(f.path.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
f.path = target;
|
||||
insertHardDisk(0, e, f);
|
||||
Emulator.computer.coldStart();
|
||||
Emulator.getComputer().coldStart();
|
||||
System.out.println("Upgrade completed");
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(LawlessImageTool.class.getName()).log(Level.SEVERE, null, ex);
|
||||
|
@ -212,9 +212,9 @@ public class LawlessImageTool implements MediaConsumer {
|
|||
// Put in new disk and boot it -- we want to use its importer in case that importer works better!
|
||||
ejectHardDisk(0);
|
||||
insertHardDisk(0, e, f);
|
||||
Emulator.computer.coldStart();
|
||||
Emulator.getComputer().coldStart();
|
||||
if (!waitForText("I)mport", 1)) {
|
||||
Emulator.computer.coldStart();
|
||||
Emulator.getComputer().coldStart();
|
||||
if (!waitForText("I)mport", 1000)) {
|
||||
throw new Exception("Unable to detect upgrade prompt - Upgrade aborted.");
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ public class LawlessImageTool implements MediaConsumer {
|
|||
}
|
||||
|
||||
private boolean waitForText(String message, int timeout) throws InterruptedException {
|
||||
LawlessComputer compy = Emulator.computer;
|
||||
LawlessComputer compy = Emulator.getComputer();
|
||||
RAM128k mem = (RAM128k) compy.getMemory();
|
||||
while (timeout-- > 0) {
|
||||
StringBuilder allText = new StringBuilder();
|
||||
|
|
|
@ -5,9 +5,11 @@ import jace.apple2e.RAM128k;
|
|||
import jace.apple2e.VideoNTSC;
|
||||
import jace.core.Computer;
|
||||
import jace.core.PagedMemory;
|
||||
import java.util.Arrays;
|
||||
import jace.core.Video;
|
||||
import javafx.scene.image.WritableImage;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Lawless-enhanced video output for readable text
|
||||
*/
|
||||
|
@ -15,10 +17,10 @@ public class LawlessVideo extends VideoNTSC {
|
|||
|
||||
private static RenderEngine activeEngine = RenderEngine.UNKNOWN;
|
||||
private boolean titleScreen = true;
|
||||
private boolean[][] activeMask = new boolean[192][80];
|
||||
private final boolean[][] activeMask = new boolean[192][80];
|
||||
|
||||
|
||||
public static enum RenderEngine {
|
||||
public enum RenderEngine {
|
||||
FULL_COLOR,
|
||||
FULL_TEXT(new int[]{
|
||||
2, 6, 78, 186
|
||||
|
@ -72,7 +74,7 @@ public class LawlessVideo extends VideoNTSC {
|
|||
Arrays.fill(colorMask[y], true);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public LawlessVideo(Computer computer) {
|
||||
super(computer);
|
||||
|
@ -85,7 +87,7 @@ public class LawlessVideo extends VideoNTSC {
|
|||
for (int y=0; y < 192; y++) {
|
||||
System.arraycopy(activeEngine.colorMask[y], 0, activeMask[y], 0, 80);
|
||||
}
|
||||
Emulator.computer.onNextVBL(() -> Emulator.computer.getVideo().forceRefresh());
|
||||
Emulator.getComputer().onNextVBL(Video::forceRefresh);
|
||||
System.out.println("Detected engine: " + e.name());
|
||||
} else {
|
||||
System.out.println("Detected engine same as before: " + e.name());
|
||||
|
|
|
@ -28,11 +28,11 @@ import javafx.scene.control.Label;
|
|||
* @author brobert
|
||||
*/
|
||||
public interface MediaConsumer {
|
||||
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();
|
||||
public boolean isAccepted(MediaEntry e, MediaFile f);
|
||||
public void eject();
|
||||
Optional<Label> getIcon();
|
||||
void setIcon(Optional<Label> i);
|
||||
void insertMedia(MediaEntry e, MediaFile f) throws IOException;
|
||||
MediaEntry getMediaEntry();
|
||||
MediaFile getMediaFile();
|
||||
boolean isAccepted(MediaEntry e, MediaFile f);
|
||||
void eject();
|
||||
}
|
||||
|
|
|
@ -23,5 +23,5 @@ package jace.library;
|
|||
* @author brobert
|
||||
*/
|
||||
public interface MediaConsumerParent {
|
||||
public MediaConsumer[] getConsumers();
|
||||
MediaConsumer[] getConsumers();
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ public class TocTreeModel implements TreeModel {
|
|||
public Set<Long> getEntries(Object selection) {
|
||||
if (selection.equals(this)) return getEntries(tree);
|
||||
if (selection instanceof Set) return (Set<Long>) selection;
|
||||
if (Map.class.isInstance(selection)) {
|
||||
if (selection instanceof Map) {
|
||||
Set<Long> all = new LinkedHashSet<Long>();
|
||||
for (Object val : ((Map) selection).values()) {
|
||||
Set<Long> entries = getEntries(val);
|
||||
|
|
|
@ -49,10 +49,10 @@ public class ObjectGraphNode<T> implements Serializable {
|
|||
transient public Class<T> type;
|
||||
public boolean isPrimitive = false;
|
||||
|
||||
public static enum DirtyFlag {
|
||||
public enum DirtyFlag {
|
||||
|
||||
UNKNOWN, CLEAN, DIRTY
|
||||
};
|
||||
}
|
||||
|
||||
public ObjectGraphNode(Class<T> clazz) {
|
||||
children = new ArrayList<>();
|
||||
|
|
|
@ -26,21 +26,16 @@ import jace.config.Reconfigurable;
|
|||
import jace.core.Computer;
|
||||
import jace.core.PagedMemory;
|
||||
import jace.core.Video;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.WritableImage;
|
||||
|
||||
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;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.*;
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -210,7 +205,7 @@ public class StateManager implements Reconfigurable {
|
|||
/**
|
||||
* Track a stateful video framebuffer.
|
||||
*
|
||||
* @param objectGraphNode
|
||||
* @param node
|
||||
* @param f
|
||||
*/
|
||||
private void addVideoFrame(ObjectGraphNode<BufferedImage> node, Field f) {
|
||||
|
@ -220,7 +215,7 @@ public class StateManager implements Reconfigurable {
|
|||
/**
|
||||
* Track a stateful set of memory.
|
||||
*
|
||||
* @param objectGraphNode
|
||||
* @param node
|
||||
* @param f
|
||||
*/
|
||||
private void addMemoryPages(ObjectGraphNode<PagedMemory> node, Field f) {
|
||||
|
@ -371,9 +366,7 @@ public class StateManager implements Reconfigurable {
|
|||
// If there are no changes to this node value, don't waste memory on it.
|
||||
s.put(node, new StateValue(node));
|
||||
return node;
|
||||
}).forEach((node) -> {
|
||||
node.markClean();
|
||||
});
|
||||
}).forEach(ObjectGraphNode::markClean);
|
||||
return s;
|
||||
|
||||
}
|
||||
|
@ -414,7 +407,7 @@ public class StateManager implements Reconfigurable {
|
|||
defaultKeyMapping = {"ctrl+shift+Open Bracket"}
|
||||
)
|
||||
public static void beKindRewind() {
|
||||
StateManager manager = getInstance(Emulator.computer);
|
||||
StateManager manager = getInstance(Emulator.getComputer());
|
||||
new Thread(()->manager.rewind(60 / manager.captureFrequency)).start();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ package jace.tracker;
|
|||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public class Command {
|
||||
public static enum CommandScope {global, channel}
|
||||
public static enum CommandType {
|
||||
public enum CommandScope {global, channel}
|
||||
public enum CommandType {
|
||||
Rest(0, 0, 0, false),
|
||||
Ay1(1, 1, 17, false),
|
||||
Ay2(2, 1, 17, false),
|
||||
|
|
|
@ -19,24 +19,12 @@
|
|||
package jace.tracker;
|
||||
|
||||
import jace.core.Utility;
|
||||
import java.awt.CardLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -59,10 +47,10 @@ public class EditableLabel extends JPanel implements MouseListener, FocusListene
|
|||
labelComponent.setText(s);
|
||||
}
|
||||
|
||||
public static enum cards {
|
||||
public enum cards {
|
||||
|
||||
label, edit
|
||||
};
|
||||
}
|
||||
|
||||
public EditableLabel(JLabel label, Component edit, int width, Object owner, String property) {
|
||||
this(label, edit, width, DEFAULT_GAP, DEFAULT_GAP, owner, property);
|
||||
|
@ -235,14 +223,11 @@ public class EditableLabel extends JPanel implements MouseListener, FocusListene
|
|||
}
|
||||
|
||||
public void focus(final Component c) {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (c instanceof EditableLabel) {
|
||||
((EditableLabel) c).activateEdit();
|
||||
} else {
|
||||
c.requestFocusInWindow();
|
||||
}
|
||||
Runnable r = () -> {
|
||||
if (c instanceof EditableLabel) {
|
||||
((EditableLabel) c).activateEdit();
|
||||
} else {
|
||||
c.requestFocusInWindow();
|
||||
}
|
||||
};
|
||||
EventQueue.invokeLater(r);
|
||||
|
|
|
@ -31,7 +31,7 @@ import javax.swing.ImageIcon;
|
|||
*/
|
||||
public class Row {
|
||||
|
||||
public static enum Note {
|
||||
public enum Note {
|
||||
C0(3901),
|
||||
CS0(3682),
|
||||
D0(3476),
|
||||
|
@ -149,7 +149,7 @@ public class Row {
|
|||
ENVELOPE_ICONS[i++] = shape.getIcon();
|
||||
}
|
||||
}
|
||||
public static enum EnvelopeShape {
|
||||
public enum EnvelopeShape {
|
||||
unspecified(-1, ""),
|
||||
pulse(0, "|\\____"),
|
||||
pulseinv(4, "/|____"),
|
||||
|
@ -178,7 +178,7 @@ public class Row {
|
|||
}
|
||||
}
|
||||
|
||||
public static enum Channel {A1, B1, C1, A2, B2, C2}
|
||||
public enum Channel {A1, B1, C1, A2, B2, C2}
|
||||
|
||||
public static class ChannelData {
|
||||
public Note tone;
|
||||
|
@ -189,8 +189,7 @@ public class Row {
|
|||
public Set<Command> commands = new HashSet<Command>();
|
||||
public boolean isEmpty() {
|
||||
if (!commands.isEmpty()) return false;
|
||||
if (tone != null || volume != null || toneActive != null || noiseActive != null || envelopeActive != null) return false;
|
||||
return true;
|
||||
return tone == null && volume == null && toneActive == null && noiseActive == null && envelopeActive == null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import java.util.Map;
|
|||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public class Song {
|
||||
public static enum Format {
|
||||
public enum Format {
|
||||
woz0
|
||||
}
|
||||
Format format;
|
||||
|
@ -36,9 +36,9 @@ public class Song {
|
|||
String songName;
|
||||
String authorName;
|
||||
|
||||
Map<Integer, Pattern> patterns = new HashMap<Integer, Pattern>();
|
||||
Map<Integer, Pattern> macros = new HashMap<Integer, Pattern>();
|
||||
Map<Integer, Pattern> patterns = new HashMap<>();
|
||||
Map<Integer, Pattern> macros = new HashMap<>();
|
||||
int defaultPatternLength = 64;
|
||||
int defaultMacroLength = 16;
|
||||
List<Integer> order = new ArrayList<Integer>();
|
||||
List<Integer> order = new ArrayList<>();
|
||||
}
|
|
@ -43,7 +43,7 @@ public class UserInterface {
|
|||
|
||||
static Font EDITOR_FONT = new Font(Font.MONOSPACED, Font.PLAIN, 12);
|
||||
|
||||
public static enum Theme {
|
||||
public enum Theme {
|
||||
|
||||
background(0x000000),
|
||||
foreground(0xffffff),
|
||||
|
@ -57,7 +57,7 @@ public class UserInterface {
|
|||
}
|
||||
public static int BASE_OCTAVE = 3;
|
||||
|
||||
public static enum Note {
|
||||
public enum Note {
|
||||
|
||||
C0("C", -1),
|
||||
CS0("CS", -1),
|
||||
|
@ -95,7 +95,8 @@ public class UserInterface {
|
|||
note = n;
|
||||
octaveOffset = offset;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static final Map<Integer, Note> KEYBOARD_MAP = new HashMap<Integer, Note>();
|
||||
|
||||
static {
|
||||
|
|
|
@ -167,7 +167,7 @@ public class MetacheatUI {
|
|||
|
||||
@FXML
|
||||
void loadCheats(ActionEvent event) {
|
||||
boolean resume = Emulator.computer.pause();
|
||||
boolean resume = Emulator.getComputer().pause();
|
||||
FileChooser chooser = new FileChooser();
|
||||
chooser.setTitle("Load cheats");
|
||||
chooser.setInitialFileName("cheat.txt");
|
||||
|
@ -176,13 +176,13 @@ public class MetacheatUI {
|
|||
cheatEngine.loadCheats(saveFile);
|
||||
}
|
||||
if (resume) {
|
||||
Emulator.computer.resume();
|
||||
Emulator.getComputer().resume();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void saveCheats(ActionEvent event) {
|
||||
boolean resume = Emulator.computer.pause();
|
||||
boolean resume = Emulator.getComputer().pause();
|
||||
FileChooser chooser = new FileChooser();
|
||||
chooser.setTitle("Save current cheats");
|
||||
chooser.setInitialFileName("cheat.txt");
|
||||
|
@ -191,7 +191,7 @@ public class MetacheatUI {
|
|||
cheatEngine.saveCheats(saveFile);
|
||||
}
|
||||
if (resume) {
|
||||
Emulator.computer.resume();
|
||||
Emulator.getComputer().resume();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,10 +206,10 @@ public class MetacheatUI {
|
|||
@FXML
|
||||
void pauseClicked(ActionEvent event) {
|
||||
Application.invokeLater(() -> {
|
||||
if (Emulator.computer.isRunning()) {
|
||||
Emulator.computer.pause();
|
||||
if (Emulator.getComputer().isRunning()) {
|
||||
Emulator.getComputer().pause();
|
||||
} else {
|
||||
Emulator.computer.resume();
|
||||
Emulator.getComputer().resume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ public class MetacheatUI {
|
|||
|
||||
isRetina = Screen.getPrimary().getDpi() >= 110;
|
||||
|
||||
Emulator.computer.getRunningProperty().addListener((val, oldVal, newVal) -> {
|
||||
Emulator.getComputer().getRunningProperty().addListener((val, oldVal, newVal) -> {
|
||||
Platform.runLater(() -> pauseButton.setText(newVal ? "Pause" : "Resume"));
|
||||
});
|
||||
|
||||
|
@ -429,7 +429,7 @@ public class MetacheatUI {
|
|||
}
|
||||
|
||||
private void processMemoryViewUpdates() {
|
||||
if (!Emulator.computer.getRunningProperty().get()) {
|
||||
if (!Emulator.getComputer().getRunningProperty().get()) {
|
||||
return;
|
||||
}
|
||||
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
|
||||
|
@ -457,7 +457,7 @@ public class MetacheatUI {
|
|||
if (cheatEngine == null) {
|
||||
return;
|
||||
}
|
||||
boolean resume = Emulator.computer.pause();
|
||||
boolean resume = Emulator.getComputer().pause();
|
||||
|
||||
if (animationTimer == null) {
|
||||
animationTimer = new ScheduledThreadPoolExecutor(1);
|
||||
|
@ -498,7 +498,7 @@ public class MetacheatUI {
|
|||
setZoom(1/drawScale);
|
||||
|
||||
if (resume) {
|
||||
Emulator.computer.resume();
|
||||
Emulator.getComputer().resume();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ class Watch extends VBox {
|
|||
}
|
||||
|
||||
public void redraw() {
|
||||
if (!Emulator.computer.getRunningProperty().get()) {
|
||||
if (!Emulator.getComputer().getRunningProperty().get()) {
|
||||
return;
|
||||
}
|
||||
int val = cell.value.get() & 0x0ff;
|
||||
|
@ -117,7 +117,7 @@ class Watch extends VBox {
|
|||
outer.cheatEngine.removeListener(holdListener);
|
||||
holdListener = null;
|
||||
} else {
|
||||
value = Emulator.computer.memory.readRaw(address) & 0x0ff;
|
||||
value = Emulator.getComputer().memory.readRaw(address) & 0x0ff;
|
||||
holdListener = outer.cheatEngine.forceValue(value, address);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
return scriptHint(editor, javascriptKeywords,
|
||||
function (e, cur) {return e.getTokenAt(cur);},
|
||||
options);
|
||||
};
|
||||
}
|
||||
CodeMirror.registerHelper("hint", "javascript", javascriptHint);
|
||||
|
||||
function getCoffeeScriptToken(editor, cur) {
|
||||
|
|
|
@ -1185,7 +1185,7 @@
|
|||
// Used to work around IE issue with selection being forgotten when focus moves away from textarea
|
||||
this.hasSelection = false;
|
||||
this.composing = null;
|
||||
};
|
||||
}
|
||||
|
||||
function hiddenTextarea() {
|
||||
var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
|
||||
|
@ -3363,7 +3363,7 @@
|
|||
prevTouch = d.activeTouch;
|
||||
prevTouch.end = +new Date;
|
||||
}
|
||||
};
|
||||
}
|
||||
function isMouseLikeTouchEvent(e) {
|
||||
if (e.touches.length != 1) return false;
|
||||
var touch = e.touches[0];
|
||||
|
@ -6167,7 +6167,7 @@
|
|||
|
||||
function detachSharedMarkers(markers) {
|
||||
for (var i = 0; i < markers.length; i++) {
|
||||
var marker = markers[i], linked = [marker.primary.doc];;
|
||||
var marker = markers[i], linked = [marker.primary.doc];
|
||||
linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
|
||||
for (var j = 0; j < marker.markers.length; j++) {
|
||||
var subMarker = marker.markers[j];
|
||||
|
@ -8051,7 +8051,7 @@
|
|||
list = orphanDelayedCallbacks = [];
|
||||
setTimeout(fireOrphanDelayed, 0);
|
||||
}
|
||||
function bnd(f) {return function(){f.apply(null, args);};};
|
||||
function bnd(f) {return function(){f.apply(null, args);};}
|
||||
for (var i = 0; i < arr.length; ++i)
|
||||
list.push(bnd(arr[i]));
|
||||
}
|
||||
|
@ -8181,7 +8181,7 @@
|
|||
}
|
||||
if (props) copyObj(props, inst);
|
||||
return inst;
|
||||
};
|
||||
}
|
||||
|
||||
function copyObj(obj, target, overwrite) {
|
||||
if (!target) target = {};
|
||||
|
|
|
@ -16,21 +16,21 @@
|
|||
package jace.cpu;
|
||||
|
||||
import jace.Emulator;
|
||||
import jace.apple2e.Apple2e;
|
||||
import jace.apple2e.MOS65C02;
|
||||
import jace.apple2e.RAM128k;
|
||||
import jace.core.Computer;
|
||||
import jace.core.RAM;
|
||||
import jace.core.SoundMixer;
|
||||
import jace.core.Utility;
|
||||
import jace.ide.HeadlessProgram;
|
||||
import jace.ide.Program;
|
||||
import jace.lawless.LawlessComputer;
|
||||
import org.junit.AfterClass;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
/**
|
||||
* Basic test functionality to assert correct 6502 decode and execution.
|
||||
*
|
||||
|
@ -40,17 +40,15 @@ public class Basic6502FuncationalityTest {
|
|||
|
||||
static Computer computer;
|
||||
static MOS65C02 cpu;
|
||||
static RAM ram;
|
||||
static RAM128k ram;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupClass() {
|
||||
Utility.setHeadlessMode(true);
|
||||
SoundMixer.MUTE = true;
|
||||
computer = new LawlessComputer();
|
||||
computer = Emulator.getComputer();
|
||||
cpu = (MOS65C02) computer.getCpu();
|
||||
ram = computer.getMemory();
|
||||
Emulator.computer = (LawlessComputer) computer;
|
||||
computer.pause();
|
||||
ram = (RAM128k) computer.getMemory();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -59,18 +57,30 @@ public class Basic6502FuncationalityTest {
|
|||
|
||||
@Before
|
||||
public void setup() {
|
||||
cpu.suspend();
|
||||
computer.pause();
|
||||
cpu.clearState();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertMemoryConfiguredCorrectly() {
|
||||
assertEquals("Active read bank 3 should be main memory page 3",
|
||||
ram.mainMemory.getMemoryPage(3),
|
||||
ram.activeRead.getMemoryPage(3));
|
||||
|
||||
assertEquals("Active write bank 3 should be main memory page 3",
|
||||
ram.mainMemory.getMemoryPage(3),
|
||||
ram.activeWrite.getMemoryPage(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdditionNonDecimal() {
|
||||
cpu.A = 0;
|
||||
cpu.D = false;
|
||||
cpu.C = 0;
|
||||
assemble(" adc #1");
|
||||
runAssemblyCode("adc #1");
|
||||
assertEquals("0+1 (c=0) = 1", 1, cpu.A);
|
||||
assertFalse("Result is not zero", cpu.Z);
|
||||
assertEquals("Carry is clear", 0, cpu.C);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -78,9 +88,10 @@ public class Basic6502FuncationalityTest {
|
|||
cpu.A = 0;
|
||||
cpu.D = false;
|
||||
cpu.C = 1;
|
||||
assemble(" adc #1");
|
||||
runAssemblyCode("adc #1");
|
||||
assertEquals("0+1 (c=1) = 2", 2, cpu.A);
|
||||
assertFalse("Result is not zero", cpu.Z);
|
||||
assertEquals("Carry is clear", 0, cpu.C);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -88,9 +99,10 @@ public class Basic6502FuncationalityTest {
|
|||
cpu.A = 9;
|
||||
cpu.D = true;
|
||||
cpu.C = 0;
|
||||
assemble(" adc #1");
|
||||
runAssemblyCode("adc #1");
|
||||
assertEquals("9+1 (c=0) = 0x10", 0x10, cpu.A);
|
||||
assertFalse("Result is not zero", cpu.Z);
|
||||
assertEquals("Carry is clear", 0, cpu.C);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -98,19 +110,22 @@ public class Basic6502FuncationalityTest {
|
|||
cpu.A = 9;
|
||||
cpu.D = true;
|
||||
cpu.C = 1;
|
||||
assemble(" adc #1");
|
||||
runAssemblyCode("adc #1");
|
||||
assertEquals("9+1 (c=1) = 0x11", 0x11, cpu.A);
|
||||
assertFalse("Result is not zero", cpu.Z);
|
||||
assertEquals("Carry is clear", 0, cpu.C);
|
||||
}
|
||||
|
||||
private void assemble(String code) {
|
||||
assembleAt(code, 0x0300);
|
||||
private void runAssemblyCode(String code) {
|
||||
runAssemblyCode(code, 0x0300);
|
||||
}
|
||||
|
||||
private void assembleAt(String code, int addr) {
|
||||
private void runAssemblyCode(String code, int addr) {
|
||||
cpu.trace = true;
|
||||
HeadlessProgram program = new HeadlessProgram(Program.DocumentType.assembly);
|
||||
program.setValue("*="+Integer.toHexString(addr)+"\n"+code+"\n BRK");
|
||||
program.setValue("*=$"+Integer.toHexString(addr)+"\n "+code+"\n NOP\n RTS");
|
||||
program.execute();
|
||||
cpu.tick();
|
||||
cpu.tick();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ public class ApplesoftTest {
|
|||
ApplesoftProgram program = ApplesoftProgram.fromBinary(Arrays.asList(lemonadeStandBinary), 0x0801);
|
||||
assertNotNull(program);
|
||||
assertNotSame("", program.toString());
|
||||
assertEquals("Lemonade stand has 380 lines", 380, program.getLength());
|
||||
assertTrue("Should have last line 31114", program.toString().contains("31114 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -67,8 +69,7 @@ public class ApplesoftTest {
|
|||
ApplesoftProgram deserialized = ApplesoftProgram.fromString(serialized);
|
||||
String[] serializedLines = serialized.split("\\n");
|
||||
String[] researializedLines = deserialized.toString().split("\\n");
|
||||
for (int i=0; i < serializedLines.length; i++) {
|
||||
assertEquals("Line "+(i+1)+" should match", serializedLines[i], researializedLines[i]);
|
||||
}
|
||||
assertEquals("Lemonade stand has 380 lines", 380, deserialized.getLength());
|
||||
assertArrayEquals("Program listing should be not change if re-keyed in as printed", serializedLines, researializedLines);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user