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:
Brendan Robert 2016-02-14 11:13:03 -06:00
parent 921ce3a0b0
commit d845a10de4
22 changed files with 305 additions and 102 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -175,4 +175,6 @@ public abstract class CPU extends Device {
lastTrace = trace;
singleTraceEnabled = false;
}
abstract public void clearState();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@ -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) + "\");")

View File

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

View File

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

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

View File

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