diff --git a/Source/AppleDisplay.java b/Source/AppleDisplay.java index d35a2b2..131c5ec 100644 --- a/Source/AppleDisplay.java +++ b/Source/AppleDisplay.java @@ -459,8 +459,8 @@ public class AppleDisplay implements Runnable { boolean isDoubleTextMode = ((graphicsMode & EmAppleII.GR_80CHAR) == EmAppleII.GR_80CHAR); 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 baseAddressHires = isPage2 ? apple.MEM_MAIN_RAM3 : apple.MEM_MAIN_HIRES; + int baseAddressText = isPage2 ? EmAppleII.MEM_MAIN_RAM2 : EmAppleII.MEM_MAIN_TEXT; + int baseAddressHires = isPage2 ? EmAppleII.MEM_MAIN_RAM3 : EmAppleII.MEM_MAIN_HIRES; // Set char map if (isCharsetUpdateRequested) { @@ -769,7 +769,7 @@ public class AppleDisplay implements Runnable { } private void renderText(int baseAddress, boolean isMixedMode) { int screenCharY, screenCharYStart = isMixedMode ? 20 : 0; - int displayOffset, sourceOffset; + int displayOffset; int address, addressEnd, addressStart; displayOffset = screenCharYStart * DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X; @@ -820,7 +820,7 @@ public class AppleDisplay implements Runnable { } private void renderDoubleText(int baseAddress, boolean isMixedMode) { int screenCharY, screenCharYStart = isMixedMode ? 20 : 0; - int displayOffset, sourceOffset; + int displayOffset; int address, addressEnd, addressStart; displayOffset = screenCharYStart * DISPLAY_CHAR_SIZE_Y * DISPLAY_SIZE_X; @@ -873,7 +873,7 @@ public class AppleDisplay implements Runnable { } private void renderLores(int baseAddress, boolean isMixedMode) { int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24; - int displayOffset, sourceOffset; + int displayOffset; int address, addressEnd, addressStart; displayOffset = 0; @@ -904,7 +904,7 @@ public class AppleDisplay implements Runnable { */ private void renderDoubleLores(int baseAddress, boolean isMixedMode) { int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24; - int displayOffset, sourceOffset; + int displayOffset; int address, addressEnd, addressStart; displayOffset = 0; @@ -998,7 +998,7 @@ public class AppleDisplay implements Runnable { } private void renderHires(int baseAddress, boolean isMixedMode) { int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24; - int displayOffset, sourceOffset; + int displayOffset; int address, addressEnd, addressStart; displayOffset = 0; @@ -1089,7 +1089,7 @@ public class AppleDisplay implements Runnable { } private void renderDoubleHires(int baseAddress, boolean isMixedMode) { int screenCharY, screenCharYEnd = isMixedMode ? 20 : 24; - int displayOffset, sourceOffset; + int displayOffset; int address, addressEnd, addressStart; displayOffset = 0; diff --git a/Source/AppleIIGo.java b/Source/AppleIIGo.java index cdb3126..98e4fe1 100644 --- a/Source/AppleIIGo.java +++ b/Source/AppleIIGo.java @@ -2,25 +2,32 @@ /** * AppleIIGo * The Java Apple II Emulator - * (C) 2006 by Marc S. Ressl (ressl@lonetree.com) - * (C) 2009 by Nick Westgate (Nick.Westgate@gmail.com) - * Released under the GPL + * Copyright 2009 by Nick Westgate (Nick.Westgate@gmail.com) + * Copyright 2006 by Marc S. Ressl (ressl@lonetree.com) + * Released under the GNU General Public License version 2 + * See http://www.gnu.org/licenses/ * * Change list: * - * Version 1.0.6 - changes by Nick: + * 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 @@ -66,8 +73,11 @@ import java.util.zip.ZipInputStream; public class AppleIIGo extends Applet implements KeyListener, ComponentListener, MouseListener, MouseMotionListener { - final String version = "1.0.6"; + private static final long serialVersionUID = -3302282815441501352L; + + final String version = "1.0.7"; final String versionString = "AppleIIGo Version " + version; + final String metaStart = "_meta_"; // Class instances private EmAppleII apple; @@ -82,6 +92,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener, private boolean keyboardUppercaseOnly; // Paddle variables + private boolean isPaddleEnabled; private boolean isPaddleInverted; // Disk variables - TODO: refactor into a class @@ -132,6 +143,10 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener, // Keyboard keyboardUppercaseOnly = getAppletParameter("keyboardUppercaseOnly", "true").equals("true"); + // Paddles + isPaddleEnabled = getAppletParameter("paddleEnabled", "true").equals("true"); + isPaddleInverted = getAppletParameter("paddleInverted", "false").equals("true"); + // Display display = new AppleDisplay(this, apple); display.setScale(new Float(getAppletParameter("displayScale", "1")).floatValue()); @@ -361,8 +376,65 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener, StringBuffer diskname = new StringBuffer(); DataInputStream is = openInputStream(resource, diskname); + + int diskVolumeNumber = DiskII.DEFAULT_VOLUME; - success = disk.readDisk(drive, is, diskname.toString(), 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; + } + command = 0; + operand = 0; + } + } + } + + success = disk.readDisk(drive, is, diskname.toString(), !diskWritable, diskVolumeNumber); is.close(); showStatus("Drive " + (drive + 1) + ": " + resource); } catch (Exception e) { @@ -377,7 +449,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener, */ public void unmountDisk(int drive) { debug("unmountDisk(drive: " + drive + ")"); - if ((drive < 0) || (drive > 2)) + if ((drive < 0) || (drive > 1)) return; if (!diskWritable) @@ -399,14 +471,35 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener, 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 */ public boolean getDiskActivity() { return (!isCpuPaused && disk.isMotorOn()); } - - /** * KeyListener event handling @@ -575,11 +668,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener, // else fall through case KeyEvent.VK_KP_LEFT: case KeyEvent.VK_KP_RIGHT: - if (isPaddleInverted) { - apple.paddle.setPaddlePos(1, 127); - } else { - apple.paddle.setPaddlePos(0, 127); - } + handleKeypadCentreX(); break; case KeyEvent.VK_UP: case KeyEvent.VK_DOWN: @@ -588,11 +677,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener, // else fall through case KeyEvent.VK_KP_UP: case KeyEvent.VK_KP_DOWN: - if (isPaddleInverted) { - apple.paddle.setPaddlePos(0, 127); - } else { - apple.paddle.setPaddlePos(1, 127); - } + handleKeypadCentreY(); break; case KeyEvent.VK_HOME: if (!e.isControlDown()) @@ -604,36 +689,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() { + 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); } + } } /** @@ -677,20 +796,24 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener, public void mousePressed(MouseEvent e) { int modifiers = e.getModifiers(); - - if ((modifiers & InputEvent.BUTTON1_MASK) != 0) - apple.paddle.setButton(0, true); - if ((modifiers & InputEvent.BUTTON3_MASK) != 0) - apple.paddle.setButton(1, true); + if (isPaddleEnabled) + { + if ((modifiers & InputEvent.BUTTON1_MASK) != 0) + apple.paddle.setButton(0, true); + if ((modifiers & InputEvent.BUTTON3_MASK) != 0) + apple.paddle.setButton(1, true); + } } public void mouseReleased(MouseEvent e) { int modifiers = e.getModifiers(); - - if ((modifiers & InputEvent.BUTTON1_MASK) != 0) - apple.paddle.setButton(0, false); - if ((modifiers & InputEvent.BUTTON3_MASK) != 0) - apple.paddle.setButton(1, false); + if (isPaddleEnabled) + { + if ((modifiers & InputEvent.BUTTON1_MASK) != 0) + apple.paddle.setButton(0, false); + if ((modifiers & InputEvent.BUTTON3_MASK) != 0) + apple.paddle.setButton(1, false); + } } public void mouseDragged(MouseEvent e) { @@ -699,12 +822,15 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener, public void mouseMoved(MouseEvent e) { float scale = display.getScale(); - if (isPaddleInverted) { - apple.paddle.setPaddlePos(0, (int) (255 - e.getY() * 256 / (192 * scale))); - 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))); + if (isPaddleEnabled) + { + if (isPaddleInverted) { + apple.paddle.setPaddlePos(0, (int) (255 - e.getY() * 256 / (192 * scale))); + 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))); + } } } diff --git a/Source/AppleSpeaker.java b/Source/AppleSpeaker.java index 84b1eaa..fc8f691 100644 --- a/Source/AppleSpeaker.java +++ b/Source/AppleSpeaker.java @@ -13,7 +13,7 @@ public class AppleSpeaker implements Runnable { private EmAppleII apple; // Refresh - private int refreshRate; +// private int refreshRate; private long refreshInterval; // Sound stuff @@ -58,7 +58,7 @@ public class AppleSpeaker implements Runnable { if (value <= 0.0f) return; - this.refreshRate = value; +// this.refreshRate = value; refreshInterval = (int) (1000.0 / value); speakerClocksPerSample = (int) (apple.getCpuSpeed() * 1000.0f / SPEAKER_SAMPLERATE); @@ -67,9 +67,9 @@ public class AppleSpeaker implements Runnable { /** * Get refresh rate */ - private int getRefreshRate() { - return refreshRate; - } +// private int getRefreshRate() { +// return refreshRate; +// } /** * Set speaker volume @@ -120,7 +120,7 @@ public class AppleSpeaker implements Runnable { SPEAKER_BIGENDIAN); DataLine.Info info = new DataLine.Info( - DataLine.class, + SourceDataLine.class, audioFormat); try { diff --git a/Source/DiskII.java b/Source/DiskII.java index 993e9ee..008a71b 100644 --- a/Source/DiskII.java +++ b/Source/DiskII.java @@ -37,6 +37,7 @@ public class DiskII extends Peripheral { private static final int DOS_NUM_TRACKS = 35; private static final int DOS_TRACK_BYTES = 256 * DOS_NUM_SECTORS; private static final int RAW_TRACK_BYTES = 0x1A00; // 0x1A00 (6656) for .NIB (was 6250) + public static final int DEFAULT_VOLUME = 254; // Disk II direct access variables private int drive = 0; @@ -108,8 +109,8 @@ public class DiskII extends Peripheral { super(); this.apple = apple; - readDisk(0, null, "", false); - readDisk(1, null, "", false); + readDisk(0, null, "", false, DEFAULT_VOLUME); + readDisk(1, null, "", false, DEFAULT_VOLUME); } /** @@ -137,6 +138,7 @@ public class DiskII extends Peripheral { if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) currPhysTrack++; } + //System.out.println("half track=" + currPhysTrack); realTrack = diskData[drive][currPhysTrack >> 1]; break; case 0x3: @@ -149,6 +151,7 @@ public class DiskII extends Peripheral { if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) currPhysTrack++; } + //System.out.println("half track=" + currPhysTrack); realTrack = diskData[drive][currPhysTrack >> 1]; break; case 0x5: @@ -161,6 +164,7 @@ public class DiskII extends Peripheral { if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) currPhysTrack++; } + //System.out.println("half track=" + currPhysTrack); realTrack = diskData[drive][currPhysTrack >> 1]; break; case 0x7: @@ -173,6 +177,7 @@ public class DiskII extends Peripheral { if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) currPhysTrack++; } + //System.out.println("half track=" + currPhysTrack); realTrack = diskData[drive][currPhysTrack >> 1]; break; case 0x8: @@ -272,12 +277,12 @@ public class DiskII extends Peripheral { * @param is InputStream * @param drive Disk II drive */ - public boolean readDisk(int drive, DataInputStream is, String name, boolean isWriteProtected) { + public boolean readDisk(int drive, DataInputStream is, String name, boolean isWriteProtected, int volumeNumber) { byte[] track = new byte[DOS_TRACK_BYTES]; - String lowerDiskname = name.toLowerCase(); - boolean proDos = lowerDiskname.indexOf(".po") != -1; - boolean nib = lowerDiskname.indexOf(".nib") != -1; + String lowerName = name.toLowerCase(); + boolean proDos = lowerName.indexOf(".po") != -1; + boolean nib = lowerName.indexOf(".nib") != -1; try { for (int trackNum = 0; trackNum < DOS_NUM_TRACKS; trackNum++) { @@ -291,7 +296,7 @@ public class DiskII extends Peripheral { else { is.readFully(track, 0, DOS_TRACK_BYTES); - trackToNibbles(track, diskData[drive][trackNum], 254, trackNum, !proDos); + trackToNibbles(track, diskData[drive][trackNum], volumeNumber, trackNum, !proDos); } } } @@ -341,10 +346,10 @@ public class DiskII extends Peripheral { { // Read data: C0xE, C0xC latchData = (realTrack[currNibble] & 0xff); - + // simple hack to help DOS find address prologues ($B94F) - if (apple.memoryRead(apple.PC + 3) == 0xD5 && // #$D5 - latchData != 0xD5 && + if (/* fastDisk && */ latchData != 0xD5 && // TODO: fastDisk property to enable/disable + apple.memoryRead(apple.PC + 3) == 0xD5 && // #$D5 apple.memoryRead(apple.PC + 2) == 0xC9 && // CMP apple.memoryRead(apple.PC + 1) == 0xFB && // PC - 3 apple.memoryRead(apple.PC + 0) == 0x10) // BPL @@ -359,7 +364,7 @@ public class DiskII extends Peripheral { } while (latchData != 0xD5 && --count > 0); } - // simple hack to fool DOS drive spin detect routine ($BD34) + // simple hack to fool DOS 3.3 RWTS drive spin detect routine ($BD34) else if (apple.memoryRead(apple.PC - 3) == 0xDD && // CMP $C08C,X apple.memoryRead(apple.PC + 1) == 0x03 && // PC + 3 apple.memoryRead(apple.PC + 0) == 0xD0) // BNE @@ -394,9 +399,7 @@ public class DiskII extends Peripheral { * @param address Address */ private void ioLatchD(int value) { - // Prepare write - writeMode = true; - latchData = value; + // Prepare for write protect sense latchAddress = 0xd; } diff --git a/Source/Em6502.java b/Source/Em6502.java index 56785d6..9631220 100644 --- a/Source/Em6502.java +++ b/Source/Em6502.java @@ -183,11 +183,11 @@ public class Em6502 { 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) { - if ((((addr + offset) ^ addr) & 0xff00) != 0) clock++; - } +// private final void checkCrossPage(int addr, int offset) { +// if ((((addr + offset) ^ addr) & 0xff00) != 0) clock++; +// } /* * Macros for effective address calculation @@ -1539,6 +1539,9 @@ public class Em6502 { default: // unknown instructions clock += 2; } + +// if (PC == 0xB30) +// throw (new RuntimeException()); // TODO: for breakpoint hack - disable } public final int executeInstructions(int num) { diff --git a/Source/EmAppleII.java b/Source/EmAppleII.java index 5f805b5..f16f4a5 100644 --- a/Source/EmAppleII.java +++ b/Source/EmAppleII.java @@ -74,8 +74,9 @@ public class EmAppleII extends Em6502 implements Runnable { public static final int GR_DHIRES = (1 << 7); // Sound - public static final int SPEAKER_FLIPS_SIZE = 4096; - public static final int SPEAKER_FLIPS_MASK = 4095; + public static final int SPEAKER_FLIPS_BITS = 12; + 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 speakerFlipsPointer = 0; @@ -1126,6 +1127,7 @@ public class EmAppleII extends Em6502 implements Runnable { checkInterrupts(); +// try { if (isStepMode) { if (isNextStep) { isNextStep = false; @@ -1136,6 +1138,11 @@ public class EmAppleII extends Em6502 implements Runnable { while (clocksNeeded > 0) 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()) diff --git a/Source/Paddle.java b/Source/Paddle.java index 43472d9..12b9800 100644 --- a/Source/Paddle.java +++ b/Source/Paddle.java @@ -22,7 +22,7 @@ public class Paddle { private int[] buttonRegister = new int[4]; // Paddle variables - private int paddleMode; + // private int paddleMode; // TODO: Was this for analog/digital mode? (Nick) private int[] paddleClockEvent = new int[4]; private int[] paddleClockInc = new int[4]; @@ -35,10 +35,10 @@ public class Paddle { public Paddle(EmAppleII apple) { this.apple = apple; - setPaddlePos(0, PADDLE_CENTER); - setPaddlePos(1, PADDLE_CENTER); - setPaddlePos(2, PADDLE_CENTER); - setPaddlePos(3, PADDLE_CENTER); + setPaddlePos(0, PADDLE_HIGH); + setPaddlePos(1, PADDLE_HIGH); + setPaddlePos(2, PADDLE_HIGH); + setPaddlePos(3, PADDLE_HIGH); } /**