forked from Apple-2-Tools/jace
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);
|
Scene s = new Scene(node);
|
||||||
primaryStage.setScene(s);
|
primaryStage.setScene(s);
|
||||||
primaryStage.setTitle("Jace");
|
primaryStage.setTitle("Jace");
|
||||||
primaryStage.getIcons().add(Utility.loadIcon("woz_figure.gif"));
|
Utility.loadIcon("woz_figure.gif").ifPresent(primaryStage.getIcons()::add);
|
||||||
} catch (IOException exception) {
|
} catch (IOException exception) {
|
||||||
throw new RuntimeException(exception);
|
throw new RuntimeException(exception);
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ public class JaceApplication extends Application {
|
|||||||
Scene s = new Scene(node);
|
Scene s = new Scene(node);
|
||||||
cheatStage.setScene(s);
|
cheatStage.setScene(s);
|
||||||
cheatStage.setTitle("Jace: MetaCheat");
|
cheatStage.setTitle("Jace: MetaCheat");
|
||||||
cheatStage.getIcons().add(Utility.loadIcon("woz_figure.gif"));
|
Utility.loadIcon("woz_figure.gif").ifPresent(cheatStage.getIcons()::add);
|
||||||
} catch (IOException exception) {
|
} catch (IOException exception) {
|
||||||
throw new RuntimeException(exception);
|
throw new RuntimeException(exception);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
* To change this template file, choose Tools | Templates
|
* To change this template file, choose Tools | Templates
|
||||||
* and open the template in the editor.
|
* and open the template in the editor.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package jace;
|
package jace;
|
||||||
|
|
||||||
import com.sun.glass.ui.Application;
|
import com.sun.glass.ui.Application;
|
||||||
@ -120,25 +119,30 @@ public class JaceUIController {
|
|||||||
endDragEvent();
|
endDragEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private File getDraggedFile(List<File> files) {
|
private File getDraggedFile(List<File> files) {
|
||||||
if (files == null || files.isEmpty()) {
|
if (files == null || files.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (File f : files) {
|
for (File f : files) {
|
||||||
if (f.exists()) return f;
|
if (f.exists()) {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HBox drivePanel;
|
HBox drivePanel;
|
||||||
|
|
||||||
private void startDragEvent(MediaEntry media) {
|
private void startDragEvent(MediaEntry media) {
|
||||||
List<MediaConsumer> consumers = getMediaConsumers();
|
List<MediaConsumer> consumers = getMediaConsumers();
|
||||||
drivePanel = new HBox();
|
drivePanel = new HBox();
|
||||||
consumers.stream()
|
consumers.stream()
|
||||||
.filter((consumer) -> (consumer.isAccepted(media, media.files.get(0))))
|
.filter((consumer) -> (consumer.isAccepted(media, media.files.get(0))))
|
||||||
.forEach((consumer) -> {
|
.forEach((consumer) -> {
|
||||||
Label icon = consumer.getIcon();
|
Label icon = consumer.getIcon().orElse(null);
|
||||||
|
if (icon == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
icon.setTextFill(Color.WHITE);
|
icon.setTextFill(Color.WHITE);
|
||||||
icon.setPadding(new Insets(2.0));
|
icon.setPadding(new Insets(2.0));
|
||||||
drivePanel.getChildren().add(icon);
|
drivePanel.getChildren().add(icon);
|
||||||
@ -183,9 +187,11 @@ public class JaceUIController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<Label, Long> iconTTL = new ConcurrentHashMap<>();
|
Map<Label, Long> iconTTL = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
void addIndicator(Label icon) {
|
void addIndicator(Label icon) {
|
||||||
addIndicator(icon, 250);
|
addIndicator(icon, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addIndicator(Label icon, long TTL) {
|
void addIndicator(Label icon, long TTL) {
|
||||||
if (!iconTTL.containsKey(icon)) {
|
if (!iconTTL.containsKey(icon)) {
|
||||||
Application.invokeLater(() -> {
|
Application.invokeLater(() -> {
|
||||||
@ -206,6 +212,7 @@ public class JaceUIController {
|
|||||||
|
|
||||||
ScheduledExecutorService notificationExecutor = Executors.newSingleThreadScheduledExecutor();
|
ScheduledExecutorService notificationExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
ScheduledFuture ttlCleanupTask = null;
|
ScheduledFuture ttlCleanupTask = null;
|
||||||
|
|
||||||
private void trackTTL(Label icon, long TTL) {
|
private void trackTTL(Label icon, long TTL) {
|
||||||
iconTTL.put(icon, System.currentTimeMillis() + TTL);
|
iconTTL.put(icon, System.currentTimeMillis() + TTL);
|
||||||
|
|
||||||
|
@ -77,12 +77,29 @@ public class MOS65C02 extends CPU {
|
|||||||
|
|
||||||
public MOS65C02(Computer computer) {
|
public MOS65C02(Computer computer) {
|
||||||
super(computer);
|
super(computer);
|
||||||
|
clearState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reconfigure() {
|
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 {
|
public enum OPCODE {
|
||||||
ADC_IMM(0x0069, COMMAND.ADC, MODE.IMMEDIATE, 2),
|
ADC_IMM(0x0069, COMMAND.ADC, MODE.IMMEDIATE, 2),
|
||||||
ADC_ZP(0x0065, COMMAND.ADC, MODE.ZEROPAGE, 3),
|
ADC_ZP(0x0065, COMMAND.ADC, MODE.ZEROPAGE, 3),
|
||||||
@ -369,7 +386,7 @@ public class MOS65C02 extends CPU {
|
|||||||
int pc = cpu.getProgramCounter();
|
int pc = cpu.getProgramCounter();
|
||||||
int address = pc + 2 + cpu.getMemory().read(pc + 1, TYPE.READ_OPERAND, cpu.readAddressTriggersEvent, false);
|
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!
|
// 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;
|
return address;
|
||||||
}, false),
|
}, false),
|
||||||
IMMEDIATE(2, "#$~1", (cpu) -> cpu.getProgramCounter() + 1),
|
IMMEDIATE(2, "#$~1", (cpu) -> cpu.getProgramCounter() + 1),
|
||||||
|
@ -32,7 +32,11 @@ public class AssemblyHandler implements LanguageHandler<File> {
|
|||||||
public void execute(CompileResult<File> lastResult) {
|
public void execute(CompileResult<File> lastResult) {
|
||||||
if (lastResult.isSuccessful()) {
|
if (lastResult.isSuccessful()) {
|
||||||
try {
|
try {
|
||||||
|
boolean resume = false;
|
||||||
|
if (Emulator.computer.isRunning()) {
|
||||||
|
resume = true;
|
||||||
Emulator.computer.pause();
|
Emulator.computer.pause();
|
||||||
|
}
|
||||||
RAM memory = Emulator.computer.getMemory();
|
RAM memory = Emulator.computer.getMemory();
|
||||||
FileInputStream input = new FileInputStream(lastResult.getCompiledAsset());
|
FileInputStream input = new FileInputStream(lastResult.getCompiledAsset());
|
||||||
int startLSB = input.read();
|
int startLSB = input.read();
|
||||||
@ -43,7 +47,9 @@ public class AssemblyHandler implements LanguageHandler<File> {
|
|||||||
while ((next=input.read()) != -1) {
|
while ((next=input.read()) != -1) {
|
||||||
memory.write(pos++, (byte) next, false, true);
|
memory.write(pos++, (byte) next, false, true);
|
||||||
}
|
}
|
||||||
|
if (resume) {
|
||||||
Emulator.computer.resume();
|
Emulator.computer.resume();
|
||||||
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.getLogger(AssemblyHandler.class.getName()).log(Level.SEVERE, null, 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 java.util.logging.Logger;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.scene.control.TreeItem;
|
import javafx.scene.control.TreeItem;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,8 +94,13 @@ public class Configuration implements Reconfigurable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageView getChangedIcon() {
|
public static Optional<ImageView> getChangedIcon() {
|
||||||
return new ImageView(Utility.loadIcon("icon_exclaim.gif"));
|
Optional<Image> icon = Utility.loadIcon("icon_exclaim.gif");
|
||||||
|
if (icon.isPresent()) {
|
||||||
|
return Optional.of(new ImageView(icon.get()));
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -251,7 +257,7 @@ public class Configuration implements Reconfigurable {
|
|||||||
if (!changed) {
|
if (!changed) {
|
||||||
setGraphic(null);
|
setGraphic(null);
|
||||||
} else {
|
} else {
|
||||||
setGraphic(getChangedIcon());
|
getChangedIcon().ifPresent(this::setGraphic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,4 +175,6 @@ public abstract class CPU extends Device {
|
|||||||
lastTrace = trace;
|
lastTrace = trace;
|
||||||
singleTraceEnabled = false;
|
singleTraceEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract public void clearState();
|
||||||
}
|
}
|
@ -31,6 +31,7 @@ import java.util.Comparator;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -280,13 +281,24 @@ public class Utility {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Image loadIcon(String filename) {
|
private static boolean isHeadless = false;
|
||||||
InputStream stream = Utility.class.getClassLoader().getResourceAsStream("jace/data/" + filename);
|
public static void setHeadlessMode(boolean headless) {
|
||||||
return new Image(stream);
|
isHeadless = headless;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Label loadIconLabel(String filename) {
|
public static Optional<Image> loadIcon(String filename) {
|
||||||
Image img = loadIcon(filename);
|
if (isHeadless) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
InputStream stream = Utility.class.getClassLoader().getResourceAsStream("jace/data/" + filename);
|
||||||
|
return Optional.of(new Image(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<Label> loadIconLabel(String filename) {
|
||||||
|
if (isHeadless) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
Image img = loadIcon(filename).get();
|
||||||
Label label = new Label() {
|
Label label = new Label() {
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
@ -309,7 +321,7 @@ public class Utility {
|
|||||||
label.setTextFill(Color.WHITE);
|
label.setTextFill(Color.WHITE);
|
||||||
DropShadow shadow = new DropShadow(5.0, Color.BLACK);
|
DropShadow shadow = new DropShadow(5.0, Color.BLACK);
|
||||||
label.setEffect(shadow);
|
label.setEffect(shadow);
|
||||||
return label;
|
return Optional.of(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public static void runModalProcess(String title, final Runnable runnable) {
|
// public static void runModalProcess(String title, final Runnable runnable) {
|
||||||
|
@ -84,7 +84,7 @@ public class CardAppleMouse extends Card {
|
|||||||
public boolean fullscreenFix = true;
|
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.")
|
@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;
|
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 movedSinceLastTick = false;
|
||||||
public boolean movedSinceLastRead = false;
|
public boolean movedSinceLastRead = false;
|
||||||
|
|
||||||
|
@ -104,13 +104,13 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
|
|||||||
case 0x8:
|
case 0x8:
|
||||||
// drive off
|
// drive off
|
||||||
currentDrive.setOn(false);
|
currentDrive.setOn(false);
|
||||||
EmulatorUILogic.removeIndicator(this, currentDrive.getIcon());
|
currentDrive.getIcon().ifPresent(icon->EmulatorUILogic.removeIndicator(this, icon));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x9:
|
case 0x9:
|
||||||
// drive on
|
// drive on
|
||||||
currentDrive.setOn(true);
|
currentDrive.setOn(true);
|
||||||
EmulatorUILogic.addIndicator(this, currentDrive.getIcon());
|
currentDrive.getIcon().ifPresent(icon->EmulatorUILogic.addIndicator(this, icon));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xA:
|
case 0xA:
|
||||||
@ -128,7 +128,7 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
|
|||||||
currentDrive.write();
|
currentDrive.write();
|
||||||
e.setNewValue(currentDrive.readLatch());
|
e.setNewValue(currentDrive.readLatch());
|
||||||
if (currentDrive.isOn()) {
|
if (currentDrive.isOn()) {
|
||||||
EmulatorUILogic.addIndicator(this, currentDrive.getIcon());
|
currentDrive.getIcon().ifPresent(icon->EmulatorUILogic.removeIndicator(this, icon));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0xF:
|
case 0xF:
|
||||||
@ -226,8 +226,8 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar
|
|||||||
@Override
|
@Override
|
||||||
public void setSlot(int slot) {
|
public void setSlot(int slot) {
|
||||||
super.setSlot(slot);
|
super.setSlot(slot);
|
||||||
drive1.getIcon().setText("S" + slot + "D1");
|
drive1.getIcon().ifPresent(icon->icon.setText("S" + slot + "D1"));
|
||||||
drive2.getIcon().setText("S" + slot + "D2");
|
drive2.getIcon().ifPresent(icon->icon.setText("S" + slot + "D2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -24,11 +24,12 @@ import jace.core.Card;
|
|||||||
import jace.core.Computer;
|
import jace.core.Computer;
|
||||||
import jace.core.RAMEvent;
|
import jace.core.RAMEvent;
|
||||||
import jace.core.RAMEvent.TYPE;
|
import jace.core.RAMEvent.TYPE;
|
||||||
import jace.state.Stateful;
|
|
||||||
import jace.core.Utility;
|
import jace.core.Utility;
|
||||||
|
import jace.state.Stateful;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
@ -62,7 +63,7 @@ public class CardRamFactor extends Card {
|
|||||||
public String getDeviceName() {
|
public String getDeviceName() {
|
||||||
return "RamFactor";
|
return "RamFactor";
|
||||||
}
|
}
|
||||||
Label indicator;
|
Optional<Label> indicator;
|
||||||
public CardRamFactor(Computer computer) {
|
public CardRamFactor(Computer computer) {
|
||||||
super(computer);
|
super(computer);
|
||||||
indicator = Utility.loadIconLabel("ram.png");
|
indicator = Utility.loadIconLabel("ram.png");
|
||||||
@ -204,7 +205,9 @@ public class CardRamFactor extends Card {
|
|||||||
@Override
|
@Override
|
||||||
public void setSlot(int slot) {
|
public void setSlot(int slot) {
|
||||||
super.setSlot(slot);
|
super.setSlot(slot);
|
||||||
indicator.setText("Slot "+getSlot());
|
indicator.ifPresent(icon->
|
||||||
|
icon.setText("Slot "+getSlot())
|
||||||
|
);
|
||||||
// Rom has different images for each slot
|
// Rom has different images for each slot
|
||||||
updateFirmwareMemory();
|
updateFirmwareMemory();
|
||||||
}
|
}
|
||||||
|
@ -116,8 +116,10 @@ public class CardSSC extends Card implements Reconfigurable {
|
|||||||
Logger.getLogger(CardSSC.class.getName()).log(Level.SEVERE, null, ex);
|
Logger.getLogger(CardSSC.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
}
|
}
|
||||||
super.setSlot(slot);
|
super.setSlot(slot);
|
||||||
activityIndicator = Utility.loadIconLabel("network-wired.png");
|
Utility.loadIconLabel("network-wired.png").ifPresent(icon->{
|
||||||
|
activityIndicator = icon;
|
||||||
activityIndicator.setText("Slot " + slot);
|
activityIndicator.setText("Slot " + slot);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean newInputAvailable = false;
|
boolean newInputAvailable = false;
|
||||||
|
@ -32,6 +32,7 @@ import jace.core.Utility;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -50,8 +51,7 @@ import javafx.scene.control.Label;
|
|||||||
@Name("ThunderClock Plus")
|
@Name("ThunderClock Plus")
|
||||||
public class CardThunderclock extends Card {
|
public class CardThunderclock extends Card {
|
||||||
|
|
||||||
Label clockIcon;
|
Optional<Label> clockIcon;
|
||||||
Label clockFixIcon;
|
|
||||||
long lastShownIcon = -1;
|
long lastShownIcon = -1;
|
||||||
// Only mention that the clock is read if it hasn't been checked for over 30 seconds
|
// 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
|
// 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);
|
performProdosPatch(computer);
|
||||||
}
|
}
|
||||||
getTime();
|
getTime();
|
||||||
clockIcon.setText("Slot " + getSlot());
|
clockIcon.ifPresent(icon->{
|
||||||
|
icon.setText("Slot " + getSlot());
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
if ((now - lastShownIcon) > MIN_WAIT) {
|
if ((now - lastShownIcon) > MIN_WAIT) {
|
||||||
EmulatorUILogic.addIndicator(this, clockIcon, 3000);
|
EmulatorUILogic.addIndicator(this, icon, 3000);
|
||||||
}
|
}
|
||||||
lastShownIcon = now;
|
lastShownIcon = now;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
shiftMode = isShift;
|
shiftMode = isShift;
|
||||||
}
|
}
|
||||||
@ -324,8 +326,9 @@ public class CardThunderclock extends Card {
|
|||||||
ram.writeByte(patchLoc + 1, (byte) year);
|
ram.writeByte(patchLoc + 1, (byte) year);
|
||||||
ram.writeByte(patchLoc + 2, (byte) MOS65C02.OPCODE.NOP.getCode());
|
ram.writeByte(patchLoc + 2, (byte) MOS65C02.OPCODE.NOP.getCode());
|
||||||
ram.writeByte(patchLoc + 3, (byte) MOS65C02.OPCODE.NOP.getCode());
|
ram.writeByte(patchLoc + 3, (byte) MOS65C02.OPCODE.NOP.getCode());
|
||||||
Label clockFixIcon = Utility.loadIconLabel("clock_fix.png");
|
Utility.loadIconLabel("clock_fix.png").ifPresent(clockFixIcon->{
|
||||||
clockFixIcon.setText("Fixed");
|
clockFixIcon.setText("Fixed");
|
||||||
EmulatorUILogic.addIndicator(CardThunderclock.class, clockFixIcon, 4000);
|
EmulatorUILogic.addIndicator(CardThunderclock.class, clockFixIcon, 4000);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -27,6 +27,7 @@ import jace.state.Stateful;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.locks.LockSupport;
|
import java.util.concurrent.locks.LockSupport;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
@ -225,20 +226,19 @@ public class DiskIIDrive implements MediaConsumer {
|
|||||||
|
|
||||||
void insertDisk(File diskPath) throws IOException {
|
void insertDisk(File diskPath) throws IOException {
|
||||||
disk = new FloppyDisk(diskPath, computer);
|
disk = new FloppyDisk(diskPath, computer);
|
||||||
System.out.println("Inserting "+diskPath.getPath()+" into "+getIcon().getText());
|
|
||||||
dirtyTracks = new HashSet<>();
|
dirtyTracks = new HashSet<>();
|
||||||
// Emulator state has changed significantly, reset state manager
|
// Emulator state has changed significantly, reset state manager
|
||||||
StateManager.getInstance(computer).invalidate();
|
StateManager.getInstance(computer).invalidate();
|
||||||
}
|
}
|
||||||
private Label icon;
|
private Optional<Label> icon;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Label getIcon() {
|
public Optional<Label> getIcon() {
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setIcon(Label i) {
|
public void setIcon(Optional<Label> i) {
|
||||||
icon = i;
|
icon = i;
|
||||||
}
|
}
|
||||||
private MediaEntry currentMediaEntry;
|
private MediaEntry currentMediaEntry;
|
||||||
|
@ -9,6 +9,7 @@ import jace.core.RAMEvent;
|
|||||||
import jace.core.RAMListener;
|
import jace.core.RAMListener;
|
||||||
import jace.core.Utility;
|
import jace.core.Utility;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
import java.util.Optional;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,7 +29,7 @@ public class NoSlotClock extends Device {
|
|||||||
public boolean writeEnabled = false;
|
public boolean writeEnabled = false;
|
||||||
@ConfigurableField(category = "Clock", name = "Patch Prodos", description = "If enabled, prodos clock routines will be patched directly", defaultValue = "false")
|
@ConfigurableField(category = "Clock", name = "Patch Prodos", description = "If enabled, prodos clock routines will be patched directly", defaultValue = "false")
|
||||||
public boolean patchProdosClock = 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) {
|
private final RAMListener listener = new RAMListener(RAMEvent.TYPE.ANY, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
|
||||||
@Override
|
@Override
|
||||||
@ -89,7 +90,7 @@ public class NoSlotClock extends Device {
|
|||||||
public NoSlotClock(Computer computer) {
|
public NoSlotClock(Computer computer) {
|
||||||
super(computer);
|
super(computer);
|
||||||
this.clockIcon = Utility.loadIconLabel("clock.png");
|
this.clockIcon = Utility.loadIconLabel("clock.png");
|
||||||
this.clockIcon.setText("No Slot Clock");
|
this.clockIcon.ifPresent(icon -> icon.setText("No Slot Clock"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -133,7 +134,8 @@ public class NoSlotClock extends Device {
|
|||||||
storeBCD(now.get(Calendar.MONTH) + 1, 6);
|
storeBCD(now.get(Calendar.MONTH) + 1, 6);
|
||||||
storeBCD(now.get(Calendar.YEAR) % 100, 7);
|
storeBCD(now.get(Calendar.YEAR) % 100, 7);
|
||||||
clockActive = true;
|
clockActive = true;
|
||||||
EmulatorUILogic.addIndicator(this, clockIcon, 1000);
|
clockIcon.ifPresent(icon
|
||||||
|
-> EmulatorUILogic.addIndicator(this, icon, 1000));
|
||||||
if (patchProdosClock) {
|
if (patchProdosClock) {
|
||||||
CardThunderclock.performProdosPatch(computer);
|
CardThunderclock.performProdosPatch(computer);
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,8 @@ public class CardMassStorage extends Card implements MediaConsumerParent {
|
|||||||
@Override
|
@Override
|
||||||
public void setSlot(int slot) {
|
public void setSlot(int slot) {
|
||||||
super.setSlot(slot);
|
super.setSlot(slot);
|
||||||
drive1.getIcon().setText("S" + getSlot() + "D1");
|
drive1.getIcon().ifPresent(icon->icon.setText("S" + getSlot() + "D1"));
|
||||||
drive2.getIcon().setText("S" + getSlot() + "D2");
|
drive2.getIcon().ifPresent(icon->icon.setText("S" + getSlot() + "D2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,6 +23,7 @@ import jace.library.MediaEntry;
|
|||||||
import jace.library.MediaEntry.MediaFile;
|
import jace.library.MediaEntry.MediaFile;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
@ -33,10 +34,10 @@ import javafx.scene.control.Label;
|
|||||||
*/
|
*/
|
||||||
public class MassStorageDrive implements MediaConsumer {
|
public class MassStorageDrive implements MediaConsumer {
|
||||||
IDisk disk = null;
|
IDisk disk = null;
|
||||||
Label icon = null;
|
Optional<Label> icon = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Label getIcon() {
|
public Optional<Label> getIcon() {
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ public class MassStorageDrive implements MediaConsumer {
|
|||||||
* @param i
|
* @param i
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setIcon(Label i) {
|
public void setIcon(Optional<Label> i) {
|
||||||
icon = 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);
|
getHandler().clean(lastResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void manageCompileResult(CompileResult lastResult) {
|
protected void manageCompileResult(CompileResult lastResult) {
|
||||||
editor.getEngine().executeScript("clearHighlights()");
|
editor.getEngine().executeScript("clearHighlights()");
|
||||||
lastResult.getWarnings().forEach((line, message)
|
lastResult.getWarnings().forEach((line, message)
|
||||||
-> editor.getEngine().executeScript("highlightLine(" + line + ",false,\"" + escapeString(message) + "\");")
|
-> editor.getEngine().executeScript("highlightLine(" + line + ",false,\"" + escapeString(message) + "\");")
|
||||||
|
@ -21,6 +21,7 @@ package jace.library;
|
|||||||
import jace.core.Utility;
|
import jace.core.Utility;
|
||||||
import jace.hardware.FloppyDisk;
|
import jace.hardware.FloppyDisk;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Optional;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +41,7 @@ public enum DiskType {
|
|||||||
public boolean isProdosOrdered = false;
|
public boolean isProdosOrdered = false;
|
||||||
public boolean is140kb = false;
|
public boolean is140kb = false;
|
||||||
public String description;
|
public String description;
|
||||||
public Image diskIcon;
|
public Optional<Image> diskIcon;
|
||||||
DiskType(String desc, boolean is140, boolean po, String iconPath) {
|
DiskType(String desc, boolean is140, boolean po, String iconPath) {
|
||||||
description = desc;
|
description = desc;
|
||||||
is140kb = is140;
|
is140kb = is140;
|
||||||
|
@ -20,6 +20,7 @@ package jace.library;
|
|||||||
|
|
||||||
import jace.library.MediaEntry.MediaFile;
|
import jace.library.MediaEntry.MediaFile;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,8 +28,8 @@ import javafx.scene.control.Label;
|
|||||||
* @author brobert
|
* @author brobert
|
||||||
*/
|
*/
|
||||||
public interface MediaConsumer {
|
public interface MediaConsumer {
|
||||||
public Label getIcon();
|
public Optional<Label> getIcon();
|
||||||
public void setIcon(Label i);
|
public void setIcon(Optional<Label> i);
|
||||||
public void insertMedia(MediaEntry e, MediaFile f) throws IOException;
|
public void insertMedia(MediaEntry e, MediaFile f) throws IOException;
|
||||||
public MediaEntry getMediaEntry();
|
public MediaEntry getMediaEntry();
|
||||||
public MediaFile getMediaFile();
|
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
|
@Test
|
||||||
public void deserializeBinary() {
|
public void deserializeBinaryTest() {
|
||||||
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
|
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
|
||||||
assertNotNull(program);
|
assertNotNull(program);
|
||||||
assertNotSame("", program.toString());
|
assertNotSame("", program.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void roundTripStringComparison() {
|
public void roundTripStringComparisonTest() {
|
||||||
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
|
ApplesoftProgram program = ApplesoftProgram.fromBinary(Lists.newArrayList(lemonadeStandBinary), 0x0801);
|
||||||
String serialized = program.toString();
|
String serialized = program.toString();
|
||||||
ApplesoftProgram deserialized = ApplesoftProgram.fromString(serialized);
|
ApplesoftProgram deserialized = ApplesoftProgram.fromString(serialized);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user