mirror of
https://github.com/badvision/jace.git
synced 2025-01-04 10:31:34 +00:00
Issue #24: Nearly-hedless mode is confirmed and it is possible to JUnit test the actual emulator in headless mode! Right now sound doesn't respect headless mode though so it might cause issues on travis-ci.
This commit is contained in:
parent
921ce3a0b0
commit
d845a10de4
@ -47,7 +47,7 @@ public class JaceApplication extends Application {
|
||||
Scene s = new Scene(node);
|
||||
primaryStage.setScene(s);
|
||||
primaryStage.setTitle("Jace");
|
||||
primaryStage.getIcons().add(Utility.loadIcon("woz_figure.gif"));
|
||||
Utility.loadIcon("woz_figure.gif").ifPresent(primaryStage.getIcons()::add);
|
||||
} catch (IOException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
@ -86,7 +86,7 @@ public class JaceApplication extends Application {
|
||||
Scene s = new Scene(node);
|
||||
cheatStage.setScene(s);
|
||||
cheatStage.setTitle("Jace: MetaCheat");
|
||||
cheatStage.getIcons().add(Utility.loadIcon("woz_figure.gif"));
|
||||
Utility.loadIcon("woz_figure.gif").ifPresent(cheatStage.getIcons()::add);
|
||||
} catch (IOException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package jace;
|
||||
|
||||
import com.sun.glass.ui.Application;
|
||||
@ -57,7 +56,7 @@ public class JaceUIController {
|
||||
|
||||
@FXML
|
||||
private AnchorPane rootPane;
|
||||
|
||||
|
||||
@FXML
|
||||
private StackPane stackPane;
|
||||
|
||||
@ -68,7 +67,7 @@ public class JaceUIController {
|
||||
private ImageView appleScreen;
|
||||
|
||||
Computer computer;
|
||||
|
||||
|
||||
@FXML
|
||||
void initialize() {
|
||||
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'JaceUI.fxml'.";
|
||||
@ -80,18 +79,18 @@ public class JaceUIController {
|
||||
rootPane.setOnDragEntered(this::processDragEnteredEvent);
|
||||
rootPane.setOnDragExited(this::processDragExitedEvent);
|
||||
}
|
||||
|
||||
|
||||
public void connectComputer(Computer computer, Stage primaryStage) {
|
||||
this.computer = computer;
|
||||
appleScreen.setImage(computer.getVideo().getFrameBuffer());
|
||||
EventHandler<KeyEvent> keyboardHandler = computer.getKeyboard().getListener();
|
||||
primaryStage.setOnShowing(evt->computer.getKeyboard().resetState());
|
||||
primaryStage.setOnShowing(evt -> computer.getKeyboard().resetState());
|
||||
rootPane.setFocusTraversable(true);
|
||||
rootPane.setOnKeyPressed(keyboardHandler);
|
||||
rootPane.setOnKeyReleased(keyboardHandler);
|
||||
rootPane.requestFocus();
|
||||
}
|
||||
|
||||
|
||||
private void processDragEnteredEvent(DragEvent evt) {
|
||||
MediaEntry media = null;
|
||||
if (evt.getDragboard().hasFiles()) {
|
||||
@ -115,63 +114,68 @@ public class JaceUIController {
|
||||
startDragEvent(media);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processDragExitedEvent(DragEvent evt) {
|
||||
endDragEvent();
|
||||
}
|
||||
|
||||
|
||||
private File getDraggedFile(List<File> files) {
|
||||
if (files == null || files.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (File f : files) {
|
||||
if (f.exists()) return f;
|
||||
if (f.exists()) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
HBox drivePanel;
|
||||
|
||||
private void startDragEvent(MediaEntry media) {
|
||||
List<MediaConsumer> consumers = getMediaConsumers();
|
||||
drivePanel = new HBox();
|
||||
consumers.stream()
|
||||
.filter((consumer) -> (consumer.isAccepted(media, media.files.get(0))))
|
||||
.forEach((consumer) -> {
|
||||
Label icon = consumer.getIcon();
|
||||
icon.setTextFill(Color.WHITE);
|
||||
icon.setPadding(new Insets(2.0));
|
||||
drivePanel.getChildren().add(icon);
|
||||
icon.setOnDragOver(event -> {
|
||||
event.acceptTransferModes(TransferMode.ANY);
|
||||
event.consume();
|
||||
});
|
||||
icon.setOnDragDropped(event -> {
|
||||
System.out.println("Dropping media on "+icon.getText());
|
||||
try {
|
||||
computer.pause();
|
||||
consumer.insertMedia(media, media.files.get(0));
|
||||
computer.resume();
|
||||
event.setDropCompleted(true);
|
||||
event.consume();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(JaceUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
.filter((consumer) -> (consumer.isAccepted(media, media.files.get(0))))
|
||||
.forEach((consumer) -> {
|
||||
Label icon = consumer.getIcon().orElse(null);
|
||||
if (icon == null) {
|
||||
return;
|
||||
}
|
||||
endDragEvent();
|
||||
icon.setTextFill(Color.WHITE);
|
||||
icon.setPadding(new Insets(2.0));
|
||||
drivePanel.getChildren().add(icon);
|
||||
icon.setOnDragOver(event -> {
|
||||
event.acceptTransferModes(TransferMode.ANY);
|
||||
event.consume();
|
||||
});
|
||||
icon.setOnDragDropped(event -> {
|
||||
System.out.println("Dropping media on " + icon.getText());
|
||||
try {
|
||||
computer.pause();
|
||||
consumer.insertMedia(media, media.files.get(0));
|
||||
computer.resume();
|
||||
event.setDropCompleted(true);
|
||||
event.consume();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(JaceUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
endDragEvent();
|
||||
});
|
||||
});
|
||||
});
|
||||
stackPane.getChildren().add(drivePanel);
|
||||
stackPane.getChildren().add(drivePanel);
|
||||
drivePanel.setLayoutX(10);
|
||||
drivePanel.setLayoutY(10);
|
||||
}
|
||||
|
||||
|
||||
private void endDragEvent() {
|
||||
stackPane.getChildren().remove(drivePanel);
|
||||
drivePanel.getChildren().stream().forEach((n) -> {
|
||||
n.setOnDragDropped(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private List<MediaConsumer> getMediaConsumers() {
|
||||
List<MediaConsumer> consumers = new ArrayList<>();
|
||||
for (Optional<Card> card : computer.memory.getAllCards()) {
|
||||
@ -181,14 +185,16 @@ public class JaceUIController {
|
||||
}
|
||||
return consumers;
|
||||
}
|
||||
|
||||
|
||||
Map<Label, Long> iconTTL = new ConcurrentHashMap<>();
|
||||
|
||||
void addIndicator(Label icon) {
|
||||
addIndicator(icon, 250);
|
||||
}
|
||||
|
||||
void addIndicator(Label icon, long TTL) {
|
||||
if (!iconTTL.containsKey(icon)) {
|
||||
Application.invokeLater(()->{
|
||||
Application.invokeLater(() -> {
|
||||
if (!notificationBox.getChildren().contains(icon)) {
|
||||
notificationBox.getChildren().add(icon);
|
||||
}
|
||||
@ -198,39 +204,40 @@ public class JaceUIController {
|
||||
}
|
||||
|
||||
void removeIndicator(Label icon) {
|
||||
Application.invokeLater(()->{
|
||||
Application.invokeLater(() -> {
|
||||
notificationBox.getChildren().remove(icon);
|
||||
iconTTL.remove(icon);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ScheduledExecutorService notificationExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
ScheduledFuture ttlCleanupTask = null;
|
||||
|
||||
private void trackTTL(Label icon, long TTL) {
|
||||
iconTTL.put(icon, System.currentTimeMillis()+TTL);
|
||||
|
||||
if (ttlCleanupTask == null || ttlCleanupTask.isCancelled()) {
|
||||
iconTTL.put(icon, System.currentTimeMillis() + TTL);
|
||||
|
||||
if (ttlCleanupTask == null || ttlCleanupTask.isCancelled()) {
|
||||
ttlCleanupTask = notificationExecutor.scheduleWithFixedDelay(this::processTTL, 1, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processTTL() {
|
||||
Long now = System.currentTimeMillis();
|
||||
iconTTL.keySet().stream()
|
||||
.filter((icon) -> (iconTTL.get(icon) <= now))
|
||||
.forEach((icon) -> {
|
||||
removeIndicator(icon);
|
||||
});
|
||||
.filter((icon) -> (iconTTL.get(icon) <= now))
|
||||
.forEach((icon) -> {
|
||||
removeIndicator(icon);
|
||||
});
|
||||
if (iconTTL.isEmpty()) {
|
||||
ttlCleanupTask.cancel(true);
|
||||
ttlCleanupTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addMouseListener(EventHandler<MouseEvent> handler) {
|
||||
appleScreen.addEventHandler(MouseEvent.ANY, handler);
|
||||
}
|
||||
|
||||
|
||||
public void removeMouseListener(EventHandler<MouseEvent> handler) {
|
||||
appleScreen.removeEventHandler(MouseEvent.ANY, handler);
|
||||
}
|
||||
|
@ -77,12 +77,29 @@ public class MOS65C02 extends CPU {
|
||||
|
||||
public MOS65C02(Computer computer) {
|
||||
super(computer);
|
||||
clearState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconfigure() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearState() {
|
||||
A = 0x0ff;
|
||||
X = 0x0ff;
|
||||
Y = 0x0ff;
|
||||
C = 1;
|
||||
interruptSignalled = false;
|
||||
Z = true;
|
||||
I = true;
|
||||
D = true;
|
||||
B = true;
|
||||
V = true;
|
||||
N = true;
|
||||
STACK = 0xff;
|
||||
}
|
||||
|
||||
public enum OPCODE {
|
||||
ADC_IMM(0x0069, COMMAND.ADC, MODE.IMMEDIATE, 2),
|
||||
ADC_ZP(0x0065, COMMAND.ADC, MODE.ZEROPAGE, 3),
|
||||
@ -369,7 +386,7 @@ public class MOS65C02 extends CPU {
|
||||
int pc = cpu.getProgramCounter();
|
||||
int address = pc + 2 + cpu.getMemory().read(pc + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
|
||||
// The wait cycles are not added unless the branch actually happens!
|
||||
cpu.setPageBoundaryPenalty((address & 0x00ff00) != (pc & 0x00ff00));
|
||||
cpu.setPageBoundaryPenalty((address & 0x00ff00) != ((pc+2) & 0x00ff00));
|
||||
return address;
|
||||
}, false),
|
||||
IMMEDIATE(2, "#$~1", (cpu) -> cpu.getProgramCounter() + 1),
|
||||
|
@ -32,7 +32,11 @@ public class AssemblyHandler implements LanguageHandler<File> {
|
||||
public void execute(CompileResult<File> lastResult) {
|
||||
if (lastResult.isSuccessful()) {
|
||||
try {
|
||||
Emulator.computer.pause();
|
||||
boolean resume = false;
|
||||
if (Emulator.computer.isRunning()) {
|
||||
resume = true;
|
||||
Emulator.computer.pause();
|
||||
}
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
FileInputStream input = new FileInputStream(lastResult.getCompiledAsset());
|
||||
int startLSB = input.read();
|
||||
@ -43,7 +47,9 @@ public class AssemblyHandler implements LanguageHandler<File> {
|
||||
while ((next=input.read()) != -1) {
|
||||
memory.write(pos++, (byte) next, false, true);
|
||||
}
|
||||
Emulator.computer.resume();
|
||||
if (resume) {
|
||||
Emulator.computer.resume();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.TreeItem;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
|
||||
/**
|
||||
@ -93,8 +94,13 @@ public class Configuration implements Reconfigurable {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ImageView getChangedIcon() {
|
||||
return new ImageView(Utility.loadIcon("icon_exclaim.gif"));
|
||||
public static Optional<ImageView> getChangedIcon() {
|
||||
Optional<Image> icon = Utility.loadIcon("icon_exclaim.gif");
|
||||
if (icon.isPresent()) {
|
||||
return Optional.of(new ImageView(icon.get()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -251,7 +257,7 @@ public class Configuration implements Reconfigurable {
|
||||
if (!changed) {
|
||||
setGraphic(null);
|
||||
} else {
|
||||
setGraphic(getChangedIcon());
|
||||
getChangedIcon().ifPresent(this::setGraphic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -175,4 +175,6 @@ public abstract class CPU extends Device {
|
||||
lastTrace = trace;
|
||||
singleTraceEnabled = false;
|
||||
}
|
||||
|
||||
abstract public void clearState();
|
||||
}
|
@ -31,6 +31,7 @@ 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;
|
||||
@ -280,13 +281,24 @@ public class Utility {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Image loadIcon(String filename) {
|
||||
private static boolean isHeadless = false;
|
||||
public static void setHeadlessMode(boolean headless) {
|
||||
isHeadless = headless;
|
||||
}
|
||||
|
||||
public static Optional<Image> loadIcon(String filename) {
|
||||
if (isHeadless) {
|
||||
return Optional.empty();
|
||||
}
|
||||
InputStream stream = Utility.class.getClassLoader().getResourceAsStream("jace/data/" + filename);
|
||||
return new Image(stream);
|
||||
return Optional.of(new Image(stream));
|
||||
}
|
||||
|
||||
public static Label loadIconLabel(String filename) {
|
||||
Image img = loadIcon(filename);
|
||||
public static Optional<Label> loadIconLabel(String filename) {
|
||||
if (isHeadless) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Image img = loadIcon(filename).get();
|
||||
Label label = new Label() {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
@ -309,7 +321,7 @@ public class Utility {
|
||||
label.setTextFill(Color.WHITE);
|
||||
DropShadow shadow = new DropShadow(5.0, Color.BLACK);
|
||||
label.setEffect(shadow);
|
||||
return label;
|
||||
return Optional.of(label);
|
||||
}
|
||||
|
||||
// public static void runModalProcess(String title, final Runnable runnable) {
|
||||
|
@ -84,7 +84,7 @@ public class CardAppleMouse extends Card {
|
||||
public boolean fullscreenFix = true;
|
||||
@ConfigurableField(name = "Blazing Paddles fix", shortName = "bpfix", category = "Mouse", description = "Use different clamping values to make Blazing Paddles work more reliably.")
|
||||
public boolean blazingPaddles = false;
|
||||
Label mouseActive = Utility.loadIconLabel("input-mouse.png");
|
||||
Label mouseActive = Utility.loadIconLabel("input-mouse.png").orElse(null);
|
||||
public boolean movedSinceLastTick = false;
|
||||
public boolean movedSinceLastRead = false;
|
||||
|
||||
|
@ -104,13 +104,13 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
|
||||
case 0x8:
|
||||
// drive off
|
||||
currentDrive.setOn(false);
|
||||
EmulatorUILogic.removeIndicator(this, currentDrive.getIcon());
|
||||
currentDrive.getIcon().ifPresent(icon->EmulatorUILogic.removeIndicator(this, icon));
|
||||
break;
|
||||
|
||||
case 0x9:
|
||||
// drive on
|
||||
currentDrive.setOn(true);
|
||||
EmulatorUILogic.addIndicator(this, currentDrive.getIcon());
|
||||
currentDrive.getIcon().ifPresent(icon->EmulatorUILogic.addIndicator(this, icon));
|
||||
break;
|
||||
|
||||
case 0xA:
|
||||
@ -128,7 +128,7 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
|
||||
currentDrive.write();
|
||||
e.setNewValue(currentDrive.readLatch());
|
||||
if (currentDrive.isOn()) {
|
||||
EmulatorUILogic.addIndicator(this, currentDrive.getIcon());
|
||||
currentDrive.getIcon().ifPresent(icon->EmulatorUILogic.removeIndicator(this, icon));
|
||||
}
|
||||
break;
|
||||
case 0xF:
|
||||
@ -226,8 +226,8 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
|
||||
@Override
|
||||
public void setSlot(int slot) {
|
||||
super.setSlot(slot);
|
||||
drive1.getIcon().setText("S" + slot + "D1");
|
||||
drive2.getIcon().setText("S" + slot + "D2");
|
||||
drive1.getIcon().ifPresent(icon->icon.setText("S" + slot + "D1"));
|
||||
drive2.getIcon().ifPresent(icon->icon.setText("S" + slot + "D2"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -24,11 +24,12 @@ import jace.core.Card;
|
||||
import jace.core.Computer;
|
||||
import jace.core.RAMEvent;
|
||||
import jace.core.RAMEvent.TYPE;
|
||||
import jace.state.Stateful;
|
||||
import jace.core.Utility;
|
||||
import jace.state.Stateful;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.scene.control.Label;
|
||||
@ -62,10 +63,10 @@ public class CardRamFactor extends Card {
|
||||
public String getDeviceName() {
|
||||
return "RamFactor";
|
||||
}
|
||||
Label indicator;
|
||||
Optional<Label> indicator;
|
||||
public CardRamFactor(Computer computer) {
|
||||
super(computer);
|
||||
indicator=Utility.loadIconLabel("ram.png");
|
||||
indicator = Utility.loadIconLabel("ram.png");
|
||||
try {
|
||||
loadRom("jace/data/RAMFactor14.rom");
|
||||
} catch (IOException ex) {
|
||||
@ -204,7 +205,9 @@ public class CardRamFactor extends Card {
|
||||
@Override
|
||||
public void setSlot(int slot) {
|
||||
super.setSlot(slot);
|
||||
indicator.setText("Slot "+getSlot());
|
||||
indicator.ifPresent(icon->
|
||||
icon.setText("Slot "+getSlot())
|
||||
);
|
||||
// Rom has different images for each slot
|
||||
updateFirmwareMemory();
|
||||
}
|
||||
|
@ -116,8 +116,10 @@ public class CardSSC extends Card implements Reconfigurable {
|
||||
Logger.getLogger(CardSSC.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
super.setSlot(slot);
|
||||
activityIndicator = Utility.loadIconLabel("network-wired.png");
|
||||
activityIndicator.setText("Slot " + slot);
|
||||
Utility.loadIconLabel("network-wired.png").ifPresent(icon->{
|
||||
activityIndicator = icon;
|
||||
activityIndicator.setText("Slot " + slot);
|
||||
});
|
||||
}
|
||||
|
||||
boolean newInputAvailable = false;
|
||||
|
@ -32,6 +32,7 @@ import jace.core.Utility;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Calendar;
|
||||
import java.util.Optional;
|
||||
import java.util.Stack;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@ -50,8 +51,7 @@ import javafx.scene.control.Label;
|
||||
@Name("ThunderClock Plus")
|
||||
public class CardThunderclock extends Card {
|
||||
|
||||
Label clockIcon;
|
||||
Label clockFixIcon;
|
||||
Optional<Label> clockIcon;
|
||||
long lastShownIcon = -1;
|
||||
// Only mention that the clock is read if it hasn't been checked for over 30 seconds
|
||||
// This is to avoid showing it all the time in programs that poll it constantly
|
||||
@ -152,12 +152,14 @@ public class CardThunderclock extends Card {
|
||||
performProdosPatch(computer);
|
||||
}
|
||||
getTime();
|
||||
clockIcon.setText("Slot " + getSlot());
|
||||
long now = System.currentTimeMillis();
|
||||
if ((now - lastShownIcon) > MIN_WAIT) {
|
||||
EmulatorUILogic.addIndicator(this, clockIcon, 3000);
|
||||
}
|
||||
lastShownIcon = now;
|
||||
clockIcon.ifPresent(icon->{
|
||||
icon.setText("Slot " + getSlot());
|
||||
long now = System.currentTimeMillis();
|
||||
if ((now - lastShownIcon) > MIN_WAIT) {
|
||||
EmulatorUILogic.addIndicator(this, icon, 3000);
|
||||
}
|
||||
lastShownIcon = now;
|
||||
});
|
||||
}
|
||||
shiftMode = isShift;
|
||||
}
|
||||
@ -324,8 +326,9 @@ public class CardThunderclock extends Card {
|
||||
ram.writeByte(patchLoc + 1, (byte) year);
|
||||
ram.writeByte(patchLoc + 2, (byte) MOS65C02.OPCODE.NOP.getCode());
|
||||
ram.writeByte(patchLoc + 3, (byte) MOS65C02.OPCODE.NOP.getCode());
|
||||
Label clockFixIcon = Utility.loadIconLabel("clock_fix.png");
|
||||
clockFixIcon.setText("Fixed");
|
||||
EmulatorUILogic.addIndicator(CardThunderclock.class, clockFixIcon, 4000);
|
||||
Utility.loadIconLabel("clock_fix.png").ifPresent(clockFixIcon->{
|
||||
clockFixIcon.setText("Fixed");
|
||||
EmulatorUILogic.addIndicator(CardThunderclock.class, clockFixIcon, 4000);
|
||||
});
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ import jace.state.Stateful;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import javafx.scene.control.Label;
|
||||
@ -225,20 +226,19 @@ public class DiskIIDrive implements MediaConsumer {
|
||||
|
||||
void insertDisk(File diskPath) throws IOException {
|
||||
disk = new FloppyDisk(diskPath, computer);
|
||||
System.out.println("Inserting "+diskPath.getPath()+" into "+getIcon().getText());
|
||||
dirtyTracks = new HashSet<>();
|
||||
// Emulator state has changed significantly, reset state manager
|
||||
StateManager.getInstance(computer).invalidate();
|
||||
}
|
||||
private Label icon;
|
||||
private Optional<Label> icon;
|
||||
|
||||
@Override
|
||||
public Label getIcon() {
|
||||
public Optional<Label> getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(Label i) {
|
||||
public void setIcon(Optional<Label> i) {
|
||||
icon = i;
|
||||
}
|
||||
private MediaEntry currentMediaEntry;
|
||||
|
@ -9,6 +9,7 @@ import jace.core.RAMEvent;
|
||||
import jace.core.RAMListener;
|
||||
import jace.core.Utility;
|
||||
import java.util.Calendar;
|
||||
import java.util.Optional;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
/**
|
||||
@ -28,7 +29,7 @@ public class NoSlotClock extends Device {
|
||||
public boolean writeEnabled = false;
|
||||
@ConfigurableField(category = "Clock", name = "Patch Prodos", description = "If enabled, prodos clock routines will be patched directly", defaultValue = "false")
|
||||
public boolean patchProdosClock = false;
|
||||
Label clockIcon;
|
||||
Optional<Label> clockIcon;
|
||||
|
||||
private final RAMListener listener = new RAMListener(RAMEvent.TYPE.ANY, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
|
||||
@Override
|
||||
@ -89,7 +90,7 @@ public class NoSlotClock extends Device {
|
||||
public NoSlotClock(Computer computer) {
|
||||
super(computer);
|
||||
this.clockIcon = Utility.loadIconLabel("clock.png");
|
||||
this.clockIcon.setText("No Slot Clock");
|
||||
this.clockIcon.ifPresent(icon -> icon.setText("No Slot Clock"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -133,7 +134,8 @@ public class NoSlotClock extends Device {
|
||||
storeBCD(now.get(Calendar.MONTH) + 1, 6);
|
||||
storeBCD(now.get(Calendar.YEAR) % 100, 7);
|
||||
clockActive = true;
|
||||
EmulatorUILogic.addIndicator(this, clockIcon, 1000);
|
||||
clockIcon.ifPresent(icon
|
||||
-> EmulatorUILogic.addIndicator(this, icon, 1000));
|
||||
if (patchProdosClock) {
|
||||
CardThunderclock.performProdosPatch(computer);
|
||||
}
|
||||
|
@ -59,8 +59,8 @@ public class CardMassStorage extends Card implements MediaConsumerParent {
|
||||
@Override
|
||||
public void setSlot(int slot) {
|
||||
super.setSlot(slot);
|
||||
drive1.getIcon().setText("S" + getSlot() + "D1");
|
||||
drive2.getIcon().setText("S" + getSlot() + "D2");
|
||||
drive1.getIcon().ifPresent(icon->icon.setText("S" + getSlot() + "D1"));
|
||||
drive2.getIcon().ifPresent(icon->icon.setText("S" + getSlot() + "D2"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,6 +23,7 @@ import jace.library.MediaEntry;
|
||||
import jace.library.MediaEntry.MediaFile;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.scene.control.Label;
|
||||
@ -33,10 +34,10 @@ import javafx.scene.control.Label;
|
||||
*/
|
||||
public class MassStorageDrive implements MediaConsumer {
|
||||
IDisk disk = null;
|
||||
Label icon = null;
|
||||
Optional<Label> icon = null;
|
||||
|
||||
@Override
|
||||
public Label getIcon() {
|
||||
public Optional<Label> getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@ -45,7 +46,7 @@ public class MassStorageDrive implements MediaConsumer {
|
||||
* @param i
|
||||
*/
|
||||
@Override
|
||||
public void setIcon(Label i) {
|
||||
public void setIcon(Optional<Label> i) {
|
||||
icon = i;
|
||||
}
|
||||
|
||||
|
50
src/main/java/jace/ide/HeadlessProgram.java
Normal file
50
src/main/java/jace/ide/HeadlessProgram.java
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2016 Brendan Robert
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package jace.ide;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is a program that is intended to be defined and executed outside of a IDE session
|
||||
* @author blurry
|
||||
*/
|
||||
public class HeadlessProgram extends Program {
|
||||
public HeadlessProgram(DocumentType type) {
|
||||
super(type, Collections.EMPTY_MAP);
|
||||
}
|
||||
|
||||
String program;
|
||||
@Override
|
||||
public String getValue() {
|
||||
return program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
program = value;
|
||||
}
|
||||
|
||||
public HeadlessProgram() {
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
CompileResult lastResult = null;
|
||||
@Override
|
||||
protected void manageCompileResult(CompileResult lastResult) {
|
||||
this.lastResult = lastResult;
|
||||
}
|
||||
}
|
@ -231,7 +231,7 @@ public class Program {
|
||||
getHandler().clean(lastResult);
|
||||
}
|
||||
|
||||
private void manageCompileResult(CompileResult lastResult) {
|
||||
protected void manageCompileResult(CompileResult lastResult) {
|
||||
editor.getEngine().executeScript("clearHighlights()");
|
||||
lastResult.getWarnings().forEach((line, message)
|
||||
-> editor.getEngine().executeScript("highlightLine(" + line + ",false,\"" + escapeString(message) + "\");")
|
||||
|
@ -21,6 +21,7 @@ package jace.library;
|
||||
import jace.core.Utility;
|
||||
import jace.hardware.FloppyDisk;
|
||||
import java.io.File;
|
||||
import java.util.Optional;
|
||||
import javafx.scene.image.Image;
|
||||
|
||||
/**
|
||||
@ -40,7 +41,7 @@ public enum DiskType {
|
||||
public boolean isProdosOrdered = false;
|
||||
public boolean is140kb = false;
|
||||
public String description;
|
||||
public Image diskIcon;
|
||||
public Optional<Image> diskIcon;
|
||||
DiskType(String desc, boolean is140, boolean po, String iconPath) {
|
||||
description = desc;
|
||||
is140kb = is140;
|
||||
|
@ -20,6 +20,7 @@ package jace.library;
|
||||
|
||||
import jace.library.MediaEntry.MediaFile;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
/**
|
||||
@ -27,8 +28,8 @@ import javafx.scene.control.Label;
|
||||
* @author brobert
|
||||
*/
|
||||
public interface MediaConsumer {
|
||||
public Label getIcon();
|
||||
public void setIcon(Label i);
|
||||
public Optional<Label> getIcon();
|
||||
public void setIcon(Optional<Label> i);
|
||||
public void insertMedia(MediaEntry e, MediaFile f) throws IOException;
|
||||
public MediaEntry getMediaEntry();
|
||||
public MediaFile getMediaFile();
|
||||
|
90
src/test/java/jace/cpu/Basic6502FuncationalityTest.java
Normal file
90
src/test/java/jace/cpu/Basic6502FuncationalityTest.java
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2016 Brendan Robert
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package jace.cpu;
|
||||
|
||||
import jace.Emulator;
|
||||
import jace.apple2e.Apple2e;
|
||||
import jace.apple2e.MOS65C02;
|
||||
import jace.core.Computer;
|
||||
import jace.core.RAM;
|
||||
import jace.core.Utility;
|
||||
import jace.ide.HeadlessProgram;
|
||||
import jace.ide.Program;
|
||||
import java.util.Collections;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Basic test functionality to assert correct 6502 decode and execution.
|
||||
*
|
||||
* @author blurry
|
||||
*/
|
||||
public class Basic6502FuncationalityTest {
|
||||
|
||||
static Computer computer;
|
||||
static MOS65C02 cpu;
|
||||
static RAM ram;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupClass() {
|
||||
Utility.setHeadlessMode(true);
|
||||
computer = new Apple2e();
|
||||
cpu = (MOS65C02) computer.getCpu();
|
||||
ram = computer.getMemory();
|
||||
Emulator.computer = (Apple2e) computer;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void teardownClass() {
|
||||
computer.deactivate();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
ram.write(i, (byte) 0, false, false);
|
||||
}
|
||||
cpu.setProgramCounter(0);
|
||||
cpu.setWaitCycles(0);
|
||||
cpu.clearState();
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdditionNonDecimal() {
|
||||
cpu.A = 0;
|
||||
cpu.D = false;
|
||||
assemble(" adc #1");
|
||||
assertEquals("Nothing should change yet", 0, cpu.A);
|
||||
cpu.tick();
|
||||
assertEquals("0+1 = 1", 1, cpu.A);
|
||||
assertFalse("Result is not zero", cpu.Z);
|
||||
}
|
||||
|
||||
private void assemble(String code) {
|
||||
HeadlessProgram program = new HeadlessProgram(Program.DocumentType.assembly);
|
||||
program.setValue("*=0\n"+code+"\n BRK");
|
||||
program.execute();
|
||||
}
|
||||
}
|
@ -54,14 +54,14 @@ public class ApplesoftTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeBinary() {
|
||||
public void deserializeBinaryTest() {
|
||||
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
|
||||
assertNotNull(program);
|
||||
assertNotSame("", program.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripStringComparison() {
|
||||
public void roundTripStringComparisonTest() {
|
||||
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
|
||||
String serialized = program.toString();
|
||||
ApplesoftProgram deserialized = ApplesoftProgram.fromString(serialized);
|
||||
|
Loading…
Reference in New Issue
Block a user