diff --git a/README.md b/README.md index 925c9de..b99b130 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,18 @@ Java 1.8 or greater, just type: When Symon is running, you should be presented with a simple graphical interface. +#### 4.1.1 Command Line Options + +Two command line options may be passed to the JAR file on startup, +to specify machine type and CPU type. The options are: + + - `-cpu 6502`: Use the NMOS 6502 CPU type by default. + - `-cpu 65c02`: Use the CMOS 65C02 CPU type by default. + - `-machine symon`: Use the **Symon** machine type by default. + - `-machine multicomp`: Use the **Multicomp** machine type by default. + - `-machine simple`: Use the **Simple** machine type by default. + - `-rom `: Use the specified file as the ROM image. + ### 4.2 ROM images The simulator requires a ROM image loaded into memory to work @@ -235,6 +247,10 @@ running. ## 5.0 Revision History + - **1.3.1:** 12 October, 2019 - Add support for new command line + option `-cpu ` to specify one of `6502` or `65c02` on startup, + and new option `-rom ` to specify a ROM file to load. + - **1.3.0:** 24 February, 2018 - Adds support for 65C02 opcodes. - **1.2.1:** 8 January, 2016 - Remove dependency on Java 8. Now diff --git a/pom.xml b/pom.xml index b59b061..4f3213c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.loomcom.symon symon jar - 1.3.0-SNAPSHOT + 1.3.1 symon http://www.loomcom.com/symon @@ -33,6 +33,11 @@ 1.10.19 test + + commons-cli + commons-cli + 1.4 + diff --git a/src/main/java/com/loomcom/symon/Main.java b/src/main/java/com/loomcom/symon/Main.java index 253f887..c41a43e 100644 --- a/src/main/java/com/loomcom/symon/Main.java +++ b/src/main/java/com/loomcom/symon/Main.java @@ -28,6 +28,8 @@ package com.loomcom.symon; import com.loomcom.symon.machines.MulticompMachine; import com.loomcom.symon.machines.SimpleMachine; import com.loomcom.symon.machines.SymonMachine; +import org.apache.commons.cli.*; + import java.util.Locale; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; @@ -41,71 +43,115 @@ public class Main { * * @param args Program arguments */ - public static void main(String args[]) throws Exception { + public static void main(String[] args) throws Exception { Class machineClass = SymonMachine.class; - for(int i = 0; i < args.length; ++i) { - String arg = args[i].toLowerCase(Locale.ENGLISH); - if(arg.equals("-machine") && (i+1) < args.length) { - String machine = args[i+1].trim().toLowerCase(Locale.ENGLISH); + + Options options = new Options(); + + options.addOption(new Option("m", "machine", true, "Specify machine type.")); + options.addOption(new Option("c", "cpu", true, "Specify CPU type.")); + options.addOption(new Option("r", "rom", true, "Specify ROM file.")); + + CommandLineParser parser = new DefaultParser(); + + try { + CommandLine line = parser.parse(options, args); + InstructionTable.CpuBehavior cpuBehavior = null; + String romFile = null; + + if (line.hasOption("machine")) { + String machine = line.getOptionValue("machine").toLowerCase(Locale.ENGLISH); switch (machine) { - case "symon": - machineClass = SymonMachine.class; - break; case "multicomp": machineClass = MulticompMachine.class; break; case "simple": machineClass = SimpleMachine.class; break; + case "symon": + machineClass = SymonMachine.class; + break; + default: + System.err.println("Could not start Symon. Unknown machine type " + machine); + return; } } - } - - while (true) { - if (machineClass == null) { - Object[] possibilities = {"Symon", "Multicomp", "Simple"}; - String s = (String)JOptionPane.showInputDialog( - null, - "Please choose the machine type to be emulated:", - "Machine selection", - JOptionPane.PLAIN_MESSAGE, - null, - possibilities, - "Symon"); - - if (s != null && s.equals("Multicomp")) { - machineClass = MulticompMachine.class; - } else if (s != null && s.equals("Simple")) { - machineClass = SimpleMachine.class; - } else { - machineClass = SymonMachine.class; + if (line.hasOption("cpu")) { + String cpu = line.getOptionValue("cpu").toLowerCase(Locale.ENGLISH); + switch (cpu) { + case "6502": + cpuBehavior = InstructionTable.CpuBehavior.NMOS_6502; + break; + case "65c02": + cpuBehavior = InstructionTable.CpuBehavior.CMOS_6502; + break; + case "65c816": + cpuBehavior = InstructionTable.CpuBehavior.CMOS_65816; + break; + default: + System.err.println("Could not start Symon. Unknown cpu type " + cpu); + return; } } - - final Simulator simulator = new Simulator(machineClass); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - // Create the main UI window - simulator.createAndShowUi(); - } catch (Exception e) { - e.printStackTrace(); + + if (line.hasOption("rom")) { + romFile = line.getOptionValue("rom"); + } + + while (true) { + if (machineClass == null) { + Object[] possibilities = {"Symon", "Multicomp", "Simple"}; + String s = (String)JOptionPane.showInputDialog( + null, + "Please choose the machine type to be emulated:", + "Machine selection", + JOptionPane.PLAIN_MESSAGE, + null, + possibilities, + "Symon"); + + + if (s != null && s.equals("Multicomp")) { + machineClass = MulticompMachine.class; + } else if (s != null && s.equals("Simple")) { + machineClass = SimpleMachine.class; + } else { + machineClass = SymonMachine.class; } } - }); - - - Simulator.MainCommand cmd = simulator.waitForCommand(); - if (cmd.equals(Simulator.MainCommand.SELECTMACHINE)) { - machineClass = null; - } else { - break; + + if (cpuBehavior == null) { + cpuBehavior = InstructionTable.CpuBehavior.NMOS_6502; + } + + final Simulator simulator = new Simulator(machineClass, cpuBehavior, romFile); + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + // Create the main UI window + simulator.createAndShowUi(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + + Simulator.MainCommand cmd = simulator.waitForCommand(); + + if (cmd.equals(Simulator.MainCommand.SELECTMACHINE)) { + machineClass = null; + } else { + break; + } } + } catch (ParseException ex) { + System.err.println("Could not start Symon. Reason: " + ex.getMessage()); } } } diff --git a/src/main/java/com/loomcom/symon/Simulator.java b/src/main/java/com/loomcom/symon/Simulator.java index ab14bf3..589fce6 100644 --- a/src/main/java/com/loomcom/symon/Simulator.java +++ b/src/main/java/com/loomcom/symon/Simulator.java @@ -31,6 +31,7 @@ import com.loomcom.symon.ui.Console; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.management.monitor.CounterMonitor; import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.*; @@ -136,9 +137,15 @@ public class Simulator { private static final String[] STEPS = {"1", "5", "10", "20", "50", "100"}; public Simulator(Class machineClass) throws Exception { + this(machineClass, InstructionTable.CpuBehavior.NMOS_6502, null); + } + + public Simulator(Class machineClass, InstructionTable.CpuBehavior cpuType, String romFile) throws Exception { this.breakpoints = new Breakpoints(this); - this.machine = (Machine) machineClass.getConstructors()[0].newInstance(); + this.machine = (Machine) machineClass.getConstructors()[0].newInstance(romFile); + this.machine.getCpu().setBehavior(cpuType); + // Initialize final fields in the constructor. this.traceLog = new TraceLog(); @@ -887,8 +894,9 @@ public class Simulator { JMenu cpuTypeMenu = new JMenu("CPU"); ButtonGroup cpuGroup = new ButtonGroup(); - makeCpuMenuItem("NMOS 6502", Cpu.CpuBehavior.NMOS_6502, cpuTypeMenu, cpuGroup); - makeCpuMenuItem("CMOS 65C02", Cpu.CpuBehavior.CMOS_6502, cpuTypeMenu, cpuGroup); + makeCpuMenuItem("6502", Cpu.CpuBehavior.NMOS_6502, cpuTypeMenu, cpuGroup); + makeCpuMenuItem("65C02", Cpu.CpuBehavior.CMOS_6502, cpuTypeMenu, cpuGroup); + makeCpuMenuItem("65C816", Cpu.CpuBehavior.CMOS_65816, cpuTypeMenu, cpuGroup); // "Clock Speed" sub-menu JMenu speedSubMenu = new JMenu("Clock Speed"); diff --git a/src/main/java/com/loomcom/symon/machines/MulticompMachine.java b/src/main/java/com/loomcom/symon/machines/MulticompMachine.java index 08c4fa5..d16a13f 100644 --- a/src/main/java/com/loomcom/symon/machines/MulticompMachine.java +++ b/src/main/java/com/loomcom/symon/machines/MulticompMachine.java @@ -68,7 +68,7 @@ public class MulticompMachine implements Machine { private Memory rom; - public MulticompMachine() throws Exception { + public MulticompMachine(String romFile) throws Exception { this.bus = new Bus(BUS_BOTTOM, BUS_TOP); this.cpu = new Cpu(); this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false); @@ -79,20 +79,23 @@ public class MulticompMachine implements Machine { bus.addDevice(ram); bus.addDevice(acia, 1); bus.addDevice(new SdController(SD_BASE), 1); - - // TODO: Make this configurable, of course. - File romImage = new File("rom.bin"); - if (romImage.canRead()) { - logger.info("Loading ROM image from file " + romImage); - this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage); - } else { - logger.info("Default ROM file " + romImage + + + if (romFile != null) { + File romImage = new File("rom.bin"); + if (romImage.canRead()) { + logger.info("Loading ROM image from file " + romImage); + this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage); + } else { + logger.info("Default ROM file " + romImage + " not found, loading empty R/W memory image."); + this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1); + } + } else { + logger.info("No ROM file specified, loading empty R/W memory image."); this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1); } bus.addDevice(rom); - } @Override diff --git a/src/main/java/com/loomcom/symon/machines/SimpleMachine.java b/src/main/java/com/loomcom/symon/machines/SimpleMachine.java index 430d543..fc49ec4 100644 --- a/src/main/java/com/loomcom/symon/machines/SimpleMachine.java +++ b/src/main/java/com/loomcom/symon/machines/SimpleMachine.java @@ -46,7 +46,7 @@ public class SimpleMachine implements Machine { private final Memory ram; private final Cpu cpu; - public SimpleMachine() throws MemoryRangeException { + public SimpleMachine(String romFile) throws MemoryRangeException { this.bus = new Bus(BUS_BOTTOM, BUS_TOP); this.ram = new Memory(BUS_BOTTOM, BUS_TOP, false); this.cpu = new Cpu(); diff --git a/src/main/java/com/loomcom/symon/machines/SymonMachine.java b/src/main/java/com/loomcom/symon/machines/SymonMachine.java index 50abf31..550f157 100644 --- a/src/main/java/com/loomcom/symon/machines/SymonMachine.java +++ b/src/main/java/com/loomcom/symon/machines/SymonMachine.java @@ -70,7 +70,7 @@ public class SymonMachine implements Machine { private Memory rom; - public SymonMachine() throws Exception { + public SymonMachine(String romFile) throws Exception { this.bus = new Bus(BUS_BOTTOM, BUS_TOP); this.cpu = new Cpu(); this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false); @@ -83,14 +83,18 @@ public class SymonMachine implements Machine { bus.addDevice(pia); bus.addDevice(acia); bus.addDevice(crtc); - - // TODO: Make this configurable, of course. - File romImage = new File("rom.bin"); - if (romImage.canRead()) { - logger.info("Loading ROM image from file {}", romImage); - this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage); + + if (romFile != null) { + File romImage = new File(romFile); + if (romImage.canRead()) { + logger.info("Loading ROM image from file {}", romImage); + this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage); + } else { + logger.info("Default ROM file {} not found, loading empty R/W memory image.", romImage); + this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1); + } } else { - logger.info("Default ROM file {} not found, loading empty R/W memory image.", romImage); + logger.info("No ROM file specified, loading empty R/W memory image."); this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1); }