mirror of
https://github.com/sicklittlemonkey/AppleIIGo.git
synced 2024-06-26 15:29:31 +00:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fe27c7d1e8 | ||
|
68cfda45c1 | ||
|
dc8f597c62 | ||
|
add3d41448 | ||
|
af1cec23d3 | ||
|
912d6c3dbc | ||
|
d8b391a80c | ||
|
c3ee6f8baa | ||
|
84c9d44b32 | ||
|
bd8ca74c1e | ||
|
2e59a5c31b |
|
@ -1,6 +1,10 @@
|
||||||
# AppleIIGo
|
# AppleIIGo
|
||||||
Apple //e emulator Java applet
|
Apple //e emulator Java applet
|
||||||
|
|
||||||
Original version: Copyright 2006 Marc S. Ressl
|
Original version: Copyright 2006 Marc S. Ressl, with parts adapted from code by
|
||||||
|
- Steven E. Hugg
|
||||||
|
- Doug Kwan
|
||||||
|
- Randy Frank
|
||||||
|
- Ben Koning
|
||||||
|
|
||||||
Subsequent versions: Copyright 2008-2015 Nick Westgate
|
Subsequent versions: Copyright 2008-2015 Nick Westgate
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* AppleIIGo
|
* AppleIIGo
|
||||||
* Display processing
|
* Display processing
|
||||||
* (C) 2006 by Marc S. Ressl (ressl@lonetree.com)
|
* Copyright 2006 by Marc S. Ressl (mressl@gmail.com)
|
||||||
* Released under the GPL
|
* Released under the GPL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -224,6 +224,18 @@ public class AppleDisplay implements Runnable {
|
||||||
return displayScale;
|
return displayScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getSizeX()
|
||||||
|
{
|
||||||
|
precalcDisplay();
|
||||||
|
return displayScaledSizeX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSizeY()
|
||||||
|
{
|
||||||
|
precalcDisplay();
|
||||||
|
return displayScaledSizeY;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set refresh rate
|
* Set refresh rate
|
||||||
*
|
*
|
||||||
|
@ -459,8 +471,8 @@ public class AppleDisplay implements Runnable {
|
||||||
boolean isDoubleTextMode = ((graphicsMode & EmAppleII.GR_80CHAR) == EmAppleII.GR_80CHAR);
|
boolean isDoubleTextMode = ((graphicsMode & EmAppleII.GR_80CHAR) == EmAppleII.GR_80CHAR);
|
||||||
boolean isDoubleGraphicsMode = ((graphicsMode & (EmAppleII.GR_80CHAR | EmAppleII.GR_DHIRES)) == (EmAppleII.GR_80CHAR | EmAppleII.GR_DHIRES));
|
boolean isDoubleGraphicsMode = ((graphicsMode & (EmAppleII.GR_80CHAR | EmAppleII.GR_DHIRES)) == (EmAppleII.GR_80CHAR | EmAppleII.GR_DHIRES));
|
||||||
|
|
||||||
int baseAddressText = isPage2 ? apple.MEM_MAIN_RAM2 : apple.MEM_MAIN_TEXT;
|
int baseAddressText = isPage2 ? EmAppleII.MEM_MAIN_RAM2 : EmAppleII.MEM_MAIN_TEXT;
|
||||||
int baseAddressHires = isPage2 ? apple.MEM_MAIN_RAM3 : apple.MEM_MAIN_HIRES;
|
int baseAddressHires = isPage2 ? EmAppleII.MEM_MAIN_RAM3 : EmAppleII.MEM_MAIN_HIRES;
|
||||||
|
|
||||||
// Set char map
|
// Set char map
|
||||||
if (isCharsetUpdateRequested) {
|
if (isCharsetUpdateRequested) {
|
||||||
|
@ -769,7 +781,7 @@ public class AppleDisplay implements Runnable {
|
||||||
}
|
}
|
||||||
private void renderText(int baseAddress, boolean isMixedMode) {
|
private void renderText(int baseAddress, boolean isMixedMode) {
|
||||||
int screenCharY, screenCharYStart = isMixedMode ? 20 : 0;
|
int screenCharY, screenCharYStart = isMixedMode ? 20 : 0;
|
||||||
int displayOffset, sourceOffset;
|
int displayOffset;
|
||||||
int address, addressEnd, addressStart;
|
int address, addressEnd, addressStart;
|
||||||
|
|
||||||
displayOffset = screenCharYStart * DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
displayOffset = screenCharYStart * DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
||||||
|
@ -820,7 +832,7 @@ public class AppleDisplay implements Runnable {
|
||||||
}
|
}
|
||||||
private void renderDoubleText(int baseAddress, boolean isMixedMode) {
|
private void renderDoubleText(int baseAddress, boolean isMixedMode) {
|
||||||
int screenCharY, screenCharYStart = isMixedMode ? 20 : 0;
|
int screenCharY, screenCharYStart = isMixedMode ? 20 : 0;
|
||||||
int displayOffset, sourceOffset;
|
int displayOffset;
|
||||||
int address, addressEnd, addressStart;
|
int address, addressEnd, addressStart;
|
||||||
|
|
||||||
displayOffset = screenCharYStart * DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
displayOffset = screenCharYStart * DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X;
|
||||||
|
@ -873,7 +885,7 @@ public class AppleDisplay implements Runnable {
|
||||||
}
|
}
|
||||||
private void renderLores(int baseAddress, boolean isMixedMode) {
|
private void renderLores(int baseAddress, boolean isMixedMode) {
|
||||||
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
||||||
int displayOffset, sourceOffset;
|
int displayOffset;
|
||||||
int address, addressEnd, addressStart;
|
int address, addressEnd, addressStart;
|
||||||
|
|
||||||
displayOffset = 0;
|
displayOffset = 0;
|
||||||
|
@ -904,7 +916,7 @@ public class AppleDisplay implements Runnable {
|
||||||
*/
|
*/
|
||||||
private void renderDoubleLores(int baseAddress, boolean isMixedMode) {
|
private void renderDoubleLores(int baseAddress, boolean isMixedMode) {
|
||||||
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
||||||
int displayOffset, sourceOffset;
|
int displayOffset;
|
||||||
int address, addressEnd, addressStart;
|
int address, addressEnd, addressStart;
|
||||||
|
|
||||||
displayOffset = 0;
|
displayOffset = 0;
|
||||||
|
@ -998,7 +1010,7 @@ public class AppleDisplay implements Runnable {
|
||||||
}
|
}
|
||||||
private void renderHires(int baseAddress, boolean isMixedMode) {
|
private void renderHires(int baseAddress, boolean isMixedMode) {
|
||||||
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
||||||
int displayOffset, sourceOffset;
|
int displayOffset;
|
||||||
int address, addressEnd, addressStart;
|
int address, addressEnd, addressStart;
|
||||||
|
|
||||||
displayOffset = 0;
|
displayOffset = 0;
|
||||||
|
@ -1089,7 +1101,7 @@ public class AppleDisplay implements Runnable {
|
||||||
}
|
}
|
||||||
private void renderDoubleHires(int baseAddress, boolean isMixedMode) {
|
private void renderDoubleHires(int baseAddress, boolean isMixedMode) {
|
||||||
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24;
|
||||||
int displayOffset, sourceOffset;
|
int displayOffset;
|
||||||
int address, addressEnd, addressStart;
|
int address, addressEnd, addressStart;
|
||||||
|
|
||||||
displayOffset = 0;
|
displayOffset = 0;
|
||||||
|
|
|
@ -2,17 +2,86 @@
|
||||||
/**
|
/**
|
||||||
* AppleIIGo
|
* AppleIIGo
|
||||||
* The Java Apple II Emulator
|
* The Java Apple II Emulator
|
||||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
* Copyright 2015 by Nick Westgate (Nick.Westgate@gmail.com)
|
||||||
* Released under the GPL
|
* Copyright 2006 by Marc S. Ressl (mressl@gmail.com)
|
||||||
|
* Released under the GNU General Public License version 2
|
||||||
|
* See http://www.gnu.org/licenses/
|
||||||
|
*
|
||||||
|
* Change list:
|
||||||
|
*
|
||||||
|
* Version 1.0.10 - changes by Nick:
|
||||||
|
* - fixed disk stepping bug for Mabel's Mansion using my code from AppleWin
|
||||||
|
* - patch loaded ROM's empty slots with faux-floating bus data so Mabel's Mansion works
|
||||||
|
* - revert CPU status bug introduced in 1.0.9 - V and R used the same bit
|
||||||
|
* - fixed BRK bug by adding extra PC increment
|
||||||
|
* - NOTE: decimal mode arithmetic fails some tests and should be fixed someday
|
||||||
|
*
|
||||||
|
* Version 1.0.9 - changes by Nick:
|
||||||
|
* - fixed disk speed-up bug (Sherwood Forest reads with the drive motor off)
|
||||||
|
* - added check for 2IMG header ID
|
||||||
|
* - fixed processor status bugs in BRK, PLP, RTI, NMI, IRQ
|
||||||
|
*
|
||||||
|
* Version 1.0.8 - changes by Nick:
|
||||||
|
* - implemented disk writing (only in memory, not persisted)
|
||||||
|
* - added support for .2MG (2IMG) disk images, including lock flag and volume number
|
||||||
|
* - support meta tag for write protect in disk filename eg: NotWritable_Meta_DW0.dsk
|
||||||
|
*
|
||||||
|
* Version 1.0.7 - changes by Nick:
|
||||||
|
* - fixed disk emulation bug (sense write protect entered write mode)
|
||||||
|
* - now honour diskWritable parameter (but writing is not implemented)
|
||||||
|
* - support meta tag for volume number in disk filename eg: Vol2_Meta_DV2.dsk
|
||||||
|
* - added isPaddleEnabled parameter
|
||||||
|
* - exposed setPaddleEnabled(boolean value), setPaddleInverted(boolean value)
|
||||||
|
* - paddle values are now 255 at startup (ie. correct if disabled/not present)
|
||||||
|
* - minor AppleSpeaker fix (SourceDataLine.class) thanks to William Halliburton
|
||||||
|
*
|
||||||
|
* Version 1.0.6 - changes by Nick:
|
||||||
|
* - exposed F3/F4 disk swapping method: cycleDisk(int driveNumber)
|
||||||
|
* - exposed reset() method
|
||||||
|
* - exposed setSpeed(int value) method
|
||||||
|
*
|
||||||
|
* Version 1.0.5 - changes by Nick:
|
||||||
|
* - added support for .NIB (nibble) disk images (also inside ZIP archives)
|
||||||
|
* - added disk speedup hacks for DOS (expect ~2x faster reads)
|
||||||
|
*
|
||||||
|
* Version 1.0.4 - changes by Nick:
|
||||||
|
* - added support for .PO (ProDOS order) disk images (also inside ZIP archives)
|
||||||
|
* - added Command key for Closed-Apple on Mac OS X
|
||||||
|
* - added Home and End keys for Open-Apple and Closed-Apple on full keyboards
|
||||||
|
*
|
||||||
|
* Version 1.0.3 - changes by Nick:
|
||||||
|
* - fixed paddle values for scaled display window
|
||||||
|
* - added "digital" joystick support via numeric keypad arrows
|
||||||
|
* - added Left-Alt and Right-Alt keys for Open-Apple and Closed-Apple
|
||||||
|
* - changed reset key from Home to Ctrl-F12 and Ctrl-Pause/Break
|
||||||
|
*
|
||||||
|
* Version 1.0.2 - changes by Nick:
|
||||||
|
* - improved sound sync by moving AppleSpeaker into the main thread
|
||||||
|
* - added version (F1)
|
||||||
|
* - added multiple disks & swapping (F3, F4)
|
||||||
|
* - added ZIP archive support
|
||||||
|
* - fixed HTTP disk image access bug
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.applet.*;
|
import java.applet.Applet;
|
||||||
import java.io.*;
|
import java.awt.Graphics;
|
||||||
import java.net.*;
|
import java.awt.event.ComponentEvent;
|
||||||
import java.util.zip.*;
|
import java.awt.event.ComponentListener;
|
||||||
import java.awt.*;
|
import java.awt.event.InputEvent;
|
||||||
import java.awt.event.*;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.event.KeyListener;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
import java.awt.event.MouseMotionListener;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AppleIIGo class<p>
|
* AppleIIGo class<p>
|
||||||
|
@ -20,10 +89,16 @@ import java.awt.Graphics2D;
|
||||||
*/
|
*/
|
||||||
public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
MouseListener, MouseMotionListener {
|
MouseListener, MouseMotionListener {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -3302282815441501352L;
|
||||||
|
|
||||||
|
final String version = "1.0.10";
|
||||||
|
final String versionString = "AppleIIGo Version " + version;
|
||||||
|
final String metaStart = "_meta_";
|
||||||
|
|
||||||
// Class instances
|
// Class instances
|
||||||
private EmAppleII apple;
|
private EmAppleII apple;
|
||||||
private AppleDisplay display;
|
private AppleDisplay display;
|
||||||
private AppleSpeaker speaker;
|
|
||||||
private DiskII disk;
|
private DiskII disk;
|
||||||
|
|
||||||
// Machine variables
|
// Machine variables
|
||||||
|
@ -34,11 +109,14 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
private boolean keyboardUppercaseOnly;
|
private boolean keyboardUppercaseOnly;
|
||||||
|
|
||||||
// Paddle variables
|
// Paddle variables
|
||||||
|
private boolean isPaddleEnabled;
|
||||||
private boolean isPaddleInverted;
|
private boolean isPaddleInverted;
|
||||||
|
|
||||||
// Disk variables
|
// Disk variables - TODO: refactor into a class
|
||||||
private String diskDriveResource[] = new String[2];
|
private String diskDriveResource[] = new String[2];
|
||||||
private boolean diskWritable;
|
private boolean diskWritable;
|
||||||
|
private String[][] diskImageNames = {{}, {}};
|
||||||
|
private int diskImageNumber[] = {0, 0};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug
|
* Debug
|
||||||
|
@ -82,6 +160,10 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
// Keyboard
|
// Keyboard
|
||||||
keyboardUppercaseOnly = getAppletParameter("keyboardUppercaseOnly", "true").equals("true");
|
keyboardUppercaseOnly = getAppletParameter("keyboardUppercaseOnly", "true").equals("true");
|
||||||
|
|
||||||
|
// Paddles
|
||||||
|
isPaddleEnabled = getAppletParameter("paddleEnabled", "true").equals("true");
|
||||||
|
isPaddleInverted = getAppletParameter("paddleInverted", "false").equals("true");
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
display = new AppleDisplay(this, apple);
|
display = new AppleDisplay(this, apple);
|
||||||
display.setScale(new Float(getAppletParameter("displayScale", "1")).floatValue());
|
display.setScale(new Float(getAppletParameter("displayScale", "1")).floatValue());
|
||||||
|
@ -91,17 +173,21 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
display.setGlare(getAppletParameter("displayGlare", "false").equals("true"));
|
display.setGlare(getAppletParameter("displayGlare", "false").equals("true"));
|
||||||
|
|
||||||
// Speaker
|
// Speaker
|
||||||
speaker = new AppleSpeaker(apple);
|
apple.speaker = new AppleSpeaker(apple);
|
||||||
speaker.setVolume(new Integer(getAppletParameter("speakerVolume", "3")).intValue());
|
apple.speaker.setVolume(new Integer(getAppletParameter("speakerVolume", "6")).intValue());
|
||||||
|
|
||||||
// Peripherals
|
// Peripherals
|
||||||
disk = new DiskII();
|
disk = new DiskII(apple);
|
||||||
apple.setPeripheral(disk, 6);
|
apple.setPeripheral(disk, 6);
|
||||||
|
|
||||||
// Initialize disk drives
|
// Initialize disk drives
|
||||||
diskWritable = getAppletParameter("diskWritable", "false").equals("true");
|
diskWritable = getAppletParameter("diskWritable", "false").equals("true");
|
||||||
mountDisk(0, getAppletParameter("diskDrive1", ""));
|
diskImageNames[0] = getAppletParameter("diskDrive1", "").split("[|]");
|
||||||
mountDisk(1, getAppletParameter("diskDrive2", ""));
|
diskImageNumber[0] = 0;
|
||||||
|
diskImageNames[1] = getAppletParameter("diskDrive2", "").split("[|]");
|
||||||
|
diskImageNumber[1] = 0;
|
||||||
|
mountDisk(0, diskImageNames[0][diskImageNumber[0]]);
|
||||||
|
mountDisk(1, diskImageNames[1][diskImageNumber[1]]);
|
||||||
|
|
||||||
// Start CPU
|
// Start CPU
|
||||||
if (!isCpuPaused)
|
if (!isCpuPaused)
|
||||||
|
@ -112,6 +198,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
* Start applet
|
* Start applet
|
||||||
*/
|
*/
|
||||||
public void start() {
|
public void start() {
|
||||||
|
debug("AppleIIGo Version " + version);
|
||||||
debug("start()");
|
debug("start()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,8 +218,6 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
unmountDisk(1);
|
unmountDisk(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Public Java interface
|
// Public Java interface
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,9 +234,9 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
public void pause() {
|
public void pause() {
|
||||||
debug("pause()");
|
debug("pause()");
|
||||||
isCpuPaused = true;
|
isCpuPaused = true;
|
||||||
apple.setPaused(isCpuPaused);
|
apple.setPaused(true);
|
||||||
display.setPaused(isCpuPaused);
|
display.setPaused(true);
|
||||||
// speaker.setPaused(isCpuPaused);
|
apple.speaker.setPaused(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,9 +245,9 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
public void resume() {
|
public void resume() {
|
||||||
debug("resume()");
|
debug("resume()");
|
||||||
isCpuPaused = false;
|
isCpuPaused = false;
|
||||||
// speaker.setPaused(isCpuPaused);
|
apple.speaker.setPaused(false);
|
||||||
display.setPaused(isCpuPaused);
|
display.setPaused(false);
|
||||||
apple.setPaused(isCpuPaused);
|
apple.setPaused(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -173,22 +258,87 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
apple.restart();
|
apple.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
debug("reset()");
|
||||||
|
apple.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpeed(int value) {
|
||||||
|
debug("setSpeed(" + value + ")");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pause();
|
||||||
|
this.wait(1000);
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
apple.setCpuSpeed(value);
|
||||||
|
resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cycleDisk(int driveNumber)
|
||||||
|
{
|
||||||
|
debug("cycleDisk(" + driveNumber + ")");
|
||||||
|
if (diskImageNames[driveNumber].length > 1) {
|
||||||
|
diskImageNumber[driveNumber] = ++diskImageNumber[driveNumber] % diskImageNames[driveNumber].length;
|
||||||
|
mountDisk(driveNumber, diskImageNames[driveNumber][diskImageNumber[driveNumber]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open input stream
|
* Open input stream
|
||||||
*/
|
*/
|
||||||
private InputStream openInputStream(String resource) {
|
private DataInputStream openInputStream(String resource) {
|
||||||
|
return openInputStream(resource, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataInputStream openInputStream(String resource, StringBuffer OutFilename) {
|
||||||
InputStream is = null;
|
InputStream is = null;
|
||||||
|
|
||||||
try {
|
if (OutFilename != null)
|
||||||
URL url = new URL(getCodeBase(), resource);
|
{
|
||||||
is = url.openStream();
|
OutFilename.setLength(0);
|
||||||
|
int slashPos = resource.lastIndexOf('/');
|
||||||
if (resource.toLowerCase().endsWith(".gz"))
|
int backslashPos = resource.lastIndexOf('\\');
|
||||||
is = new GZIPInputStream(is);
|
int index = Math.max(slashPos, backslashPos);
|
||||||
} catch (Exception e) {
|
OutFilename.append(resource.substring((index > 0) ? index : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
return is;
|
try {
|
||||||
|
URL codeBase = getCodeBase();
|
||||||
|
URL url = new URL(codeBase, resource);
|
||||||
|
debug("resource: " + url.toString());
|
||||||
|
|
||||||
|
is = url.openStream();
|
||||||
|
if (resource.toLowerCase().endsWith(".gz"))
|
||||||
|
{
|
||||||
|
is = new GZIPInputStream(is);
|
||||||
|
}
|
||||||
|
else if (resource.toLowerCase().endsWith(".zip"))
|
||||||
|
{
|
||||||
|
is = new ZipInputStream(is);
|
||||||
|
ZipEntry entry = ((ZipInputStream)is).getNextEntry();
|
||||||
|
if (OutFilename != null)
|
||||||
|
{
|
||||||
|
OutFilename.setLength(0);
|
||||||
|
OutFilename.append(entry.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
debug("Exeption: " + e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is == null)
|
||||||
|
{
|
||||||
|
debug("failed");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
debug("ok");
|
||||||
|
return new DataInputStream(is);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -214,10 +364,11 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputStream is = openInputStream(resource);
|
DataInputStream is = openInputStream(resource);
|
||||||
success = apple.loadRom(is);
|
success = apple.loadRom(is);
|
||||||
is.close();
|
is.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
debug("Exeption: " + e.getLocalizedMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
@ -230,7 +381,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
debug("mountDisk(drive: " + drive + ", resource: " + resource + ")");
|
debug("mountDisk(drive: " + drive + ", resource: " + resource + ")");
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
|
|
||||||
if ((drive < 0) || (drive > 2))
|
if ((drive < 0) || (drive > 1))
|
||||||
return success;
|
return success;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -238,10 +389,76 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
|
|
||||||
diskDriveResource[drive] = resource;
|
diskDriveResource[drive] = resource;
|
||||||
|
|
||||||
InputStream is = openInputStream(resource);
|
StringBuffer diskname = new StringBuffer();
|
||||||
success = disk.readDisk(drive, is, 254, false);
|
DataInputStream is = openInputStream(resource, diskname);
|
||||||
|
|
||||||
|
int diskVolumeNumber = DiskII.DEFAULT_VOLUME;
|
||||||
|
boolean diskWritableOverride = diskWritable;
|
||||||
|
|
||||||
|
// handle disk meta tag for disk volume (etc?)
|
||||||
|
// could break this out into a method, but then multiple tags ...?
|
||||||
|
String lowerDiskname = diskname.toString().toLowerCase();
|
||||||
|
int metaIndex = lowerDiskname.indexOf(metaStart);
|
||||||
|
if (metaIndex != -1)
|
||||||
|
{
|
||||||
|
metaIndex += metaStart.length();
|
||||||
|
int command = 0;
|
||||||
|
int operand = 0;
|
||||||
|
boolean execute = false;
|
||||||
|
while (metaIndex < lowerDiskname.length())
|
||||||
|
{
|
||||||
|
char c = lowerDiskname.charAt(metaIndex++);
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '0': case '1':case '2':case '3':case '4':
|
||||||
|
case '5': case '6':case '7':case '8':case '9':
|
||||||
|
{
|
||||||
|
operand = 10 * operand + (c - '0');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '.': // end meta
|
||||||
|
metaIndex = lowerDiskname.length();
|
||||||
|
execute = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '_': // end word
|
||||||
|
execute = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if (c >= 'a' && c <= 'z')
|
||||||
|
{
|
||||||
|
command = (command << 16) + c;
|
||||||
|
execute = (command & 0xFFFF0000) != 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (execute)
|
||||||
|
{
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case ('d' << 16) + 'v':
|
||||||
|
diskVolumeNumber = operand;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ('d' << 16) + 'w':
|
||||||
|
diskWritableOverride = (operand != 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
command = 0;
|
||||||
|
operand = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success = disk.readDisk(drive, is, diskname.toString(), !diskWritableOverride, diskVolumeNumber);
|
||||||
is.close();
|
is.close();
|
||||||
|
showStatus("Drive " + (drive + 1) + ": " + resource);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
debug("Exeption: " + e.getLocalizedMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
@ -252,18 +469,19 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
*/
|
*/
|
||||||
public void unmountDisk(int drive) {
|
public void unmountDisk(int drive) {
|
||||||
debug("unmountDisk(drive: " + drive + ")");
|
debug("unmountDisk(drive: " + drive + ")");
|
||||||
if ((drive < 0) || (drive > 2))
|
if ((drive < 0) || (drive > 1))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!diskWritable)
|
if (!diskWritable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
// TODO: only for local disk cache when it's working
|
||||||
OutputStream os = openOutputStream(diskDriveResource[drive]);
|
//try {
|
||||||
disk.writeDisk(drive, os);
|
//OutputStream os = openOutputStream(diskDriveResource[drive]);
|
||||||
os.close();
|
//disk.writeDisk(drive, os);
|
||||||
} catch (Exception e) {
|
//os.close();
|
||||||
}
|
//} catch (Exception e) {
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -274,6 +492,29 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
display.setColorMode(value);
|
display.setColorMode(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set paddle enabled/disabled
|
||||||
|
*/
|
||||||
|
public void setPaddleEnabled(boolean value) {
|
||||||
|
debug("setPaddleEnabled(value: " + value + ")");
|
||||||
|
isPaddleEnabled = value;
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
apple.paddle.setPaddlePos(0, Paddle.PADDLE_HIGH);
|
||||||
|
apple.paddle.setPaddlePos(1, Paddle.PADDLE_HIGH);
|
||||||
|
apple.paddle.setButton(0, false);
|
||||||
|
apple.paddle.setButton(1, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set paddle inverted/normal
|
||||||
|
*/
|
||||||
|
public void setPaddleInverted(boolean value) {
|
||||||
|
debug("setPaddleInverted(value: " + value + ")");
|
||||||
|
isPaddleInverted = value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get disk activity
|
* Get disk activity
|
||||||
*/
|
*/
|
||||||
|
@ -281,7 +522,15 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
return (!isCpuPaused && disk.isMotorOn());
|
return (!isCpuPaused && disk.isMotorOn());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getSizeX()
|
||||||
|
{
|
||||||
|
return display.getSizeX();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSizeY()
|
||||||
|
{
|
||||||
|
return display.getSizeY();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KeyListener event handling
|
* KeyListener event handling
|
||||||
|
@ -301,24 +550,68 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
|
|
||||||
public void keyPressed(KeyEvent e) {
|
public void keyPressed(KeyEvent e) {
|
||||||
switch(e.getKeyCode()) {
|
switch(e.getKeyCode()) {
|
||||||
|
|
||||||
|
case KeyEvent.VK_META:
|
||||||
|
apple.paddle.setButton(1, true);
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_ALT:
|
||||||
|
if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_LEFT)
|
||||||
|
{
|
||||||
|
apple.paddle.setButton(0, true);
|
||||||
|
}
|
||||||
|
else if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT)
|
||||||
|
{
|
||||||
|
apple.paddle.setButton(1, true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case KeyEvent.VK_BACK_SPACE:
|
case KeyEvent.VK_BACK_SPACE:
|
||||||
case KeyEvent.VK_LEFT:
|
case KeyEvent.VK_LEFT:
|
||||||
apple.setKeyLatch(8);
|
if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD)
|
||||||
|
{
|
||||||
|
handleKeypadLeft();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
apple.setKeyLatch(8);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyEvent.VK_RIGHT:
|
case KeyEvent.VK_RIGHT:
|
||||||
apple.setKeyLatch(21);
|
if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD)
|
||||||
|
{
|
||||||
|
handleKeypadRight();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
apple.setKeyLatch(21);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyEvent.VK_UP:
|
case KeyEvent.VK_UP:
|
||||||
if (e.isControlDown())
|
if (e.isControlDown())
|
||||||
speaker.setVolume(speaker.getVolume() + 1);
|
{
|
||||||
|
apple.speaker.setVolume(apple.speaker.getVolume() + 1);
|
||||||
|
}
|
||||||
|
else if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD)
|
||||||
|
{
|
||||||
|
handleKeypadUp();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
apple.setKeyLatch(11);
|
apple.setKeyLatch(11);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyEvent.VK_DOWN:
|
case KeyEvent.VK_DOWN:
|
||||||
if (e.isControlDown())
|
if (e.isControlDown())
|
||||||
speaker.setVolume(speaker.getVolume() - 1);
|
{
|
||||||
|
apple.speaker.setVolume(apple.speaker.getVolume() - 1);
|
||||||
|
}
|
||||||
|
else if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD)
|
||||||
|
{
|
||||||
|
handleKeypadDown();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
apple.setKeyLatch(10);
|
apple.setKeyLatch(10);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyEvent.VK_ESCAPE:
|
case KeyEvent.VK_ESCAPE:
|
||||||
apple.setKeyLatch(27);
|
apple.setKeyLatch(27);
|
||||||
|
@ -328,9 +621,21 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
break;
|
break;
|
||||||
case KeyEvent.VK_HOME:
|
case KeyEvent.VK_HOME:
|
||||||
if (e.isControlDown())
|
if (e.isControlDown())
|
||||||
apple.restart();
|
restart();
|
||||||
else
|
else
|
||||||
apple.reset();
|
apple.paddle.setButton(0, true);
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_END:
|
||||||
|
apple.paddle.setButton(1, true);
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_F1:
|
||||||
|
showStatus("AppleIIGo Version " + version);
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_F3:
|
||||||
|
cycleDisk(0);
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_F4:
|
||||||
|
cycleDisk(1);
|
||||||
break;
|
break;
|
||||||
case KeyEvent.VK_F5:
|
case KeyEvent.VK_F5:
|
||||||
if (isCpuDebugEnabled)
|
if (isCpuDebugEnabled)
|
||||||
|
@ -352,12 +657,135 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
apple.stepInstructions(128);
|
apple.stepInstructions(128);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case KeyEvent.VK_CANCEL: // Pause/Break sends this (as Mac OS swallows Ctrl-F12)
|
||||||
|
case KeyEvent.VK_F12:
|
||||||
|
if (e.isControlDown())
|
||||||
|
reset();
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_KP_LEFT:
|
||||||
|
handleKeypadLeft();
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_KP_RIGHT:
|
||||||
|
handleKeypadRight();
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_KP_UP:
|
||||||
|
handleKeypadUp();
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_KP_DOWN:
|
||||||
|
handleKeypadDown();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void keyReleased(KeyEvent e) {
|
public void keyReleased(KeyEvent e) {
|
||||||
|
switch(e.getKeyCode()) {
|
||||||
|
case KeyEvent.VK_META:
|
||||||
|
apple.paddle.setButton(1, false);
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_ALT:
|
||||||
|
if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_LEFT)
|
||||||
|
{
|
||||||
|
apple.paddle.setButton(0, false);
|
||||||
|
}
|
||||||
|
else if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT)
|
||||||
|
{
|
||||||
|
apple.paddle.setButton(1, false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_LEFT:
|
||||||
|
case KeyEvent.VK_RIGHT:
|
||||||
|
if (e.getKeyLocation() != KeyEvent.KEY_LOCATION_NUMPAD)
|
||||||
|
break;
|
||||||
|
// else fall through
|
||||||
|
case KeyEvent.VK_KP_LEFT:
|
||||||
|
case KeyEvent.VK_KP_RIGHT:
|
||||||
|
handleKeypadCentreX();
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_UP:
|
||||||
|
case KeyEvent.VK_DOWN:
|
||||||
|
if (e.getKeyLocation() != KeyEvent.KEY_LOCATION_NUMPAD)
|
||||||
|
break;
|
||||||
|
// else fall through
|
||||||
|
case KeyEvent.VK_KP_UP:
|
||||||
|
case KeyEvent.VK_KP_DOWN:
|
||||||
|
handleKeypadCentreY();
|
||||||
|
break;
|
||||||
|
case KeyEvent.VK_HOME:
|
||||||
|
if (!e.isControlDown())
|
||||||
|
{
|
||||||
|
apple.paddle.setButton(0, false);
|
||||||
|
}
|
||||||
|
case KeyEvent.VK_END:
|
||||||
|
apple.paddle.setButton(1, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleKeypadCentreX() {
|
||||||
|
if (isPaddleEnabled)
|
||||||
|
{
|
||||||
|
if (isPaddleInverted) {
|
||||||
|
apple.paddle.setPaddlePos(1, 127);
|
||||||
|
} else {
|
||||||
|
apple.paddle.setPaddlePos(0, 127);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleKeypadCentreY() {
|
||||||
|
if (isPaddleEnabled)
|
||||||
|
{
|
||||||
|
if (isPaddleInverted) {
|
||||||
|
apple.paddle.setPaddlePos(0, 127);
|
||||||
|
} else {
|
||||||
|
apple.paddle.setPaddlePos(1, 127);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleKeypadLeft() {
|
||||||
|
if (isPaddleEnabled)
|
||||||
|
{
|
||||||
|
if (isPaddleInverted) {
|
||||||
|
apple.paddle.setPaddlePos(1, 255);
|
||||||
|
} else {
|
||||||
|
apple.paddle.setPaddlePos(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleKeypadRight() {
|
||||||
|
if (isPaddleEnabled)
|
||||||
|
{
|
||||||
|
if (isPaddleInverted) {
|
||||||
|
apple.paddle.setPaddlePos(1, 0);
|
||||||
|
} else {
|
||||||
|
apple.paddle.setPaddlePos(0, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleKeypadUp() {
|
||||||
|
if (isPaddleEnabled)
|
||||||
|
{
|
||||||
|
if (isPaddleInverted) {
|
||||||
|
apple.paddle.setPaddlePos(0, 255);
|
||||||
|
} else {
|
||||||
|
apple.paddle.setPaddlePos(1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleKeypadDown() {
|
||||||
|
if (isPaddleEnabled)
|
||||||
|
{
|
||||||
|
if (isPaddleInverted) {
|
||||||
|
apple.paddle.setPaddlePos(0, 0);
|
||||||
|
} else {
|
||||||
|
apple.paddle.setPaddlePos(1, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ComponentListener event handling
|
* ComponentListener event handling
|
||||||
*/
|
*/
|
||||||
|
@ -399,20 +827,24 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
|
|
||||||
public void mousePressed(MouseEvent e) {
|
public void mousePressed(MouseEvent e) {
|
||||||
int modifiers = e.getModifiers();
|
int modifiers = e.getModifiers();
|
||||||
|
if (isPaddleEnabled)
|
||||||
if ((modifiers & InputEvent.BUTTON1_MASK) != 0)
|
{
|
||||||
apple.paddle.setButton(0, true);
|
if ((modifiers & InputEvent.BUTTON1_MASK) != 0)
|
||||||
if ((modifiers & InputEvent.BUTTON3_MASK) != 0)
|
apple.paddle.setButton(0, true);
|
||||||
apple.paddle.setButton(1, true);
|
if ((modifiers & InputEvent.BUTTON3_MASK) != 0)
|
||||||
|
apple.paddle.setButton(1, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mouseReleased(MouseEvent e) {
|
public void mouseReleased(MouseEvent e) {
|
||||||
int modifiers = e.getModifiers();
|
int modifiers = e.getModifiers();
|
||||||
|
if (isPaddleEnabled)
|
||||||
if ((modifiers & InputEvent.BUTTON1_MASK) != 0)
|
{
|
||||||
apple.paddle.setButton(0, false);
|
if ((modifiers & InputEvent.BUTTON1_MASK) != 0)
|
||||||
if ((modifiers & InputEvent.BUTTON3_MASK) != 0)
|
apple.paddle.setButton(0, false);
|
||||||
apple.paddle.setButton(1, false);
|
if ((modifiers & InputEvent.BUTTON3_MASK) != 0)
|
||||||
|
apple.paddle.setButton(1, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mouseDragged(MouseEvent e) {
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
@ -420,12 +852,16 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mouseMoved(MouseEvent e) {
|
public void mouseMoved(MouseEvent e) {
|
||||||
if (isPaddleInverted) {
|
float scale = display.getScale();
|
||||||
apple.paddle.setPaddlePos(0, (int) (display.getScale() * (255 - e.getY() * 256 / 192)));
|
if (isPaddleEnabled)
|
||||||
apple.paddle.setPaddlePos(1, (int) (display.getScale() * (255 - e.getX() * 256 / 280)));
|
{
|
||||||
} else {
|
if (isPaddleInverted) {
|
||||||
apple.paddle.setPaddlePos(0, (int) (e.getX() * display.getScale() * 256 / 280));
|
apple.paddle.setPaddlePos(0, (int) (255 - e.getY() * 256 / (192 * scale)));
|
||||||
apple.paddle.setPaddlePos(1, (int) (e.getY() * display.getScale() * 256 / 192));
|
apple.paddle.setPaddlePos(1, (int) (255 - e.getX() * 256 / (280 * scale)));
|
||||||
|
} else {
|
||||||
|
apple.paddle.setPaddlePos(0, (int) (e.getX() * 256 / (280 * scale)));
|
||||||
|
apple.paddle.setPaddlePos(1, (int) (e.getY() * 256 / (192 * scale)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* AppleIIGo
|
* AppleIIGo
|
||||||
* Speaker processing
|
* Speaker processing
|
||||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
* Copyright 2006 by Marc S. Ressl(mressl@gmail.com)
|
||||||
* Released under the GPL
|
* Released under the GPL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -10,10 +10,10 @@ import javax.sound.sampled.*;
|
||||||
|
|
||||||
public class AppleSpeaker implements Runnable {
|
public class AppleSpeaker implements Runnable {
|
||||||
// Instances of other classes
|
// Instances of other classes
|
||||||
private EmAppleII apple;
|
private EmAppleII apple;
|
||||||
|
|
||||||
// Refresh
|
// Refresh
|
||||||
private int refreshRate;
|
// private int refreshRate;
|
||||||
private long refreshInterval;
|
private long refreshInterval;
|
||||||
|
|
||||||
// Sound stuff
|
// Sound stuff
|
||||||
|
@ -29,7 +29,6 @@ public class AppleSpeaker implements Runnable {
|
||||||
|
|
||||||
private SourceDataLine line;
|
private SourceDataLine line;
|
||||||
|
|
||||||
private int bufferSamples;
|
|
||||||
private int bufferSize;
|
private int bufferSize;
|
||||||
private byte[] buffer;
|
private byte[] buffer;
|
||||||
|
|
||||||
|
@ -43,11 +42,8 @@ public class AppleSpeaker implements Runnable {
|
||||||
// Thread stuff
|
// Thread stuff
|
||||||
private boolean isPaused = true;
|
private boolean isPaused = true;
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private String threadError = null;
|
|
||||||
|
|
||||||
|
public AppleSpeaker(EmAppleII apple) {
|
||||||
|
|
||||||
public AppleSpeaker(EmAppleII apple) {
|
|
||||||
this.apple = apple;
|
this.apple = apple;
|
||||||
|
|
||||||
setVolume(4);
|
setVolume(4);
|
||||||
|
@ -62,18 +58,18 @@ public class AppleSpeaker implements Runnable {
|
||||||
if (value <= 0.0f)
|
if (value <= 0.0f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.refreshRate = value;
|
// this.refreshRate = value;
|
||||||
refreshInterval = (int) (1000.0 / value);
|
refreshInterval = (int) (1000.0 / value);
|
||||||
|
|
||||||
speakerClocksPerSample = (int) (apple.getCpuSpeed() * 1000.0f / SPEAKER_SAMPLERATE);
|
speakerClocksPerSample = (int) (apple.getCpuSpeed() * 1000.0f / SPEAKER_SAMPLERATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get refresh rate
|
* Get refresh rate
|
||||||
*/
|
*/
|
||||||
private int getRefreshRate() {
|
// private int getRefreshRate() {
|
||||||
return refreshRate;
|
// return refreshRate;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set speaker volume
|
* Set speaker volume
|
||||||
|
@ -94,7 +90,7 @@ public class AppleSpeaker implements Runnable {
|
||||||
*/
|
*/
|
||||||
public int getVolume() {
|
public int getVolume() {
|
||||||
return speakerVolume;
|
return speakerVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set pause state
|
* Set pause state
|
||||||
|
@ -124,14 +120,12 @@ public class AppleSpeaker implements Runnable {
|
||||||
SPEAKER_BIGENDIAN);
|
SPEAKER_BIGENDIAN);
|
||||||
|
|
||||||
DataLine.Info info = new DataLine.Info(
|
DataLine.Info info = new DataLine.Info(
|
||||||
DataLine.class,
|
SourceDataLine.class,
|
||||||
audioFormat);
|
audioFormat);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
line = (SourceDataLine) AudioSystem.getLine(info);
|
line = (SourceDataLine) AudioSystem.getLine(info);
|
||||||
bufferSize = line.getBufferSize();
|
bufferSize = line.getBufferSize();
|
||||||
bufferSamples = bufferSize / SPEAKER_SAMPLESIZE;
|
|
||||||
|
|
||||||
buffer = new byte[bufferSize];
|
buffer = new byte[bufferSize];
|
||||||
|
|
||||||
line.open(audioFormat);
|
line.open(audioFormat);
|
||||||
|
@ -139,15 +133,17 @@ public class AppleSpeaker implements Runnable {
|
||||||
} catch (LineUnavailableException e) {
|
} catch (LineUnavailableException e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
thread = new Thread(this);
|
// TODO: this thread is not created any more (nick)
|
||||||
thread.start();
|
//thread = new Thread(this);
|
||||||
|
//thread.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Speaker refresh thread
|
* Speaker refresh thread
|
||||||
|
* TODO: this thread is not created any more (nick)
|
||||||
*/
|
*/
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
while (!isPaused) {
|
while (!isPaused) {
|
||||||
long refreshStart = System.currentTimeMillis();
|
long refreshStart = System.currentTimeMillis();
|
||||||
|
@ -169,7 +165,7 @@ public class AppleSpeaker implements Runnable {
|
||||||
/**
|
/**
|
||||||
* Speaker refresh
|
* Speaker refresh
|
||||||
*/
|
*/
|
||||||
private void refreshSpeaker() {
|
public void refreshSpeaker() {
|
||||||
clockEnd = apple.clock;
|
clockEnd = apple.clock;
|
||||||
int bytes;
|
int bytes;
|
||||||
|
|
||||||
|
@ -184,7 +180,7 @@ public class AppleSpeaker implements Runnable {
|
||||||
/**
|
/**
|
||||||
* Fill buffer
|
* Fill buffer
|
||||||
*/
|
*/
|
||||||
private int fillBuffer() {
|
private int fillBuffer() {
|
||||||
int value = speakerFlipStateToVolume[speakerFlipState];
|
int value = speakerFlipStateToVolume[speakerFlipState];
|
||||||
int clockEndSample = clockEnd - speakerClocksPerSample;
|
int clockEndSample = clockEnd - speakerClocksPerSample;
|
||||||
int bufferPointer = 0;
|
int bufferPointer = 0;
|
||||||
|
@ -234,7 +230,7 @@ public class AppleSpeaker implements Runnable {
|
||||||
isFlipsBufferEmpty = true;
|
isFlipsBufferEmpty = true;
|
||||||
} else {
|
} else {
|
||||||
clockNextFlip = apple.speakerFlips[speakerFlipsPointer];
|
clockNextFlip = apple.speakerFlips[speakerFlipsPointer];
|
||||||
speakerFlipsPointer = (speakerFlipsPointer + 1) & apple.SPEAKER_FLIPS_MASK;
|
speakerFlipsPointer = (speakerFlipsPointer + 1) & EmAppleII.SPEAKER_FLIPS_MASK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
/**
|
/**
|
||||||
* AppleIIGo
|
* AppleIIGo
|
||||||
* Disk II Emulator
|
* Disk II Emulator
|
||||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
* Copyright 2015 by Nick Westgate (Nick.Westgate@gmail.com)
|
||||||
|
* Copyright 2006 by Marc S. Ressl(mressl@gmail.com)
|
||||||
* Released under the GPL
|
* Released under the GPL
|
||||||
* Based on work by Doug Kwan
|
* Based on work by Doug Kwan
|
||||||
*/
|
*/
|
||||||
|
@ -31,17 +32,23 @@ public class DiskII extends Peripheral {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
|
public static final int DEFAULT_VOLUME = 254;
|
||||||
private static final int NUM_DRIVES = 2;
|
private static final int NUM_DRIVES = 2;
|
||||||
private static final int DOS_NUM_SECTORS = 16;
|
private static final int DOS_NUM_SECTORS = 16;
|
||||||
private static final int DOS_NUM_TRACKS = 35;
|
private static final int DOS_NUM_TRACKS = 35;
|
||||||
|
private static final int MAX_PHYS_TRACK = (2 * DOS_NUM_TRACKS) - 1;
|
||||||
private static final int DOS_TRACK_BYTES = 256 * DOS_NUM_SECTORS;
|
private static final int DOS_TRACK_BYTES = 256 * DOS_NUM_SECTORS;
|
||||||
private static final int RAW_TRACK_BYTES = 6250;
|
private static final int RAW_TRACK_BYTES = 0x1A00; // 0x1A00 (6656) for .NIB (was 6250)
|
||||||
|
private static final int STANDARD_2IMG_HEADER_ID = 0x32494D47;
|
||||||
|
private static final int STANDARD_2IMG_HEADER_SIZE = 64;
|
||||||
|
private static final int STANDARD_PRODOS_BLOCKS = 280;
|
||||||
|
|
||||||
// Disk II direct access variables
|
// Disk II direct access variables
|
||||||
private int drive = 0;
|
private int drive = 0;
|
||||||
|
private int phases = 0;
|
||||||
private boolean isMotorOn = false;
|
private boolean isMotorOn = false;
|
||||||
|
|
||||||
private byte[][][] disk = new byte[NUM_DRIVES][DOS_NUM_TRACKS][];
|
private byte[][][] diskData = new byte[NUM_DRIVES][DOS_NUM_TRACKS][];
|
||||||
private boolean[] isWriteProtected = new boolean[NUM_DRIVES];
|
private boolean[] isWriteProtected = new boolean[NUM_DRIVES];
|
||||||
|
|
||||||
private int currPhysTrack;
|
private int currPhysTrack;
|
||||||
|
@ -64,9 +71,10 @@ public class DiskII extends Peripheral {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Internal registers
|
// Internal registers
|
||||||
private int latchAddress;
|
|
||||||
private int latchData;
|
private int latchData;
|
||||||
private boolean writeMode;
|
private boolean writeMode;
|
||||||
|
private boolean loadMode;
|
||||||
|
private int driveSpin;
|
||||||
|
|
||||||
// GCR encoding and decoding tables
|
// GCR encoding and decoding tables
|
||||||
private static final int[] gcrEncodingTable = {
|
private static final int[] gcrEncodingTable = {
|
||||||
|
@ -79,30 +87,36 @@ public class DiskII extends Peripheral {
|
||||||
0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
|
0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
|
||||||
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
|
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
|
||||||
};
|
};
|
||||||
private int[] gcrDecodingTable = new int[256];
|
//private int[] gcrDecodingTable = new int[256];
|
||||||
private int[] gcrSwapBit = {0, 2, 1, 3};
|
private int[] gcrSwapBit = {0, 2, 1, 3};
|
||||||
private int[] gcrBuffer = new int[256];
|
private int[] gcrBuffer = new int[256];
|
||||||
private int[] gcrBuffer2 = new int[86];
|
private int[] gcrBuffer2 = new int[86];
|
||||||
|
|
||||||
// Physical sector to DOS 3.3 logical sector table
|
// Physical sector to DOS 3.3 logical sector table
|
||||||
private static final int[] gcrLogicalSector = {
|
private static final int[] gcrLogicalDos33Sector = {
|
||||||
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4,
|
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4,
|
||||||
0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
|
0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
|
||||||
|
|
||||||
|
// Physical sector to DOS 3.3 logical sector table
|
||||||
|
private static final int[] gcrLogicalProdosSector = {
|
||||||
|
0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB,
|
||||||
|
0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
|
||||||
|
|
||||||
// Temporary variables for conversion
|
// Temporary variables for conversion
|
||||||
private byte[] gcrNibbles = new byte[RAW_TRACK_BYTES];
|
private byte[] gcrNibbles = new byte[RAW_TRACK_BYTES];
|
||||||
private int gcrNibblesPos;
|
private int gcrNibblesPos;
|
||||||
|
|
||||||
|
EmAppleII apple;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public DiskII() {
|
public DiskII(EmAppleII apple) {
|
||||||
super();
|
super();
|
||||||
|
this.apple = apple;
|
||||||
|
|
||||||
readDisk(0, null, 254, false);
|
readDisk(0, null, "", false, DEFAULT_VOLUME);
|
||||||
readDisk(1, null, 254, false);
|
readDisk(1, null, "", false, DEFAULT_VOLUME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -111,100 +125,54 @@ public class DiskII extends Peripheral {
|
||||||
* @param address Address
|
* @param address Address
|
||||||
*/
|
*/
|
||||||
public int ioRead(int address) {
|
public int ioRead(int address) {
|
||||||
int phase;
|
|
||||||
|
|
||||||
switch (address & 0xf) {
|
switch (address & 0xf) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
case 0x2:
|
|
||||||
case 0x4:
|
|
||||||
case 0x6:
|
|
||||||
// Q0, Q1, Q2, Q3 off
|
|
||||||
break;
|
|
||||||
case 0x1:
|
case 0x1:
|
||||||
// Q0 on
|
case 0x2:
|
||||||
phase = currPhysTrack & 3;
|
|
||||||
if (phase == 1) {
|
|
||||||
if (currPhysTrack > 0)
|
|
||||||
currPhysTrack--;
|
|
||||||
} else if (phase == 3) {
|
|
||||||
if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1))
|
|
||||||
currPhysTrack++;
|
|
||||||
}
|
|
||||||
realTrack = disk[drive][currPhysTrack >> 1];
|
|
||||||
break;
|
|
||||||
case 0x3:
|
case 0x3:
|
||||||
// Q1 on
|
case 0x4:
|
||||||
phase = currPhysTrack & 3;
|
|
||||||
if (phase == 2) {
|
|
||||||
if (currPhysTrack > 0)
|
|
||||||
currPhysTrack--;
|
|
||||||
} else if (phase == 0) {
|
|
||||||
if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1))
|
|
||||||
currPhysTrack++;
|
|
||||||
}
|
|
||||||
realTrack = disk[drive][currPhysTrack >> 1];
|
|
||||||
break;
|
|
||||||
case 0x5:
|
case 0x5:
|
||||||
// Q2 on
|
case 0x6:
|
||||||
phase = currPhysTrack & 3;
|
|
||||||
if (phase == 3) {
|
|
||||||
if (currPhysTrack > 0)
|
|
||||||
currPhysTrack--;
|
|
||||||
} else if (phase == 1) {
|
|
||||||
if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1))
|
|
||||||
currPhysTrack++;
|
|
||||||
}
|
|
||||||
realTrack = disk[drive][currPhysTrack >> 1];
|
|
||||||
break;
|
|
||||||
case 0x7:
|
case 0x7:
|
||||||
// Q3 on
|
setPhase(address);
|
||||||
phase = currPhysTrack & 3;
|
|
||||||
if (phase == 0) {
|
|
||||||
if (currPhysTrack > 0)
|
|
||||||
currPhysTrack--;
|
|
||||||
} else if (phase == 2) {
|
|
||||||
if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1))
|
|
||||||
currPhysTrack++;
|
|
||||||
}
|
|
||||||
realTrack = disk[drive][currPhysTrack >> 1];
|
|
||||||
break;
|
break;
|
||||||
case 0x8:
|
case 0x8:
|
||||||
// Motor off
|
|
||||||
isMotorOn = false;
|
isMotorOn = false;
|
||||||
break;
|
break;
|
||||||
case 0x9:
|
case 0x9:
|
||||||
// Motor on
|
|
||||||
isMotorOn = true;
|
isMotorOn = true;
|
||||||
break;
|
break;
|
||||||
case 0xa:
|
case 0xa:
|
||||||
// Drive 1
|
setDrive(0);
|
||||||
driveCurrPhysTrack[drive] = currPhysTrack;
|
|
||||||
drive = 0;
|
|
||||||
currPhysTrack = driveCurrPhysTrack[drive];
|
|
||||||
|
|
||||||
realTrack = disk[drive][currPhysTrack >> 1];
|
|
||||||
break;
|
break;
|
||||||
case 0xb:
|
case 0xb:
|
||||||
// Drive 2
|
setDrive(1);
|
||||||
driveCurrPhysTrack[drive] = currPhysTrack;
|
|
||||||
drive = 1;
|
|
||||||
currPhysTrack = driveCurrPhysTrack[drive];
|
|
||||||
|
|
||||||
realTrack = disk[drive][currPhysTrack >> 1];
|
|
||||||
break;
|
break;
|
||||||
case 0xc:
|
case 0xc:
|
||||||
return ioLatchC();
|
ioLatchC();
|
||||||
|
break;
|
||||||
case 0xd:
|
case 0xd:
|
||||||
ioLatchD(0xff);
|
loadMode = true;
|
||||||
|
if (isMotorOn && !writeMode)
|
||||||
|
{
|
||||||
|
latchData &= 0x7F;
|
||||||
|
// TODO: check phase - write protect is forced if phase 1 is on [F9.7]
|
||||||
|
if (isWriteProtected[drive])
|
||||||
|
{
|
||||||
|
latchData |= 0x80;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 0xe:
|
case 0xe:
|
||||||
return ioLatchE();
|
writeMode = false;
|
||||||
|
break;
|
||||||
case 0xf:
|
case 0xf:
|
||||||
ioLatchF(0xff);
|
writeMode = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rand.nextInt(256);
|
// only even addresses return the latch
|
||||||
|
return ((address & 1) == 0) ? latchData : rand.nextInt(256); // TODO: floating bus
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -222,25 +190,39 @@ public class DiskII extends Peripheral {
|
||||||
case 0x5:
|
case 0x5:
|
||||||
case 0x6:
|
case 0x6:
|
||||||
case 0x7:
|
case 0x7:
|
||||||
|
setPhase(address);
|
||||||
|
break;
|
||||||
case 0x8:
|
case 0x8:
|
||||||
|
isMotorOn = false;
|
||||||
|
break;
|
||||||
case 0x9:
|
case 0x9:
|
||||||
|
isMotorOn = true;
|
||||||
|
break;
|
||||||
case 0xa:
|
case 0xa:
|
||||||
|
setDrive(0);
|
||||||
|
break;
|
||||||
case 0xb:
|
case 0xb:
|
||||||
ioRead(address);
|
setDrive(1);
|
||||||
break;
|
break;
|
||||||
case 0xc:
|
case 0xc:
|
||||||
ioLatchC();
|
ioLatchC();
|
||||||
break;
|
break;
|
||||||
case 0xd:
|
case 0xd:
|
||||||
ioLatchD(value);
|
loadMode = true;
|
||||||
break;
|
break;
|
||||||
case 0xe:
|
case 0xe:
|
||||||
ioLatchE();
|
writeMode = false;
|
||||||
break;
|
break;
|
||||||
case 0xf:
|
case 0xf:
|
||||||
ioLatchF(value);
|
writeMode = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isMotorOn && writeMode && loadMode)
|
||||||
|
{
|
||||||
|
// any address writes latch for sequencer LD; OE1/2 irrelevant ['323 datasheet]
|
||||||
|
latchData = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -256,29 +238,88 @@ public class DiskII extends Peripheral {
|
||||||
* Reset peripheral
|
* Reset peripheral
|
||||||
*/
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
ioRead(0x8);
|
drive = 0;
|
||||||
|
isMotorOn = false;
|
||||||
|
loadMode = false;
|
||||||
|
writeMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a disk
|
* Loads a disk
|
||||||
*
|
|
||||||
* @param is InputStream
|
|
||||||
* @param drive Disk II drive
|
|
||||||
*/
|
*/
|
||||||
public boolean readDisk(int drive, InputStream is, int volume, boolean isWriteProtected) {
|
public boolean readDisk(int drive, DataInputStream is, String name, boolean isWriteProtected, int volumeNumber) {
|
||||||
byte[] track = new byte[RAW_TRACK_BYTES];
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
byte[] track = new byte[DOS_TRACK_BYTES];
|
||||||
|
boolean proDos = false;
|
||||||
|
boolean nib = false;
|
||||||
|
|
||||||
|
String lowerName = name.toLowerCase();
|
||||||
|
if (lowerName.indexOf(".2mg") != -1 || lowerName.indexOf(".2img") != -1)
|
||||||
|
{
|
||||||
|
// 2IMG, so check if we can handle it
|
||||||
|
byte[] header = new byte[STANDARD_2IMG_HEADER_SIZE];
|
||||||
|
is.readFully(header, 0, STANDARD_2IMG_HEADER_SIZE);
|
||||||
|
|
||||||
|
int id = (header[0x00] << 24) | (header[0x01] << 16) | (header[0x02] << 8) | (header[0x03]);
|
||||||
|
if (id != STANDARD_2IMG_HEADER_ID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int headerSize = (header[0x09] << 8) | (header[0x08]);
|
||||||
|
if (headerSize != STANDARD_2IMG_HEADER_SIZE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int format = (header[0x0F] << 24) | (header[0x0E] << 16) | (header[0x0D] << 8) | (header[0x0C]);
|
||||||
|
if (format == 1)
|
||||||
|
{
|
||||||
|
proDos = true;
|
||||||
|
int blocks = (header[0x17] << 24) | (header[0x16] << 16) | (header[0x15] << 8) | (header[0x14]);
|
||||||
|
if (blocks != STANDARD_PRODOS_BLOCKS)
|
||||||
|
return false; // only handle standard 5.25 inch images
|
||||||
|
}
|
||||||
|
else if (format == 2)
|
||||||
|
{
|
||||||
|
nib = true;
|
||||||
|
}
|
||||||
|
else if (format != 0)
|
||||||
|
{
|
||||||
|
return false; // if not ProDOS, NIB or DSK
|
||||||
|
}
|
||||||
|
|
||||||
|
// use write protected and volume number if present
|
||||||
|
int flags = (header[0x13] << 24) | (header[0x12] << 16) | (header[0x11] << 8) | (header[0x10]);
|
||||||
|
if ((flags & (1 << 31)) != 0)
|
||||||
|
{
|
||||||
|
isWriteProtected = true; // only override if set
|
||||||
|
}
|
||||||
|
if ((flags & (1 << 8)) != 0)
|
||||||
|
{
|
||||||
|
volumeNumber = (flags & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// check for PO and NIB in the name
|
||||||
|
proDos = lowerName.indexOf(".po") != -1;
|
||||||
|
nib = lowerName.indexOf(".nib") != -1;
|
||||||
|
}
|
||||||
|
|
||||||
for (int trackNum = 0; trackNum < DOS_NUM_TRACKS; trackNum++) {
|
for (int trackNum = 0; trackNum < DOS_NUM_TRACKS; trackNum++) {
|
||||||
disk[drive][trackNum] = new byte[RAW_TRACK_BYTES];
|
diskData[drive][trackNum] = new byte[RAW_TRACK_BYTES];
|
||||||
|
|
||||||
if (is != null) {
|
if (is != null) {
|
||||||
is.read(track, 0, DOS_TRACK_BYTES);
|
if (nib)
|
||||||
trackToNibbles(track, disk[drive][trackNum], volume, trackNum);
|
{
|
||||||
|
is.readFully(diskData[drive][trackNum], 0, RAW_TRACK_BYTES);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
is.readFully(track, 0, DOS_TRACK_BYTES);
|
||||||
|
trackToNibbles(track, diskData[drive][trackNum], volumeNumber, trackNum, !proDos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.realTrack = disk[drive][currPhysTrack >> 1];
|
this.realTrack = diskData[drive][currPhysTrack >> 1];
|
||||||
this.isWriteProtected[drive] = isWriteProtected;
|
this.isWriteProtected[drive] = isWriteProtected;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -305,68 +346,112 @@ public class DiskII extends Peripheral {
|
||||||
return isMotorOn;
|
return isMotorOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ioLatchC() {
|
||||||
|
loadMode = false;
|
||||||
|
if (!writeMode)
|
||||||
|
{
|
||||||
|
if (!isMotorOn)
|
||||||
|
{
|
||||||
|
// simple hack to fool RWTS SAMESLOT drive spin check (usually at $BD34)
|
||||||
|
driveSpin = (driveSpin + 1) & 0xF;
|
||||||
|
if (driveSpin == 0)
|
||||||
|
{
|
||||||
|
latchData = 0x7F;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* I/O read Latch C
|
|
||||||
*
|
|
||||||
* @param address Address
|
|
||||||
*/
|
|
||||||
private int ioLatchC() {
|
|
||||||
if (writeMode)
|
|
||||||
// Write data: C0xD, C0xC
|
|
||||||
realTrack[currNibble] = (byte) latchData;
|
|
||||||
else
|
|
||||||
// Read data: C0xE, C0xC
|
// Read data: C0xE, C0xC
|
||||||
latchData = (realTrack[currNibble] & 0xff);
|
latchData = (realTrack[currNibble] & 0xff);
|
||||||
|
|
||||||
|
// is RWTS looking for an address prologue? (RDADR)
|
||||||
|
if (/* fastDisk && */ // TODO: fastDisk property to enable/disable
|
||||||
|
apple.memoryRead(apple.PC + 3) == 0xD5 && // #$D5
|
||||||
|
apple.memoryRead(apple.PC + 2) == 0xC9 && // CMP (usually at $B94F)
|
||||||
|
apple.memoryRead(apple.PC + 1) == 0xFB && // PC - 3
|
||||||
|
apple.memoryRead(apple.PC + 0) == 0x10 && // BPL
|
||||||
|
latchData != 0xD5)
|
||||||
|
{
|
||||||
|
// Y: find the next address prologues
|
||||||
|
int count = RAW_TRACK_BYTES / 16;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
currNibble++;
|
||||||
|
if (currNibble >= RAW_TRACK_BYTES)
|
||||||
|
currNibble = 0;
|
||||||
|
latchData = (realTrack[currNibble] & 0xff);
|
||||||
|
}
|
||||||
|
while (latchData != 0xD5 && --count > 0);
|
||||||
|
}
|
||||||
|
// N: skip invalid nibbles we padded the track buffer with
|
||||||
|
else if (latchData == 0x7F)
|
||||||
|
{
|
||||||
|
int count = RAW_TRACK_BYTES / 16;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
currNibble++;
|
||||||
|
if (currNibble >= RAW_TRACK_BYTES)
|
||||||
|
currNibble = 0;
|
||||||
|
latchData = (realTrack[currNibble] & 0xff);
|
||||||
|
}
|
||||||
|
while (latchData == 0x7F && --count > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Write data: C0xD, C0xC
|
||||||
|
realTrack[currNibble] = (byte) latchData;
|
||||||
|
}
|
||||||
|
|
||||||
currNibble++;
|
currNibble++;
|
||||||
if (currNibble >= RAW_TRACK_BYTES)
|
if (currNibble >= RAW_TRACK_BYTES)
|
||||||
currNibble = 0;
|
currNibble = 0;
|
||||||
|
|
||||||
latchAddress = 0xc;
|
|
||||||
return latchData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void setPhase(int address) {
|
||||||
* I/O write Latch D
|
int phase = (address >> 1) & 3;
|
||||||
*
|
int phase_bit = (1 << phase);
|
||||||
* @param address Address
|
|
||||||
*/
|
|
||||||
private void ioLatchD(int value) {
|
|
||||||
// Prepare write
|
|
||||||
writeMode = true;
|
|
||||||
latchData = value;
|
|
||||||
latchAddress = 0xd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// update the magnet states
|
||||||
* I/O read Latch E
|
if ((address & 1) != 0)
|
||||||
*
|
{
|
||||||
* @param address Address
|
// phase on
|
||||||
*/
|
phases |= phase_bit;
|
||||||
private int ioLatchE() {
|
}
|
||||||
// Read write-protect: C0xD, C0xE
|
else
|
||||||
if (latchAddress == 0xd) {
|
{
|
||||||
latchAddress = 0xe;
|
// phase off
|
||||||
return isWriteProtected[drive] ? 0x80 : 0x00;
|
phases &= ~phase_bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeMode = false;
|
// check for any stepping effect from a magnet
|
||||||
latchAddress = 0xe;
|
// - move only when the magnet opposite the cog is off
|
||||||
return 0x3c;
|
// - move in the direction of an adjacent magnet if one is on
|
||||||
|
// - do not move if both adjacent magnets are on
|
||||||
|
// momentum and timing are not accounted for ... maybe one day!
|
||||||
|
int direction = 0;
|
||||||
|
if ((phases & (1 << ((currPhysTrack + 1) & 3))) != 0)
|
||||||
|
direction += 1;
|
||||||
|
if ((phases & (1 << ((currPhysTrack + 3) & 3))) != 0)
|
||||||
|
direction -= 1;
|
||||||
|
|
||||||
|
// apply magnet step, if any
|
||||||
|
if (direction != 0)
|
||||||
|
{
|
||||||
|
currPhysTrack += direction;
|
||||||
|
if (currPhysTrack < 0)
|
||||||
|
currPhysTrack = 0;
|
||||||
|
else if (currPhysTrack > MAX_PHYS_TRACK)
|
||||||
|
currPhysTrack = MAX_PHYS_TRACK;
|
||||||
|
}
|
||||||
|
realTrack = diskData[drive][currPhysTrack >> 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void setDrive(int newDrive) {
|
||||||
* I/O write Latch F
|
driveCurrPhysTrack[drive] = currPhysTrack;
|
||||||
*
|
drive = newDrive;
|
||||||
* @param address Address
|
currPhysTrack = driveCurrPhysTrack[drive];
|
||||||
*/
|
realTrack = diskData[drive][currPhysTrack >> 1];
|
||||||
private void ioLatchF(int value) {
|
|
||||||
// Prepare write
|
|
||||||
writeMode = true;
|
|
||||||
latchData = value;
|
|
||||||
latchAddress = 0xf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -384,15 +469,24 @@ public class DiskII extends Peripheral {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes sync bits
|
* Writes nibbles
|
||||||
|
*
|
||||||
|
* @param length Number of bits
|
||||||
|
*/
|
||||||
|
private final void writeNibbles(int nibble, int length) {
|
||||||
|
while(length > 0) {
|
||||||
|
length--;
|
||||||
|
gcrWriteNibble(nibble);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes sync nibbles
|
||||||
*
|
*
|
||||||
* @param length Number of bits
|
* @param length Number of bits
|
||||||
*/
|
*/
|
||||||
private final void writeSync(int length) {
|
private final void writeSync(int length) {
|
||||||
while(length > 0) {
|
writeNibbles(0xff, length);
|
||||||
length--;
|
|
||||||
gcrWriteNibble(0xff);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -487,17 +581,18 @@ public class DiskII extends Peripheral {
|
||||||
/**
|
/**
|
||||||
* Converts a track to nibbles
|
* Converts a track to nibbles
|
||||||
*/
|
*/
|
||||||
private void trackToNibbles(byte[] track, byte[] nibbles, int volumeNum, int trackNum) {
|
private void trackToNibbles(byte[] track, byte[] nibbles, int volumeNum, int trackNum, boolean dos) {
|
||||||
this.gcrNibbles = nibbles;
|
this.gcrNibbles = nibbles;
|
||||||
gcrNibblesPos = 0;
|
gcrNibblesPos = 0;
|
||||||
|
int logicalSector[] = (dos) ? gcrLogicalDos33Sector : gcrLogicalProdosSector;
|
||||||
|
|
||||||
for (int sectorNum = 0; sectorNum < DOS_NUM_SECTORS; sectorNum++) {
|
for (int sectorNum = 0; sectorNum < DOS_NUM_SECTORS; sectorNum++) {
|
||||||
encode62(track, gcrLogicalSector[sectorNum] << 8);
|
encode62(track, logicalSector[sectorNum] << 8);
|
||||||
writeSync(12);
|
writeSync(12);
|
||||||
writeAddressField(volumeNum, trackNum, sectorNum);
|
writeAddressField(volumeNum, trackNum, sectorNum);
|
||||||
writeSync(8);
|
writeSync(8);
|
||||||
writeDataField();
|
writeDataField();
|
||||||
}
|
}
|
||||||
writeSync(RAW_TRACK_BYTES - gcrNibblesPos);
|
writeNibbles(0x7F, RAW_TRACK_BYTES - gcrNibblesPos); // invalid nibbles to skip on read
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AppleIIGo
|
* AppleIIGo
|
||||||
* Apple II Emulator for J2SE
|
* Apple II Emulator for J2SE
|
||||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
* Copyright 2006 by Marc S. Ressl(mressl@gmail.com)
|
||||||
* Released under the GPL
|
* Released under the GPL
|
||||||
* Adapted from code by Doug Kwan
|
* Adapted from code by Doug Kwan
|
||||||
* Adapted from code by Randy Frank randy@tessa.iaf.uiowa.edu
|
* Adapted from code by Randy Frank randy@tessa.iaf.uiowa.edu
|
||||||
* Adapted from code (C) 1989 Ben Koning [556498717 408/738-1763 ben@apple.com]
|
* Adapted from code Copyright 1989 Ben Koning [556498717 408/738-1763 ben@apple.com]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class Em6502 {
|
public class Em6502 {
|
||||||
|
@ -78,6 +80,7 @@ public class Em6502 {
|
||||||
public static final int FLAG_I = (1 << 2);
|
public static final int FLAG_I = (1 << 2);
|
||||||
public static final int FLAG_D = (1 << 3);
|
public static final int FLAG_D = (1 << 3);
|
||||||
public static final int FLAG_B = (1 << 4);
|
public static final int FLAG_B = (1 << 4);
|
||||||
|
public static final int FLAG_R = (1 << 5);
|
||||||
public static final int FLAG_V = (1 << 6);
|
public static final int FLAG_V = (1 << 6);
|
||||||
public static final int FLAG_N = (1 << 7);
|
public static final int FLAG_N = (1 << 7);
|
||||||
/*
|
/*
|
||||||
|
@ -119,7 +122,9 @@ public class Em6502 {
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public void Em6502() {
|
public Em6502() {
|
||||||
|
// createRunFile(); // TODO: for debugging - disable
|
||||||
|
|
||||||
// Init BCD tables
|
// Init BCD tables
|
||||||
BCDTableAdd = new int[512];
|
BCDTableAdd = new int[512];
|
||||||
BCDTableSub = new int[512];
|
BCDTableSub = new int[512];
|
||||||
|
@ -133,6 +138,10 @@ public class Em6502 {
|
||||||
BCDTableSub[i] = ((i & 0x0f) <= 0x09) ? i : (i - 0x06);
|
BCDTableSub[i] = ((i & 0x0f) <= 0x09) ? i : (i - 0x06);
|
||||||
BCDTableSub[i] -= ((BCDTableSub[i] & 0xf0) <= 0x90) ? 0 : 0x60;
|
BCDTableSub[i] -= ((BCDTableSub[i] & 0xf0) <= 0x90) ? 0 : 0x60;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init CPU
|
||||||
|
S = 0xFF;
|
||||||
|
P = FLAG_B | FLAG_R;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -154,14 +163,14 @@ public class Em6502 {
|
||||||
*/
|
*/
|
||||||
private final void setN(boolean b) {if (b) P |= FLAG_N; else P &= ~FLAG_N;}
|
private final void setN(boolean b) {if (b) P |= FLAG_N; else P &= ~FLAG_N;}
|
||||||
private final void setV(boolean b) {if (b) P |= FLAG_V; else P &= ~FLAG_V;}
|
private final void setV(boolean b) {if (b) P |= FLAG_V; else P &= ~FLAG_V;}
|
||||||
private final void setB(boolean b) {if (b) P |= FLAG_B; else P &= ~FLAG_B;}
|
// private final void setB(boolean b) {if (b) P |= FLAG_B; else P &= ~FLAG_B;}
|
||||||
private final void setD(boolean b) {if (b) P |= FLAG_D; else P &= ~FLAG_D;}
|
private final void setD(boolean b) {if (b) P |= FLAG_D; else P &= ~FLAG_D;}
|
||||||
private final void setI(boolean b) {if (b) P |= FLAG_I; else P &= ~FLAG_I;}
|
private final void setI(boolean b) {if (b) P |= FLAG_I; else P &= ~FLAG_I;}
|
||||||
private final void setZ(boolean b) {if (b) P |= FLAG_Z; else P &= ~FLAG_Z;}
|
private final void setZ(boolean b) {if (b) P |= FLAG_Z; else P &= ~FLAG_Z;}
|
||||||
private final void setC(boolean b) {if (b) P |= FLAG_C; else P &= ~FLAG_C;}
|
private final void setC(boolean b) {if (b) P |= FLAG_C; else P &= ~FLAG_C;}
|
||||||
private final boolean getN() {return ((P & FLAG_N) != 0);}
|
private final boolean getN() {return ((P & FLAG_N) != 0);}
|
||||||
private final boolean getV() {return ((P & FLAG_V) != 0);}
|
private final boolean getV() {return ((P & FLAG_V) != 0);}
|
||||||
private final boolean getB() {return ((P & FLAG_B) != 0);}
|
// private final boolean getB() {return ((P & FLAG_B) != 0);}
|
||||||
private final boolean getD() {return ((P & FLAG_D) != 0);}
|
private final boolean getD() {return ((P & FLAG_D) != 0);}
|
||||||
private final boolean getI() {return ((P & FLAG_I) != 0);}
|
private final boolean getI() {return ((P & FLAG_I) != 0);}
|
||||||
private final boolean getZ() {return ((P & FLAG_Z) != 0);}
|
private final boolean getZ() {return ((P & FLAG_Z) != 0);}
|
||||||
|
@ -183,11 +192,11 @@ public class Em6502 {
|
||||||
private final void setFC(boolean c) {result = (c ? 0x100 : 0x00);}
|
private final void setFC(boolean c) {result = (c ? 0x100 : 0x00);}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Macro for page crossing cycle regulation
|
* Macro for page crossing cycle regulation - TODO: Why not used!? CPU probably not cycle accurate.
|
||||||
*/
|
*/
|
||||||
private final void checkCrossPage(int addr, int offset) {
|
// private final void checkCrossPage(int addr, int offset) {
|
||||||
if ((((addr + offset) ^ addr) & 0xff00) != 0) clock++;
|
// if ((((addr + offset) ^ addr) & 0xff00) != 0) clock++;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Macros for effective address calculation
|
* Macros for effective address calculation
|
||||||
|
@ -232,9 +241,9 @@ public class Em6502 {
|
||||||
// return easp1 + Y;
|
// return easp1 + Y;
|
||||||
return eaabs() + Y;
|
return eaabs() + Y;
|
||||||
}
|
}
|
||||||
private final int eaabsyNC() {
|
// private final int eaabsyNC() {
|
||||||
return eaabs() + Y;
|
// return eaabs() + Y;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Indirect addressing
|
* Indirect addressing
|
||||||
|
@ -258,11 +267,11 @@ public class Em6502 {
|
||||||
// return easp2 + Y;
|
// return easp2 + Y;
|
||||||
return easp2 + (zeroPageRead((easp1 + 1) & 0xff) << 8) + Y;
|
return easp2 + (zeroPageRead((easp1 + 1) & 0xff) << 8) + Y;
|
||||||
}
|
}
|
||||||
private final int eazpindyNC() {
|
// private final int eazpindyNC() {
|
||||||
easp1 = eaimm();
|
// easp1 = eaimm();
|
||||||
easp2 = zeroPageRead(easp1);
|
// easp2 = zeroPageRead(easp1);
|
||||||
return easp2 + (zeroPageRead((easp1 + 1) & 0xff) << 8) + Y;
|
// return easp2 + (zeroPageRead((easp1 + 1) & 0xff) << 8) + Y;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* New 65C02 addressing mode
|
* New 65C02 addressing mode
|
||||||
|
@ -294,9 +303,48 @@ public class Em6502 {
|
||||||
clock++;
|
clock++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private PrintWriter runFile;
|
||||||
|
// private boolean runFlag = false;
|
||||||
|
//
|
||||||
|
// private void createRunFile()
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// //runFile = new PrintWriter("C:\\Dev\\Emulators\\AppleIIGo\\AppleIIGoRun.txt");
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// // swallow
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private final void writeRunFile(int opcode)
|
||||||
|
// {
|
||||||
|
// if (PC == 0x2000)
|
||||||
|
// runFlag = true;
|
||||||
|
// if (
|
||||||
|
// runFlag &&
|
||||||
|
// (PC > 0x1000)
|
||||||
|
// &&
|
||||||
|
// (PC < 0xC000)
|
||||||
|
// )
|
||||||
|
// {
|
||||||
|
// if (runFile != null)
|
||||||
|
// {
|
||||||
|
// setN(getFN());
|
||||||
|
// setZ(getFZ());
|
||||||
|
// setC(getFC());
|
||||||
|
//
|
||||||
|
// runFile.printf("%04X-%02X P=%02X A=%02X\r\n", PC, opcode, P, A);
|
||||||
|
// runFile.flush();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
/** This executes a single instruction. */
|
/** This executes a single instruction. */
|
||||||
private final void executeInstruction() {
|
private final void executeInstruction() {
|
||||||
opcode = memoryRead(PC);
|
opcode = memoryRead(PC);
|
||||||
|
// writeRunFile(opcode); // TODO: for debugging = disable
|
||||||
PC++;
|
PC++;
|
||||||
|
|
||||||
switch(opcode) {
|
switch(opcode) {
|
||||||
|
@ -530,13 +578,13 @@ public class Em6502 {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x00: // BRK
|
case 0x00: // BRK
|
||||||
|
PC++;
|
||||||
push(PC >> 8); // save PCH, PCL & P
|
push(PC >> 8); // save PCH, PCL & P
|
||||||
push(PC);
|
push(PC);
|
||||||
setN(getFN());
|
setN(getFN());
|
||||||
setZ(getFZ());
|
setZ(getFZ());
|
||||||
setC(getFC());
|
setC(getFC());
|
||||||
setB(true);
|
push(P); // B and R always set
|
||||||
push(P);
|
|
||||||
setI(true);
|
setI(true);
|
||||||
PC = memoryRead(0xfffe);
|
PC = memoryRead(0xfffe);
|
||||||
PC |= memoryRead(0xffff) << 8;
|
PC |= memoryRead(0xffff) << 8;
|
||||||
|
@ -1043,7 +1091,7 @@ public class Em6502 {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x28: // PLP
|
case 0x28: // PLP
|
||||||
P = pop() | 0x20; // fix bug in bit5 of P
|
P = pop() | FLAG_B | FLAG_R; // B and R always set
|
||||||
setFC(getC());
|
setFC(getC());
|
||||||
setFNZ(getN(), getZ());
|
setFNZ(getN(), getZ());
|
||||||
clock += 4;
|
clock += 4;
|
||||||
|
@ -1144,7 +1192,7 @@ public class Em6502 {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x40: // RTI
|
case 0x40: // RTI
|
||||||
P = pop() | 0x20; // bit 5 bug of 6502
|
P = pop() | FLAG_B | FLAG_R; // B and R always set
|
||||||
setFC(getC());
|
setFC(getC());
|
||||||
setFNZ(getN(), getZ());
|
setFNZ(getN(), getZ());
|
||||||
PC = pop(); // splitting is necessary
|
PC = pop(); // splitting is necessary
|
||||||
|
@ -1539,6 +1587,9 @@ public class Em6502 {
|
||||||
default: // unknown instructions
|
default: // unknown instructions
|
||||||
clock += 2;
|
clock += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (PC == 0xB30)
|
||||||
|
// throw (new RuntimeException()); // TODO: for breakpoint hack - disable
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int executeInstructions(int num) {
|
public final int executeInstructions(int num) {
|
||||||
|
@ -1567,13 +1618,14 @@ public class Em6502 {
|
||||||
if ((exceptionRegister & SIG_6502_RESET) != 0) {
|
if ((exceptionRegister & SIG_6502_RESET) != 0) {
|
||||||
onReset();
|
onReset();
|
||||||
|
|
||||||
A = X = Y = 0;
|
setD(false); // not on NMOS 6502
|
||||||
P = 0x20;
|
|
||||||
setFC(getC());
|
setFC(getC());
|
||||||
setFNZ(getN(), getZ());
|
setFNZ(getN(), getZ());
|
||||||
S = 0xff;
|
S = (S - 3) & 0xff;
|
||||||
|
setI(true);
|
||||||
PC = memoryRead(0xfffc);
|
PC = memoryRead(0xfffc);
|
||||||
PC |= (memoryRead(0xfffd) << 8);
|
PC |= (memoryRead(0xfffd) << 8);
|
||||||
|
clock += 7;
|
||||||
exceptionRegister &= ~SIG_6502_RESET;
|
exceptionRegister &= ~SIG_6502_RESET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1586,7 +1638,8 @@ public class Em6502 {
|
||||||
setN(getFN());
|
setN(getFN());
|
||||||
setZ(getFZ());
|
setZ(getFZ());
|
||||||
setC(getFC());
|
setC(getFC());
|
||||||
push(P);
|
push(P & ~FLAG_B);
|
||||||
|
setI(true);
|
||||||
PC = memoryRead(0xfffa);
|
PC = memoryRead(0xfffa);
|
||||||
PC |= memoryRead(0xfffb) << 8;
|
PC |= memoryRead(0xfffb) << 8;
|
||||||
clock += 7;
|
clock += 7;
|
||||||
|
@ -1604,8 +1657,7 @@ public class Em6502 {
|
||||||
setN(getFN());
|
setN(getFN());
|
||||||
setZ(getFZ());
|
setZ(getFZ());
|
||||||
setC(getFC());
|
setC(getFC());
|
||||||
setB(false);
|
push(P & ~FLAG_B);
|
||||||
push(P);
|
|
||||||
setI(true);
|
setI(true);
|
||||||
PC = memoryRead(0xfffe);
|
PC = memoryRead(0xfffe);
|
||||||
PC |= memoryRead(0xffff) << 8;
|
PC |= memoryRead(0xffff) << 8;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* AppleIIGo
|
* AppleIIGo
|
||||||
* Apple II Emulator for J2ME
|
* Apple II Emulator for J2ME
|
||||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
* Copyright 2006 by Marc S. Ressl(mressl@gmail.com)
|
||||||
* Released under the GPL
|
* Released under the GPL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||||
// Peripherals
|
// Peripherals
|
||||||
public Paddle paddle;
|
public Paddle paddle;
|
||||||
public Peripheral[] slots;
|
public Peripheral[] slots;
|
||||||
|
public AppleSpeaker speaker;
|
||||||
|
|
||||||
// Graphics (dirty buffer every 0x80 bytes)
|
// Graphics (dirty buffer every 0x80 bytes)
|
||||||
public int graphicsMode;
|
public int graphicsMode;
|
||||||
|
@ -73,8 +74,9 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||||
public static final int GR_DHIRES = (1 << 7);
|
public static final int GR_DHIRES = (1 << 7);
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
public static final int SPEAKER_FLIPS_SIZE = 4096;
|
public static final int SPEAKER_FLIPS_BITS = 12;
|
||||||
public static final int SPEAKER_FLIPS_MASK = 4095;
|
public static final int SPEAKER_FLIPS_SIZE = 1 << SPEAKER_FLIPS_BITS;
|
||||||
|
public static final int SPEAKER_FLIPS_MASK = SPEAKER_FLIPS_SIZE - 1;
|
||||||
|
|
||||||
public int speakerFlips[] = new int[SPEAKER_FLIPS_SIZE];
|
public int speakerFlips[] = new int[SPEAKER_FLIPS_SIZE];
|
||||||
public int speakerFlipsPointer = 0;
|
public int speakerFlipsPointer = 0;
|
||||||
|
@ -187,7 +189,7 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||||
* Apple II class constructor
|
* Apple II class constructor
|
||||||
*/
|
*/
|
||||||
public EmAppleII() {
|
public EmAppleII() {
|
||||||
Em6502();
|
super();
|
||||||
|
|
||||||
// Allocate compute memory
|
// Allocate compute memory
|
||||||
mem = new byte[MEM_END];
|
mem = new byte[MEM_END];
|
||||||
|
@ -240,15 +242,11 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean loadRom(InputStream is) {
|
public boolean loadRom(DataInputStream is) throws IOException {
|
||||||
byte[] rom = new byte[0x8000];
|
byte[] rom = new byte[0x8000];
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
try {
|
is.readFully(rom, 0, 0x08000);
|
||||||
is.read(rom, 0, 0x08000);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValidRom(rom, 0x0))
|
if (isValidRom(rom, 0x0))
|
||||||
offset = 0x0;
|
offset = 0x0;
|
||||||
|
@ -266,6 +264,17 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||||
System.arraycopy(rom, offset + 0x3000, mem, MEM_ROM_INTERNAL + 0x00000, 0x01000);
|
System.arraycopy(rom, offset + 0x3000, mem, MEM_ROM_INTERNAL + 0x00000, 0x01000);
|
||||||
System.arraycopy(rom, offset + 0x3800, mem, MEM_ROM_EXTERNAL + 0x00800, 0x00800);
|
System.arraycopy(rom, offset + 0x3800, mem, MEM_ROM_EXTERNAL + 0x00800, 0x00800);
|
||||||
|
|
||||||
|
for (int slot = 0x100; slot <= 0x700; slot += 0x100)
|
||||||
|
{
|
||||||
|
if (mem[MEM_ROM_EXTERNAL + slot] == 0)
|
||||||
|
{
|
||||||
|
// 0 data is a bad default for empty external slots (e.g. Mabel's Mansion reboots)
|
||||||
|
// so ideally we would emulate the floating bus, but for now we just hardcode 0xA0
|
||||||
|
for (int i = 0; i <= 0xFF; i++)
|
||||||
|
mem[MEM_ROM_EXTERNAL + slot + i] = (byte)0xA0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1115,6 +1124,11 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulator thread
|
* Emulator thread
|
||||||
|
*
|
||||||
|
* TODO: The speaker has been merged into this thread.
|
||||||
|
* This keeps it in sync but it still needs some work.
|
||||||
|
* Speeding up the CPU (or adding fast disk access as below)
|
||||||
|
* requires proper refactoring of the AppleSpeaker class.
|
||||||
*/
|
*/
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
@ -1124,6 +1138,7 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||||
|
|
||||||
checkInterrupts();
|
checkInterrupts();
|
||||||
|
|
||||||
|
// try {
|
||||||
if (isStepMode) {
|
if (isStepMode) {
|
||||||
if (isNextStep) {
|
if (isNextStep) {
|
||||||
isNextStep = false;
|
isNextStep = false;
|
||||||
|
@ -1134,7 +1149,16 @@ public class EmAppleII extends Em6502 implements Runnable {
|
||||||
while (clocksNeeded > 0)
|
while (clocksNeeded > 0)
|
||||||
clocksNeeded -= executeInstructions(1 + (clocksNeeded >> 3));
|
clocksNeeded -= executeInstructions(1 + (clocksNeeded >> 3));
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
|
// catch (RuntimeException e)
|
||||||
|
// {
|
||||||
|
// setStepMode(true); // TODO: for breakpoint hack - disable
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: need something like the following for fast disk access
|
||||||
|
//if (slots[6] instanceof DiskII && !((DiskII)slots[6]).isMotorOn())
|
||||||
|
|
||||||
|
speaker.refreshSpeaker(); // NOTE: this blocks, syncing emulation and sound
|
||||||
refreshDelay = System.currentTimeMillis() - refreshStart;
|
refreshDelay = System.currentTimeMillis() - refreshStart;
|
||||||
|
|
||||||
refreshDelayCumulative += refreshDelay;
|
refreshDelayCumulative += refreshDelay;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* AppleIIGo
|
* AppleIIGo
|
||||||
* Apple II Emulator for J2ME
|
* Apple II Emulator for J2ME
|
||||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
* Copyright 2006 by Marc S. Ressl(mressl@gmail.com)
|
||||||
* Released under the GPL
|
* Released under the GPL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ public class Paddle {
|
||||||
private int[] buttonRegister = new int[4];
|
private int[] buttonRegister = new int[4];
|
||||||
|
|
||||||
// Paddle variables
|
// Paddle variables
|
||||||
private int paddleMode;
|
// private int paddleMode; // TODO: Was this for analog/digital mode? (Nick)
|
||||||
|
|
||||||
private int[] paddleClockEvent = new int[4];
|
private int[] paddleClockEvent = new int[4];
|
||||||
private int[] paddleClockInc = new int[4];
|
private int[] paddleClockInc = new int[4];
|
||||||
|
@ -35,10 +35,10 @@ public class Paddle {
|
||||||
public Paddle(EmAppleII apple) {
|
public Paddle(EmAppleII apple) {
|
||||||
this.apple = apple;
|
this.apple = apple;
|
||||||
|
|
||||||
setPaddlePos(0, PADDLE_CENTER);
|
setPaddlePos(0, PADDLE_HIGH);
|
||||||
setPaddlePos(1, PADDLE_CENTER);
|
setPaddlePos(1, PADDLE_HIGH);
|
||||||
setPaddlePos(2, PADDLE_CENTER);
|
setPaddlePos(2, PADDLE_HIGH);
|
||||||
setPaddlePos(3, PADDLE_CENTER);
|
setPaddlePos(3, PADDLE_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* AppleIIGo
|
* AppleIIGo
|
||||||
* Slot interface
|
* Slot interface
|
||||||
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
|
* Copyright 2006 by Marc S. Ressl(mressl@gmail.com)
|
||||||
* Released under the GPL
|
* Released under the GPL
|
||||||
* Based on work by Steven E. Hugg
|
* Based on work by Steven E. Hugg
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3,17 +3,16 @@
|
||||||
<delete dir="Build" />
|
<delete dir="Build" />
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="compile">
|
<target name="compile" depends="clean">
|
||||||
<mkdir dir="Build/classes"/>
|
<mkdir dir="Build/classes"/>
|
||||||
<javac srcdir="Source" destdir="Build/classes" optimize="yes"/>
|
<javac srcdir="src" destdir="Build/classes" source="1.4" target="1.4"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="jar" depends="compile">
|
<target name="jar" depends="compile">
|
||||||
<mkdir dir="Build/jar"/>
|
<mkdir dir="Build/jar"/>
|
||||||
<copy todir="Build/classes/Resources">
|
<copy todir="Build/classes/Resources">
|
||||||
<fileset dir="Resources"/>
|
<fileset dir="src/Resources"/>
|
||||||
</copy>
|
</copy>
|
||||||
<jar destfile="Build/jar/AppleIIGo.jar" basedir="Build/classes"/>
|
<jar destfile="Build/jar/AppleIIGo.jar" basedir="Build/classes"/>
|
||||||
<copy file="Build/jar/AppleIIGo.jar" todir="/Users/mressl/Library/Widgets/AppleIIGo.wdgt"/>
|
|
||||||
</target>
|
</target>
|
||||||
</project>
|
</project>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user