Compare commits

...

3 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
9 changed files with 164 additions and 105 deletions

View File

@ -1,6 +1,10 @@
# AppleIIGo
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

View File

@ -2,7 +2,7 @@
/**
* AppleIIGo
* Display processing
* (C) 2006 by Marc S. Ressl (mressl@gmail.com)
* Copyright 2006 by Marc S. Ressl (mressl@gmail.com)
* Released under the GPL
*/

View File

@ -2,13 +2,25 @@
/**
* AppleIIGo
* The Java Apple II Emulator
* Copyright 2011 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)
* Released under the GNU General Public License version 2
* See http://www.gnu.org/licenses/
*
* 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
@ -80,7 +92,7 @@ public class AppleIIGo extends Applet implements KeyListener, ComponentListener,
private static final long serialVersionUID = -3302282815441501352L;
final String version = "1.0.8";
final String version = "1.0.10";
final String versionString = "AppleIIGo Version " + version;
final String metaStart = "_meta_";

View File

@ -2,7 +2,7 @@
/**
* AppleIIGo
* Speaker processing
* (C) 2006 by Marc S. Ressl(mressl@gmail.com)
* Copyright 2006 by Marc S. Ressl(mressl@gmail.com)
* Released under the GPL
*/

View File

@ -2,8 +2,8 @@
/**
* AppleIIGo
* Disk II Emulator
* (C) 2006 by Marc S. Ressl(mressl@gmail.com)
* (C) 2011 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)
* Released under the GPL
* Based on work by Doug Kwan
*/
@ -36,13 +36,16 @@ public class DiskII extends Peripheral {
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 MAX_PHYS_TRACK = (2 * DOS_NUM_TRACKS) - 1;
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 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
private int drive = 0;
private int phases = 0;
private boolean isMotorOn = false;
private byte[][][] diskData = new byte[NUM_DRIVES][DOS_NUM_TRACKS][];
@ -71,7 +74,7 @@ public class DiskII extends Peripheral {
private int latchData;
private boolean writeMode;
private boolean loadMode;
private boolean driveSpin;
private int driveSpin;
// GCR encoding and decoding tables
private static final int[] gcrEncodingTable = {
@ -168,20 +171,8 @@ public class DiskII extends Peripheral {
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
// only even addresses return the latch
return ((address & 1) == 0) ? latchData : rand.nextInt(256); // TODO: floating bus
}
/**
@ -247,7 +238,10 @@ public class DiskII extends Peripheral {
* Reset peripheral
*/
public void reset() {
ioRead(0x8);
drive = 0;
isMotorOn = false;
loadMode = false;
writeMode = false;
}
/**
@ -266,6 +260,10 @@ public class DiskII extends Peripheral {
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;
@ -352,17 +350,29 @@ public class DiskII extends Peripheral {
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;
}
}
// Read data: C0xE, C0xC
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
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 + 0) == 0x10 && // BPL
latchData != 0xD5)
{
// Y: find the next address prologues
int count = RAW_TRACK_BYTES / 16;
do
{
@ -373,8 +383,8 @@ public class DiskII extends Peripheral {
}
while (latchData != 0xD5 && --count > 0);
}
// skip invalid nibbles we padded the track buffer with
else if (latchData == 0x7F) // TODO: maybe the above covers this?
// N: skip invalid nibbles we padded the track buffer with
else if (latchData == 0x7F)
{
int count = RAW_TRACK_BYTES / 16;
do
@ -399,68 +409,42 @@ public class DiskII extends Peripheral {
}
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;
int phase = (address >> 1) & 3;
int phase_bit = (1 << phase);
// update the magnet states
if ((address & 1) != 0)
{
// phase on
phases |= phase_bit;
}
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) {

View File

@ -1,12 +1,14 @@
import java.io.PrintWriter;
/**
* AppleIIGo
* Apple II Emulator for J2SE
* (C) 2006 by Marc S. Ressl(mressl@gmail.com)
* Copyright 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
* 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 {
@ -78,6 +80,7 @@ public class Em6502 {
public static final int FLAG_I = (1 << 2);
public static final int FLAG_D = (1 << 3);
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_N = (1 << 7);
/*
@ -120,6 +123,8 @@ public class Em6502 {
* Constructor
*/
public Em6502() {
// createRunFile(); // TODO: for debugging - disable
// Init BCD tables
BCDTableAdd = new int[512];
BCDTableSub = new int[512];
@ -133,6 +138,10 @@ public class Em6502 {
BCDTableSub[i] = ((i & 0x0f) <= 0x09) ? i : (i - 0x06);
BCDTableSub[i] -= ((BCDTableSub[i] & 0xf0) <= 0x90) ? 0 : 0x60;
}
// Init CPU
S = 0xFF;
P = FLAG_B | FLAG_R;
}
/*
@ -154,7 +163,7 @@ public class Em6502 {
*/
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 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 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;}
@ -294,9 +303,48 @@ public class Em6502 {
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. */
private final void executeInstruction() {
opcode = memoryRead(PC);
// writeRunFile(opcode); // TODO: for debugging = disable
PC++;
switch(opcode) {
@ -530,13 +578,13 @@ public class Em6502 {
break;
case 0x00: // BRK
PC++;
push(PC >> 8); // save PCH, PCL & P
push(PC);
setN(getFN());
setZ(getFZ());
setC(getFC());
setB(true);
push(P);
push(P); // B and R always set
setI(true);
PC = memoryRead(0xfffe);
PC |= memoryRead(0xffff) << 8;
@ -1043,7 +1091,7 @@ public class Em6502 {
break;
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());
setFNZ(getN(), getZ());
clock += 4;
@ -1144,7 +1192,7 @@ public class Em6502 {
break;
case 0x40: // RTI
P = pop() | 0x20; // bit 5 bug of 6502
P = pop() | FLAG_B | FLAG_R; // B and R always set
setFC(getC());
setFNZ(getN(), getZ());
PC = pop(); // splitting is necessary
@ -1570,13 +1618,13 @@ public class Em6502 {
if ((exceptionRegister & SIG_6502_RESET) != 0) {
onReset();
PC = memoryRead(0xfffc);
PC |= (memoryRead(0xfffd) << 8);
S = (S - 3) & 0xff;
setI(true);
setD(false); // not on NMOS 6502
setFC(getC());
setFNZ(getN(), getZ());
S = (S - 3) & 0xff;
setI(true);
PC = memoryRead(0xfffc);
PC |= (memoryRead(0xfffd) << 8);
clock += 7;
exceptionRegister &= ~SIG_6502_RESET;
}
@ -1590,7 +1638,8 @@ public class Em6502 {
setN(getFN());
setZ(getFZ());
setC(getFC());
push(P);
push(P & ~FLAG_B);
setI(true);
PC = memoryRead(0xfffa);
PC |= memoryRead(0xfffb) << 8;
clock += 7;
@ -1608,8 +1657,7 @@ public class Em6502 {
setN(getFN());
setZ(getFZ());
setC(getFC());
setB(false);
push(P);
push(P & ~FLAG_B);
setI(true);
PC = memoryRead(0xfffe);
PC |= memoryRead(0xffff) << 8;

View File

@ -2,7 +2,7 @@
/**
* AppleIIGo
* Apple II Emulator for J2ME
* (C) 2006 by Marc S. Ressl(mressl@gmail.com)
* Copyright 2006 by Marc S. Ressl(mressl@gmail.com)
* Released under the GPL
*/
@ -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 + 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;
}

View File

@ -2,7 +2,7 @@
/**
* AppleIIGo
* Apple II Emulator for J2ME
* (C) 2006 by Marc S. Ressl(mressl@gmail.com)
* Copyright 2006 by Marc S. Ressl(mressl@gmail.com)
* Released under the GPL
*/

View File

@ -2,7 +2,7 @@
/**
* AppleIIGo
* Slot interface
* (C) 2006 by Marc S. Ressl(mressl@gmail.com)
* Copyright 2006 by Marc S. Ressl(mressl@gmail.com)
* Released under the GPL
* Based on work by Steven E. Hugg
*/