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
This commit is contained in:
parent
af1cec23d3
commit
add3d41448
|
@ -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 class AppleDisplay implements Runnable {
|
|||
return displayScale;
|
||||
}
|
||||
|
||||
public int getSizeX()
|
||||
{
|
||||
precalcDisplay();
|
||||
return displayScaledSizeX;
|
||||
}
|
||||
|
||||
public int getSizeY()
|
||||
{
|
||||
precalcDisplay();
|
||||
return displayScaledSizeY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set refresh rate
|
||||
*
|
||||
|
|
|
@ -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 class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||
unmountDisk(0);
|
||||
unmountDisk(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Public Java interface
|
||||
|
||||
|
@ -247,7 +250,6 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||
debug("reset()");
|
||||
apple.reset();
|
||||
}
|
||||
|
||||
|
||||
public void setSpeed(int value) {
|
||||
debug("setSpeed(" + value + ")");
|
||||
|
@ -292,7 +294,8 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||
}
|
||||
|
||||
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 class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||
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 class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||
case ('d' << 16) + 'v':
|
||||
diskVolumeNumber = operand;
|
||||
break;
|
||||
|
||||
case ('d' << 16) + 'w':
|
||||
diskWritableOverride = (operand != 0);
|
||||
break;
|
||||
}
|
||||
command = 0;
|
||||
operand = 0;
|
||||
|
@ -434,7 +442,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||
}
|
||||
}
|
||||
|
||||
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 class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||
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 class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||
return (!isCpuPaused && disk.isMotorOn());
|
||||
}
|
||||
|
||||
public int getSizeX()
|
||||
{
|
||||
return display.getSizeX();
|
||||
}
|
||||
|
||||
public int getSizeY()
|
||||
{
|
||||
return display.getSizeY();
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyListener event handling
|
||||
*/
|
||||
|
@ -626,7 +645,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||
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 class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||
*/
|
||||
public void update(Graphics g) {
|
||||
display.paint(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
|
@ -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,6 +122,283 @@ public class DiskII extends Peripheral {
|
|||
* @param address Address
|
||||
*/
|
||||
public int ioRead(int address) {
|
||||
switch (address & 0xf) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
case 0x4:
|
||||
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:
|
||||
setDrive(1);
|
||||
break;
|
||||
case 0xc:
|
||||
ioLatchC();
|
||||
break;
|
||||
case 0xd:
|
||||
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:
|
||||
writeMode = false;
|
||||
break;
|
||||
case 0xf:
|
||||
writeMode = true;
|
||||
break;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* I/O write
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
public void ioWrite(int address, int value) {
|
||||
switch (address & 0xf) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
case 0x4:
|
||||
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:
|
||||
setDrive(1);
|
||||
break;
|
||||
case 0xc:
|
||||
ioLatchC();
|
||||
break;
|
||||
case 0xd:
|
||||
loadMode = true;
|
||||
break;
|
||||
case 0xe:
|
||||
writeMode = false;
|
||||
break;
|
||||
case 0xf:
|
||||
writeMode = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isMotorOn && writeMode && loadMode)
|
||||
{
|
||||
// any address writes latch for sequencer LD; OE1/2 irrelevant ['323 datasheet]
|
||||
latchData = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory read
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
public int memoryRead(int address) {
|
||||
return rom[address & 0xff];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset peripheral
|
||||
*/
|
||||
public void reset() {
|
||||
ioRead(0x8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a disk
|
||||
*/
|
||||
public boolean readDisk(int drive, DataInputStream is, String name, boolean isWriteProtected, int volumeNumber) {
|
||||
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];
|
||||
|
||||
if (is != null) {
|
||||
if (nib)
|
||||
{
|
||||
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 = diskData[drive][currPhysTrack >> 1];
|
||||
this.isWriteProtected[drive] = isWriteProtected;
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a disk
|
||||
*
|
||||
* @param is InputStream
|
||||
* @param drive Disk II drive
|
||||
*/
|
||||
public boolean writeDisk(int drive, OutputStream os) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Motor on indicator
|
||||
*/
|
||||
public boolean isMotorOn() {
|
||||
return isMotorOn;
|
||||
}
|
||||
|
||||
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 && */ // 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
|
||||
latchData != 0xD5)
|
||||
{
|
||||
int count = RAW_TRACK_BYTES / 16;
|
||||
do
|
||||
{
|
||||
currNibble++;
|
||||
if (currNibble >= RAW_TRACK_BYTES)
|
||||
currNibble = 0;
|
||||
latchData = (realTrack[currNibble] & 0xff);
|
||||
}
|
||||
while (latchData != 0xD5 && --count > 0);
|
||||
}
|
||||
// skip invalid nibbles we padded the track buffer with
|
||||
else if (latchData == 0x7F) // TODO: maybe the above covers this?
|
||||
{
|
||||
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++;
|
||||
if (currNibble >= RAW_TRACK_BYTES)
|
||||
currNibble = 0;
|
||||
}
|
||||
|
||||
private void setPhase(int address) {
|
||||
int phase;
|
||||
|
||||
switch (address & 0xf) {
|
||||
|
@ -180,256 +460,14 @@ public class DiskII extends Peripheral {
|
|||
//System.out.println("half track=" + currPhysTrack);
|
||||
realTrack = diskData[drive][currPhysTrack >> 1];
|
||||
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];
|
||||
break;
|
||||
case 0xb:
|
||||
// Drive 2
|
||||
driveCurrPhysTrack[drive] = currPhysTrack;
|
||||
drive = 1;
|
||||
currPhysTrack = driveCurrPhysTrack[drive];
|
||||
|
||||
realTrack = diskData[drive][currPhysTrack >> 1];
|
||||
break;
|
||||
case 0xc:
|
||||
return ioLatchC();
|
||||
case 0xd:
|
||||
ioLatchD(0xff);
|
||||
break;
|
||||
case 0xe:
|
||||
return ioLatchE();
|
||||
case 0xf:
|
||||
ioLatchF(0xff);
|
||||
break;
|
||||
}
|
||||
|
||||
return rand.nextInt(256);
|
||||
}
|
||||
|
||||
/**
|
||||
* I/O write
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
public void ioWrite(int address, int value) {
|
||||
switch (address & 0xf) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
case 0x4:
|
||||
case 0x5:
|
||||
case 0x6:
|
||||
case 0x7:
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
case 0xa:
|
||||
case 0xb:
|
||||
ioRead(address);
|
||||
break;
|
||||
case 0xc:
|
||||
ioLatchC();
|
||||
break;
|
||||
case 0xd:
|
||||
ioLatchD(value);
|
||||
break;
|
||||
case 0xe:
|
||||
ioLatchE();
|
||||
break;
|
||||
case 0xf:
|
||||
ioLatchF(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory read
|
||||
*
|
||||
* @param address Address
|
||||
*/
|
||||
public int memoryRead(int address) {
|
||||
return rom[address & 0xff];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset peripheral
|
||||
*/
|
||||
public void reset() {
|
||||
ioRead(0x8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
for (int trackNum = 0; trackNum < DOS_NUM_TRACKS; trackNum++) {
|
||||
diskData[drive][trackNum] = new byte[RAW_TRACK_BYTES];
|
||||
|
||||
if (is != null) {
|
||||
if (nib)
|
||||
{
|
||||
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 = diskData[drive][currPhysTrack >> 1];
|
||||
this.isWriteProtected[drive] = isWriteProtected;
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a disk
|
||||
*
|
||||
* @param is InputStream
|
||||
* @param drive Disk II drive
|
||||
*/
|
||||
public boolean writeDisk(int drive, OutputStream os) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Motor on indicator
|
||||
*/
|
||||
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
|
||||
{
|
||||
// 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
|
||||
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
|
||||
{
|
||||
int count = RAW_TRACK_BYTES / 16;
|
||||
do
|
||||
{
|
||||
currNibble++;
|
||||
if (currNibble >= RAW_TRACK_BYTES)
|
||||
currNibble = 0;
|
||||
latchData = (realTrack[currNibble] & 0xff);
|
||||
}
|
||||
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)
|
||||
{
|
||||
int count = RAW_TRACK_BYTES / 16;
|
||||
do
|
||||
{
|
||||
currNibble++;
|
||||
if (currNibble >= RAW_TRACK_BYTES)
|
||||
currNibble = 0;
|
||||
latchData = (realTrack[currNibble] & 0xff);
|
||||
}
|
||||
while (latchData == 0x7F && --count > 0);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue