Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a59dedc29 | ||
|
|
7268766cd6 | ||
|
|
2284535b7f | ||
|
|
963526621d | ||
|
|
c317544e7b | ||
|
|
ecd4bbbd9a | ||
|
|
a537328852 | ||
|
|
4975fca506 |
No files matched your search
@@ -1,9 +1,9 @@
|
||||
SYMON - A 6502 System Simulator
|
||||
===============================
|
||||
|
||||
**Version:** 1.3.2
|
||||
**Version:** 1.5.0
|
||||
|
||||
**Last Updated:** 08 March, 2021
|
||||
**Last Updated:** 30 March, 2025
|
||||
|
||||
See the file COPYING for license.
|
||||
|
||||
@@ -25,20 +25,20 @@ Klaus Dormann's 6502 Functional Test Suite as of version 0.8.2
|
||||
(See [this thread on the 6502.org Forums](http://forum.6502.org/viewtopic.php?f=2&t=2241)
|
||||
for more information about this functional test suite).
|
||||
|
||||
Symon is under constant, active development. Feedback and patches
|
||||
are always welcome.
|
||||
Symon is under active maintenance. Feedback and patches are always
|
||||
welcome.
|
||||
|
||||
## 2.0 Requirements
|
||||
|
||||
- Java 1.8 or higher
|
||||
- Maven 2.0.x or higher (for building from source)
|
||||
- Java 10 or higher
|
||||
- Maven 3.0.x or higher (for building from source)
|
||||
- JUnit 4 or higher (for testing)
|
||||
|
||||
## 3.0 Features
|
||||
|
||||
Symon can simulate multiple 6502 based architectures. At present, four
|
||||
machines are implemented: Symon (the default), MULTICOMP, BenEater, and a "Simple"
|
||||
machine useful for debugging.
|
||||
machines are implemented: Symon (the default), MULTICOMP, BenEater, and
|
||||
a "Simple" machine useful for debugging.
|
||||
|
||||
### 3.1 Memory Maps
|
||||
|
||||
@@ -270,6 +270,17 @@ running.
|
||||
|
||||
## 5.0 Revision History
|
||||
|
||||
- **1.5.0:** 30 March 2025 - Fixed a bug that caused some 6502
|
||||
instructions to be disassembled improperly in the trace log
|
||||
(GitHub issue #17).
|
||||
|
||||
Added a new "Step Over" button that allows stepping over the
|
||||
current subroutine. This button only becomes active when the
|
||||
next instruction is a "JSR" while stepping through execution
|
||||
one instruction at a time. It will continue execution until
|
||||
the subroutine returns through a corresponding "RTS" instruction.
|
||||
(GitHub feature request #18).
|
||||
|
||||
- **1.4.0:** 11 November 2023 - Adds a new machine, the Ben Eater
|
||||
machine. Correct handling of 6551 interrupts, and several 6551
|
||||
bug fixes. Fixes power-on status of 6502 status register. Fixes a
|
||||
@@ -355,17 +366,12 @@ running.
|
||||
|
||||
- Feedback (in the form of dialogs, status bar, etc).
|
||||
|
||||
- Better debugging tools from the UI, including breakpoints
|
||||
and disassembly.
|
||||
|
||||
- UI needs a ton more polish.
|
||||
|
||||
- More extensive testing.
|
||||
|
||||
- Clean up JavaDoc.
|
||||
|
||||
- Implement CMOS 65C02 instructions and NMOS / CMOS mode flag.
|
||||
|
||||
- Allow displaying ACIA status and dumping ACIA buffers, for
|
||||
debugging.
|
||||
|
||||
@@ -375,13 +381,19 @@ running.
|
||||
|
||||
- Symbolic debugging.
|
||||
|
||||
- Better / correct disassembly and tracing of 65C02 and 65C816
|
||||
instructions.
|
||||
|
||||
## 8.0 Copyright and Acknowledgements
|
||||
|
||||
**Copyright (c) 2014 Seth J. Morabito <web@loomcom.com>**
|
||||
**Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>**
|
||||
|
||||
Portions Copyright (c) 2014 Maik Merten <maikmerten@googlemail.com>
|
||||
Portions Copyright (c) 2022 Tim Allen <thristian@gmail.com>
|
||||
Portions Copyright (c) 2023 Chelsea Wilkinson <mail@chelseawilkinson.me>
|
||||
- Portions Copyright (c) 2014 Maik Merten
|
||||
<maikmerten@googlemail.com>
|
||||
- Portions Copyright (c) 2022 Tim Allen
|
||||
<thristian@gmail.com>
|
||||
- Portions Copyright (c) 2023 Chelsea Wilkinson
|
||||
<mail@chelseawilkinson.me>
|
||||
|
||||
Additional components used in this project are copyright their respective owners.
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.loomcom.symon</groupId>
|
||||
<artifactId>symon</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.4.0</version>
|
||||
<version>1.5.0</version>
|
||||
<name>symon</name>
|
||||
<url>http://www.loomcom.com/symon</url>
|
||||
<url>https://git.loomcom.com/seth/symon</url>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>
|
||||
UTF-8
|
||||
@@ -19,7 +19,7 @@
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.4.5</version>
|
||||
<version>1.5.16</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
@@ -30,7 +30,7 @@
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>4.8.0</version>
|
||||
<version>4.11.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
@@ -9,8 +32,8 @@ import java.util.TreeSet;
|
||||
|
||||
public class Breakpoints extends AbstractTableModel {
|
||||
|
||||
private TreeSet<Integer> breakpoints;
|
||||
private Simulator simulator;
|
||||
private final TreeSet<Integer> breakpoints;
|
||||
private final Simulator simulator;
|
||||
|
||||
public Breakpoints(Simulator simulator) {
|
||||
this.breakpoints = new TreeSet<>();
|
||||
@@ -26,11 +49,6 @@ public class Breakpoints extends AbstractTableModel {
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void removeBreakpoint(int address) {
|
||||
this.breakpoints.remove(address);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void removeBreakpointAtIndex(int index) {
|
||||
if (index < 0) {
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -42,19 +42,19 @@ import java.util.TreeSet;
|
||||
public class Bus {
|
||||
|
||||
// The default address at which to load programs
|
||||
public static int DEFAULT_LOAD_ADDRESS = 0x0200;
|
||||
public static final int DEFAULT_LOAD_ADDRESS = 0x0200;
|
||||
|
||||
// By default, our bus starts at 0, and goes up to 64K
|
||||
private int startAddress = 0x0000;
|
||||
private int endAddress = 0xffff;
|
||||
private final int startAddress;
|
||||
private final int endAddress;
|
||||
|
||||
// The CPU
|
||||
private Cpu cpu;
|
||||
|
||||
// Ordered sets of IO devices, associated with their priority
|
||||
private Map<Integer, SortedSet<Device>> deviceMap;
|
||||
private final Map<Integer, SortedSet<Device>> deviceMap;
|
||||
|
||||
// an array for quick lookup of adresses, brute-force style
|
||||
// an array for quick lookup of addresses, brute-force style
|
||||
private Device[] deviceAddressArray;
|
||||
|
||||
|
||||
@@ -94,8 +94,8 @@ public class Bus {
|
||||
* Add a device to the bus.
|
||||
*
|
||||
* @param device Device to add
|
||||
* @param priority Bus prioirity.
|
||||
* @throws MemoryRangeException
|
||||
* @param priority Bus priority.
|
||||
* @throws MemoryRangeException Invalid device address range.
|
||||
*/
|
||||
public void addDevice(Device device, int priority) throws MemoryRangeException {
|
||||
|
||||
@@ -109,12 +109,7 @@ public class Bus {
|
||||
throw new MemoryRangeException("end address of device " + device.getName() + " does not fall within the address range of the bus");
|
||||
}
|
||||
|
||||
SortedSet<Device> deviceSet = deviceMap.get(priority);
|
||||
|
||||
if (deviceSet == null) {
|
||||
deviceSet = new TreeSet<>();
|
||||
deviceMap.put(priority, deviceSet);
|
||||
}
|
||||
SortedSet<Device> deviceSet = deviceMap.computeIfAbsent(priority, k -> new TreeSet<>());
|
||||
|
||||
device.setBus(this);
|
||||
deviceSet.add(device);
|
||||
@@ -125,7 +120,7 @@ public class Bus {
|
||||
* Add a device to the bus. Throws a MemoryRangeException if the device overlaps with any others.
|
||||
*
|
||||
* @param device Device to add
|
||||
* @throws MemoryRangeException
|
||||
* @throws MemoryRangeException Invalid device memory range.
|
||||
*/
|
||||
public void addDevice(Device device) throws MemoryRangeException {
|
||||
addDevice(device, 0);
|
||||
@@ -224,9 +219,7 @@ public class Bus {
|
||||
|
||||
for (int priority : priorities) {
|
||||
SortedSet<Device> deviceSet = deviceMap.get(priority);
|
||||
for (Device device : deviceSet) {
|
||||
devices.add(device);
|
||||
}
|
||||
devices.addAll(deviceSet);
|
||||
}
|
||||
|
||||
return devices;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -158,6 +158,8 @@ public class Cpu implements InstructionTable {
|
||||
* Performs an individual instruction cycle.
|
||||
*/
|
||||
public void step() throws MemoryAccessException {
|
||||
int hi, lo; // Address calculation
|
||||
|
||||
opBeginTime = System.nanoTime();
|
||||
|
||||
// Store the address from which the IR was read, for debugging
|
||||
@@ -317,8 +319,8 @@ public class Cpu implements InstructionTable {
|
||||
break;
|
||||
case 0x40: // RTI - Return from Interrupt - Implied
|
||||
setProcessorStatus(stackPop());
|
||||
int lo = stackPop();
|
||||
int hi = stackPop();
|
||||
lo = stackPop();
|
||||
hi = stackPop();
|
||||
setProgramCounter(Utils.address(lo, hi));
|
||||
break;
|
||||
case 0x48: // PHA - Push Accumulator - Implied
|
||||
@@ -1697,6 +1699,10 @@ public class Cpu implements InstructionTable {
|
||||
return state.ir;
|
||||
}
|
||||
|
||||
public int getNextIr() {
|
||||
return state.nextIr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @value The value of the Process Status Register bits to be set.
|
||||
*/
|
||||
@@ -1923,6 +1929,7 @@ public class Cpu implements InstructionTable {
|
||||
break;
|
||||
case AIX:
|
||||
sb.append(" ($").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",X)");
|
||||
break;
|
||||
case ABX:
|
||||
sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",X");
|
||||
break;
|
||||
@@ -1935,6 +1942,9 @@ public class Cpu implements InstructionTable {
|
||||
case IND:
|
||||
sb.append(" ($").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(")");
|
||||
break;
|
||||
case ZPI:
|
||||
sb.append(" ($").append(Utils.byteToHex(args[0])).append(")");
|
||||
break;
|
||||
case XIN:
|
||||
sb.append(" ($").append(Utils.byteToHex(args[0])).append(",X)");
|
||||
break;
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.util.Utils;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -25,26 +25,24 @@ package com.loomcom.symon;
|
||||
|
||||
public interface InstructionTable {
|
||||
|
||||
int BRK = 0x00;
|
||||
int JSR = 0x20;
|
||||
int RTS = 0x60;
|
||||
|
||||
/**
|
||||
* Enumeration of valid CPU behaviors. These determine what behavior and instruction
|
||||
* set will be simulated, depending on desired version of 6502.
|
||||
*
|
||||
* TODO: As of version 0.6, this is still not used! All CPUs are "idealized" NMOS 6502 only.
|
||||
*/
|
||||
enum CpuBehavior {
|
||||
/**
|
||||
* The earliest NMOS 6502 includes a bug that causes the ROR instruction
|
||||
* to behave like an ASL that does not affect the carry bit. This version
|
||||
* is very rare in the wild.
|
||||
*
|
||||
* NB: Does NOT implement "unimplemented" NMOS instructions.
|
||||
*/
|
||||
NMOS_WITH_ROR_BUG,
|
||||
|
||||
/**
|
||||
* All NMOS 6502's have a bug with the indirect JMP instruction. If the
|
||||
*
|
||||
* NB: Does NOT implement "unimplemented" NMOS instructions.
|
||||
*/
|
||||
NMOS_6502,
|
||||
|
||||
@@ -68,11 +66,13 @@ public interface InstructionTable {
|
||||
return "Accumulator";
|
||||
}
|
||||
},
|
||||
|
||||
AIX {
|
||||
public String toString() {
|
||||
return "Absolute, X-Indexed Indirect";
|
||||
}
|
||||
},
|
||||
|
||||
ABS {
|
||||
public String toString() {
|
||||
return "Absolute";
|
||||
@@ -168,41 +168,44 @@ public interface InstructionTable {
|
||||
|
||||
/**
|
||||
* Instruction opcode names. This lists all opcodes for
|
||||
* NMOS 6502, CMOS 65C02, and CMOS 65C816
|
||||
* NMOS 6502, CMOS 65C02, and CMOS 65C816.
|
||||
* <p>
|
||||
* TODO (2025-03-14): This table is clearly wrong for 65C816.
|
||||
* TODO (2025-03-14): Rockwell/WDC 65C02 names conflict with 65C816 names.
|
||||
*/
|
||||
String[] opcodeNames = {
|
||||
"BRK", "ORA", "NOP", "NOP", "TSB", "ORA", "ASL", "RMB0", // 0x00-0x07
|
||||
"PHP", "ORA", "ASL", "NOP", "TSB", "ORA", "ASL", "BBR0", // 0x08-0x0f
|
||||
"BPL", "ORA", "ORA", "NOP", "TRB", "ORA", "ASL", "RMB1", // 0x10-0x17
|
||||
"CLC", "ORA", "INC", "NOP", "TRB", "ORA", "ASL", "BBR1", // 0x18-0x1f
|
||||
"JSR", "AND", "NOP", "NOP", "BIT", "AND", "ROL", "RMB2", // 0x20-0x27
|
||||
"PLP", "AND", "ROL", "NOP", "BIT", "AND", "ROL", "BBR2", // 0x28-0x2f
|
||||
"BMI", "AND", "AND", "NOP", "BIT", "AND", "ROL", "RMB3", // 0x30-0x37
|
||||
"SEC", "AND", "DEC", "NOP", "BIT", "AND", "ROL", "BBR3", // 0x38-0x3f
|
||||
"RTI", "EOR", "NOP", "NOP", "NOP", "EOR", "LSR", "RMB4", // 0x40-0x47
|
||||
"PHA", "EOR", "LSR", "NOP", "JMP", "EOR", "LSR", "BBR4", // 0x48-0x4f
|
||||
"BVC", "EOR", "EOR", "NOP", "NOP", "EOR", "LSR", "RMB5", // 0x50-0x57
|
||||
"CLI", "EOR", "PHY", "NOP", "NOP", "EOR", "LSR", "BBR5", // 0x58-0x5f
|
||||
"RTS", "ADC", "NOP", "NOP", "STZ", "ADC", "ROR", "RMB6", // 0x60-0x67
|
||||
"PLA", "ADC", "ROR", "NOP", "JMP", "ADC", "ROR", "BBR6", // 0x68-0x6f
|
||||
"BVS", "ADC", "ADC", "NOP", "STZ", "ADC", "ROR", "RMB7", // 0x70-0x77
|
||||
"SEI", "ADC", "PLY", "NOP", "JMP", "ADC", "ROR", "BBR7", // 0x78-0x7f
|
||||
"BRA", "STA", "NOP", "NOP", "STY", "STA", "STX", "SMB0", // 0x80-0x87
|
||||
"DEY", "BIT", "TXA", "NOP", "STY", "STA", "STX", "BBS0", // 0x88-0x8f
|
||||
"BCC", "STA", "STA", "NOP", "STY", "STA", "STX", "SMB1", // 0x90-0x97
|
||||
"TYA", "STA", "TXS", "NOP", "STZ", "STA", "STZ", "BBS1", // 0x98-0x9f
|
||||
"LDY", "LDA", "LDX", "NOP", "LDY", "LDA", "LDX", "SMB2", // 0xa0-0xa7
|
||||
"TAY", "LDA", "TAX", "NOP", "LDY", "LDA", "LDX", "BBS2", // 0xa8-0xaf
|
||||
"BCS", "LDA", "LDA", "NOP", "LDY", "LDA", "LDX", "SMB3", // 0xb0-0xb7
|
||||
"CLV", "LDA", "TSX", "NOP", "LDY", "LDA", "LDX", "BBS3", // 0xb8-0xbf
|
||||
"CPY", "CMP", "NOP", "NOP", "CPY", "CMP", "DEC", "SMB4", // 0xc0-0xc7
|
||||
"INY", "CMP", "DEX", "NOP", "CPY", "CMP", "DEC", "BBS4", // 0xc8-0xcf
|
||||
"BNE", "CMP", "CMP", "NOP", "NOP", "CMP", "DEC", "SMB5", // 0xd0-0xd7
|
||||
"CLD", "CMP", "PHX", "NOP", "NOP", "CMP", "DEC", "BBS5", // 0xd8-0xdf
|
||||
"CPX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "SMB6", // 0xe0-0xe7
|
||||
"INX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "BBS6", // 0xe8-0xef
|
||||
"BEQ", "SBC", "SBC", "NOP", "NOP", "SBC", "INC", "SMB7", // 0xf0-0xf7
|
||||
"SED", "SBC", "PLX", "NOP", "NOP", "SBC", "INC", "BBS7" // 0xf8-0xff
|
||||
"BRK", "ORA", "NOP", "NOP", "TSB", "ORA", "ASL", "RMB0", // 0x00-0x07
|
||||
"PHP", "ORA", "ASL", "NOP", "TSB", "ORA", "ASL", "BBR0", // 0x08-0x0f
|
||||
"BPL", "ORA", "ORA", "NOP", "TRB", "ORA", "ASL", "RMB1", // 0x10-0x17
|
||||
"CLC", "ORA", "INC", "NOP", "TRB", "ORA", "ASL", "BBR1", // 0x18-0x1f
|
||||
"JSR", "AND", "NOP", "NOP", "BIT", "AND", "ROL", "RMB2", // 0x20-0x27
|
||||
"PLP", "AND", "ROL", "NOP", "BIT", "AND", "ROL", "BBR2", // 0x28-0x2f
|
||||
"BMI", "AND", "AND", "NOP", "BIT", "AND", "ROL", "RMB3", // 0x30-0x37
|
||||
"SEC", "AND", "DEC", "NOP", "BIT", "AND", "ROL", "BBR3", // 0x38-0x3f
|
||||
"RTI", "EOR", "NOP", "NOP", "NOP", "EOR", "LSR", "RMB4", // 0x40-0x47
|
||||
"PHA", "EOR", "LSR", "NOP", "JMP", "EOR", "LSR", "BBR4", // 0x48-0x4f
|
||||
"BVC", "EOR", "EOR", "NOP", "NOP", "EOR", "LSR", "RMB5", // 0x50-0x57
|
||||
"CLI", "EOR", "PHY", "NOP", "NOP", "EOR", "LSR", "BBR5", // 0x58-0x5f
|
||||
"RTS", "ADC", "NOP", "NOP", "STZ", "ADC", "ROR", "RMB6", // 0x60-0x67
|
||||
"PLA", "ADC", "ROR", "NOP", "JMP", "ADC", "ROR", "BBR6", // 0x68-0x6f
|
||||
"BVS", "ADC", "ADC", "NOP", "STZ", "ADC", "ROR", "RMB7", // 0x70-0x77
|
||||
"SEI", "ADC", "PLY", "NOP", "JMP", "ADC", "ROR", "BBR7", // 0x78-0x7f
|
||||
"BRA", "STA", "NOP", "NOP", "STY", "STA", "STX", "SMB0", // 0x80-0x87
|
||||
"DEY", "BIT", "TXA", "NOP", "STY", "STA", "STX", "BBS0", // 0x88-0x8f
|
||||
"BCC", "STA", "STA", "NOP", "STY", "STA", "STX", "SMB1", // 0x90-0x97
|
||||
"TYA", "STA", "TXS", "NOP", "STZ", "STA", "STZ", "BBS1", // 0x98-0x9f
|
||||
"LDY", "LDA", "LDX", "NOP", "LDY", "LDA", "LDX", "SMB2", // 0xa0-0xa7
|
||||
"TAY", "LDA", "TAX", "NOP", "LDY", "LDA", "LDX", "BBS2", // 0xa8-0xaf
|
||||
"BCS", "LDA", "LDA", "NOP", "LDY", "LDA", "LDX", "SMB3", // 0xb0-0xb7
|
||||
"CLV", "LDA", "TSX", "NOP", "LDY", "LDA", "LDX", "BBS3", // 0xb8-0xbf
|
||||
"CPY", "CMP", "NOP", "NOP", "CPY", "CMP", "DEC", "SMB4", // 0xc0-0xc7
|
||||
"INY", "CMP", "DEX", "NOP", "CPY", "CMP", "DEC", "BBS4", // 0xc8-0xcf
|
||||
"BNE", "CMP", "CMP", "NOP", "NOP", "CMP", "DEC", "SMB5", // 0xd0-0xd7
|
||||
"CLD", "CMP", "PHX", "NOP", "NOP", "CMP", "DEC", "BBS5", // 0xd8-0xdf
|
||||
"CPX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "SMB6", // 0xe0-0xe7
|
||||
"INX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "BBS6", // 0xe8-0xef
|
||||
"BEQ", "SBC", "SBC", "NOP", "NOP", "SBC", "INC", "SMB7", // 0xf0-0xf7
|
||||
"SED", "SBC", "PLX", "NOP", "NOP", "SBC", "INC", "BBS7" // 0xf8-0xff
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -22,14 +22,12 @@
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.machines.MulticompMachine;
|
||||
import com.loomcom.symon.machines.SimpleMachine;
|
||||
import com.loomcom.symon.machines.SymonMachine;
|
||||
import com.loomcom.symon.machines.BenEaterMachine;
|
||||
import com.loomcom.symon.machines.*;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Locale;
|
||||
import javax.swing.JOptionPane;
|
||||
@@ -37,7 +35,8 @@ import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
public class Main {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(Main.class.getName());
|
||||
|
||||
/**
|
||||
* Main entry point to the simulator. Creates a simulator and shows the main
|
||||
* window.
|
||||
@@ -45,8 +44,8 @@ public class Main {
|
||||
* @param args Program arguments
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
Class machineClass = SymonMachine.class;
|
||||
|
||||
Class<?> machineClass = null;
|
||||
|
||||
Options options = new Options();
|
||||
|
||||
@@ -79,7 +78,7 @@ public class Main {
|
||||
machineClass = BenEaterMachine.class;
|
||||
break;
|
||||
default:
|
||||
System.err.println("Could not start Symon. Unknown machine type " + machine);
|
||||
logger.error("Could not start Symon. Unknown machine type {}", machine);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -97,7 +96,7 @@ public class Main {
|
||||
cpuBehavior = InstructionTable.CpuBehavior.CMOS_65816;
|
||||
break;
|
||||
default:
|
||||
System.err.println("Could not start Symon. Unknown cpu type " + cpu);
|
||||
logger.error("Could not start Symon. Unknown cpu type {}", cpu);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -140,16 +139,14 @@ public class Main {
|
||||
|
||||
final Simulator simulator = new Simulator(machineClass, cpuBehavior, romFile, haltOnBreak);
|
||||
|
||||
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();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
// Create the main UI window
|
||||
simulator.createAndShowUi();
|
||||
} catch (Exception e) {
|
||||
logger.error("Error during Symon UI initialization: {}", e.getMessage());
|
||||
System.exit(-1);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -163,7 +160,7 @@ public class Main {
|
||||
}
|
||||
}
|
||||
} catch (ParseException ex) {
|
||||
System.err.println("Could not start Symon. Reason: " + ex.getMessage());
|
||||
logger.error("Could not start Symon. Reason: {}", ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -27,7 +27,7 @@ import com.loomcom.symon.exceptions.*;
|
||||
|
||||
/**
|
||||
* MemoryRange is a simple container class representing a literal
|
||||
* range of memory, with a staraddress, and an end address. It has
|
||||
* range of memory, with a start address, and an end address. It has
|
||||
* guards against creating impossible memory ranges, and implements
|
||||
* some useful methods for checking address inclusion and range
|
||||
* overlaps.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyrighi (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -31,14 +31,13 @@ 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.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static com.loomcom.symon.InstructionTable.*;
|
||||
|
||||
/**
|
||||
* Symon Simulator Interface and Control.
|
||||
@@ -72,7 +71,7 @@ public class Simulator {
|
||||
private static final int MAX_STEPS_BETWEEN_UPDATES = 20000;
|
||||
|
||||
// The simulated machine
|
||||
private Machine machine;
|
||||
private final Machine machine;
|
||||
|
||||
// Number of CPU steps between CRT repaints.
|
||||
// TODO: Dynamically refresh the value at runtime based on performance figures to reach ~ 30fps.
|
||||
@@ -115,18 +114,19 @@ public class Simulator {
|
||||
|
||||
private JButton runStopButton;
|
||||
private JButton stepButton;
|
||||
private JButton stepOverButton;
|
||||
private JComboBox<String> stepCountBox;
|
||||
|
||||
private JFileChooser fileChooser;
|
||||
private PreferencesDialog preferences;
|
||||
|
||||
private Breakpoints breakpoints;
|
||||
private final Breakpoints breakpoints;
|
||||
|
||||
private final Object commandMonitorObject = new Object();
|
||||
|
||||
private MainCommand command = MainCommand.NONE;
|
||||
|
||||
private boolean haltOnBreak;
|
||||
private final boolean haltOnBreak;
|
||||
|
||||
public enum MainCommand {
|
||||
NONE,
|
||||
@@ -138,11 +138,11 @@ public class Simulator {
|
||||
*/
|
||||
private static final String[] STEPS = {"1", "5", "10", "20", "50", "100"};
|
||||
|
||||
public Simulator(Class machineClass) throws Exception {
|
||||
public Simulator(Class<?> machineClass) throws Exception {
|
||||
this(machineClass, InstructionTable.CpuBehavior.NMOS_6502, null, false);
|
||||
}
|
||||
|
||||
public Simulator(Class machineClass, InstructionTable.CpuBehavior cpuType,
|
||||
public Simulator(Class<?> machineClass, InstructionTable.CpuBehavior cpuType,
|
||||
String romFile, boolean haltOnBreak) throws Exception {
|
||||
this.haltOnBreak = haltOnBreak;
|
||||
this.breakpoints = new Breakpoints(this);
|
||||
@@ -165,7 +165,7 @@ public class Simulator {
|
||||
/**
|
||||
* Display the main simulator UI.
|
||||
*/
|
||||
public void createAndShowUi() throws IOException {
|
||||
public void createAndShowUi() {
|
||||
mainWindow = new JFrame();
|
||||
mainWindow.setTitle("6502 Simulator - " + machine.getName());
|
||||
mainWindow.setResizable(false);
|
||||
@@ -191,18 +191,22 @@ public class Simulator {
|
||||
|
||||
runStopButton = new JButton("Run");
|
||||
stepButton = new JButton("Step");
|
||||
stepOverButton = new JButton("Step Over");
|
||||
stepOverButton.setEnabled(false);
|
||||
JButton softResetButton = new JButton("Soft Reset");
|
||||
JButton hardResetButton = new JButton("Hard Reset");
|
||||
|
||||
stepCountBox = new JComboBox<>(STEPS);
|
||||
stepCountBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
stepCountBox.addActionListener(actionEvent -> {
|
||||
JComboBox<?> cb = (JComboBox<?>) actionEvent.getSource();
|
||||
String cbStepsPerClick = (String) cb.getSelectedItem();
|
||||
|
||||
stepsPerClick = 1; // Default in case of error
|
||||
|
||||
if (cbStepsPerClick != null) {
|
||||
try {
|
||||
JComboBox cb = (JComboBox) actionEvent.getSource();
|
||||
stepsPerClick = Integer.parseInt((String) cb.getSelectedItem());
|
||||
stepsPerClick = Integer.parseInt(cbStepsPerClick);
|
||||
} catch (NumberFormatException ex) {
|
||||
stepsPerClick = 1;
|
||||
stepCountBox.setSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
@@ -210,6 +214,7 @@ public class Simulator {
|
||||
|
||||
buttonContainer.add(runStopButton);
|
||||
buttonContainer.add(stepButton);
|
||||
buttonContainer.add(stepOverButton);
|
||||
buttonContainer.add(stepCountBox);
|
||||
buttonContainer.add(softResetButton);
|
||||
buttonContainer.add(hardResetButton);
|
||||
@@ -224,38 +229,25 @@ public class Simulator {
|
||||
// Bottom - buttons.
|
||||
mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END);
|
||||
|
||||
runStopButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
if (runLoop != null && runLoop.isRunning()) {
|
||||
Simulator.this.handleStop();
|
||||
} else {
|
||||
Simulator.this.handleStart();
|
||||
}
|
||||
runStopButton.addActionListener(actionEvent -> {
|
||||
if (runLoop != null && runLoop.isRunning()) {
|
||||
Simulator.this.handleStop();
|
||||
} else {
|
||||
Simulator.this.handleStart();
|
||||
}
|
||||
});
|
||||
|
||||
stepButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
Simulator.this.handleStep(stepsPerClick);
|
||||
}
|
||||
stepButton.addActionListener(actionEvent -> Simulator.this.handleStep(stepsPerClick));
|
||||
stepOverButton.addActionListener(actionEvent -> Simulator.this.handleStepOver());
|
||||
|
||||
softResetButton.addActionListener(actionEvent -> {
|
||||
// If this was a CTRL-click, do a hard reset.
|
||||
Simulator.this.handleReset(false);
|
||||
});
|
||||
|
||||
softResetButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
// If this was a CTRL-click, do a hard reset.
|
||||
Simulator.this.handleReset(false);
|
||||
}
|
||||
});
|
||||
|
||||
hardResetButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
// If this was a CTRL-click, do a hard reset.
|
||||
Simulator.this.handleReset(true);
|
||||
}
|
||||
hardResetButton.addActionListener(actionEvent -> {
|
||||
// If this was a CTRL-click, do a hard reset.
|
||||
Simulator.this.handleReset(true);
|
||||
});
|
||||
|
||||
mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
@@ -277,7 +269,7 @@ public class Simulator {
|
||||
try {
|
||||
commandMonitorObject.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
logger.error("Interrupted during wait on command monitor.");
|
||||
}
|
||||
}
|
||||
return command;
|
||||
@@ -340,12 +332,28 @@ public class Simulator {
|
||||
step();
|
||||
}
|
||||
updateVisibleState();
|
||||
stepOverButton.setEnabled(machine.getCpu().getNextIr() == JSR);
|
||||
} catch (SymonException ex) {
|
||||
logger.error("Exception during simulator step", ex);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Step over a JSR / RTS function. This function is much more like a "run()"
|
||||
* than a "step()", and may enter an infinite loop if no RTS is encountered.
|
||||
*/
|
||||
private void handleStepOver() {
|
||||
if (runLoop != null && runLoop.isRunning()) {
|
||||
// This should really never happen...
|
||||
logger.error("Can't step over when the simulator is already running!");
|
||||
return;
|
||||
}
|
||||
|
||||
runLoop = new RunLoop();
|
||||
runLoop.haltOnRts(true);
|
||||
runLoop.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a single step of the simulated system.
|
||||
*/
|
||||
@@ -365,7 +373,7 @@ public class Simulator {
|
||||
// If a key has been pressed, fill the ACIA.
|
||||
try {
|
||||
if (machine.getAcia() != null && console.hasInput()) {
|
||||
machine.getAcia().rxWrite((int) console.readInputChar());
|
||||
machine.getAcia().rxWrite(console.readInputChar());
|
||||
}
|
||||
} catch (FifoUnderrunException ex) {
|
||||
logger.error("Console type-ahead buffer underrun!");
|
||||
@@ -414,53 +422,73 @@ public class Simulator {
|
||||
*/
|
||||
class RunLoop extends Thread {
|
||||
private boolean isRunning = false;
|
||||
private boolean haltOnRts = false;
|
||||
// The number of JSR instructions we've seen since being asked to
|
||||
// step over a subroutine. While stepping over, this is incremented
|
||||
// on RTS instructions, and decremented on JSR instructions.
|
||||
private int callStackDepth = 0;
|
||||
|
||||
public boolean isRunning() {
|
||||
return isRunning;
|
||||
return this.isRunning;
|
||||
}
|
||||
|
||||
public void requestStop() {
|
||||
isRunning = false;
|
||||
this.isRunning = false;
|
||||
}
|
||||
|
||||
public void haltOnRts(boolean value) {
|
||||
this.haltOnRts = value;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
logger.debug("Starting main run loop.");
|
||||
isRunning = true;
|
||||
if (this.haltOnRts) {
|
||||
logger.debug("Running until next RTS instruction");
|
||||
} else {
|
||||
logger.debug("Running");
|
||||
}
|
||||
this.isRunning = true;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Don't allow step while the simulator is running
|
||||
stepButton.setEnabled(false);
|
||||
stepCountBox.setEnabled(false);
|
||||
menuBar.simulatorDidStart();
|
||||
// Toggle the state of the run button
|
||||
runStopButton.setText("Stop");
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
// Don't allow step while the simulator is running
|
||||
stepButton.setEnabled(false);
|
||||
stepOverButton.setEnabled(false);
|
||||
stepCountBox.setEnabled(false);
|
||||
menuBar.simulatorDidStart();
|
||||
// Toggle the state of the run button
|
||||
runStopButton.setText("Stop");
|
||||
});
|
||||
|
||||
try {
|
||||
do {
|
||||
step();
|
||||
if (this.haltOnRts) {
|
||||
var instruction = machine.getCpu().getInstruction();
|
||||
if (instruction == JSR) {
|
||||
this.callStackDepth++;
|
||||
logger.trace("Step-over call stack increased to {}", this.callStackDepth);
|
||||
} else if (this.callStackDepth > 0 && instruction == RTS) {
|
||||
this.callStackDepth--;
|
||||
logger.trace("Step-over call stack decreased to {}", this.callStackDepth);
|
||||
}
|
||||
}
|
||||
|
||||
} while (shouldContinue());
|
||||
} catch (SymonException ex) {
|
||||
logger.error("Exception in main simulator run thread. Exiting run.", ex);
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
statusPane.updateState();
|
||||
memoryWindow.updateState();
|
||||
runStopButton.setText("Run");
|
||||
stepButton.setEnabled(true);
|
||||
stepCountBox.setEnabled(true);
|
||||
if (traceLog.isVisible()) {
|
||||
traceLog.refresh();
|
||||
}
|
||||
menuBar.simulatorDidStop();
|
||||
traceLog.simulatorDidStop();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusPane.updateState();
|
||||
memoryWindow.updateState();
|
||||
runStopButton.setText("Run");
|
||||
stepButton.setEnabled(true);
|
||||
stepOverButton.setEnabled(machine.getCpu().getNextIr() == JSR);
|
||||
stepCountBox.setEnabled(true);
|
||||
if (traceLog.isVisible()) {
|
||||
traceLog.refresh();
|
||||
}
|
||||
menuBar.simulatorDidStop();
|
||||
traceLog.simulatorDidStop();
|
||||
});
|
||||
|
||||
isRunning = false;
|
||||
@@ -470,9 +498,13 @@ public class Simulator {
|
||||
* @return True if the run loop should proceed to the next step.
|
||||
*/
|
||||
private boolean shouldContinue() {
|
||||
return !breakpoints.contains(machine.getCpu().getProgramCounter()) &&
|
||||
isRunning &&
|
||||
!(preferences.getHaltOnBreak() && machine.getCpu().getInstruction() == 0x00);
|
||||
var instruction = machine.getCpu().getInstruction();
|
||||
|
||||
var stepOverHalt = this.haltOnRts && this.callStackDepth == 0 && instruction == RTS;
|
||||
var breakpointHalt = breakpoints.contains(machine.getCpu().getProgramCounter());
|
||||
var brkHalt = preferences.getHaltOnBreak() && instruction == BRK;
|
||||
|
||||
return isRunning && !(stepOverHalt || breakpointHalt || brkHalt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,12 +545,9 @@ public class Simulator {
|
||||
// Now load the program at the starting address.
|
||||
loadProgram(program, preferences.getProgramStartAddress());
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
console.reset();
|
||||
breakpoints.refresh();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
console.reset();
|
||||
breakpoints.refresh();
|
||||
});
|
||||
|
||||
// TODO: "Don't Show Again" checkbox
|
||||
@@ -556,7 +585,7 @@ public class Simulator {
|
||||
long fileSize = romFile.length();
|
||||
|
||||
if (fileSize != machine.getRomSize()) {
|
||||
throw new IOException("ROM file must be exactly " + String.valueOf(machine.getRomSize()) + " bytes.");
|
||||
throw new IOException("ROM file must be exactly " + machine.getRomSize() + " bytes.");
|
||||
}
|
||||
|
||||
// Load the new ROM image
|
||||
@@ -650,30 +679,27 @@ public class Simulator {
|
||||
}
|
||||
|
||||
class SetFontAction extends AbstractAction {
|
||||
private int size;
|
||||
private final int size;
|
||||
|
||||
public SetFontAction(int size) {
|
||||
super(Integer.toString(size) + " pt", null);
|
||||
super(size + " pt", null);
|
||||
this.size = size;
|
||||
putValue(SHORT_DESCRIPTION, "Set font to " + size + "pt.");
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
console.setFont(new Font("Monospaced", Font.PLAIN, size));
|
||||
mainWindow.pack();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
console.setFont(new Font("Monospaced", Font.PLAIN, size));
|
||||
mainWindow.pack();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class SetSpeedAction extends AbstractAction {
|
||||
private int speed;
|
||||
private final int speed;
|
||||
|
||||
public SetSpeedAction(int speed) {
|
||||
super(Integer.toString(speed) + " MHz", null);
|
||||
super(speed + " MHz", null);
|
||||
this.speed = speed;
|
||||
putValue(SHORT_DESCRIPTION, "Set simulated speed to " + speed + " MHz.");
|
||||
}
|
||||
@@ -689,7 +715,7 @@ public class Simulator {
|
||||
}
|
||||
|
||||
class SetCpuAction extends AbstractAction {
|
||||
private Cpu.CpuBehavior behavior;
|
||||
private final Cpu.CpuBehavior behavior;
|
||||
|
||||
public SetCpuAction(String cpu, Cpu.CpuBehavior behavior) {
|
||||
super(cpu, null);
|
||||
@@ -729,11 +755,7 @@ public class Simulator {
|
||||
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
synchronized (memoryWindow) {
|
||||
if (memoryWindow.isVisible()) {
|
||||
memoryWindow.setVisible(false);
|
||||
} else {
|
||||
memoryWindow.setVisible(true);
|
||||
}
|
||||
memoryWindow.setVisible(!memoryWindow.isVisible());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -746,11 +768,7 @@ public class Simulator {
|
||||
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
synchronized (videoWindow) {
|
||||
if (videoWindow.isVisible()) {
|
||||
videoWindow.setVisible(false);
|
||||
} else {
|
||||
videoWindow.setVisible(true);
|
||||
}
|
||||
videoWindow.setVisible(!videoWindow.isVisible());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -763,11 +781,7 @@ public class Simulator {
|
||||
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
synchronized (breakpointsWindow) {
|
||||
if (breakpointsWindow.isVisible()) {
|
||||
breakpointsWindow.setVisible(false);
|
||||
} else {
|
||||
breakpointsWindow.setVisible(true);
|
||||
}
|
||||
breakpointsWindow.setVisible(!breakpointsWindow.isVisible());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -963,15 +977,11 @@ public class Simulator {
|
||||
|
||||
private void updateVisibleState() {
|
||||
// Immediately update the UI.
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Now update the state
|
||||
statusPane.updateState();
|
||||
memoryWindow.updateState();
|
||||
if (traceLog.shouldUpdate()) {
|
||||
traceLog.refresh();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusPane.updateState();
|
||||
memoryWindow.updateState();
|
||||
if (traceLog.shouldUpdate()) {
|
||||
traceLog.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -32,7 +32,7 @@ import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
|
||||
public abstract class Acia extends Device {
|
||||
|
||||
private String name;
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Register addresses
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -121,9 +121,6 @@ public class Acia6551 extends Acia {
|
||||
// Mask the lower four bits to get the baud rate.
|
||||
int baudSelector = data & 0x0f;
|
||||
switch (baudSelector) {
|
||||
case 0:
|
||||
rate = 0;
|
||||
break;
|
||||
case 1:
|
||||
rate = 50;
|
||||
break;
|
||||
@@ -169,6 +166,8 @@ public class Acia6551 extends Acia {
|
||||
case 15:
|
||||
rate = 19200;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
setBaudRate(rate);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -31,7 +31,7 @@ import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
/**
|
||||
* This is a simulation of the Motorola 6850 ACIA, with limited
|
||||
* functionality.
|
||||
*
|
||||
* <p>
|
||||
* Unlike a 16550 UART, the 6850 ACIA has only one-byte transmit and
|
||||
* receive buffers. It is the programmer's responsibility to check the
|
||||
* status (full or empty) for transmit and receive buffers before
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -26,8 +26,6 @@ package com.loomcom.symon.devices;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Simulation of a 6545 CRTC and virtual CRT output.
|
||||
*/
|
||||
@@ -87,9 +85,9 @@ public class Crtc extends Device {
|
||||
private boolean displayEnableSkew = false;
|
||||
private boolean cursorSkew = false;
|
||||
|
||||
private Memory memory;
|
||||
private final Memory memory;
|
||||
|
||||
public Crtc(int deviceAddress, Memory memory) throws MemoryRangeException, IOException {
|
||||
public Crtc(int deviceAddress, Memory memory) throws MemoryRangeException {
|
||||
super(deviceAddress, deviceAddress + 2, "CRTC");
|
||||
this.memory = memory;
|
||||
|
||||
@@ -120,19 +118,17 @@ public class Crtc extends Device {
|
||||
|
||||
@Override
|
||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||
switch (address) {
|
||||
case REGISTER_RW:
|
||||
switch (currentRegister) {
|
||||
case CURSOR_POSITION_LOW:
|
||||
return cursorPosition & 0xff;
|
||||
case CURSOR_POSITION_HIGH:
|
||||
return cursorPosition >> 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
if (address == REGISTER_RW) {
|
||||
switch (currentRegister) {
|
||||
case CURSOR_POSITION_LOW:
|
||||
return cursorPosition & 0xff;
|
||||
case CURSOR_POSITION_HIGH:
|
||||
return cursorPosition >> 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -45,7 +45,7 @@ public abstract class Device implements Comparable<Device> {
|
||||
/**
|
||||
* The memory range for this device.
|
||||
*/
|
||||
private MemoryRange memoryRange;
|
||||
private final MemoryRange memoryRange;
|
||||
|
||||
/**
|
||||
* The name of the device.
|
||||
@@ -60,7 +60,7 @@ public abstract class Device implements Comparable<Device> {
|
||||
/**
|
||||
* Listeners to notify on update.
|
||||
*/
|
||||
private Set<DeviceChangeListener> deviceChangeListeners;
|
||||
private final Set<DeviceChangeListener> deviceChangeListeners;
|
||||
|
||||
public Device(int startAddress, int endAddress, String name) throws MemoryRangeException {
|
||||
this.memoryRange = new MemoryRange(startAddress, endAddress);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -30,8 +30,8 @@ import com.loomcom.symon.exceptions.*;
|
||||
|
||||
public class Memory extends Device {
|
||||
|
||||
private boolean readOnly;
|
||||
private int[] mem;
|
||||
private final boolean readOnly;
|
||||
private final int[] mem;
|
||||
|
||||
/* Initialize all locations to 0x00 (BRK) */
|
||||
private static final int DEFAULT_FILL = 0x00;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -119,7 +119,10 @@ public class SdController extends Device {
|
||||
if (sdImageFile != null) {
|
||||
try {
|
||||
FileInputStream fis = new FileInputStream(sdImageFile);
|
||||
fis.skip(this.position);
|
||||
long bytesSkipped = fis.skip(this.position);
|
||||
if (bytesSkipped < this.position) {
|
||||
logger.log(Level.WARNING, "Failed to seek to position " + this.position);
|
||||
}
|
||||
int read = fis.read(readBuffer);
|
||||
if (read < SECTOR_SIZE) {
|
||||
logger.log(Level.WARNING, "not enough data to fill read buffer from SD image file");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -27,83 +27,23 @@ import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
|
||||
/**
|
||||
* Very basic implementation of a MOS 6522 VIA.
|
||||
*
|
||||
* TODO: Implement timers as threads.
|
||||
* Skeleton for future implementation of a MOS 6522 VIA.
|
||||
*/
|
||||
public class Via6522 extends Pia {
|
||||
public static final int VIA_SIZE = 16;
|
||||
|
||||
enum Register {
|
||||
ORB, ORA, DDRB, DDRA, T1C_L, T1C_H, T1L_L, T1L_H,
|
||||
T2C_L, T2C_H, SR, ACR, PCR, IFR, IER, ORA_H
|
||||
}
|
||||
|
||||
public Via6522(int address) throws MemoryRangeException {
|
||||
super(address, address + VIA_SIZE - 1, "MOS 6522 VIA");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int address, int data) throws MemoryAccessException {
|
||||
Register[] registers = Register.values();
|
||||
|
||||
if (address >= registers.length) {
|
||||
throw new MemoryAccessException("Unknown register: " + address);
|
||||
}
|
||||
|
||||
Register r = registers[address];
|
||||
|
||||
switch (r) {
|
||||
case ORA:
|
||||
case ORB:
|
||||
case DDRA:
|
||||
case DDRB:
|
||||
case T1C_L:
|
||||
case T1C_H:
|
||||
case T1L_L:
|
||||
case T1L_H:
|
||||
case T2C_L:
|
||||
case T2C_H:
|
||||
case SR:
|
||||
case ACR:
|
||||
case PCR:
|
||||
case IFR:
|
||||
case IER:
|
||||
case ORA_H:
|
||||
default:
|
||||
}
|
||||
// TODO: Implement write.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||
Register[] registers = Register.values();
|
||||
|
||||
if (address >= registers.length) {
|
||||
throw new MemoryAccessException("Unknown register: " + address);
|
||||
}
|
||||
|
||||
Register r = registers[address];
|
||||
|
||||
switch (r) {
|
||||
case ORA:
|
||||
case ORB:
|
||||
case DDRA:
|
||||
case DDRB:
|
||||
case T1C_L:
|
||||
case T1C_H:
|
||||
case T1L_L:
|
||||
case T1L_H:
|
||||
case T2C_L:
|
||||
case T2C_H:
|
||||
case SR:
|
||||
case ACR:
|
||||
case PCR:
|
||||
case IFR:
|
||||
case IER:
|
||||
case ORA_H:
|
||||
default:
|
||||
}
|
||||
|
||||
// TODO: Implement read.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Portions Copyright (c) 2009-2011 Graham Edgecombe
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
@@ -25,13 +25,8 @@
|
||||
package com.loomcom.symon.jterminal;
|
||||
|
||||
import com.loomcom.symon.jterminal.vt100.Vt100TerminalModel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.AdjustmentEvent;
|
||||
import java.awt.event.AdjustmentListener;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JScrollBar;
|
||||
|
||||
@@ -88,10 +83,6 @@ public class JTerminal extends JComponent {
|
||||
revalidate();
|
||||
}
|
||||
|
||||
public int getBorderWidth() {
|
||||
return borderWidth;
|
||||
}
|
||||
|
||||
public void setFont(Font font) {
|
||||
this.font = font;
|
||||
setCellWidthAndHeight(font);
|
||||
@@ -113,13 +104,6 @@ public class JTerminal extends JComponent {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void println(String str) {
|
||||
if (str == null) {
|
||||
throw new NullPointerException("str");
|
||||
}
|
||||
print(str.concat("\r\n"));
|
||||
}
|
||||
|
||||
public void print(String str) {
|
||||
model.print(str);
|
||||
}
|
||||
@@ -177,12 +161,8 @@ public class JTerminal extends JComponent {
|
||||
int bufferSize = model.getBufferSize();
|
||||
|
||||
if (bufferSize > rows) {
|
||||
scrollBar = new JScrollBar(1, 0, rows, 0, bufferSize + 1);
|
||||
scrollBar.addAdjustmentListener(new AdjustmentListener() {
|
||||
public void adjustmentValueChanged(AdjustmentEvent evt) {
|
||||
repaint();
|
||||
}
|
||||
});
|
||||
scrollBar = new JScrollBar(Adjustable.VERTICAL, 0, rows, 0, bufferSize + 1);
|
||||
scrollBar.addAdjustmentListener(evt -> repaint());
|
||||
add("After", scrollBar);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,51 +32,45 @@ import com.loomcom.symon.jterminal.bell.BellStrategy;
|
||||
*/
|
||||
public interface TerminalModel {
|
||||
|
||||
/**
|
||||
* Gets the bell strategy.
|
||||
* @return The bell strategy.
|
||||
*/
|
||||
public BellStrategy getBellStrategy();
|
||||
|
||||
/**
|
||||
* Sets the bell strategy.
|
||||
* @param strategy The bell strategy.
|
||||
* @throws NullPointerException if the strategy is {@code null}.
|
||||
*/
|
||||
public void setBellStrategy(BellStrategy strategy);
|
||||
void setBellStrategy(BellStrategy strategy);
|
||||
|
||||
/**
|
||||
* Clears the terminal.
|
||||
*/
|
||||
public void clear();
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Moves the cursor back n characters.
|
||||
* @param n The number of characters.
|
||||
* @throws IllegalArgumentException if n is not positive.
|
||||
*/
|
||||
public void moveCursorBack(int n);
|
||||
void moveCursorBack(int n);
|
||||
|
||||
/**
|
||||
* Moves the cursor forward n characters.
|
||||
* @param n The number of characters.
|
||||
* @throws IllegalArgumentException if n is not positive.
|
||||
*/
|
||||
public void moveCursorForward(int n);
|
||||
void moveCursorForward(int n);
|
||||
|
||||
/**
|
||||
* Moves the cursor down n characters.
|
||||
* @param n The number of characters.
|
||||
* @throws IllegalArgumentException if n is not positive.
|
||||
*/
|
||||
public void moveCursorDown(int n);
|
||||
void moveCursorDown(int n);
|
||||
|
||||
/**
|
||||
* Moves the cursor up n characters.
|
||||
* @param n The number of characters.
|
||||
* @throws IllegalArgumentException if n is not positive.
|
||||
*/
|
||||
public void moveCursorUp(int n);
|
||||
void moveCursorUp(int n);
|
||||
|
||||
/**
|
||||
* Sets a cell.
|
||||
@@ -86,7 +80,7 @@ public interface TerminalModel {
|
||||
* @throws IndexOutOfBoundsException if the column and/or row number(s) are
|
||||
* out of bounds.
|
||||
*/
|
||||
public void setCell(int column, int row, TerminalCell cell);
|
||||
void setCell(int column, int row, TerminalCell cell);
|
||||
|
||||
/**
|
||||
* Gets a cell.
|
||||
@@ -96,7 +90,7 @@ public interface TerminalModel {
|
||||
* @throws IndexOutOfBoundsException if the column and/or row number(s) are
|
||||
* out of bounds.
|
||||
*/
|
||||
public TerminalCell getCell(int column, int row);
|
||||
TerminalCell getCell(int column, int row);
|
||||
|
||||
/**
|
||||
* Prints the specified string to the terminal at the cursor position,
|
||||
@@ -105,63 +99,63 @@ public interface TerminalModel {
|
||||
* @param str The string to print.
|
||||
* @throws NullPointerException if the string is {@code null}.
|
||||
*/
|
||||
public void print(String str);
|
||||
void print(String str);
|
||||
|
||||
/**
|
||||
* Gets the number of columns.
|
||||
* @return The number of columns.
|
||||
*/
|
||||
public int getColumns();
|
||||
int getColumns();
|
||||
|
||||
/**
|
||||
* Gets the number of rows.
|
||||
* @return The number of rows.
|
||||
*/
|
||||
public int getRows();
|
||||
int getRows();
|
||||
|
||||
/**
|
||||
* Gets the buffer size.
|
||||
* @return The buffer size.
|
||||
*/
|
||||
public int getBufferSize();
|
||||
int getBufferSize();
|
||||
|
||||
/**
|
||||
* Gets the cursor row.
|
||||
* @return The cursor row.
|
||||
*/
|
||||
public int getCursorRow();
|
||||
int getCursorRow();
|
||||
|
||||
/**
|
||||
* Sets the cursor row.
|
||||
* @param row The cursor row.
|
||||
* @throws IllegalArgumentException if the row is out of the valid range.
|
||||
*/
|
||||
public void setCursorRow(int row);
|
||||
void setCursorRow(int row);
|
||||
|
||||
/**
|
||||
* Gets the cursor column.
|
||||
* @return The cursor column.
|
||||
*/
|
||||
public int getCursorColumn();
|
||||
int getCursorColumn();
|
||||
|
||||
/**
|
||||
* Sets the cursor column.
|
||||
* @param column The cursor column.
|
||||
* @throws IllegalArgumentException if the column is out of the valid range.
|
||||
*/
|
||||
public void setCursorColumn(int column);
|
||||
void setCursorColumn(int column);
|
||||
|
||||
/**
|
||||
* Gets the default background color.
|
||||
* @return The default background color.
|
||||
*/
|
||||
public Color getDefaultBackgroundColor();
|
||||
Color getDefaultBackgroundColor();
|
||||
|
||||
/**
|
||||
* Gets the default foreground color.
|
||||
* @return The default foreground color.
|
||||
*/
|
||||
public Color getDefaultForegroundColor();
|
||||
Color getDefaultForegroundColor();
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public interface BellStrategy {
|
||||
/**
|
||||
* Sounds the bell.
|
||||
*/
|
||||
public void soundBell();
|
||||
void soundBell();
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class AnsiControlSequence {
|
||||
throw new NullPointerException("parameters");
|
||||
}
|
||||
this.command = command;
|
||||
if (parameters.length == 1 && parameters[0].equals("")) {
|
||||
if (parameters.length == 1 && parameters[0].isEmpty()) {
|
||||
this.parameters = new String[0];
|
||||
} else {
|
||||
this.parameters = parameters.clone();
|
||||
|
||||
@@ -32,13 +32,13 @@ public interface AnsiControlSequenceListener {
|
||||
* Called when a control sequence has been parsed.
|
||||
* @param seq The control sequence.
|
||||
*/
|
||||
public void parsedControlSequence(AnsiControlSequence seq);
|
||||
void parsedControlSequence(AnsiControlSequence seq);
|
||||
|
||||
/**
|
||||
* Called when a string has been parsed.
|
||||
* @param str The string.
|
||||
*/
|
||||
public void parsedString(String str);
|
||||
void parsedString(String str);
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import java.io.StringReader;
|
||||
public class AnsiControlSequenceParser {
|
||||
|
||||
/**
|
||||
* The multi-byte control sequence introducer.
|
||||
* The multibyte control sequence introducer.
|
||||
*/
|
||||
private static final char[] MULTI_CSI = new char[] { 27, '[' };
|
||||
|
||||
@@ -43,7 +43,7 @@ public class AnsiControlSequenceParser {
|
||||
private static final char SINGLE_CSI = 155;
|
||||
|
||||
/**
|
||||
* The buffer of data from the last call to {@link #parse()}. This is
|
||||
* The buffer of data from the last call to "parse()". This is
|
||||
* populated with data if an escape sequence is not complete.
|
||||
*/
|
||||
private StringBuilder buffer = new StringBuilder();
|
||||
@@ -70,13 +70,10 @@ public class AnsiControlSequenceParser {
|
||||
str = buffer.toString().concat(str);
|
||||
buffer = new StringBuilder();
|
||||
}
|
||||
Reader reader = new StringReader(str);
|
||||
try {
|
||||
try {
|
||||
parse(reader);
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
try {
|
||||
try (Reader reader = new StringReader(str)) {
|
||||
parse(reader);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
/* ignore */
|
||||
}
|
||||
@@ -147,7 +144,7 @@ public class AnsiControlSequenceParser {
|
||||
if (!finishedSequence) {
|
||||
// not an ideal solution if they used the two byte CSI, but it's
|
||||
// easier and cleaner than keeping track of it
|
||||
buffer.append((char) SINGLE_CSI);
|
||||
buffer.append(SINGLE_CSI);
|
||||
buffer.append(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
||||
@Override
|
||||
public void parsedControlSequence(AnsiControlSequence seq) {
|
||||
char command = seq.getCommand();
|
||||
int n;
|
||||
String[] parameters = seq.getParameters();
|
||||
|
||||
switch (command) {
|
||||
@@ -63,7 +64,7 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
int n = 1;
|
||||
n = 1;
|
||||
if (parameters.length == 1) {
|
||||
n = Integer.parseInt(parameters[0]);
|
||||
}
|
||||
@@ -73,7 +74,7 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
||||
moveCursorDown(n);
|
||||
} else if (command == 'C') {
|
||||
moveCursorForward(n);
|
||||
} else if (command == 'D') {
|
||||
} else {
|
||||
moveCursorBack(n);
|
||||
}
|
||||
break;
|
||||
@@ -85,7 +86,7 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
||||
}
|
||||
if (command == 'E') {
|
||||
moveCursorDown(n);
|
||||
} else if (command == 'F') {
|
||||
} else {
|
||||
moveCursorUp(n);
|
||||
}
|
||||
setCursorColumn(0);
|
||||
@@ -465,12 +466,7 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
||||
return bufferSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BellStrategy getBellStrategy() {
|
||||
return bellStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void setBellStrategy(BellStrategy strategy) {
|
||||
if (strategy == null) {
|
||||
throw new NullPointerException("strategy");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Chelsea Wilkinson <mail@chelseawilkinson.me>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Chelsea Wilkinson <mail@chelseawilkinson.me>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -42,9 +42,9 @@ public interface Machine {
|
||||
Memory getRam();
|
||||
|
||||
Acia getAcia();
|
||||
|
||||
|
||||
Pia getPia();
|
||||
|
||||
|
||||
Crtc getCrtc();
|
||||
|
||||
Memory getRom();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -91,7 +91,7 @@ public class SimpleMachine implements Machine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRom(Memory rom) throws MemoryRangeException {
|
||||
public void setRom(Memory rom) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -30,10 +30,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
/**
|
||||
@@ -46,8 +43,8 @@ public class BreakpointsWindow extends JFrame {
|
||||
private static final Dimension FRAME_SIZE = new Dimension(240, 280);
|
||||
private static final String EMPTY_STRING = "";
|
||||
|
||||
private JFrame mainWindow;
|
||||
private Breakpoints breakpoints;
|
||||
private final JFrame mainWindow;
|
||||
private final Breakpoints breakpoints;
|
||||
|
||||
public BreakpointsWindow(Breakpoints breakpoints,
|
||||
JFrame mainWindow) {
|
||||
@@ -75,16 +72,7 @@ public class BreakpointsWindow extends JFrame {
|
||||
breakpointsTable.setShowGrid(true);
|
||||
breakpointsTable.setGridColor(Color.LIGHT_GRAY);
|
||||
breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
breakpointsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
if (e.getFirstIndex() > -1) {
|
||||
removeButton.setEnabled(true);
|
||||
} else {
|
||||
removeButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
breakpointsTable.getSelectionModel().addListSelectionListener(e -> removeButton.setEnabled(e.getFirstIndex() > -1));
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(breakpointsTable);
|
||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
@@ -92,45 +80,33 @@ public class BreakpointsWindow extends JFrame {
|
||||
|
||||
breakpointsPanel.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
ActionListener addBreakpointListener = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int value;
|
||||
ActionListener addBreakpointListener = e -> {
|
||||
int value;
|
||||
|
||||
String newBreakpoint = addTextField.getText();
|
||||
String newBreakpoint = addTextField.getText();
|
||||
|
||||
if (newBreakpoint == null || newBreakpoint.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
value = (Integer.parseInt(addTextField.getText(), 16) & 0xffff);
|
||||
} catch (NumberFormatException ex) {
|
||||
logger.warn("Can't parse page number {}", newBreakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
breakpoints.addBreakpoint(value);
|
||||
|
||||
logger.debug("Added breakpoint ${}", Utils.wordToHex(value));
|
||||
|
||||
addTextField.setText(EMPTY_STRING);
|
||||
if (newBreakpoint == null || newBreakpoint.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
value = (Integer.parseInt(addTextField.getText(), 16) & 0xffff);
|
||||
} catch (NumberFormatException ex) {
|
||||
logger.warn("Can't parse page number {}", newBreakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
breakpoints.addBreakpoint(value);
|
||||
|
||||
logger.debug("Added breakpoint ${}", Utils.wordToHex(value));
|
||||
|
||||
addTextField.setText(EMPTY_STRING);
|
||||
};
|
||||
|
||||
addButton.addActionListener(addBreakpointListener);
|
||||
addTextField.addActionListener(addBreakpointListener);
|
||||
|
||||
removeButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow());
|
||||
}
|
||||
});
|
||||
removeButton.addActionListener(e -> breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow()));
|
||||
|
||||
controlPanel.add(addTextField);
|
||||
controlPanel.add(addButton);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -51,8 +51,8 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
|
||||
private static final boolean SWAP_CR_AND_LF = true;
|
||||
|
||||
// If true, send CRLF (0x0d 0x0a) whenever CR is typed
|
||||
private boolean sendCrForLf;
|
||||
private FifoRingBuffer<Character> typeAheadBuffer;
|
||||
private final boolean sendCrForLf;
|
||||
private final FifoRingBuffer<Character> typeAheadBuffer;
|
||||
|
||||
public Console(int columns, int rows, Font font, boolean sendCrForLf) {
|
||||
super(new Vt100TerminalModel(columns, rows), font);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -49,7 +49,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MemoryWindow.class);
|
||||
|
||||
private MemoryTableModel memoryTableModel;
|
||||
private final MemoryTableModel memoryTableModel;
|
||||
private JTable memoryTable;
|
||||
private JTextField pageNumberTextField;
|
||||
private JButton previousPageButton;
|
||||
@@ -111,7 +111,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set-up the UI.
|
||||
* Set up the UI.
|
||||
*/
|
||||
private void createUi() {
|
||||
setTitle("Memory Contents");
|
||||
@@ -230,7 +230,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
||||
* A JTable that will automatically select all text in a cell
|
||||
* being edited.
|
||||
*/
|
||||
private class MemoryTable extends JTable {
|
||||
private static class MemoryTable extends JTable {
|
||||
|
||||
public MemoryTable(TableModel tableModel) {
|
||||
super(tableModel);
|
||||
@@ -242,7 +242,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
||||
|
||||
final Component editor = getEditorComponent();
|
||||
|
||||
if (editor != null && editor instanceof JTextComponent) {
|
||||
if (editor instanceof JTextComponent) {
|
||||
((JTextComponent) editor).selectAll();
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
||||
}
|
||||
}
|
||||
|
||||
private class MemoryTableCellRenderer extends DefaultTableCellRenderer {
|
||||
private static class MemoryTableCellRenderer extends DefaultTableCellRenderer {
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value,
|
||||
@@ -272,9 +272,9 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
||||
/**
|
||||
* The model that backs the Memory Table.
|
||||
*/
|
||||
private class MemoryTableModel extends AbstractTableModel {
|
||||
private static class MemoryTableModel extends AbstractTableModel {
|
||||
|
||||
private Bus bus;
|
||||
private final Bus bus;
|
||||
private int pageNumber;
|
||||
|
||||
private static final int COLUMN_COUNT = 17;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -64,12 +64,13 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
private static final long WINDOW_REPAINT_INTERVAL = 66; // 30fps rate
|
||||
private static final int CHAR_WIDTH = 8;
|
||||
private static final int CHAR_HEIGHT = 8;
|
||||
private static final String ASCII_ROM = "/ascii.rom";
|
||||
|
||||
private final int scaleX, scaleY;
|
||||
private final boolean shouldScale;
|
||||
|
||||
private BufferedImage image;
|
||||
private int[] charRom;
|
||||
private final int[] charRom;
|
||||
|
||||
private int horizontalDisplayed;
|
||||
private int verticalDisplayed;
|
||||
@@ -78,9 +79,9 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
private boolean hideCursor;
|
||||
|
||||
private Dimension dimensions;
|
||||
private Crtc crtc;
|
||||
private final Crtc crtc;
|
||||
|
||||
private ScheduledExecutorService scheduler;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private ScheduledFuture<?> cursorBlinker;
|
||||
|
||||
/**
|
||||
@@ -123,12 +124,9 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
*/
|
||||
private class CursorBlinker implements Runnable {
|
||||
public void run() {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (cursorBlinkRate > 0) {
|
||||
hideCursor = !hideCursor;
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (cursorBlinkRate > 0) {
|
||||
hideCursor = !hideCursor;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -136,12 +134,9 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
|
||||
private class WindowPainter implements Runnable {
|
||||
public void run() {
|
||||
SwingUtilities.invokeLater(new Runnable () {
|
||||
@Override
|
||||
public void run() {
|
||||
if (VideoWindow.this.isVisible()) {
|
||||
VideoWindow.this.repaint();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (VideoWindow.this.isVisible()) {
|
||||
VideoWindow.this.repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -152,7 +147,7 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
|
||||
this.scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
this.crtc = crtc;
|
||||
this.charRom = loadCharRom("/ascii.rom");
|
||||
this.charRom = loadCharRom();
|
||||
this.scaleX = scaleX;
|
||||
this.scaleY = scaleY;
|
||||
this.shouldScale = (scaleX > 1 || scaleY > 1);
|
||||
@@ -292,14 +287,17 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
* Since the BufferedImage is a TYPE_BYTE_BINARY, the data must be converted
|
||||
* into a single byte per pixel, 0 for black and 255 for white.
|
||||
|
||||
* @param resource The ROM file resource to load.
|
||||
* @return An array of glyphs, each ready for insertion.
|
||||
* @throws IOException
|
||||
* @throws IOException Failure to load character ROM.
|
||||
*/
|
||||
private int[] loadCharRom(String resource) throws IOException {
|
||||
BufferedInputStream bis = null;
|
||||
try {
|
||||
bis = new BufferedInputStream(this.getClass().getResourceAsStream(resource));
|
||||
private int[] loadCharRom() throws IOException {
|
||||
var resource = this.getClass().getResourceAsStream(ASCII_ROM);
|
||||
|
||||
if (resource == null) {
|
||||
throw new IOException("Resource " + ASCII_ROM + " not found");
|
||||
}
|
||||
|
||||
try (BufferedInputStream bis = new BufferedInputStream(resource)) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
while (bis.available() > 0) {
|
||||
bos.write(bis.read());
|
||||
@@ -314,7 +312,7 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
int[] converted = new int[raw.length * CHAR_WIDTH];
|
||||
|
||||
int romIndex = 0;
|
||||
for (int i = 0; i < converted.length;) {
|
||||
for (int i = 0; i < converted.length; ) {
|
||||
byte charRow = raw[romIndex++];
|
||||
|
||||
for (int j = 7; j >= 0; j--) {
|
||||
@@ -322,10 +320,6 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
}
|
||||
}
|
||||
return converted;
|
||||
} finally {
|
||||
if (bis != null) {
|
||||
bis.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -34,8 +34,8 @@ import java.util.Queue;
|
||||
*/
|
||||
public class FifoRingBuffer<E> implements Iterable<E> {
|
||||
|
||||
private Queue<E> fifoBuffer;
|
||||
private int maxLength;
|
||||
private final Queue<E> fifoBuffer;
|
||||
private final int maxLength;
|
||||
|
||||
public FifoRingBuffer(int maxLength) {
|
||||
this.fifoBuffer = new LinkedList<>();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -670,4 +670,78 @@ public class CpuTest extends TestCase {
|
||||
cpu.step();
|
||||
assertEquals(0x3E, cpu.getAccumulator());
|
||||
}
|
||||
|
||||
// Have tests:
|
||||
// ACC, AIX, ABS, ABX, ABY, IMP, IMM, IND, XIN, INY,
|
||||
// REL, ZPG, ZPI
|
||||
|
||||
// Need to test:
|
||||
// ZPR, ZPX, ZPY, NUL
|
||||
|
||||
// Question: Should 'NUL' even exist? Isn't it just 'IMP'?
|
||||
|
||||
public void testDisassembleOpHandlesAccumulatorCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x2a, new int[]{});
|
||||
assertEquals("ROL", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesAbsoluteXIndexedIndirectCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x7c, new int[]{0x00, 0x1a});
|
||||
assertEquals("JMP ($1A00,X)", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesAbsoluteCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x0d, new int[]{0x10, 0x0c});
|
||||
assertEquals("ORA $0C10", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesAbsoluteXIndexedCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x1d, new int[]{0x00, 0x1c});
|
||||
assertEquals("ORA $1C00,X", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesAbsoluteYIndexedCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x19, new int[]{0x00, 0x1c});
|
||||
assertEquals("ORA $1C00,Y", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesImpliedCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x00, new int[]{});
|
||||
assertEquals("BRK", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesIndirectCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x6c, new int[]{0x00, 0x1c});
|
||||
assertEquals("JMP ($1C00)", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesImmediateCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0xA9, new int[]{0xff});
|
||||
assertEquals("LDA #$FF", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesXIndexedIndirectCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x01, new int[]{0x10});
|
||||
assertEquals("ORA ($10,X)", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesIndirectYIndexedCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x11, new int[]{0x10});
|
||||
assertEquals("ORA ($10),Y", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesRelativeCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x10, new int[]{0xff});
|
||||
assertEquals("BPL $FF", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesZeroPageCorrectly() {
|
||||
var dis = Cpu.disassembleOp(0x24, new int[]{0x10});
|
||||
assertEquals("BIT $10", dis);
|
||||
}
|
||||
|
||||
public void testDisassembleOpHandlesZeroPageIndirectCorrectly() throws Exception {
|
||||
var dis = Cpu.disassembleOp(0xB2, new int[]{0x1a});
|
||||
assertEquals("LDA ($1A)", dis);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user