From add3d41448cefd2ff8bbd0dc395570cf2884afd5 Mon Sep 17 00:00:00 2001 From: sicklittlemonkey Date: Sat, 1 Aug 2015 20:00:43 +1200 Subject: [PATCH] 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 --- Source/AppleDisplay.java | 14 +- Source/AppleIIGo.java | 51 ++++-- Source/AppleSpeaker.java | 2 +- Source/DiskII.java | 342 ++++++++++++++++++++++----------------- Source/Em6502.java | 2 +- Source/EmAppleII.java | 2 +- Source/Paddle.java | 2 +- Source/Peripheral.java | 2 +- 8 files changed, 243 insertions(+), 174 deletions(-) diff --git a/Source/AppleDisplay.java b/Source/AppleDisplay.java index 131c5ec..64a6b5d 100644 --- a/Source/AppleDisplay.java +++ b/Source/AppleDisplay.java @@ -2,7 +2,7 @@ /** * AppleIIGo * Display processing - * (C) 2006 by Marc S. Ressl (ressl@lonetree.com) + * (C) 2006 by Marc S. Ressl (mressl@gmail.com) * Released under the GPL */ @@ -224,6 +224,18 @@ public float getScale() { return displayScale; } + public int getSizeX() + { + precalcDisplay(); + return displayScaledSizeX; + } + + public int getSizeY() + { + precalcDisplay(); + return displayScaledSizeY; + } + /** * Set refresh rate * diff --git a/Source/AppleIIGo.java b/Source/AppleIIGo.java index 98e4fe1..1e9775b 100644 --- a/Source/AppleIIGo.java +++ b/Source/AppleIIGo.java @@ -2,13 +2,18 @@ /** * AppleIIGo * The Java Apple II Emulator - * Copyright 2009 by Nick Westgate (Nick.Westgate@gmail.com) - * Copyright 2006 by Marc S. Ressl (ressl@lonetree.com) + * Copyright 2011 by Nick Westgate (Nick.Westgate@gmail.com) + * 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.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) @@ -75,7 +80,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener, private static final long serialVersionUID = -3302282815441501352L; - final String version = "1.0.7"; + final String version = "1.0.8"; final String versionString = "AppleIIGo Version " + version; final String metaStart = "_meta_"; @@ -200,8 +205,6 @@ public void destroy() { unmountDisk(0); unmountDisk(1); } - - // Public Java interface @@ -247,7 +250,6 @@ public void reset() { debug("reset()"); apple.reset(); } - public void setSpeed(int value) { debug("setSpeed(" + value + ")"); @@ -292,7 +294,8 @@ private DataInputStream openInputStream(String resource, StringBuffer OutFilenam } try { - URL url = new URL(getCodeBase(), resource); + URL codeBase = getCodeBase(); + URL url = new URL(codeBase, resource); debug("resource: " + url.toString()); is = url.openStream(); @@ -378,6 +381,7 @@ public boolean mountDisk(int drive, String resource) { 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 ...? @@ -427,6 +431,10 @@ public boolean mountDisk(int drive, String resource) { case ('d' << 16) + 'v': diskVolumeNumber = operand; break; + + case ('d' << 16) + 'w': + diskWritableOverride = (operand != 0); + break; } command = 0; operand = 0; @@ -434,7 +442,7 @@ public boolean mountDisk(int drive, String resource) { } } - success = disk.readDisk(drive, is, diskname.toString(), !diskWritable, diskVolumeNumber); + success = disk.readDisk(drive, is, diskname.toString(), !diskWritableOverride, diskVolumeNumber); is.close(); showStatus("Drive " + (drive + 1) + ": " + resource); } catch (Exception e) { @@ -455,12 +463,13 @@ public void unmountDisk(int drive) { if (!diskWritable) return; - try { - OutputStream os = openOutputStream(diskDriveResource[drive]); - disk.writeDisk(drive, os); - os.close(); - } catch (Exception e) { - } + // TODO: only for local disk cache when it's working + //try { + //OutputStream os = openOutputStream(diskDriveResource[drive]); + //disk.writeDisk(drive, os); + //os.close(); + //} catch (Exception e) { + //} } /** @@ -501,6 +510,16 @@ public boolean getDiskActivity() { return (!isCpuPaused && disk.isMotorOn()); } + public int getSizeX() + { + return display.getSizeX(); + } + + public int getSizeY() + { + return display.getSizeY(); + } + /** * KeyListener event handling */ @@ -626,7 +645,7 @@ else if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD) apple.stepInstructions(128); } 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: if (e.isControlDown()) reset(); @@ -846,5 +865,5 @@ public void paint(Graphics g) { */ public void update(Graphics g) { display.paint(g); - } + } } diff --git a/Source/AppleSpeaker.java b/Source/AppleSpeaker.java index fc8f691..2ba3450 100644 --- a/Source/AppleSpeaker.java +++ b/Source/AppleSpeaker.java @@ -2,7 +2,7 @@ /** * AppleIIGo * Speaker processing - * (C) 2006 by Marc S. Ressl(ressl@lonetree.com) + * (C) 2006 by Marc S. Ressl(mressl@gmail.com) * Released under the GPL */ diff --git a/Source/DiskII.java b/Source/DiskII.java index 008a71b..4594d15 100644 --- a/Source/DiskII.java +++ b/Source/DiskII.java @@ -2,8 +2,8 @@ /** * AppleIIGo * Disk II Emulator - * (C) 2006 by Marc S. Ressl(ressl@lonetree.com) - * (C) 2009 by Nick Westgate (Nick.Westgate@gmail.com) + * (C) 2006 by Marc S. Ressl(mressl@gmail.com) + * (C) 2011 by Nick Westgate (Nick.Westgate@gmail.com) * Released under the GPL * Based on work by Doug Kwan */ @@ -32,12 +32,14 @@ public class DiskII extends Peripheral { }; // Constants + public static final int DEFAULT_VOLUME = 254; private static final int NUM_DRIVES = 2; private static final int DOS_NUM_SECTORS = 16; 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; + private static final int STANDARD_2IMG_HEADER_SIZE = 64; + private static final int STANDARD_PRODOS_BLOCKS = 280; // Disk II direct access variables private int drive = 0; @@ -66,9 +68,10 @@ public class DiskII extends Peripheral { */ // Internal registers - private int latchAddress; private int latchData; private boolean writeMode; + private boolean loadMode; + private boolean driveSpin; // GCR encoding and decoding tables private static final int[] gcrEncodingTable = { @@ -119,104 +122,66 @@ public DiskII(EmAppleII apple) { * @param address Address */ public int ioRead(int address) { - int phase; - switch (address & 0xf) { case 0x0: - case 0x2: - case 0x4: - case 0x6: - // Q0, Q1, Q2, Q3 off - break; case 0x1: - // Q0 on - phase = currPhysTrack & 3; - if (phase == 1) { - if (currPhysTrack > 0) - currPhysTrack--; - } else if (phase == 3) { - if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) - currPhysTrack++; - } - //System.out.println("half track=" + currPhysTrack); - realTrack = diskData[drive][currPhysTrack >> 1]; - break; + case 0x2: case 0x3: - // Q1 on - phase = currPhysTrack & 3; - if (phase == 2) { - if (currPhysTrack > 0) - currPhysTrack--; - } else if (phase == 0) { - if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) - currPhysTrack++; - } - //System.out.println("half track=" + currPhysTrack); - realTrack = diskData[drive][currPhysTrack >> 1]; - break; + case 0x4: case 0x5: - // Q2 on - phase = currPhysTrack & 3; - if (phase == 3) { - if (currPhysTrack > 0) - currPhysTrack--; - } else if (phase == 1) { - if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) - currPhysTrack++; - } - //System.out.println("half track=" + currPhysTrack); - realTrack = diskData[drive][currPhysTrack >> 1]; - break; + case 0x6: case 0x7: - // Q3 on - phase = currPhysTrack & 3; - if (phase == 0) { - if (currPhysTrack > 0) - currPhysTrack--; - } else if (phase == 2) { - if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) - currPhysTrack++; - } - //System.out.println("half track=" + currPhysTrack); - realTrack = diskData[drive][currPhysTrack >> 1]; + setPhase(address); break; case 0x8: - // Motor off isMotorOn = false; break; case 0x9: - // Motor on isMotorOn = true; break; case 0xa: - // Drive 1 - driveCurrPhysTrack[drive] = currPhysTrack; - drive = 0; - currPhysTrack = driveCurrPhysTrack[drive]; - - realTrack = diskData[drive][currPhysTrack >> 1]; + setDrive(0); break; case 0xb: - // Drive 2 - driveCurrPhysTrack[drive] = currPhysTrack; - drive = 1; - currPhysTrack = driveCurrPhysTrack[drive]; - - realTrack = diskData[drive][currPhysTrack >> 1]; + setDrive(1); break; case 0xc: - return ioLatchC(); + ioLatchC(); + break; 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; case 0xe: - return ioLatchE(); + writeMode = false; + break; case 0xf: - ioLatchF(0xff); + writeMode = true; break; } - return rand.nextInt(256); + if ((address & 1) == 0) + { + // only even addresses return the latch + if (isMotorOn) + { + return latchData; + } + + // simple hack to fool DOS SAMESLOT drive spin check (usually at $BD34) + driveSpin = !driveSpin; + return driveSpin ? 0x7E : 0x7F; + } + + return rand.nextInt(256); // TODO: floating bus } /** @@ -234,25 +199,39 @@ public void ioWrite(int address, int value) { case 0x5: case 0x6: case 0x7: + setPhase(address); + break; case 0x8: + isMotorOn = false; + break; case 0x9: + isMotorOn = true; + break; case 0xa: + setDrive(0); + break; case 0xb: - ioRead(address); + setDrive(1); break; case 0xc: ioLatchC(); break; case 0xd: - ioLatchD(value); + loadMode = true; break; case 0xe: - ioLatchE(); + writeMode = false; break; case 0xf: - ioLatchF(value); + writeMode = true; break; } + + if (isMotorOn && writeMode && loadMode) + { + // any address writes latch for sequencer LD; OE1/2 irrelevant ['323 datasheet] + latchData = value; + } } /** @@ -273,18 +252,59 @@ public void reset() { /** * Loads a disk - * - * @param is InputStream - * @param drive Disk II drive */ public boolean readDisk(int drive, DataInputStream is, String name, boolean isWriteProtected, int volumeNumber) { - byte[] track = new byte[DOS_TRACK_BYTES]; - - String lowerName = name.toLowerCase(); - boolean proDos = lowerName.indexOf(".po") != -1; - boolean nib = lowerName.indexOf(".nib") != -1; - 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 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++) { diskData[drive][trackNum] = new byte[RAW_TRACK_BYTES]; @@ -328,31 +348,20 @@ public boolean isMotorOn() { return isMotorOn; } - - - /** - * I/O read Latch C - * - * @param address Address - */ - private int ioLatchC() { - latchAddress = 0xc; - if (writeMode) - { - // Write data: C0xD, C0xC - realTrack[currNibble] = (byte) latchData; - } - else + private void ioLatchC() { + loadMode = false; + if (!writeMode) { // Read data: C0xE, C0xC latchData = (realTrack[currNibble] & 0xff); // simple hack to help DOS find address prologues ($B94F) - if (/* fastDisk && */ latchData != 0xD5 && // TODO: fastDisk property to enable/disable + if (/* fastDisk && */ // 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 + apple.memoryRead(apple.PC + 0) == 0x10 && // BPL + latchData != 0xD5) { int count = RAW_TRACK_BYTES / 16; do @@ -364,15 +373,8 @@ private int ioLatchC() { } while (latchData != 0xD5 && --count > 0); } - // 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 - { - return 0x7F; - } // skip invalid nibbles we padded the track buffer with - else if (latchData == 0x7F) + else if (latchData == 0x7F) // TODO: maybe the above covers this? { int count = RAW_TRACK_BYTES / 16; do @@ -385,51 +387,87 @@ else if (latchData == 0x7F) while (latchData == 0x7F && --count > 0); } } + else + { + // Write data: C0xD, C0xC + realTrack[currNibble] = (byte) latchData; + } currNibble++; if (currNibble >= RAW_TRACK_BYTES) currNibble = 0; - - return latchData; } - - /** - * I/O write Latch D - * - * @param address Address - */ - private void ioLatchD(int value) { - // Prepare for write protect sense - latchAddress = 0xd; - } - - /** - * 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; - latchAddress = 0xe; - return 0x3c; + private void setPhase(int address) { + int phase; + + switch (address & 0xf) { + case 0x0: + case 0x2: + case 0x4: + case 0x6: + // Q0, Q1, Q2, Q3 off + break; + case 0x1: + // Q0 on + phase = currPhysTrack & 3; + if (phase == 1) { + if (currPhysTrack > 0) + currPhysTrack--; + } else if (phase == 3) { + if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) + currPhysTrack++; + } + //System.out.println("half track=" + currPhysTrack); + realTrack = diskData[drive][currPhysTrack >> 1]; + break; + case 0x3: + // Q1 on + phase = currPhysTrack & 3; + if (phase == 2) { + if (currPhysTrack > 0) + currPhysTrack--; + } else if (phase == 0) { + if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) + currPhysTrack++; + } + //System.out.println("half track=" + currPhysTrack); + realTrack = diskData[drive][currPhysTrack >> 1]; + break; + case 0x5: + // Q2 on + phase = currPhysTrack & 3; + if (phase == 3) { + if (currPhysTrack > 0) + currPhysTrack--; + } else if (phase == 1) { + if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) + currPhysTrack++; + } + //System.out.println("half track=" + currPhysTrack); + realTrack = diskData[drive][currPhysTrack >> 1]; + break; + case 0x7: + // Q3 on + phase = currPhysTrack & 3; + if (phase == 0) { + if (currPhysTrack > 0) + currPhysTrack--; + } else if (phase == 2) { + if (currPhysTrack < ((2 * DOS_NUM_TRACKS) - 1)) + currPhysTrack++; + } + //System.out.println("half track=" + currPhysTrack); + realTrack = diskData[drive][currPhysTrack >> 1]; + break; + } } - /** - * I/O write Latch F - * - * @param address Address - */ - private void ioLatchF(int value) { - // Prepare write - writeMode = true; - latchData = value; - latchAddress = 0xf; + private void setDrive(int newDrive) { + driveCurrPhysTrack[drive] = currPhysTrack; + drive = newDrive; + currPhysTrack = driveCurrPhysTrack[drive]; + realTrack = diskData[drive][currPhysTrack >> 1]; } /** diff --git a/Source/Em6502.java b/Source/Em6502.java index 9631220..e107539 100644 --- a/Source/Em6502.java +++ b/Source/Em6502.java @@ -2,7 +2,7 @@ /** * AppleIIGo * Apple II Emulator for J2SE - * (C) 2006 by Marc S. Ressl(ressl@lonetree.com) + * (C) 2006 by Marc S. Ressl(mressl@gmail.com) * Released under the GPL * Adapted from code by Doug Kwan * Adapted from code by Randy Frank randy@tessa.iaf.uiowa.edu diff --git a/Source/EmAppleII.java b/Source/EmAppleII.java index f16f4a5..09501c6 100644 --- a/Source/EmAppleII.java +++ b/Source/EmAppleII.java @@ -2,7 +2,7 @@ /** * AppleIIGo * Apple II Emulator for J2ME - * (C) 2006 by Marc S. Ressl(ressl@lonetree.com) + * (C) 2006 by Marc S. Ressl(mressl@gmail.com) * Released under the GPL */ diff --git a/Source/Paddle.java b/Source/Paddle.java index 12b9800..f78cd43 100644 --- a/Source/Paddle.java +++ b/Source/Paddle.java @@ -2,7 +2,7 @@ /** * AppleIIGo * Apple II Emulator for J2ME - * (C) 2006 by Marc S. Ressl(ressl@lonetree.com) + * (C) 2006 by Marc S. Ressl(mressl@gmail.com) * Released under the GPL */ diff --git a/Source/Peripheral.java b/Source/Peripheral.java index 4b4e4d6..372b3ec 100644 --- a/Source/Peripheral.java +++ b/Source/Peripheral.java @@ -2,7 +2,7 @@ /** * AppleIIGo * Slot interface - * (C) 2006 by Marc S. Ressl(ressl@lonetree.com) + * (C) 2006 by Marc S. Ressl(mressl@gmail.com) * Released under the GPL * Based on work by Steven E. Hugg */