diff --git a/README.md b/README.md index 27f26d6..b99b130 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ SYMON - A 6502 System Simulator See the file COPYING for license. -![Symon Simulator in Action](https://static.loomcom.com/symon/screenshots/full.jpg) +![Symon Simulator in Action](https://github.com/sethm/symon/raw/master/screenshots/full.jpg) ## 1.0 About @@ -66,7 +66,7 @@ memory. ### 3.2 Serial Console and CPU Status -![Serial Console](https://static.loomcom.com/symon/screenshots/console.png) +![Serial Console](https://github.com/sethm/symon/raw/master/screenshots/console.png) The main window of the simulator acts as the primary Input/Output system through a virtual serial terminal. The terminal is attached to @@ -78,13 +78,13 @@ It also provides CPU status. Contents of the accumulator, index registers, processor status flags, disassembly of the instruction register, and stack pointer are all displayed. -![Font Selection](https://static.loomcom.com/symon/screenshots/font_selection.png) +![Font Selection](https://github.com/sethm/symon/raw/master/screenshots/font_selection.png) The console supports font sizes from 10 to 20 points. ### 3.3 ROM Loading -![ROM Loading](https://static.loomcom.com/symon/screenshots/load_rom.png) +![ROM Loading](https://github.com/sethm/symon/raw/master/screenshots/load_rom.png) Symon can load any appropriately sized ROM image. The Symon architecture expects as 16KB (16384 byte) ROM image, while the @@ -95,32 +95,32 @@ address. ### 3.4 Memory Window -![Memory Window](https://static.loomcom.com/symon/screenshots/memory_window.png) +![Memory Window](https://github.com/sethm/symon/raw/master/screenshots/memory_window.png) Memory contents can be viewed (and edited) one page at a time through the Memory Window. ### 3.5 Trace Log -![Trace Log](https://static.loomcom.com/symon/screenshots/trace_log.png) +![Trace Log](https://github.com/sethm/symon/raw/master/screenshots/trace_log.png) The last 20,000 execution steps are disassembled and logged to the Trace Log Window. ### 3.6 Simulator Speeds -![Speeds](https://static.loomcom.com/symon/screenshots/simulator_menu.png) +![Speeds](https://github.com/sethm/symon/raw/master/screenshots/simulator_menu.png) Simulated speeds may be set from 1MHz to 8MHz. ### 3.7 Breakpoints -![Breakpoints](https://static.loomcom.com/symon/screenshots/breakpoints.png) +![Breakpoints](https://github.com/sethm/symon/raw/master/screenshots/breakpoints.png) Breakpoints can be set and removed through the Breakpoints window. ### 3.8 Experimental 6545 CRTC Video -![Composite Video](https://static.loomcom.com/symon/screenshots/video_window.png) +![Composite Video](https://github.com/sethm/symon/raw/master/screenshots/video_window.png) This feature is highly experimental. It's possible to open a video window from the "View" menu. This window simulates the output of a MOS 6545 CRT @@ -154,7 +154,7 @@ between the simulated 6545 and a real 6545: of 6545 programming tricks that were achieved by updating the frame address during vertical and horizontal sync times are not achievable. There is no way (for example) to change the Display Start - Address (R12 and R13) while a frame is being drawn. + Address (R12 and R13) while a frame is being drawn. For more information on the 6545 CRTC and its programming model, please see the following resources @@ -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 @@ -242,11 +258,11 @@ running. - **1.2.0:** 3 January, 2016 - Add symbolic disassembly to breakpoints window. - + - **1.1.1:** 2 January, 2016 - Minor enhancement: Allows breakpoints to be added with the Enter key. - - **1.1.0:** 31 December, 2015 - Fixed delay loop to better + - **1.1.0:** 31 December, 2015 - Fixed delay loop to better simulate various clock speeds. Added ability to select clock speed at runtime. Status display now shows the next instruction to be executed, instead of the last instruction executed. diff --git a/pom.xml b/pom.xml index 106abbc..4f3213c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.loomcom.symon symon jar - 1.3.0 + 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); }