Added better standalone testing for command line arguments. Got video mode to be a first-class startup parameter

This commit is contained in:
Brendan Robert 2019-06-19 00:47:04 -05:00
parent 553d439ff8
commit cc0cead894
7 changed files with 150 additions and 41 deletions

View File

@ -18,15 +18,15 @@
*/ */
package jace; package jace;
import jace.config.Configuration;
import jace.apple2e.Apple2e; import jace.apple2e.Apple2e;
import jace.config.Configuration;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Created on January 15, 2007, 10:10 PM * Created on January 15, 2007, 10:10 PM
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com * @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/ */
public class Emulator { public class Emulator {
@ -51,6 +51,10 @@ public class Emulator {
Configuration.buildTree(); Configuration.buildTree();
Configuration.loadSettings(); Configuration.loadSettings();
mainThread = Thread.currentThread(); mainThread = Thread.currentThread();
applyConfiguration(args);
}
public void applyConfiguration(List<String> args) {
Map<String, String> settings = new LinkedHashMap<>(); Map<String, String> settings = new LinkedHashMap<>();
if (args != null) { if (args != null) {
for (int i = 0; i < args.size(); i++) { for (int i = 0; i < args.size(); i++) {

View File

@ -30,7 +30,6 @@ import jace.core.Computer;
import jace.core.Debugger; import jace.core.Debugger;
import jace.core.RAM; import jace.core.RAM;
import jace.core.RAMListener; import jace.core.RAMListener;
import static jace.core.Utility.*;
import jace.ide.IdeController; import jace.ide.IdeController;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -59,6 +58,8 @@ import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import javafx.stage.Stage; import javafx.stage.Stage;
import static jace.core.Utility.*;
/** /**
* This class contains miscellaneous user-invoked actions such as debugger * This class contains miscellaneous user-invoked actions such as debugger
* operations and running arbitrary files in the emulator. It is possible for * operations and running arbitrary files in the emulator. It is possible for
@ -81,7 +82,7 @@ public class EmulatorUILogic implements Reconfigurable {
} }
}; };
} }
@ConfigurableField( @ConfigurableField(
category = "General", category = "General",
name = "Speed Setting" name = "Speed Setting"
@ -450,7 +451,7 @@ public class EmulatorUILogic implements Reconfigurable {
} }
}); });
} }
@InvokableAction( @InvokableAction(
name = "About", name = "About",
category = "general", category = "general",
@ -459,7 +460,7 @@ public class EmulatorUILogic implements Reconfigurable {
defaultKeyMapping = {"ctrl+shift+."}) defaultKeyMapping = {"ctrl+shift+."})
public static void showAboutWindow() { public static void showAboutWindow() {
//TODO: Implement //TODO: Implement
} }
public static boolean confirm(String message) { public static boolean confirm(String message) {
// return JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Emulator.getFrame(), message); // return JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Emulator.getFrame(), message);
@ -560,6 +561,8 @@ public class EmulatorUILogic implements Reconfigurable {
@Override @Override
public void reconfigure() { public void reconfigure() {
JaceApplication.getApplication().controller.setSpeed(speedSetting); if (JaceApplication.getApplication() != null) {
JaceApplication.getApplication().controller.setSpeed(speedSetting);
}
} }
} }

View File

@ -20,7 +20,6 @@ package jace.apple2e;
import jace.Emulator; import jace.Emulator;
import jace.EmulatorUILogic; import jace.EmulatorUILogic;
import static jace.apple2e.VideoDHGR.BLACK;
import jace.config.ConfigurableField; import jace.config.ConfigurableField;
import jace.config.InvokableAction; import jace.config.InvokableAction;
import jace.core.Computer; import jace.core.Computer;
@ -34,6 +33,8 @@ import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage; import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import static jace.apple2e.VideoDHGR.BLACK;
/** /**
* Provides a clean color monitor simulation, complete with text-friendly * Provides a clean color monitor simulation, complete with text-friendly
* palette and mixed color/bw (mode 7) rendering. This class extends the * palette and mixed color/bw (mode 7) rendering. This class extends the
@ -59,6 +60,9 @@ public class VideoNTSC extends VideoDHGR {
int[] scanline = new int[20]; int[] scanline = new int[20];
static public int[] divBy28 = new int[560]; static public int[] divBy28 = new int[560];
@ConfigurableField(name = "Video Mode", category = "video", shortName = "mode", defaultValue = "TextFriendly", description = "Set Video Mode (Color|TextFriendly|Mode7|Mode7TextFriendly|Monochrome|Greenscreen|Amber)")
public static VideoMode videoMode = VideoMode.TextFriendly;
static { static {
for (int i = 0; i < 560; i++) { for (int i = 0; i < 560; i++) {
divBy28[i] = i / 28; divBy28[i] = i / 28;
@ -81,58 +85,68 @@ public class VideoNTSC extends VideoDHGR {
Greenscreen("Green"), Greenscreen("Green"),
Amber("Amber"); Amber("Amber");
String name; String name;
VideoMode(String n) { VideoMode(String n) {
name = n; name = n;
} }
} }
static int currentMode = -1;
@InvokableAction(name = "Toggle video mode", @InvokableAction(name = "Toggle video mode",
category = "video", category = "video",
alternatives = "Gfx mode;color;b&w;monochrome", alternatives = "Gfx mode;color;b&w;monochrome",
defaultKeyMapping = {"ctrl+shift+g"}) defaultKeyMapping = {"ctrl+shift+g"})
public static void changeVideoMode() { public static void changeVideoMode() {
VideoNTSC thiss = (VideoNTSC) Emulator.computer.video; VideoNTSC thiss = (VideoNTSC) Emulator.computer.video;
int currentMode = Arrays.asList(VideoMode.values()).indexOf(thiss.getVideoMode());
currentMode++; currentMode++;
if (currentMode >= VideoMode.values().length) { if (currentMode >= VideoMode.values().length) {
currentMode = 0; currentMode = 0;
} }
thiss.monochomeMode = false; thiss.setVideoMode(VideoMode.values()[currentMode]);
}
public void setVideoMode(VideoMode mode) {
videoMode = mode;
monochomeMode = false;
WHITE = Color.WHITE; WHITE = Color.WHITE;
switch (VideoMode.values()[currentMode]) { switch (mode) {
case Amber: case Amber:
thiss.monochomeMode = true; monochomeMode = true;
WHITE = Color.web("ff8000"); WHITE = Color.web("ff8000");
break; break;
case Greenscreen: case Greenscreen:
thiss.monochomeMode = true; monochomeMode = true;
WHITE = Color.web("0ccc68"); WHITE = Color.web("0ccc68");
break; break;
case Monochrome: case Monochrome:
thiss.monochomeMode = true; monochomeMode = true;
break; break;
case Color: case Color:
thiss.useTextPalette = false; useTextPalette = false;
thiss.enableVideo7 = false; enableVideo7 = false;
break; break;
case Mode7: case Mode7:
thiss.useTextPalette = false; useTextPalette = false;
thiss.enableVideo7 = true; enableVideo7 = true;
break; break;
case Mode7TextFriendly: case Mode7TextFriendly:
thiss.useTextPalette = true; useTextPalette = true;
thiss.enableVideo7 = true; enableVideo7 = true;
break; break;
case TextFriendly: case TextFriendly:
thiss.useTextPalette = true; useTextPalette = true;
thiss.enableVideo7 = false; enableVideo7 = false;
break; break;
} }
thiss.activePalette = thiss.useTextPalette ? TEXT_PALETTE : SOLID_PALETTE; activePalette = useTextPalette ? TEXT_PALETTE : SOLID_PALETTE;
EmulatorUILogic.notify("Video mode: "+VideoMode.values()[currentMode].name); EmulatorUILogic.notify("Video mode: " + mode.name);
forceRefresh(); forceRefresh();
} }
public VideoMode getVideoMode() {
return videoMode;
}
@Override @Override
protected void showBW(WritableImage screen, int x, int y, int dhgrWord) { protected void showBW(WritableImage screen, int x, int y, int dhgrWord) {
int pos = divBy28[x]; int pos = divBy28[x];
@ -235,6 +249,7 @@ public class VideoNTSC extends VideoDHGR {
} }
boolean monochomeMode = false; boolean monochomeMode = false;
private void renderScanline(WritableImage screen, int y) { private void renderScanline(WritableImage screen, int y) {
int p = 0; int p = 0;
if (rowStart != 0) { if (rowStart != 0) {
@ -364,12 +379,13 @@ public class VideoNTSC extends VideoDHGR {
@Override @Override
public void reconfigure() { public void reconfigure() {
setVideoMode(videoMode);
activePalette = useTextPalette ? TEXT_PALETTE : SOLID_PALETTE; activePalette = useTextPalette ? TEXT_PALETTE : SOLID_PALETTE;
super.reconfigure(); super.reconfigure();
} }
// The following section captures changes to the RGB mode // The following section captures changes to the RGB mode
// The details of this are in Brodener's patent application #4631692 // The details of this are in Brodener's patent application #4631692
// http://www.freepatentsonline.com/4631692.pdf // http://www.freepatentsonline.com/4631692.pdf
// as well as the AppleColor adapter card manual // as well as the AppleColor adapter card manual
// http://apple2.info/download/Ext80ColumnAppleColorCardHR.pdf // http://apple2.info/download/Ext80ColumnAppleColorCardHR.pdf
rgbMode graphicsMode = rgbMode.MIX; rgbMode graphicsMode = rgbMode.MIX;
@ -426,7 +442,7 @@ public class VideoNTSC extends VideoDHGR {
})); }));
rgbStateListeners.add(memory.observe(RAMEvent.TYPE.EXECUTE, 0x0fa62, (e) -> { rgbStateListeners.add(memory.observe(RAMEvent.TYPE.EXECUTE, 0x0fa62, (e) -> {
// When reset hook is called, reset the graphics mode // When reset hook is called, reset the graphics mode
// This is useful in case a program is running that // This is useful in case a program is running that
// is totally clueless how to set the RGB state correctly. // is totally clueless how to set the RGB state correctly.
f1 = true; f1 = true;
f2 = true; f2 = true;

View File

@ -25,7 +25,7 @@ import jace.core.SoftSwitch;
* A memory softswitch is a softswitch which triggers a memory reconfiguration * A memory softswitch is a softswitch which triggers a memory reconfiguration
* after its value is changed. * after its value is changed.
* *
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com * @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/ */
public class MemorySoftSwitch extends SoftSwitch { public class MemorySoftSwitch extends SoftSwitch {
@ -48,11 +48,15 @@ public class MemorySoftSwitch extends SoftSwitch {
// Todo: Implement floating bus, maybe? // Todo: Implement floating bus, maybe?
@Override @Override
protected byte readSwitch() { protected byte readSwitch() {
byte value = computer.getVideo().getFloatingBus(); if (computer.getVideo() == null) {
if (getState()) { return 0x00;
return (byte) (value | 0x080);
} else { } else {
return (byte) (value & 0x07f); byte value = computer.getVideo().getFloatingBus();
if (getState()) {
return (byte) (value | 0x080);
} else {
return (byte) (value & 0x07f);
}
} }
} }
} }

View File

@ -52,7 +52,6 @@ import java.util.logging.Logger;
import java.util.stream.Stream; import java.util.stream.Stream;
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;
/** /**
@ -190,16 +189,17 @@ public class Configuration implements Reconfigurable {
} }
public void setFieldValue(String field, Serializable value) { public void setFieldValue(String field, Serializable value) {
setChanged(true);
if (value != null) { if (value != null) {
if (value.equals(getFieldValue(field))) { if (value.equals(getFieldValue(field))) {
return; return;
} }
} else { } else {
if (getFieldValue(field) == null) { if (getFieldValue(field) == null) {
setChanged(false);
return; return;
} }
} }
setChanged(true);
setRawFieldValue(field, value); setRawFieldValue(field, value);
} }

View File

@ -18,6 +18,7 @@
*/ */
package jace.core; package jace.core;
import jace.Emulator;
import jace.config.Configuration; import jace.config.Configuration;
import jace.config.InvokableAction; import jace.config.InvokableAction;
import java.io.File; import java.io.File;
@ -25,7 +26,6 @@ import java.io.InputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.reflections.Reflections;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -38,7 +38,6 @@ import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
@ -49,6 +48,7 @@ import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import org.reflections.Reflections;
/** /**
* This is a set of helper functions which do not belong anywhere else. * This is a set of helper functions which do not belong anywhere else.
@ -104,7 +104,7 @@ public class Utility {
} }
return dist[m][n]; return dist[m][n];
} }
/** /**
* Normalize distance based on longest string * Normalize distance based on longest string
* @param s * @param s
@ -114,7 +114,7 @@ public class Utility {
public static int adjustedLevenshteinDistance(String s, String t) { public static int adjustedLevenshteinDistance(String s, String t) {
return Math.max(s.length(), t.length()) - levenshteinDistance(s, t); return Math.max(s.length(), t.length()) - levenshteinDistance(s, t);
} }
/** /**
* Compare strings based on a tally of similar patterns found, using a fixed * Compare strings based on a tally of similar patterns found, using a fixed
@ -156,6 +156,9 @@ public class Utility {
public static void setHeadlessMode(boolean headless) { public static void setHeadlessMode(boolean headless) {
isHeadless = headless; isHeadless = headless;
if (Emulator.instance == null && headless) {
Emulator.instance = new Emulator(Collections.emptyList());
}
} }
public static boolean isHeadlessMode() { public static boolean isHeadlessMode() {
@ -515,7 +518,7 @@ public class Utility {
// } // }
return actions.get(actionsList.get(0)); return actions.get(actionsList.get(0));
} }
private static int getActionNameMatch(String str, InvokableAction action) { private static int getActionNameMatch(String str, InvokableAction action) {
int nameMatch = levenshteinDistance(str, action.name()); int nameMatch = levenshteinDistance(str, action.name());
if (action.alternatives() != null) { if (action.alternatives() != null) {

View File

@ -0,0 +1,79 @@
/*
* Copyright 2019 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.scripting;
import jace.Emulator;
import jace.apple2e.Apple2e;
import jace.apple2e.MOS65C02;
import jace.apple2e.VideoNTSC;
import jace.apple2e.VideoNTSC.VideoMode;
import jace.core.Computer;
import jace.core.RAM;
import jace.core.SoundMixer;
import jace.core.Utility;
import java.util.Arrays;
import javafx.embed.swing.JFXPanel;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Check out various command line arguments to see if they do the right thing
*/
public class TestCommandlineArgs {
static Computer computer;
static MOS65C02 cpu;
static RAM ram;
@BeforeClass
public static void initJavaFX() {
new JFXPanel();
}
@Before
public void setup() {
Utility.setHeadlessMode(true);
SoundMixer.MUTE = true;
computer = new Apple2e();
cpu = (MOS65C02) computer.getCpu();
ram = computer.getMemory();
Emulator.computer = (Apple2e) computer;
computer.pause();
cpu.suspend();
cpu.clearState();
}
static String GRAPHICS_MODE = "vid.videomode";
@Test
public void testVideoModes() {
for (VideoMode mode : VideoMode.values()) {
Emulator.instance.applyConfiguration(Arrays.asList("-" + GRAPHICS_MODE, mode.name().toLowerCase()));
assertTrue("Should be NTSC video module", computer.video instanceof VideoNTSC);
VideoNTSC video = (VideoNTSC) computer.video;
assertEquals("Should have switched to " + mode.name() + " mode", mode, video.getVideoMode());
Emulator.instance.applyConfiguration(Arrays.asList("-" + GRAPHICS_MODE, mode.name().toUpperCase()));
assertTrue("Should be NTSC video module", computer.video instanceof VideoNTSC);
video = (VideoNTSC) computer.video;
assertEquals("Should have switched to " + mode.name() + " mode", mode, video.getVideoMode());
}
}
}