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",
"",
" Presented by BLuRry",
" http://goo.gl/SnzqG",
"",
"Press F1 to insert disk in Slot 6, D1",
"Press F2 to insert disk in Slot 6, D2",
"Press F3 to insert HDV or 2MG in slot 7",
"Press F4 to open configuration",
"Press F5 to run raw binary program",
"Press F8 to correct the aspect ratio",
"Press F9 to toggle fullscreen",
"Press F10 to open/close the debugger",
"",
" If metacheat is enabled:",
"Press HOME to activate memory heatmap",
"Press END to activate metacheat search"
}) {
int addr = 0x0401 + VideoDHGR.calculateTextOffset(row++);
for (char c : s.toCharArray()) {
getMemory().write(addr++, (byte) (c | 0x080), false, true);
}
}
while (getCpu().getProgramCounter() &