Compare commits

...

7 Commits

Author SHA1 Message Date
sicklittlemonkey
fe27c7d1e8 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
2015-08-10 00:11:49 +12:00
Nick Westgate
68cfda45c1 Update README.md 2015-08-01 20:25:49 +12:00
sicklittlemonkey
dc8f597c62 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
2015-08-01 20:09:35 +12:00
sicklittlemonkey
add3d41448 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
2015-08-01 20:00:43 +12:00
sicklittlemonkey
af1cec23d3 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
2015-08-01 19:49:51 +12:00
sicklittlemonkey
912d6c3dbc Version 1.0.6 - changes by Nick
- exposed F3/F4 disk swapping method: cycleDisk(int driveNumber)
- exposed reset() method
- exposed setSpeed(int value) method
2015-08-01 19:42:08 +12:00
sicklittlemonkey
d8b391a80c 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)
2015-08-01 19:38:06 +12:00
9 changed files with 653 additions and 296 deletions

View File

@ -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

View File

@ -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;

View File

@ -2,14 +2,49 @@
/** /**
* 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)
* (C) 2009 by Nick Westgate (Nick.Westgate@gmail.com) * Copyright 2006 by Marc S. Ressl (mressl@gmail.com)
* Released under the GPL * Released under the GNU General Public License version 2
* See http://www.gnu.org/licenses/
* *
* Change list: * 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: * Version 1.0.4 - changes by Nick:
*
* - added support for .PO (ProDOS order) disk images (also inside ZIP archives) * - added support for .PO (ProDOS order) disk images (also inside ZIP archives)
* - added Command key for Closed-Apple on Mac OS X * - added Command key for Closed-Apple on Mac OS X
* - added Home and End keys for Open-Apple and Closed-Apple on full keyboards * - added Home and End keys for Open-Apple and Closed-Apple on full keyboards
@ -23,7 +58,7 @@
* Version 1.0.2 - changes by Nick: * Version 1.0.2 - changes by Nick:
* - improved sound sync by moving AppleSpeaker into the main thread * - improved sound sync by moving AppleSpeaker into the main thread
* - added version (F1) * - added version (F1)
* - added multiple disks & switching (F3, F4) * - added multiple disks & swapping (F3, F4)
* - added ZIP archive support * - added ZIP archive support
* - fixed HTTP disk image access bug * - fixed HTTP disk image access bug
*/ */
@ -55,8 +90,11 @@ import java.util.zip.ZipInputStream;
public class AppleIIGo extends Applet implements KeyListener, ComponentListener, public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
MouseListener, MouseMotionListener { MouseListener, MouseMotionListener {
final String version = "1.0.4"; private static final long serialVersionUID = -3302282815441501352L;
final String version = "1.0.10";
final String versionString = "AppleIIGo Version " + version; final String versionString = "AppleIIGo Version " + version;
final String metaStart = "_meta_";
// Class instances // Class instances
private EmAppleII apple; private EmAppleII apple;
@ -71,15 +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[] disks0; private String[][] diskImageNames = {{}, {}};
private String[] disks1; private int diskImageNumber[] = {0, 0};
private int disk0;
private int disk1;
/** /**
* Debug * Debug
@ -123,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());
@ -136,17 +177,17 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
apple.speaker.setVolume(new Integer(getAppletParameter("speakerVolume", "6")).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");
disks0 = getAppletParameter("diskDrive1", "").split("[|]"); diskImageNames[0] = getAppletParameter("diskDrive1", "").split("[|]");
disk0 = 0; diskImageNumber[0] = 0;
disks1 = getAppletParameter("diskDrive2", "").split("[|]"); diskImageNames[1] = getAppletParameter("diskDrive2", "").split("[|]");
disk1 = 0; diskImageNumber[1] = 0;
mountDisk(0, disks0[disk0]); mountDisk(0, diskImageNames[0][diskImageNumber[0]]);
mountDisk(1, disks1[disk1]); mountDisk(1, diskImageNames[1][diskImageNumber[1]]);
// Start CPU // Start CPU
if (!isCpuPaused) if (!isCpuPaused)
@ -176,8 +217,6 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
unmountDisk(0); unmountDisk(0);
unmountDisk(1); unmountDisk(1);
} }
// Public Java interface // Public Java interface
@ -195,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);
apple.speaker.setPaused(isCpuPaused); apple.speaker.setPaused(true);
} }
/** /**
@ -206,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;
apple.speaker.setPaused(isCpuPaused); apple.speaker.setPaused(false);
display.setPaused(isCpuPaused); display.setPaused(false);
apple.setPaused(isCpuPaused); apple.setPaused(false);
} }
/** /**
@ -218,6 +257,34 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
debug("restart()"); debug("restart()");
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
@ -230,10 +297,17 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
InputStream is = null; InputStream is = null;
if (OutFilename != null) if (OutFilename != null)
{
OutFilename.setLength(0); OutFilename.setLength(0);
int slashPos = resource.lastIndexOf('/');
int backslashPos = resource.lastIndexOf('\\');
int index = Math.max(slashPos, backslashPos);
OutFilename.append(resource.substring((index > 0) ? index : 0));
}
try { try {
URL url = new URL(getCodeBase(), resource); URL codeBase = getCodeBase();
URL url = new URL(codeBase, resource);
debug("resource: " + url.toString()); debug("resource: " + url.toString());
is = url.openStream(); is = url.openStream();
@ -247,19 +321,10 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
ZipEntry entry = ((ZipInputStream)is).getNextEntry(); ZipEntry entry = ((ZipInputStream)is).getNextEntry();
if (OutFilename != null) if (OutFilename != null)
{ {
OutFilename.setLength(0);
OutFilename.append(entry.getName()); OutFilename.append(entry.getName());
} }
} }
else
{
if (OutFilename != null)
{
int slashPos = resource.lastIndexOf('/');
int backslashPos = resource.lastIndexOf('\\');
int index = Math.max(slashPos, backslashPos);
OutFilename.append(resource.substring((index > 0) ? index : 0));
}
}
} catch (Exception e) { } catch (Exception e) {
debug("Exeption: " + e.getLocalizedMessage()); debug("Exeption: " + e.getLocalizedMessage());
} }
@ -316,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 {
@ -326,11 +391,72 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
StringBuffer diskname = new StringBuffer(); StringBuffer diskname = new StringBuffer();
DataInputStream is = openInputStream(resource, diskname); DataInputStream is = openInputStream(resource, diskname);
String lowerDiskname = diskname.toString().toLowerCase();
boolean dos = lowerDiskname.indexOf(".po") == -1; int diskVolumeNumber = DiskII.DEFAULT_VOLUME;
boolean diskWritableOverride = diskWritable;
success = disk.readDisk(drive, is, 254, dos, false); // 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()); debug("Exeption: " + e.getLocalizedMessage());
} }
@ -343,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) {
//}
} }
/** /**
@ -365,14 +492,45 @@ 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
*/ */
public boolean getDiskActivity() { public boolean getDiskActivity() {
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
@ -463,7 +621,7 @@ 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.paddle.setButton(0, true); apple.paddle.setButton(0, true);
break; break;
@ -474,18 +632,10 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
showStatus("AppleIIGo Version " + version); showStatus("AppleIIGo Version " + version);
break; break;
case KeyEvent.VK_F3: case KeyEvent.VK_F3:
if (disks0.length > 1) { cycleDisk(0);
disk0 = ++disk0 % disks0.length;
mountDisk(0, disks0[disk0]);
showStatus("Disk 1: " + disks0[disk0]);
}
break; break;
case KeyEvent.VK_F4: case KeyEvent.VK_F4:
if (disks1.length > 1) { cycleDisk(1);
disk1 = ++disk1 % disks1.length;
mountDisk(1, disks1[disk1]);
showStatus("Disk 2: " + disks1[disk1]);
}
break; break;
case KeyEvent.VK_F5: case KeyEvent.VK_F5:
if (isCpuDebugEnabled) if (isCpuDebugEnabled)
@ -507,10 +657,10 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
apple.stepInstructions(128); apple.stepInstructions(128);
} }
break; break;
case KeyEvent.VK_CANCEL: // Ctrl-Pause/Break sends this (N/A on Mac) case KeyEvent.VK_CANCEL: // Pause/Break sends this (as Mac OS swallows Ctrl-F12)
case KeyEvent.VK_F12: case KeyEvent.VK_F12:
if (e.isControlDown()) if (e.isControlDown())
apple.reset(); reset();
break; break;
case KeyEvent.VK_KP_LEFT: case KeyEvent.VK_KP_LEFT:
handleKeypadLeft(); handleKeypadLeft();
@ -549,11 +699,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
// else fall through // else fall through
case KeyEvent.VK_KP_LEFT: case KeyEvent.VK_KP_LEFT:
case KeyEvent.VK_KP_RIGHT: case KeyEvent.VK_KP_RIGHT:
if (isPaddleInverted) { handleKeypadCentreX();
apple.paddle.setPaddlePos(1, 127);
} else {
apple.paddle.setPaddlePos(0, 127);
}
break; break;
case KeyEvent.VK_UP: case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN: case KeyEvent.VK_DOWN:
@ -562,11 +708,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
// else fall through // else fall through
case KeyEvent.VK_KP_UP: case KeyEvent.VK_KP_UP:
case KeyEvent.VK_KP_DOWN: case KeyEvent.VK_KP_DOWN:
if (isPaddleInverted) { handleKeypadCentreY();
apple.paddle.setPaddlePos(0, 127);
} else {
apple.paddle.setPaddlePos(1, 127);
}
break; break;
case KeyEvent.VK_HOME: case KeyEvent.VK_HOME:
if (!e.isControlDown()) if (!e.isControlDown())
@ -578,36 +720,70 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
} }
} }
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() { private void handleKeypadLeft() {
if (isPaddleEnabled)
{
if (isPaddleInverted) { if (isPaddleInverted) {
apple.paddle.setPaddlePos(1, 255); apple.paddle.setPaddlePos(1, 255);
} else { } else {
apple.paddle.setPaddlePos(0, 0); apple.paddle.setPaddlePos(0, 0);
} }
}
} }
private void handleKeypadRight() { private void handleKeypadRight() {
if (isPaddleEnabled)
{
if (isPaddleInverted) { if (isPaddleInverted) {
apple.paddle.setPaddlePos(1, 0); apple.paddle.setPaddlePos(1, 0);
} else { } else {
apple.paddle.setPaddlePos(0, 255); apple.paddle.setPaddlePos(0, 255);
} }
}
} }
private void handleKeypadUp() { private void handleKeypadUp() {
if (isPaddleEnabled)
{
if (isPaddleInverted) { if (isPaddleInverted) {
apple.paddle.setPaddlePos(0, 255); apple.paddle.setPaddlePos(0, 255);
} else { } else {
apple.paddle.setPaddlePos(1, 0); apple.paddle.setPaddlePos(1, 0);
} }
}
} }
private void handleKeypadDown() { private void handleKeypadDown() {
if (isPaddleEnabled)
{
if (isPaddleInverted) { if (isPaddleInverted) {
apple.paddle.setPaddlePos(0, 0); apple.paddle.setPaddlePos(0, 0);
} else { } else {
apple.paddle.setPaddlePos(1, 255); apple.paddle.setPaddlePos(1, 255);
} }
}
} }
/** /**
@ -651,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) {
@ -673,12 +853,15 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
public void mouseMoved(MouseEvent e) { public void mouseMoved(MouseEvent e) {
float scale = display.getScale(); float scale = display.getScale();
if (isPaddleInverted) { if (isPaddleEnabled)
apple.paddle.setPaddlePos(0, (int) (255 - e.getY() * 256 / (192 * scale))); {
apple.paddle.setPaddlePos(1, (int) (255 - e.getX() * 256 / (280 * scale))); if (isPaddleInverted) {
} else { apple.paddle.setPaddlePos(0, (int) (255 - e.getY() * 256 / (192 * scale)));
apple.paddle.setPaddlePos(0, (int) (e.getX() * 256 / (280 * scale))); apple.paddle.setPaddlePos(1, (int) (255 - e.getX() * 256 / (280 * scale)));
apple.paddle.setPaddlePos(1, (int) (e.getY() * 256 / (192 * scale))); } else {
apple.paddle.setPaddlePos(0, (int) (e.getX() * 256 / (280 * scale)));
apple.paddle.setPaddlePos(1, (int) (e.getY() * 256 / (192 * scale)));
}
} }
} }
@ -694,5 +877,5 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
*/ */
public void update(Graphics g) { public void update(Graphics g) {
display.paint(g); display.paint(g);
} }
} }

View File

@ -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
*/ */
@ -13,7 +13,7 @@ public class AppleSpeaker implements Runnable {
private EmAppleII apple; private EmAppleII apple;
// Refresh // Refresh
private int refreshRate; // private int refreshRate;
private long refreshInterval; private long refreshInterval;
// Sound stuff // Sound stuff
@ -58,7 +58,7 @@ 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);
@ -67,9 +67,9 @@ public class AppleSpeaker implements Runnable {
/** /**
* Get refresh rate * Get refresh rate
*/ */
private int getRefreshRate() { // private int getRefreshRate() {
return refreshRate; // return refreshRate;
} // }
/** /**
* Set speaker volume * Set speaker volume
@ -120,7 +120,7 @@ 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 {

View File

@ -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,7 +87,7 @@ 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];
@ -98,16 +106,17 @@ public class DiskII extends Peripheral {
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, true, false); readDisk(0, null, "", false, DEFAULT_VOLUME);
readDisk(1, null, 254, true, false); readDisk(1, null, "", false, DEFAULT_VOLUME);
} }
/** /**
@ -116,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
} }
/** /**
@ -227,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;
}
} }
/** /**
@ -261,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, DataInputStream is, int volume, boolean dos, 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.readFully(track, 0, DOS_TRACK_BYTES); if (nib)
trackToNibbles(track, disk[drive][trackNum], volume, trackNum, dos); {
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;
@ -310,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; private void setPhase(int address) {
return latchData; int phase = (address >> 1) & 3;
} int phase_bit = (1 << phase);
/** // update the magnet states
* I/O write Latch D if ((address & 1) != 0)
* {
* @param address Address // phase on
*/ phases |= phase_bit;
private void ioLatchD(int value) { }
// Prepare write else
writeMode = true; {
latchData = value; // phase off
latchAddress = 0xd; phases &= ~phase_bit;
}
/**
* I/O read Latch E
*
* @param address Address
*/
private int ioLatchE() {
// Read write-protect: C0xD, C0xE
if (latchAddress == 0xd) {
latchAddress = 0xe;
return isWriteProtected[drive] ? 0x80 : 0x00;
} }
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;
} }
/** /**
@ -389,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);
}
} }
/** /**
@ -504,6 +593,6 @@ public class DiskII extends Peripheral {
writeSync(8); writeSync(8);
writeDataField(); writeDataField();
} }
writeSync(RAW_TRACK_BYTES - gcrNibblesPos); writeNibbles(0x7F, RAW_TRACK_BYTES - gcrNibblesPos); // invalid nibbles to skip on read
} }
} }

View File

@ -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,13 @@ public class Em6502 {
if ((exceptionRegister & SIG_6502_RESET) != 0) { if ((exceptionRegister & SIG_6502_RESET) != 0) {
onReset(); onReset();
PC = memoryRead(0xfffc);
PC |= (memoryRead(0xfffd) << 8);
S = (S - 3) & 0xff;
setI(true);
setD(false); // not on NMOS 6502 setD(false); // not on NMOS 6502
setFC(getC()); setFC(getC());
setFNZ(getN(), getZ()); setFNZ(getN(), getZ());
S = (S - 3) & 0xff;
setI(true);
PC = memoryRead(0xfffc);
PC |= (memoryRead(0xfffd) << 8);
clock += 7; clock += 7;
exceptionRegister &= ~SIG_6502_RESET; exceptionRegister &= ~SIG_6502_RESET;
} }
@ -1587,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;
@ -1605,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;

View File

@ -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
*/ */
@ -74,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;
@ -188,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];
@ -263,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;
} }
@ -1126,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;
@ -1136,6 +1149,11 @@ 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 // TODO: need something like the following for fast disk access
//if (slots[6] instanceof DiskII && !((DiskII)slots[6]).isMotorOn()) //if (slots[6] instanceof DiskII && !((DiskII)slots[6]).isMotorOn())

View File

@ -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);
} }
/** /**

View File

@ -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
*/ */