Large code cleanup/refactoring, mostly addressing code fragility and emulator structure that results in poor startup behavior.

This commit is contained in:
Brendan Robert 2021-11-09 11:20:19 -06:00
parent f2c9e83eb7
commit 9f4952cabd
78 changed files with 969 additions and 1102 deletions

View File

@ -4,3 +4,4 @@
/.jace.conf
*.classpath
*.project
/src/main/resources/jace/data/sound/*.mp3

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -277,4 +277,4 @@ public class PSG {
}
}
}
};
}

View File

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

View File

@ -73,4 +73,4 @@ public class SoundGenerator extends TimedGenerator {
noiseActive = false;
inverted = false;
}
};
}

View File

@ -78,4 +78,4 @@ public class TimedGenerator {
counter = 0;
period = 1;
}
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,5 +23,5 @@ package jace.library;
* @author brobert
*/
public interface MediaConsumerParent {
public MediaConsumer[] getConsumers();
MediaConsumer[] getConsumers();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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