From e643eb0bdf043d05fe523450c92b89aa034d1e8c Mon Sep 17 00:00:00 2001 From: Brendan Robert Date: Wed, 4 Feb 2015 01:43:51 -0600 Subject: [PATCH] You can now boot with -s6.d1 and -s6.d2 again (fixing WUDSN compatibility) -- Configurator now understands Optional[] arrays (for card support) --- src/main/java/jace/Emulator.java | 3 +- src/main/java/jace/config/Configuration.java | 178 +++++++++++-------- src/main/java/jace/core/RAM.java | 2 +- src/main/java/jace/hardware/CardDiskII.java | 32 +++- src/main/java/jace/library/MediaLibrary.java | 2 +- 5 files changed, 133 insertions(+), 84 deletions(-) diff --git a/src/main/java/jace/Emulator.java b/src/main/java/jace/Emulator.java index 0439358..69853f7 100644 --- a/src/main/java/jace/Emulator.java +++ b/src/main/java/jace/Emulator.java @@ -23,6 +23,7 @@ import jace.config.Configuration; import jace.ui.AbstractEmulatorFrame; import java.awt.Component; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -51,7 +52,7 @@ public class Emulator { computer = new Apple2e(); Configuration.loadSettings(); mainThread = Thread.currentThread(); - Map settings = new HashMap<>(); + Map settings = new LinkedHashMap<>(); if (args != null) { for (int i = 0; i < args.size(); i++) { if (args.get(i).startsWith("-")) { diff --git a/src/main/java/jace/config/Configuration.java b/src/main/java/jace/config/Configuration.java index c9a2664..3898f6d 100644 --- a/src/main/java/jace/config/Configuration.java +++ b/src/main/java/jace/config/Configuration.java @@ -30,11 +30,17 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.logging.Level; @@ -45,7 +51,8 @@ import javax.swing.tree.TreePath; /** * Manages the configuration state of the emulator components. - * @author Brendan Robert (BLuRry) brendan.robert@gmail.com + * + * @author Brendan Robert (BLuRry) brendan.robert@gmail.com */ public class Configuration implements Reconfigurable { @@ -57,7 +64,7 @@ public class Configuration implements Reconfigurable { public String getShortName() { return "cfg"; } - + public void reconfigure() { } @@ -116,12 +123,12 @@ public class Configuration implements Reconfigurable { } /** - * Represents a serializable configuration node as part of a tree. - * The root node should be a single instance (e.g. Computer) - * The child nodes should be all object instances that stem from each object - * The overall goal of this class is two-fold: - * 1) Provide a navigable manner to inspect configuration - * 2) Provide a simple persistence mechanism to load/store configuration + * Represents a serializable configuration node as part of a tree. The root + * node should be a single instance (e.g. Computer) The child nodes should + * be all object instances that stem from each object The overall goal of + * this class is two-fold: 1) Provide a navigable manner to inspect + * configuration 2) Provide a simple persistence mechanism to load/store + * configuration */ public static class ConfigNode implements Serializable { @@ -147,8 +154,8 @@ public class Configuration implements Reconfigurable { public ConfigNode(ConfigNode parent, Reconfigurable subject) { this.subject = subject; - this.settings = new TreeMap(); - this.children = new TreeMap(); + this.settings = new TreeMap<>(); + this.children = new TreeMap<>(); this.parent = parent; if (this.parent != null) { this.root = this.parent.root != null ? this.parent.root : this.parent; @@ -156,6 +163,7 @@ public class Configuration implements Reconfigurable { } public void setFieldValue(String field, Serializable value) { + changed = true; if (value != null) { if (value.equals(getFieldValue(field))) { return; @@ -165,7 +173,6 @@ public class Configuration implements Reconfigurable { return; } } - changed = true; setRawFieldValue(field, value); } @@ -235,38 +242,66 @@ public class Configuration implements Reconfigurable { } else if (o.getClass().isArray()) { String fieldName = f.getName(); Class type = o.getClass().getComponentType(); +// System.out.println("Evaluating " + node.subject.getShortName() + "." + fieldName + "; type is " + type.toGenericString()); + List children = new ArrayList<>(); if (!Reconfigurable.class.isAssignableFrom(type)) { - continue; +// System.out.println("Looking at type " + type.getName() + " to see if optional"); + if (Optional.class.isAssignableFrom(type)) { + Type genericTypes = f.getGenericType(); +// System.out.println("Looking at generic parmeters " + genericTypes.getTypeName() + " for reconfigurable class, type "+genericTypes.getClass().getName()); + if (genericTypes instanceof GenericArrayType) { + GenericArrayType aType = (GenericArrayType) genericTypes; + ParameterizedType pType = (ParameterizedType) aType.getGenericComponentType(); + if (pType.getActualTypeArguments().length != 1) { + continue; + } + Type genericType = pType.getActualTypeArguments()[0]; +// System.out.println("Looking at type " + genericType.getTypeName() + " to see if reconfigurable"); + if (!Reconfigurable.class.isAssignableFrom((Class) genericType)) { + continue; + } + } else { + continue; + } + + for (Optional child : (Optional[]) o) { + if (child.isPresent()) { + children.add(child.get()); + } else { + children.add(null); + } + } + } + } else { + children = Arrays.asList((Reconfigurable[]) o); } - Reconfigurable[] r = (Reconfigurable[]) o; - visited.add(r); - for (int i = 0; i < r.length; i++) { + visited.add(o); + for (int i = 0; i < children.size(); i++) { + Reconfigurable child = children.get(i); String childName = fieldName + i; - if (r[i] == null) { + if (child == null) { node.children.remove(childName); continue; } ConfigNode grandchild = node.children.get(childName); - if (grandchild == null || !grandchild.subject.equals(r[i])) { - grandchild = new ConfigNode(node, r[i]); + if (grandchild == null || !grandchild.subject.equals(child)) { + grandchild = new ConfigNode(node, child); node.children.put(childName, grandchild); } buildTree(grandchild, visited); } } - } catch (IllegalArgumentException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); - } catch (IllegalAccessException ex) { + } catch (IllegalArgumentException | IllegalAccessException ex) { Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); } } } @InvokableAction( - name="Save settings", - description="Save all configuration settings as defaults", - category="general", - alternatives="save preferences;save defaults" + name = "Save settings", + description = "Save all configuration settings as defaults", + category = "general", + alternatives = "save preferences;save defaults" ) public static void saveSettings() { FileOutputStream fos = null; @@ -293,10 +328,10 @@ public class Configuration implements Reconfigurable { } @InvokableAction( - name="Load settings", - description="Load all configuration settings previously saved", - category="general", - alternatives="load preferences;revert settings;revert preferences" + name = "Load settings", + description = "Load all configuration settings previously saved", + category = "general", + alternatives = "load preferences;revert settings;revert preferences" ) public static void loadSettings() { { @@ -334,10 +369,12 @@ public class Configuration implements Reconfigurable { } /** - * Apply settings from node tree to the object model - * This also calls "reconfigure" on objects in sequence + * Apply settings from node tree to the object model This also calls + * "reconfigure" on objects in sequence + * * @param node - * @return True if any settings have changed in the node or any of its descendants + * @return True if any settings have changed in the node or any of its + * descendants */ public static boolean applySettings(ConfigNode node) { boolean resume = false; @@ -363,7 +400,7 @@ public class Configuration implements Reconfigurable { if (resume) { Emulator.computer.resume(); } - + return hasChanged; } @@ -376,14 +413,14 @@ public class Configuration implements Reconfigurable { doApply(oldRoot); buildTree(oldRoot, new HashSet()); } - for (String childName : newRoot.children.keySet()) { -// System.out.println("Applying settings for " + childName); + newRoot.children.keySet().stream().forEach((childName) -> { + // System.out.println("Applying settings for " + childName); applyConfigTree(newRoot.children.get(childName), oldRoot.children.get(childName)); - } + }); } private static void doApply(ConfigNode node) { - List removeList = new ArrayList(); + List removeList = new ArrayList<>(); for (String f : node.settings.keySet()) { try { Field ff = node.subject.getClass().getField(f); @@ -409,19 +446,15 @@ public class Configuration implements Reconfigurable { // System.out.println("Setting "+node.subject.getName()+" property "+ff.getName()+" with value "+String.valueOf(val)); ff.set(node.subject, val); } catch (NoSuchFieldException ex) { - System.out.println("Setting "+f+" no longer exists, skipping."); + System.out.println("Setting " + f + " no longer exists, skipping."); removeList.add(f); - } catch (SecurityException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); - } catch (IllegalArgumentException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); - } catch (IllegalAccessException ex) { + } catch (SecurityException | IllegalArgumentException | IllegalAccessException ex) { Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); } } - for (String f : removeList) { + removeList.stream().forEach((f) -> { node.settings.remove(f); - } + }); try { // When settings are applied, this could very well change the object structure @@ -431,39 +464,37 @@ public class Configuration implements Reconfigurable { } catch (Exception ex) { Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); } - + node.changed = false; } - + public static void applySettings(Map settings) { for (Map.Entry setting : settings.entrySet()) { - Map shortNames = new HashMap(); + Map shortNames = new HashMap<>(); buildNodeMap(BASE, shortNames); - + String settingName = setting.getKey(); String value = setting.getValue(); - String[] parts = settingName.split("\\."); + String[] parts = settingName.split("\\."); if (parts.length != 2) { - System.err.println("Unable to parse settting, should be in the form of DEVICE.PROPERTYNAME "+settingName); + System.err.println("Unable to parse settting, should be in the form of DEVICE.PROPERTYNAME " + settingName); continue; } String deviceName = parts[0]; String fieldName = parts[1]; ConfigNode n = shortNames.get(deviceName.toLowerCase()); if (n == null) { - System.err.println("Unable to find device named "+deviceName+", try one of these: "+Utility.join(shortNames.keySet(), ", ")); + System.err.println("Unable to find device named " + deviceName + ", try one of these: " + Utility.join(shortNames.keySet(), ", ")); continue; } - + boolean found = false; - List shortFieldNames = new ArrayList(); + List shortFieldNames = new ArrayList<>(); for (String longName : n.getAllSettingNames()) { ConfigurableField f = null; try { f = n.subject.getClass().getField(longName).getAnnotation(ConfigurableField.class); - } catch (NoSuchFieldException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); - } catch (SecurityException ex) { + } catch (NoSuchFieldException | SecurityException ex) { Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); } String shortName = (f != null && !f.shortName().equals("")) ? f.shortName() : longName; @@ -473,41 +504,42 @@ public class Configuration implements Reconfigurable { found = true; n.setFieldValue(longName, value); applySettings(n); - n.subject.reconfigure(); +// n.subject.reconfigure(); buildTree(); - System.out.println("Set property "+n.subject.getName()+"."+longName+" to "+value); + System.out.println("Set property " + n.subject.getName() + "." + longName + " to " + value); break; } } if (!found) { - System.err.println("Unable to find property "+fieldName+" for device "+deviceName+". Try one of these :"+Utility.join(shortFieldNames, ", ")); + System.err.println("Unable to find property " + fieldName + " for device " + deviceName + ". Try one of these :" + Utility.join(shortFieldNames, ", ")); } } } private static void buildNodeMap(ConfigNode n, Map shortNames) { +// System.out.println("Encountered " + n.subject.getShortName().toLowerCase()); shortNames.put(n.subject.getShortName().toLowerCase(), n); - for (Map.Entry c : n.children.entrySet()) { + n.children.entrySet().stream().forEach((c) -> { buildNodeMap(c.getValue(), shortNames); - } + }); } - + private static void printTree(ConfigNode n, String prefix, int i) { - for (String setting : n.getAllSettingNames()) { - for (int j=0; j < i; j++) System.out.print(" "); + n.getAllSettingNames().stream().forEach((setting) -> { + for (int j = 0; j < i; j++) { + System.out.print(" "); + } ConfigurableField f = null; try { f = n.subject.getClass().getField(setting).getAnnotation(ConfigurableField.class); - } catch (NoSuchFieldException ex) { - Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); - } catch (SecurityException ex) { + } catch (NoSuchFieldException | SecurityException ex) { Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex); } String sn = (f != null && !f.shortName().equals("")) ? f.shortName() : setting; - System.out.println(prefix+">>"+setting+" ("+n.subject.getShortName()+"."+sn+")"); - } - for (Map.Entry c : n.children.entrySet()) { - printTree(c.getValue(), prefix+"."+c.getKey(), i+1); - } + System.out.println(prefix + ">>" + setting + " (" + n.subject.getShortName() + "." + sn + ")"); + }); + n.children.entrySet().stream().forEach((c) -> { + printTree(c.getValue(), prefix + "." + c.getKey(), i + 1); + }); } } diff --git a/src/main/java/jace/core/RAM.java b/src/main/java/jace/core/RAM.java index f2e3282..d85358d 100644 --- a/src/main/java/jace/core/RAM.java +++ b/src/main/java/jace/core/RAM.java @@ -39,7 +39,7 @@ public abstract class RAM implements Reconfigurable { public List listeners; public List[] listenerMap; public List[] ioListenerMap; - protected Optional[] cards; + public Optional[] cards; // card 0 = 80 column card firmware / system rom public int activeSlot = 0; protected final Computer computer; diff --git a/src/main/java/jace/hardware/CardDiskII.java b/src/main/java/jace/hardware/CardDiskII.java index 10355ae..bfc7ba4 100644 --- a/src/main/java/jace/hardware/CardDiskII.java +++ b/src/main/java/jace/hardware/CardDiskII.java @@ -29,6 +29,7 @@ import jace.core.RAMEvent.TYPE; import jace.core.Utility; import jace.library.MediaConsumer; import jace.library.MediaConsumerParent; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.logging.Level; @@ -39,10 +40,9 @@ import java.util.logging.Logger; * side of the Disk ][ controller interface as well as the on-board "boot0" ROM. * The behavior of the actual drive stepping, reading disk images, and so on is * performed by DiskIIDrive and FloppyDisk, respectively. This class only serves - * as the I/O interface portion. - * Created on April 21, 2007 + * as the I/O interface portion. Created on April 21, 2007 * - * @author Brendan Robert (BLuRry) brendan.robert@gmail.com + * @author Brendan Robert (BLuRry) brendan.robert@gmail.com */ @Name("Disk ][ Controller") public class CardDiskII extends Card implements Reconfigurable, MediaConsumerParent { @@ -54,6 +54,10 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar static public int DEFAULT_VOLUME_NUMBER = 0x0FE; @ConfigurableField(category = "Disk", defaultValue = "true", name = "Speed boost", description = "If enabled, emulator will run at max speed during disk access") static public boolean USE_MAX_SPEED = true; + @ConfigurableField(category = "Disk", defaultValue = "", shortName = "d1", name = "Drive 1 disk image", description = "Path of disk 1") + public String disk1; + @ConfigurableField(category = "Disk", defaultValue = "", shortName = "d2", name = "Drive 2 disk image", description = "Path of disk 1") + public String disk2; public CardDiskII(Computer computer) { super(computer); @@ -143,8 +147,7 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar currentDrive.setReadMode(); if (currentDrive.disk != null && currentDrive.disk.writeProtected) { e.setNewValue(0x080); - } else - { + } else { // e.setNewValue((byte) (Math.random() * 256.0)); e.setNewValue(0); } @@ -191,6 +194,19 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar @Override public void reconfigure() { super.reconfigure(); + try { + if (disk1 != null && !disk1.isEmpty()) { + drive1.insertDisk(new File(disk1)); + disk1 = null; + } + if (disk2 != null && !disk2.isEmpty()) { + drive2.insertDisk(new File(disk2)); + disk2 = null; + } + } catch (IOException ex) { + Logger.getLogger(CardDiskII.class.getName()).log(Level.SEVERE, null, ex); + } + } private void tweakTiming() { @@ -212,10 +228,10 @@ public class CardDiskII extends Card implements Reconfigurable, MediaConsumerPar public void setSlot(int slot) { super.setSlot(slot); drive1.getIcon().setDescription("S" + slot + "D1"); - drive2.getIcon().setDescription("S" + slot + "D2"); + drive2.getIcon().setDescription("S" + slot + "D2"); } - + public MediaConsumer[] getConsumers() { - return new MediaConsumer[] {drive1, drive2}; + return new MediaConsumer[]{drive1, drive2}; } } diff --git a/src/main/java/jace/library/MediaLibrary.java b/src/main/java/jace/library/MediaLibrary.java index 6c721b9..d0dec94 100644 --- a/src/main/java/jace/library/MediaLibrary.java +++ b/src/main/java/jace/library/MediaLibrary.java @@ -37,7 +37,7 @@ import javax.swing.JPanel; */ public class MediaLibrary implements Reconfigurable { - public static MediaLibrary instance; + private static MediaLibrary instance; public static MediaLibrary getInstance() { if (instance == null) {