mirror of
https://github.com/sicklittlemonkey/AppleIIGo.git
synced 2024-09-27 08:54:51 +00:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fe27c7d1e8 | ||
|
68cfda45c1 |
@ -1,6 +1,10 @@
|
|||||||
# AppleIIGo
|
# AppleIIGo
|
||||||
Apple //e emulator Java applet
|
Apple //e emulator Java applet
|
||||||
|
|
||||||
Original version: Copyright 2006 Marc S. Ressl
|
Original version: Copyright 2006 Marc S. Ressl, with parts adapted from code by
|
||||||
|
- Steven E. Hugg
|
||||||
|
- Doug Kwan
|
||||||
|
- Randy Frank
|
||||||
|
- Ben Koning
|
||||||
|
|
||||||
Subsequent versions: Copyright 2008-2015 Nick Westgate
|
Subsequent versions: Copyright 2008-2015 Nick Westgate
|
||||||
|
@ -2,13 +2,20 @@
|
|||||||
/**
|
/**
|
||||||
* AppleIIGo
|
* AppleIIGo
|
||||||
* The Java Apple II Emulator
|
* The Java Apple II Emulator
|
||||||
* Copyright 2014 by Nick Westgate (Nick.Westgate@gmail.com)
|
* Copyright 2015 by Nick Westgate (Nick.Westgate@gmail.com)
|
||||||
* Copyright 2006 by Marc S. Ressl (mressl@gmail.com)
|
* Copyright 2006 by Marc S. Ressl (mressl@gmail.com)
|
||||||
* Released under the GNU General Public License version 2
|
* Released under the GNU General Public License version 2
|
||||||
* See http://www.gnu.org/licenses/
|
* 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:
|
* Version 1.0.9 - changes by Nick:
|
||||||
* - fixed disk speed-up bug (Sherwood Forest reads with the drive motor off)
|
* - fixed disk speed-up bug (Sherwood Forest reads with the drive motor off)
|
||||||
* - added check for 2IMG header ID
|
* - added check for 2IMG header ID
|
||||||
@ -85,7 +92,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
|
|||||||
|
|
||||||
private static final long serialVersionUID = -3302282815441501352L;
|
private static final long serialVersionUID = -3302282815441501352L;
|
||||||
|
|
||||||
final String version = "1.0.9";
|
final String version = "1.0.10";
|
||||||
final String versionString = "AppleIIGo Version " + version;
|
final String versionString = "AppleIIGo Version " + version;
|
||||||
final String metaStart = "_meta_";
|
final String metaStart = "_meta_";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* AppleIIGo
|
* AppleIIGo
|
||||||
* Disk II Emulator
|
* Disk II Emulator
|
||||||
* Copyright 2014 by Nick Westgate (Nick.Westgate@gmail.com)
|
* Copyright 2015 by Nick Westgate (Nick.Westgate@gmail.com)
|
||||||
* Copyright 2006 by Marc S. Ressl(mressl@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
|
||||||
@ -36,6 +36,7 @@ public class DiskII extends Peripheral {
|
|||||||
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 = 0x1A00; // 0x1A00 (6656) for .NIB (was 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_ID = 0x32494D47;
|
||||||
@ -44,6 +45,7 @@ public class DiskII extends Peripheral {
|
|||||||
|
|
||||||
// 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[][][] diskData = new byte[NUM_DRIVES][DOS_NUM_TRACKS][];
|
private byte[][][] diskData = new byte[NUM_DRIVES][DOS_NUM_TRACKS][];
|
||||||
@ -72,7 +74,7 @@ public class DiskII extends Peripheral {
|
|||||||
private int latchData;
|
private int latchData;
|
||||||
private boolean writeMode;
|
private boolean writeMode;
|
||||||
private boolean loadMode;
|
private boolean loadMode;
|
||||||
private boolean driveSpin;
|
private int driveSpin;
|
||||||
|
|
||||||
// GCR encoding and decoding tables
|
// GCR encoding and decoding tables
|
||||||
private static final int[] gcrEncodingTable = {
|
private static final int[] gcrEncodingTable = {
|
||||||
@ -348,28 +350,29 @@ public class DiskII extends Peripheral {
|
|||||||
loadMode = false;
|
loadMode = false;
|
||||||
if (!writeMode)
|
if (!writeMode)
|
||||||
{
|
{
|
||||||
if (!isMotorOn)
|
if (!isMotorOn)
|
||||||
{
|
{
|
||||||
// simple hack to fool DOS SAMESLOT drive spin check (usually at $BD34)
|
// simple hack to fool RWTS SAMESLOT drive spin check (usually at $BD34)
|
||||||
driveSpin = !driveSpin;
|
driveSpin = (driveSpin + 1) & 0xF;
|
||||||
if (driveSpin)
|
if (driveSpin == 0)
|
||||||
{
|
{
|
||||||
latchData = 0x7F;
|
latchData = 0x7F;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read data: C0xE, C0xC
|
// Read data: C0xE, C0xC
|
||||||
latchData = (realTrack[currNibble] & 0xff);
|
latchData = (realTrack[currNibble] & 0xff);
|
||||||
|
|
||||||
// simple hack to help DOS find address prologues ($B94F)
|
// is RWTS looking for an address prologue? (RDADR)
|
||||||
if (/* fastDisk && */ // 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 + 3) == 0xD5 && // #$D5
|
||||||
apple.memoryRead(apple.PC + 2) == 0xC9 && // CMP
|
apple.memoryRead(apple.PC + 2) == 0xC9 && // CMP (usually at $B94F)
|
||||||
apple.memoryRead(apple.PC + 1) == 0xFB && // PC - 3
|
apple.memoryRead(apple.PC + 1) == 0xFB && // PC - 3
|
||||||
apple.memoryRead(apple.PC + 0) == 0x10 && // BPL
|
apple.memoryRead(apple.PC + 0) == 0x10 && // BPL
|
||||||
latchData != 0xD5)
|
latchData != 0xD5)
|
||||||
{
|
{
|
||||||
|
// Y: find the next address prologues
|
||||||
int count = RAW_TRACK_BYTES / 16;
|
int count = RAW_TRACK_BYTES / 16;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@ -380,8 +383,8 @@ public class DiskII extends Peripheral {
|
|||||||
}
|
}
|
||||||
while (latchData != 0xD5 && --count > 0);
|
while (latchData != 0xD5 && --count > 0);
|
||||||
}
|
}
|
||||||
// skip invalid nibbles we padded the track buffer with
|
// N: skip invalid nibbles we padded the track buffer with
|
||||||
else if (latchData == 0x7F) // TODO: maybe the above covers this?
|
else if (latchData == 0x7F)
|
||||||
{
|
{
|
||||||
int count = RAW_TRACK_BYTES / 16;
|
int count = RAW_TRACK_BYTES / 16;
|
||||||
do
|
do
|
||||||
@ -406,68 +409,42 @@ public class DiskII extends Peripheral {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setPhase(int address) {
|
private void setPhase(int address) {
|
||||||
int phase;
|
int phase = (address >> 1) & 3;
|
||||||
|
int phase_bit = (1 << phase);
|
||||||
|
|
||||||
switch (address & 0xf) {
|
// update the magnet states
|
||||||
case 0x0:
|
if ((address & 1) != 0)
|
||||||
case 0x2:
|
{
|
||||||
case 0x4:
|
// phase on
|
||||||
case 0x6:
|
phases |= phase_bit;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// phase off
|
||||||
|
phases &= ~phase_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for any stepping effect from a magnet
|
||||||
|
// - move only when the magnet opposite the cog is off
|
||||||
|
// - 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) {
|
private void setDrive(int newDrive) {
|
||||||
|
@ -80,7 +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 << 6);
|
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);
|
||||||
/*
|
/*
|
||||||
@ -123,7 +123,7 @@ public class Em6502 {
|
|||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public Em6502() {
|
public Em6502() {
|
||||||
// createRunFile();
|
// createRunFile(); // TODO: for debugging - disable
|
||||||
|
|
||||||
// Init BCD tables
|
// Init BCD tables
|
||||||
BCDTableAdd = new int[512];
|
BCDTableAdd = new int[512];
|
||||||
@ -163,7 +163,7 @@ 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;}
|
||||||
@ -304,38 +304,47 @@ public class Em6502 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// private PrintWriter runFile;
|
// private PrintWriter runFile;
|
||||||
|
// private boolean runFlag = false;
|
||||||
|
//
|
||||||
// private void createRunFile()
|
// private void createRunFile()
|
||||||
// {
|
// {
|
||||||
// try
|
// try
|
||||||
// {
|
// {
|
||||||
// runFile = new PrintWriter("C:\\Users\\Public\\AppleIIGoRun.txt");
|
// //runFile = new PrintWriter("C:\\Dev\\Emulators\\AppleIIGo\\AppleIIGoRun.txt");
|
||||||
// }
|
// }
|
||||||
// catch (Exception ex)
|
// catch (Exception ex)
|
||||||
// {
|
// {
|
||||||
// // swallow
|
// // swallow
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
// private final void writeRunFile(int opcode)
|
// private final void writeRunFile(int opcode)
|
||||||
// {
|
// {
|
||||||
// if (
|
// if (PC == 0x2000)
|
||||||
// (PC > 0x0500)
|
// runFlag = true;
|
||||||
// &&
|
// if (
|
||||||
// (PC < 0x0600)
|
// runFlag &&
|
||||||
// )
|
// (PC > 0x1000)
|
||||||
// {
|
// &&
|
||||||
// setN(getFN());
|
// (PC < 0xC000)
|
||||||
// setZ(getFZ());
|
// )
|
||||||
// setC(getFC());
|
// {
|
||||||
|
// if (runFile != null)
|
||||||
|
// {
|
||||||
|
// setN(getFN());
|
||||||
|
// setZ(getFZ());
|
||||||
|
// setC(getFC());
|
||||||
//
|
//
|
||||||
// runFile.printf("%04X-%02X P=%02X A=%02X\r\n", PC, opcode, P, A);
|
// runFile.printf("%04X-%02X P=%02X A=%02X\r\n", PC, opcode, P, A);
|
||||||
// runFile.flush();
|
// 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);
|
// writeRunFile(opcode); // TODO: for debugging = disable
|
||||||
PC++;
|
PC++;
|
||||||
|
|
||||||
switch(opcode) {
|
switch(opcode) {
|
||||||
@ -569,12 +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());
|
||||||
push(P); // break flag is always set
|
push(P); // B and R always set
|
||||||
setI(true);
|
setI(true);
|
||||||
PC = memoryRead(0xfffe);
|
PC = memoryRead(0xfffe);
|
||||||
PC |= memoryRead(0xffff) << 8;
|
PC |= memoryRead(0xffff) << 8;
|
||||||
@ -1081,7 +1091,7 @@ public class Em6502 {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x28: // PLP
|
case 0x28: // PLP
|
||||||
P = pop() | FLAG_B | FLAG_R; // 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;
|
||||||
@ -1182,7 +1192,7 @@ public class Em6502 {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x40: // RTI
|
case 0x40: // RTI
|
||||||
P = pop() | FLAG_B | FLAG_R; // 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
|
||||||
|
@ -264,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user