Initial version: Based on last source available in SourceForge, converted to a Maven project format

This commit is contained in:
Brendan Robert 2014-09-07 16:10:04 -05:00
parent c41c87c17a
commit 6341001743
217 changed files with 29994 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

2
.gitignore vendored
View File

@ -10,3 +10,5 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
/jace/nbproject/private/
/jace/target/

3
manifest.mf Normal file
View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

48
nbactions.xml Normal file
View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<actions>
<action>
<actionName>run</actionName>
<packagings>
<packaging>jar</packaging>
</packagings>
<goals>
<goal>-X</goal>
<goal>-e</goal>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-classpath target/jace-2.0-SNAPSHOT.jar jace.Emulator</exec.args>
<exec.executable>java</exec.executable>
</properties>
</action>
<action>
<actionName>debug</actionName>
<packagings>
<packaging>jar</packaging>
</packagings>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath jace.Emulator</exec.args>
<exec.executable>java</exec.executable>
<jpda.listen>true</jpda.listen>
</properties>
</action>
<action>
<actionName>profile</actionName>
<packagings>
<packaging>jar</packaging>
</packagings>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
</goals>
<properties>
<exec.args>-classpath %classpath jace.Emulator</exec.args>
<exec.executable>java</exec.executable>
</properties>
</action>
</actions>

108
pom.xml Normal file
View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.badvision</groupId>
<artifactId>jace</artifactId>
<version>2.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>jace</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mainClass>jace.MainApp</mainClass>
</properties>
<organization>
<!-- Used as the 'Vendor' for JNLP generation -->
<name>Your Organisation</name>
</organization>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>unpack-dependencies</id>
<phase>package</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<excludeScope>system</excludeScope>
<excludeGroupIds>junit,org.mockito,org.hamcrest</excludeGroupIds>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<id>unpack-dependencies</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${java.home}/../bin/javafxpackager</executable>
<arguments>
<argument>-createjar</argument>
<argument>-nocss2bin</argument>
<argument>-appclass</argument>
<argument>${mainClass}</argument>
<argument>-srcdir</argument>
<argument>${project.build.directory}/classes</argument>
<argument>-outdir</argument>
<argument>${project.build.directory}</argument>
<argument>-outfile</argument>
<argument>${project.build.finalName}.jar</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>default-cli</id>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${java.home}/bin/java</executable>
<commandlineArgs>${runfx.args}</commandlineArgs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArguments>
<bootclasspath>${sun.boot.class.path}${path.separator}${java.home}/lib/jfxrt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.16</version>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>${java.home}/lib/jfxrt.jar</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
</plugin>
</plugins>
</build>
</project>

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

BIN
src/main/.DS_Store vendored Normal file

Binary file not shown.

BIN
src/main/java/.DS_Store vendored Normal file

Binary file not shown.

BIN
src/main/java/jace/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package jace;
import jace.hardware.FloppyDisk;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Generic disk conversion utility, using the FloppyDisk nibblize/denibblize to
* convert between DSK and NIB formats (wherever possible anyway)
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class ConvertDiskImage {
public static void main(String... args) {
if (args.length != 2) {
showHelp();
return;
}
File in = new File(args[0]);
File out = new File(args[1]);
if (!in.exists()) {
showHelp();
System.out.println("Cannot find input file: " + args[0]);
return;
}
if (out.exists()) {
showHelp();
System.out.println("Output file already exists!: " + args[1]);
return;
}
String ext = args[1].substring(args[1].length() - 3);
boolean writeNibblized = false;
boolean writeProdosOrdered = false;
if (ext.equalsIgnoreCase("NIB")) {
System.out.println("Preparing to write NIB image");
writeNibblized = true;
} else if (ext.equalsIgnoreCase(".DO") || ext.equalsIgnoreCase("DSK")) {
System.out.println("Preparing to write DOS 3.3 ordered disk image");
writeNibblized = false;
writeProdosOrdered = false;
} else if (ext.equalsIgnoreCase(".PO")) {
System.out.println("Preparing to write Prodos ordered image");
writeNibblized = false;
writeProdosOrdered = true;
} else {
showHelp();
System.out.println("Could not understand desired output format");
return;
}
// First read in the disk image, this decodes the disk as necessary
FloppyDisk theDisk = null;
try {
theDisk = new FloppyDisk(in);
} catch (IOException ex) {
System.out.println("Couldn't read disk image");
ex.printStackTrace();
return;
}
if (!writeNibblized) {
// Now change the disk image to point to a new file and adjust the sector ordering
System.out.println("Writing disk image with " + (writeProdosOrdered ? "prodos" : "dos 3.3") + " sector ordering");
theDisk.diskPath = out;
theDisk.isNibblizedImage = true;
theDisk.currentSectorOrder = writeProdosOrdered ? FloppyDisk.PRODOS_SECTOR_ORDER : FloppyDisk.DOS_33_SECTOR_ORDER;
theDisk.headerLength = 0;
for (int i = 0; i < FloppyDisk.TRACK_COUNT; i++) {
theDisk.updateTrack(i);
}
} else {
FileOutputStream fos = null;
System.out.println("Writing NIB image");
try {
fos = new FileOutputStream(out);
fos.write(theDisk.nibbles);
fos.close();
} catch (IOException ex) {
System.err.println("Error writing NIB image: " + ex.getMessage());
ex.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException ex) {
System.err.println("Error closing NIB image: " + ex.getMessage());
ex.printStackTrace();
}
}
}
System.out.println("Finished converting disk image.");
}
private static void showHelp() {
for (String s : new String[]{
"ConvertDiskImage",
"----------------",
"Usage: java -cp jace.jar jace.ConvertDiskImage DISK_INPUT_NAME DISK_OUTPUT_NAME",
"where DISK_INPUT_NAME is the path of a valid disk image, ",
"and DISK_OUTPUT_NAME is the path where you want to ",
"save the converted disk image.",
"Supported input formats: ",
" DSK (assumes DO), DO, PO, 2MG (140kb), NIB",
"Supported output formats: ",
" DO/DSK, PO, NIB"
}) {
System.out.println(s);
}
}
}

View File

@ -0,0 +1,180 @@
/*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package jace;
import jace.apple2e.Apple2e;
import jace.config.Configuration;
import jace.core.Computer;
import jace.ui.AbstractEmulatorFrame;
import jace.ui.EmulatorFrame;
import java.awt.Component;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
/**
* Created on January 15, 2007, 10:10 PM
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class Emulator {
public static Emulator instance;
public static Thread mainThread;
public static void main(String... args) {
mainThread = Thread.currentThread();
instance = new Emulator(args);
}
public static AbstractEmulatorFrame getFrame() {
if (instance != null) {
return instance.theApp;
} else {
return null;
}
}
public Apple2e computer;
public AbstractEmulatorFrame theApp;
/**
* Creates a new instance of Emulator
* @param args
*/
public Emulator(String... args) {
computer = new Apple2e();
Configuration.loadSettings();
Map<String, String> settings = new HashMap<>();
if (args != null) {
for (int i = 0; i < args.length; i++) {
if (args[i].startsWith("-")) {
String key = args[i].substring(1);
if ((i + 1) < args.length) {
String val = args[i + 1];
if (!val.startsWith("-")) {
settings.put(key, val);
i++;
} else {
settings.put(key, "true");
}
} else {
settings.put(key, "true");
}
} else {
System.err.println("Did not understand parameter " + args[i] + ", skipping.");
}
}
}
Configuration.applySettings(settings);
// theApp = new MainFrame();
theApp = new EmulatorFrame();
try {
theApp.setIconImage(ImageIO.read(Emulator.class.getClassLoader().getResourceAsStream("jace/data/woz_figure.gif")));
} catch (IOException ex) {
Logger.getLogger(Emulator.class.getName()).log(Level.SEVERE, null, ex);
}
//theApp.setBounds(new Rectangle((140*6),(192*3)));
theApp.setVisible(true);
theApp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theApp.setFocusTraversalKeysEnabled(false);
theApp.setTitle("Java Apple Computer Emulator");
theApp.addKeyListener(computer.getKeyboard().getListener());
theApp.addComponentListener(new ComponentListener() {
// theApp.screen.addComponentListener(new ComponentListener() {
@Override
public void componentResized(ComponentEvent e) {
// System.out.println("Screen resized");
resizeVideo();
}
@Override
public void componentMoved(ComponentEvent e) {
resizeVideo();
}
@Override
public void componentShown(ComponentEvent e) {
}
@Override
public void componentHidden(ComponentEvent e) {
}
});
theApp.addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
Computer.getComputer().getVideo().suspend();
}
@Override
public void windowDeiconified(WindowEvent e) {
Computer.getComputer().getVideo().resume();
resizeVideo();
}
@Override
public void windowActivated(WindowEvent e) {
resizeVideo();
}
@Override
public void windowDeactivated(WindowEvent e) {
resizeVideo();
}
});
EmulatorUILogic.registerDebugger();
computer.getVideo().setScreen(theApp.getScreenGraphics());
computer.coldStart();
}
public static void resizeVideo() {
AbstractEmulatorFrame window = getFrame();
if (window != null) {
window.resizeVideo();
}
}
public static Component getScreen() {
AbstractEmulatorFrame window = getFrame();
if (window != null) {
return window.getScreen();
}
return null;
}
}

View File

@ -0,0 +1,372 @@
/*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package jace;
import jace.apple2e.MOS65C02;
import jace.apple2e.RAM128k;
import jace.apple2e.SoftSwitches;
import jace.config.ConfigurationPanel;
import jace.config.InvokableAction;
import jace.core.CPU;
import jace.core.Computer;
import jace.core.Debugger;
import jace.core.RAM;
import jace.core.RAMEvent;
import jace.core.RAMListener;
import static jace.core.Utility.*;
import jace.library.MediaLibrary;
import jace.ui.AbstractEmulatorFrame;
import jace.ui.DebuggerPanel;
import java.awt.Color;
import java.awt.HeadlessException;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
/**
* This class contains miscellaneous user-invoked actions such as debugger
* operations and running arbitrary files in the emulator. It is possible for
* these methods to be later refactored into more sensible locations. Created on
* April 16, 2007, 10:30 PM
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public class EmulatorUILogic {
static Debugger debugger;
static {
debugger = new Debugger() {
@Override
public void updateStatus() {
enableDebug(true);
MOS65C02 cpu = (MOS65C02) Computer.getComputer().getCpu();
updateCPURegisters(cpu);
}
};
}
public static void updateCPURegisters(MOS65C02 cpu) {
DebuggerPanel debuggerPanel = Emulator.getFrame().getDebuggerPanel();
debuggerPanel.valueA.setText(Integer.toHexString(cpu.A));
debuggerPanel.valueX.setText(Integer.toHexString(cpu.X));
debuggerPanel.valueY.setText(Integer.toHexString(cpu.Y));
debuggerPanel.valuePC.setText(Integer.toHexString(cpu.getProgramCounter()));
debuggerPanel.valueSP.setText(Integer.toHexString(cpu.getSTACK()));
debuggerPanel.valuePC2.setText(cpu.getFlags());
debuggerPanel.valueINST.setText(cpu.disassemble());
}
public static void enableDebug(boolean b) {
DebuggerPanel debuggerPanel = Emulator.getFrame().getDebuggerPanel();
debugger.setActive(b);
debuggerPanel.enableDebug.setSelected(b);
debuggerPanel.setBackground(
b ? Color.RED : new Color(0, 0, 0x040));
}
public static void enableTrace(boolean b) {
Computer.getComputer().getCpu().setTraceEnabled(b);
}
public static void stepForward() {
debugger.step = true;
}
static void registerDebugger() {
Computer.getComputer().getCpu().setDebug(debugger);
}
public static Integer getValidAddress(String s) {
try {
int addr = Integer.parseInt(s.toUpperCase(), 16);
if (addr >= 0 && addr < 0x10000) {
return addr;
}
return null;
} catch (NumberFormatException ex) {
return null;
}
}
public static List<RAMListener> watches = new ArrayList<>();
public static void updateWatchList(final DebuggerPanel panel) {
java.awt.EventQueue.invokeLater(() -> {
watches.stream().forEach((oldWatch) -> {
Computer.getComputer().getMemory().removeListener(oldWatch);
});
if (panel == null) {
return;
}
addWatch(panel.textW1, panel.valueW1);
addWatch(panel.textW2, panel.valueW2);
addWatch(panel.textW3, panel.valueW3);
addWatch(panel.textW4, panel.valueW4);
});
}
private static void addWatch(JTextField watch, final JLabel watchValue) {
final Integer address = getValidAddress(watch.getText());
if (address != null) {
//System.out.println("Adding watch for "+Integer.toString(address, 16));
RAMListener newListener = new RAMListener(RAMEvent.TYPE.WRITE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(address);
}
@Override
protected void doEvent(RAMEvent e) {
watchValue.setText(Integer.toHexString(e.getNewValue() & 0x0FF));
}
};
Computer.getComputer().getMemory().addListener(newListener);
watches.add(newListener);
// Print out the current value right away
byte b = Computer.getComputer().getMemory().readRaw(address);
watchValue.setText(Integer.toString(b & 0x0ff, 16));
} else {
watchValue.setText("00");
}
}
public static void updateBreakpointList(final DebuggerPanel panel) {
java.awt.EventQueue.invokeLater(() -> {
Integer address;
debugger.getBreakpoints().clear();
if (panel == null) {
return;
}
address = getValidAddress(panel.textBP1.getText());
if (address != null) {
debugger.getBreakpoints().add(address);
}
address = getValidAddress(panel.textBP2.getText());
if (address != null) {
debugger.getBreakpoints().add(address);
}
address = getValidAddress(panel.textBP3.getText());
if (address != null) {
debugger.getBreakpoints().add(address);
}
address = getValidAddress(panel.textBP4.getText());
if (address != null) {
debugger.getBreakpoints().add(address);
}
debugger.updateBreakpoints();
});
}
@InvokableAction(
name = "BRUN file",
category = "file",
description = "Loads a binary file in memory and executes it. File should end with #06xxxx, where xxxx is the start address in hex",
alternatives = "Execute program;Load binary;Load program;Load rom;Play single-load game")
public static void runFile() {
Computer.pause();
JFileChooser select = new JFileChooser();
select.showDialog(Emulator.getFrame(), "Execute binary file");
File binary = select.getSelectedFile();
if (binary == null) {
Computer.resume();
return;
}
runFile(binary);
}
public static void runFile(File binary) {
String fileName = binary.getName().toLowerCase();
try {
if (fileName.contains("#06")) {
String addressStr = fileName.substring(fileName.length() - 4);
int address = Integer.parseInt(addressStr, 16);
brun(binary, address);
} else if (fileName.contains("#fc")) {
gripe("BASIC not supported yet");
}
} catch (NumberFormatException | IOException ex) {
}
Computer.getComputer().getCpu().resume();
}
public static void brun(File binary, int address) throws FileNotFoundException, IOException {
// If it was halted already, then it was initiated outside of an opcode execution
// If it was not yet halted, then it is the case that the CPU is processing another opcode
// So if that is the case, the program counter will need to be decremented here to compensate
// TODO: Find a better mousetrap for this one -- it's an ugly hack
Computer.pause();
FileInputStream in = new FileInputStream(binary);
byte[] data = new byte[in.available()];
in.read(data);
RAM ram = Computer.getComputer().getMemory();
for (int i = 0; i < data.length; i++) {
ram.write(address + i, data[i], false, true);
}
CPU cpu = Computer.getComputer().getCpu();
Computer.getComputer().getCpu().setProgramCounter(address);
Computer.resume();
}
@InvokableAction(
name = "Adjust display",
category = "display",
description = "Adjusts window size to 1:1 aspect ratio for optimal viewing.",
alternatives = "Adjust screen;Adjust window size;Adjust aspect ratio;Fix screen;Fix window size;Fix aspect ratio;Correct aspect ratio;")
static public void scaleIntegerRatio() {
AbstractEmulatorFrame frame = Emulator.getFrame();
if (frame == null) {
return;
}
Computer.pause();
frame.enforceIntegerRatio();
Computer.resume();
}
@InvokableAction(
name = "Toggle Debug",
category = "debug",
description = "Show/hide the debug panel",
alternatives = "Show Debug;Hide Debug")
public static void toggleDebugPanel() {
AbstractEmulatorFrame frame = Emulator.getFrame();
if (frame == null) {
return;
}
frame.setShowDebug(!frame.isShowDebug());
frame.reconfigure();
Emulator.resizeVideo();
}
public static void toggleFullscreen() {
AbstractEmulatorFrame frame = Emulator.getFrame();
if (frame == null) {
return;
}
Computer.pause();
frame.toggleFullscreen();
Computer.resume();
}
@InvokableAction(
name = "Save Raw Screenshot",
category = "general",
description = "Save raw (RAM) format of visible screen",
alternatives = "screendump, raw screenshot")
public static void saveScreenshotRaw() throws FileNotFoundException, IOException {
SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss");
String timestamp = df.format(new Date());
String type;
int start = Computer.getComputer().getVideo().getCurrentWriter().actualWriter().getYOffset(0);
int len = 0;
if (start < 0x02000) {
// Lo-res or double-lores
len = 0x0400;
type = "gr";
} else {
// Hi-res or double-hires
len = 0x02000;
type = "hgr";
}
boolean dres = SoftSwitches._80COL.getState() && (SoftSwitches.DHIRES.getState() || start < 0x02000);
if (dres) {
type = "d" + type;
}
File outFile = new File("screen_" + type + "_a" + Integer.toHexString(start) + "_" + timestamp);
try (FileOutputStream out = new FileOutputStream(outFile)) {
RAM128k ram = (RAM128k) Computer.getComputer().memory;
Computer.pause();
if (dres) {
for (int i = 0; i < len; i++) {
out.write(ram.getAuxVideoMemory().readByte(start + i));
}
}
for (int i = 0; i < len; i++) {
out.write(ram.getMainMemory().readByte(start + i));
}
}
System.out.println("Wrote screenshot to " + outFile.getAbsolutePath());
}
@InvokableAction(
name = "Save Screenshot",
category = "general",
description = "Save image of visible screen",
alternatives = "Save image,save framebuffer,screenshot")
public static void saveScreenshot() throws HeadlessException, IOException {
JFileChooser select = new JFileChooser();
Computer.pause();
BufferedImage i = Computer.getComputer().getVideo().getFrameBuffer();
BufferedImage j = new BufferedImage(i.getWidth(), i.getHeight(), i.getType());
j.getGraphics().drawImage(i, 0, 0, null);
select.showSaveDialog(Emulator.getFrame());
File targetFile = select.getSelectedFile();
if (targetFile == null) {
return;
}
String filename = targetFile.getName();
System.out.println("Writing screenshot to " + filename);
String extension = filename.substring(filename.lastIndexOf(".") + 1);
ImageIO.write(j, extension, targetFile);
}
public static final String CONFIGURATION_DIALOG_NAME = "Configuration";
@InvokableAction(
name = "Configuration",
category = "general",
description = "Edit emulator configuraion",
alternatives = "Reconfigure,Preferences,Settings")
public static void showConfig() {
if (Emulator.getFrame().getModalDialogUI(CONFIGURATION_DIALOG_NAME) == null) {
JPanel ui = new ConfigurationPanel();
Emulator.getFrame().registerModalDialog(ui, CONFIGURATION_DIALOG_NAME, null, false);
}
Emulator.getFrame().showDialog(CONFIGURATION_DIALOG_NAME);
}
public static final String MEDIA_MANAGER_DIALOG_NAME = "Media Manager";
public static final String MEDIA_MANAGER_EDIT_DIALOG_NAME = "Media Details";
@InvokableAction(
name = "Media Manager",
category = "general",
description = "Show the media manager",
alternatives = "Insert disk;Eject disk;Browse;Download;Select")
public static void showMediaManager() {
if (Emulator.getFrame().getModalDialogUI(MEDIA_MANAGER_DIALOG_NAME) == null) {
Emulator.getFrame().registerModalDialog(MediaLibrary.getInstance().buildUserInterface(), MEDIA_MANAGER_DIALOG_NAME, null, false);
}
Emulator.getFrame().showDialog(MEDIA_MANAGER_DIALOG_NAME);
}
public static boolean confirm(String message) {
return JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Emulator.getFrame(), message);
}
}

View File

@ -0,0 +1,38 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package jace;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* @author blurry
*/
public class JaceApplication extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("fxml/JaceUI.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

View File

@ -0,0 +1,38 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package jace;
import java.awt.Canvas;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.Region;
/**
*
* @author blurry
*/
public class JaceUIController {
@FXML
private ResourceBundle resources;
@FXML
private Canvas displayCanvas;
@FXML
private Region notificationRegion;
@FXML
public void initialize() {
assert displayCanvas != null : "fx:id=\"displayCanvas\" was not injected: check your FXML file 'JaceUI.fxml'.";
assert notificationRegion != null : "fx:id=\"notificationRegion\" was not injected: check your FXML file 'JaceUI.fxml'.";
}
}

View File

@ -0,0 +1,433 @@
/*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package jace.apple2e;
import jace.Emulator;
import jace.cheat.Cheats;
import jace.config.ClassSelection;
import jace.config.ConfigurableField;
import jace.core.Card;
import jace.core.Computer;
import jace.core.Motherboard;
import jace.core.RAM;
import jace.core.RAMEvent;
import jace.core.RAMListener;
import jace.state.Stateful;
import jace.core.Video;
import jace.hardware.CardDiskII;
import jace.hardware.CardExt80Col;
import jace.hardware.ConsoleProbe;
import jace.hardware.Joystick;
import jace.hardware.massStorage.CardMassStorage;
import java.awt.Graphics;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Apple2e is a computer with a 65c02 CPU, 128k of bankswitched ram,
* double-hires graphics, and up to seven peripheral I/O cards installed. Pause
* and resume are implemented by the Motherboard class. This class provides
* overall configuration of the computer, but the actual operation of the
* computer and its timing characteristics are managed in the Motherboard class.
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
@Stateful
public class Apple2e extends Computer {
static int IRQ_VECTOR = 0x003F2;
public Motherboard motherboard;
@ConfigurableField(name = "Slot 1", shortName = "s1card")
public ClassSelection card1 = new ClassSelection(Card.class, null);
@ConfigurableField(name = "Slot 2", shortName = "s2card")
// public Class<? extends Card> card2 = CardSSC.class;
public ClassSelection card2 = new ClassSelection(Card.class, null);
@ConfigurableField(name = "Slot 3", shortName = "s3card")
public ClassSelection card3 = new ClassSelection(Card.class, null);
@ConfigurableField(name = "Slot 4", shortName = "s4card")
public ClassSelection card4 = new ClassSelection(Card.class, null);
@ConfigurableField(name = "Slot 5", shortName = "s5card")
public ClassSelection card5 = new ClassSelection(Card.class, null);
@ConfigurableField(name = "Slot 6", shortName = "s6card")
public ClassSelection card6 = new ClassSelection(Card.class, CardDiskII.class);
@ConfigurableField(name = "Slot 7", shortName = "s7card")
public ClassSelection card7 = new ClassSelection(Card.class, CardMassStorage.class);
@ConfigurableField(name = "Debug rom", shortName = "debugRom", description = "Use debugger //e rom")
public boolean useDebugRom = false;
@ConfigurableField(name = "Console probe", description = "Enable console redirection (experimental!)")
public boolean useConsoleProbe = false;
private ConsoleProbe probe = new ConsoleProbe();
@ConfigurableField(name = "Helpful hints", shortName = "hints")
public boolean enableHints = true;
@ConfigurableField(name = "Renderer", shortName = "video", description = "Video rendering implementation")
public ClassSelection videoRenderer = new ClassSelection(Video.class, VideoNTSC.class);
@ConfigurableField(name = "Aux Ram", shortName = "ram", description = "Aux ram card")
public ClassSelection ramCard = new ClassSelection(RAM128k.class, CardExt80Col.class);
public Joystick joystick1;
public Joystick joystick2;
@ConfigurableField(name = "Activate Cheats", shortName = "cheat", defaultValue = "")
public ClassSelection cheatEngine = new ClassSelection(Cheats.class, null);
public Cheats activeCheatEngine = null;
/**
* Creates a new instance of Apple2e
*/
public Apple2e() {
super();
try {
reconfigure();
// Setup core resources
joystick1 = new Joystick(0);
joystick2 = new Joystick(1);
setCpu(new MOS65C02());
reinitMotherboard();
} catch (Throwable t) {
System.err.println("Unable to initalize virtual machine");
t.printStackTrace(System.err);
}
}
@Override
public String getName() {
return "Computer (Apple //e)";
}
private void reinitMotherboard() {
if (motherboard != null && motherboard.isRunning()) {
motherboard.suspend();
}
motherboard = new Motherboard();
motherboard.reconfigure();
Motherboard.miscDevices.add(joystick1);
Motherboard.miscDevices.add(joystick2);
}
@Override
public void coldStart() {
Computer.pause();
reinitMotherboard();
reboot();
//getMemory().dump();
for (SoftSwitches s : SoftSwitches.values()) {
s.getSwitch().reset();
}
getMemory().configureActiveMemory();
getVideo().configureVideoMode();
getCpu().reset();
for (Card c : getMemory().getAllCards()) {
if (c != null) {
c.reset();
}
}
Computer.resume();
/*
getCpu().resume();
getVideo().resume();
*/
}
public void reboot() {
RAM r = getMemory();
r.write(IRQ_VECTOR, (byte) 0x00, false, true);
r.write(IRQ_VECTOR + 1, (byte) 0x00, false, true);
r.write(IRQ_VECTOR + 2, (byte) 0x00, false, true);
warmStart();
}
@Override
public void warmStart() {
boolean restart = Computer.pause();
for (SoftSwitches s : SoftSwitches.values()) {
s.getSwitch().reset();
}
getMemory().configureActiveMemory();
getVideo().configureVideoMode();
getCpu().reset();
for (Card c : getMemory().getAllCards()) {
if (c != null) {
c.reset();
}
}
getCpu().resume();
Computer.resume();
}
private void insertCard(Class<? extends Card> type, int slot) {
if (getMemory().getCard(slot) != null) {
if (getMemory().getCard(slot).getClass().equals(type)) {
return;
}
getMemory().removeCard(slot);
}
if (type != null) {
try {
getMemory().addCard(type.newInstance(), slot);
} catch (InstantiationException | IllegalAccessException ex) {
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
@Override
public final void reconfigure() {
boolean restart = Computer.pause();
super.reconfigure();
RAM128k currentMemory = (RAM128k) getMemory();
if (currentMemory != null && !(currentMemory.getClass().equals(ramCard.getValue()))) {
try {
RAM128k newMemory = (RAM128k) ramCard.getValue().newInstance();
newMemory.copyFrom(currentMemory);
setMemory(newMemory);
} catch (InstantiationException | IllegalAccessException ex) {
}
}
if (getMemory() == null) {
try {
currentMemory = (RAM128k) ramCard.getValue().newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
}
try {
setMemory(currentMemory);
for (SoftSwitches s : SoftSwitches.values()) {
s.getSwitch().register();
}
} catch (Throwable ex) {
}
}
currentMemory.reconfigure();
try {
if (useConsoleProbe) {
probe.init(this);
} else {
probe.shutdown();
}
if (useDebugRom) {
loadRom("jace/data/apple2e_debug.rom");
} else {
loadRom("jace/data/apple2e.rom");
}
if (getVideo() == null || getVideo().getClass() != videoRenderer.getValue()) {
Graphics g = null;
if (getVideo() != null) {
getVideo().suspend();
g = getVideo().getScreen();
}
try {
setVideo((Video) videoRenderer.getValue().newInstance());
getVideo().configureVideoMode();
getVideo().reconfigure();
getVideo().setScreen(g);
Emulator.resizeVideo();
getVideo().resume();
} catch (InstantiationException | IllegalAccessException ex) {
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
}
}
// Add all new cards
insertCard(card1.getValue(), 1);
insertCard(card2.getValue(), 2);
insertCard(card3.getValue(), 3);
insertCard(card4.getValue(), 4);
insertCard(card5.getValue(), 5);
insertCard(card6.getValue(), 6);
insertCard(card7.getValue(), 7);
if (enableHints) {
enableHints();
} else {
disableHints();
}
getMemory().configureActiveMemory();
if (cheatEngine.getValue() == null) {
if (activeCheatEngine != null) {
activeCheatEngine.detach();
}
activeCheatEngine = null;
} else {
boolean startCheats = true;
if (activeCheatEngine != null) {
if (activeCheatEngine.getClass().equals(cheatEngine.getValue())) {
startCheats = false;
} else {
activeCheatEngine.detach();
activeCheatEngine = null;
}
}
if (startCheats) {
try {
activeCheatEngine = (Cheats) cheatEngine.getValue().newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
}
activeCheatEngine.attach();
}
}
} catch (IOException ex) {
Logger.getLogger(Apple2e.class.getName()).log(Level.SEVERE, null, ex);
}
if (restart) {
Computer.resume();
}
}
@Override
protected void doPause() {
if (motherboard == null) {
return;
}
motherboard.pause();
}
@Override
protected void doResume() {
if (motherboard == null) {
return;
}
motherboard.resume();
}
@Override
protected boolean isRunning() {
if (motherboard == null) {
return false;
}
return motherboard.isRunning() && !motherboard.isPaused;
}
private List<RAMListener> hints = new ArrayList<>();
private void enableHints() {
if (hints.isEmpty()) {
hints.add(new RAMListener(RAMEvent.TYPE.EXECUTE, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY) {
@Override
protected void doConfig() {
setScopeStart(0x0FB63);
}
@Override
protected void doEvent(RAMEvent e) {
if (getCpu().getProgramCounter() != getScopeStart()) {
return;
}
Thread t = new Thread(() -> {
try {
// Give the floppy drive time to start
Thread.sleep(1000);
if (getCpu().getProgramCounter() >> 8 != 0x0c6) {
return;
}
int row = 2;
for (String s : new String[]{
" Welcome to",
" _ __ ___ ____ ",
" | | / /\\ / / ` | |_ ",
" \\_|_| /_/--\\ \\_\\_, |_|__ ",
"",
" Java Apple Computer Emulator",
"",