vavi-apps-appleii-bdj/src/main/java/vavi/apps/appleii/EmAppleII.java

1209 lines
38 KiB
Java

/*
* AppleIIGo
* Apple II Emulator for J2ME
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
* Released under the GPL
*/
package vavi.apps.appleii;
public class EmAppleII extends Em6502 implements Runnable {
/*
* Apple II memory map
*/
public static final int MEM_PHYS_ZP = 0x00000;
public static final int MEM_PHYS_STACK = 0x00100;
public static final int MEM_PHYS_RAM1 = 0x00200;
public static final int MEM_PHYS_TEXT = 0x00400;
public static final int MEM_PHYS_RAM2 = 0x00800;
public static final int MEM_PHYS_HIRES = 0x02000;
public static final int MEM_PHYS_RAM3 = 0x04000;
public static final int MEM_PHYS_IO = 0x0c000;
public static final int MEM_PHYS_ROM_LOW = 0x0d000;
public static final int MEM_PHYS_ROM_HIGH = 0x0e000;
public static final int MEM_MAIN_RAM1 = 0x00200;
public static final int MEM_MAIN_TEXT = 0x00400;
public static final int MEM_MAIN_RAM2 = 0x00800;
public static final int MEM_MAIN_HIRES = 0x02000;
public static final int MEM_MAIN_RAM3 = 0x04000;
public static final int MEM_MAIN_LC1 = 0x0c000;
public static final int MEM_MAIN_LC2 = 0x0d000;
public static final int MEM_MAIN_LC_HIGH = 0x0e000;
public static final int MEM_AUX_ZP = 0x10000;
public static final int MEM_AUX_STACK = 0x10100;
public static final int MEM_AUX_RAM1 = 0x10200;
public static final int MEM_AUX_TEXT = 0x10400;
public static final int MEM_AUX_RAM2 = 0x10800;
public static final int MEM_AUX_HIRES = 0x12000;
public static final int MEM_AUX_RAM3 = 0x14000;
public static final int MEM_AUX_LC1 = 0x1c000;
public static final int MEM_AUX_LC2 = 0x1d000;
public static final int MEM_AUX_LC_HIGH = 0x1e000;
public static final int MEM_ROM_MAIN_LOW = 0x20000;
public static final int MEM_ROM_MAIN_HIGH = 0x21000;
public static final int MEM_ROM_INTERNAL = 0x23000;
public static final int MEM_ROM_EXTERNAL = 0x24000;
public static final int MEM_MAIN_ZP = 0x25000;
public static final int MEM_MAIN_STACK = 0x25100;
public static final int MEM_WASTE = 0x25200;
public static final int MEM_END = 0x28000;
// Peripherals
public Paddle paddle;
public Peripheral[] slots;
// Graphics (dirty buffer every 0x80 bytes)
public int graphicsMode;
public boolean[] graphicsDirty = new boolean[0x10000 >> 7];
public static final int GR_TEXT = (1 << 0);
public static final int GR_MIXMODE = (1 << 1);
public static final int GR_PAGE2 = (1 << 2);
public static final int GR_HIRES = (1 << 3);
public static final int GR_80STORE = (1 << 4);
public static final int GR_80CHAR = (1 << 5);
public static final int GR_ALTCHAR = (1 << 6);
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 int speakerFlips[] = new int[SPEAKER_FLIPS_SIZE];
public int speakerFlipsPointer = 0;
// Default ROM
private static final int[] defaultRom = {
// Reset routine
0xad, 0x51, 0xc0,
0xa9, 0xa0,
0xa2, 0xff, 0x9d, 0xff, 0x03, 0xca, 0xd0, 0xfa,
0xa2, 0xff, 0x9d, 0xff, 0x04, 0xca, 0xd0, 0xfa,
0xa2, 0xff, 0x9d, 0xff, 0x05, 0xca, 0xd0, 0xfa,
0xa2, 0xff, 0x9d, 0xff, 0x06, 0xca, 0xd0, 0xfa,
0xa2, 0x27, 0xbd, 0xb8, 0xfe, 0x9d, 0x80, 0x04, 0xca, 0x10, 0xf7,
0xa2, 0x27, 0xbd, 0xe0, 0xfe, 0x9d, 0x00, 0x05, 0xca, 0x10, 0xf7,
0xa2, 0x27, 0xbd, 0x08, 0xff, 0x9d, 0x80, 0x05, 0xca, 0x10, 0xf7,
0xa2, 0x27, 0xbd, 0x30, 0xff, 0x9d, 0x00, 0x06, 0xca, 0x10, 0xf7,
0xa2, 0x27, 0xbd, 0x58, 0xff, 0x9d, 0x80, 0x07, 0xca, 0x10, 0xf7,
0xa2, 0x27, 0xbd, 0x80, 0xff, 0x9d, 0x28, 0x04, 0xca, 0x10, 0xf7,
0xa2, 0x27, 0xbd, 0xa8, 0xff, 0x9d, 0x00, 0x07, 0xca, 0x10, 0xf7,
0xa2, 0x27, 0xbd, 0xd0, 0xff, 0x9d, 0x00, 0x07, 0xca, 0x10, 0xf7,
0x4c, 0xad, 0xfe,
0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea,
// Text message
0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa0,
0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x10, 0x10, 0x0c, 0x05, 0x09, 0x09,
0x07, 0x0f, 0x20, 0x12, 0x05, 0x11, 0x15, 0x09, 0x12, 0x05, 0x13, 0x20, 0x01, 0x0e, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa0, // APPLEIIGO REQUIRES AN
0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x10, 0x10, 0x0c, 0x05, 0x20, 0x09, 0x09, 0x20,
0x12, 0x0f, 0x0d, 0x20, 0x09, 0x0d, 0x01, 0x07, 0x05, 0x20, 0x14, 0x0f, 0x20, 0x12, 0x15, 0x0e,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa0, // APPLE II ROM IMAGE TO RUN
0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa0,
0xa0, 0xc6, 0xcf, 0xd2, 0xa0, 0xcd, 0xcf, 0xd2, 0xc5, 0xa0, 0xc9, 0xce, 0xc6, 0xcf, 0xd2, 0xcd,
0xc1, 0xd4, 0xc9, 0xcf, 0xce, 0xa0, 0xd0, 0xcc, 0xc5, 0xc1, 0xd3, 0xc5, 0xa0, 0xc3, 0xcc, 0xc9,
0xc3, 0xcb, 0xa0, 0xcf, 0xce, 0xa0, 0xa0, 0xa0, // FOR MORE INFORMATION PLEASE CLICK ON
0xa0, 0xd4, 0xc8, 0xc5, 0xa0, 0xc1, 0xd0, 0xd0, 0xcc, 0xc5, 0xc9, 0xc9, 0xc7, 0xcf, 0xa0, 0xcc,
0xcf, 0xc7, 0xcf, 0xa0, 0xc2, 0xc5, 0xcc, 0xcf, 0xd7, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, // THE APPLEIIGO LOGO BELOW
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
// Interrupt vectors
0x00, 0x00, 0x30, 0xfe, 0x30, 0xfe, 0x30, 0xfe
};
// Emulator
private boolean isRestart;
private int cpuSpeed;
private int clocksPerInterval;
private int refreshRate;
private long refreshInterval;
private long refreshDelayCumulative;
private long refreshDelayPerSecond;
private long refreshCycle;
// Keyboard
private int keyboardLatch;
// Memory offsets
private int[] memoryReadOffset = new int[0x101];
private int[] memoryWriteOffset = new int[0x101];
// Language card state
private boolean isLcReadEnable;
private boolean isLcWriteEnable;
private boolean isLcBank2;
// Apple IIe state
private boolean isRomInternal;
private boolean isRomC3External;
private boolean isAuxRead;
private boolean isAuxWrite;
private boolean isAuxZeroPage;
private boolean isVideoVBL;
// Thread stuff
private boolean isPaused = true;
private Thread thread;
// Step mode
private boolean isStepMode = false;
private boolean isNextStep = false;
private int stepCount;
AppleIIGo.View view;
/**
* Apple II class constructor
*/
public EmAppleII(AppleIIGo.View view) {
this.view = view;
// Allocate compute memory
mem = new byte[MEM_END];
// Initialize CPU
initMemoryMap();
setRandomSeed();
setCpuSpeed(1000);
reset();
// Setup default ROM
loadDefaultRom();
// Setup paddles
paddle = new Paddle(this);
// Setup expansion slots
slots = new Peripheral[8];
for (int slot = 1; slot < 8; slot++) {
setPeripheral(new Peripheral(), slot);
}
}
/**
* Set random seed (so programs start randomly)
*/
public void setRandomSeed() {
mem[0xcd] = (byte) System.currentTimeMillis();
}
/**
* Load default ROM
*/
public void loadDefaultRom() {
for (int offset = 0; offset < 0x1d0; offset++) {
mem[(MEM_ROM_MAIN_LOW + 0x3000 - 0x1d0) + offset] = (byte) defaultRom[offset];
}
}
/**
* Loads ROM
*/
private boolean isValidRom(byte[] rom, int offset) {
// Integer BASIC?
if ((rom[offset + 0x1000] & 0xff) == 0x20) {
System.err.println("ROM: Integer BASIC");
return true;
}
// Applesoft BASIC?
if ((rom[offset + 0x1000] & 0xff) == 0x4c) {
System.err.println("ROM: Applesoft BASIC");
return true;
}
return false;
}
/**
* @throws IllegalStateException
*/
public void loadRom(AppleIIGo.Dao dao, String resource) {
byte[] rom = new byte[0x8000];
int offset = 0;
dao.openInputStream(resource);
dao.read(rom, 0, 0x08000);
dao.closeInputStream();
if (isValidRom(rom, 0x0)) {
offset = 0x0;
} else if (isValidRom(rom, 0x1000)) {
offset = 0x1000;
} else if (isValidRom(rom, 0x2000)) {
offset = 0x2000;
} else {
throw new IllegalArgumentException(resource);
}
// Copy main ROM
System.arraycopy(rom, offset, mem, MEM_ROM_MAIN_LOW, 0x03000);
// Copy internal ROM
System.arraycopy(rom, offset + 0x3000, mem, MEM_ROM_INTERNAL + 0x00000, 0x01000);
System.arraycopy(rom, offset + 0x3800, mem, MEM_ROM_EXTERNAL + 0x00800, 0x00800);
}
/**
* Set peripheral
*/
public void setPeripheral(Peripheral peripheral, int slot) {
slots[slot] = peripheral;
int offset = MEM_ROM_EXTERNAL + (slot << 8);
for (int i = 0; i < 0x100; i++) {
mem[offset + i] = (byte) peripheral.memoryRead(i);
}
}
/**
* Set pause state
*/
public void setPaused(boolean value) {
if (isPaused == value) {
return;
}
isPaused = value;
if (isPaused) {
try {
thread.join(1000);
} catch (InterruptedException e) {
}
} else {
thread = new Thread(this);
thread.start();
}
}
/**
* Get pause state
*/
public boolean getPaused() {
return isPaused;
}
/**
* Keyboard push key function
*/
public void setKeyLatch(int key) {
key &= 0x7f;
keyboardLatch = (key | 0x80);
System.err.println("APPLE: " + keyboardLatch + ", " + ((char) key));
}
/**
* Restart
*/
public void restart() {
isRestart = true;
assertReset();
}
/**
* Reset
*/
public void reset() {
isRestart = false;
assertReset();
}
/**
* Set CPU speed
*/
public void setCpuSpeed(int value) {
if (value < 0) {
return;
}
cpuSpeed = value;
refreshRate = 20;
refreshInterval = (int) (1000.0 / refreshRate);
clocksPerInterval = (int) (cpuSpeed * refreshInterval);
}
/**
* Get debug mode
*/
public int getCpuSpeed() {
return cpuSpeed;
}
/**
* Get refresh rate
*/
public int getRefreshRate() {
return refreshRate;
}
/**
* Set debug mode
*/
public void setStepMode(boolean value) {
isNextStep = false;
isStepMode = value;
}
/**
* Get debug mode
*/
public boolean getStepMode() {
return isStepMode;
}
/**
* Step instruction in debug mode
*/
public void stepInstructions(int value) {
if (value <= 0) {
return;
}
stepCount = value;
isNextStep = true;
}
/**
* Zero pad
*/
private String zeroPad(String value, int length) {
length -= value.length();
while (length > 0) {
value = "0" + value;
length--;
}
return value;
}
/**
* Print a hex value
*/
private String formatHex(int value, int length) {
return zeroPad(Integer.toString(value, 16), length);
}
/**
* Print a decimal value
*/
private String formatDec(int value, int commaPos) {
String valueString = zeroPad(Integer.toString(value), commaPos + 1);
int length = valueString.length();
return valueString.substring(0, length - commaPos) +
"." + valueString.substring(length - commaPos, length);
}
/**
* Step instruction in debug mode
*/
public String getStatInfo() {
StringBuffer statInfo = new StringBuffer();
long cpuSpeedCurrent;
// Calculate effective CPU speed
if (isPaused || isStepMode) {
cpuSpeedCurrent = 0;
} else if (refreshDelayPerSecond > 1000) {
cpuSpeedCurrent = cpuSpeed * 1000 / refreshDelayPerSecond;
} else {
cpuSpeedCurrent = cpuSpeed;
}
// Return A, X, Y, S, P, PC
statInfo.append(" A=").append(formatHex(A, 2));
statInfo.append(" X=").append(formatHex(X, 2));
statInfo.append(" Y=").append(formatHex(Y, 2));
statInfo.append(" P=").append(formatHex(P, 2));
statInfo.append(" S=").append(formatHex(S, 2));
statInfo.append("\n");
statInfo.append(" PC=").append(formatHex(PC, 4)).append("\n");
statInfo.append(" [PC]=");
statInfo.append(" ").append(formatHex(memoryRead(PC + 0), 2));
statInfo.append(" ").append(formatHex(memoryRead(PC + 1), 2));
statInfo.append(" ").append(formatHex(memoryRead(PC + 2), 2));
statInfo.append(" ").append(formatHex(memoryRead(PC + 3), 2));
statInfo.append("\n");
statInfo.append(" MHZ=").append(formatDec((int) cpuSpeedCurrent, 3)).append(" [").append(refreshDelayPerSecond).append(" ms/s]\n");
return statInfo.toString();
}
/**
* Noise function
*
* We assume a "sort-of" floating bus
* (spanning memory locations 0000-3FFF)
*
* Correct way: We should look up the current video mode,
* and sample according to what is being shown.
*/
public int noise() {
return mem[clock & 0x3fff];
}
/**
* Read memory function
*
* @param address Address
*/
protected int memoryRead(int address) {
if ((address & 0xff00) == 0xc000) {
return ioRead(address);
}
return mem[address + memoryReadOffset[address >> 8]] & 0xff;
}
/**
* Write memory function
*
* @param adderss Address
* @param value value
*/
protected void memoryWrite(int address, int value) {
if ((address & 0xff00) == 0xc000) {
ioWrite(address, value);
} else {
mem[address + memoryWriteOffset[address >> 8]] = (byte) value;
graphicsDirty[address >> 7] = true;
}
}
/**
* Update memory maps
*/
private void updateMainMemoryMap() {
int ramReadOffset, textReadOffset, hiresReadOffset;
int ramWriteOffset, textWriteOffset, hiresWriteOffset;
boolean isPage2 = ((graphicsMode & GR_PAGE2) != 0);
boolean is80STORE = ((graphicsMode & GR_80STORE) != 0);
boolean isHires = ((graphicsMode & GR_HIRES) != 0);
textReadOffset = hiresReadOffset = ramReadOffset = isAuxRead ?
(MEM_AUX_RAM1 - MEM_PHYS_RAM1) : (MEM_MAIN_RAM1 - MEM_PHYS_RAM1);
textWriteOffset = hiresWriteOffset = ramWriteOffset = isAuxWrite ?
(MEM_AUX_RAM1 - MEM_PHYS_RAM1) : (MEM_MAIN_RAM1 - MEM_PHYS_RAM1);
if (is80STORE) {
textWriteOffset = textReadOffset = isPage2 ? (MEM_AUX_TEXT - MEM_PHYS_TEXT) :
(MEM_MAIN_TEXT - MEM_PHYS_TEXT);
if (isHires) {
hiresWriteOffset = hiresReadOffset = textReadOffset;
}
}
memoryReadOffset[0x02] = memoryReadOffset[0x03] = ramReadOffset;
memoryWriteOffset[0x02] = memoryWriteOffset[0x03] = ramWriteOffset;
memoryReadOffset[0x04] = memoryReadOffset[0x05] = memoryReadOffset[0x06] = memoryReadOffset[0x07] = textReadOffset;
memoryWriteOffset[0x04] = memoryWriteOffset[0x05] = memoryWriteOffset[0x06] = memoryWriteOffset[0x07] = textWriteOffset;
for (int offset = 0x08; offset < 0x20; offset++) {
memoryReadOffset[offset] = ramReadOffset;
memoryWriteOffset[offset] = ramWriteOffset;
}
for (int offset = 0x20; offset < 0x40; offset++) {
memoryReadOffset[offset] = hiresReadOffset;
memoryWriteOffset[offset] = hiresWriteOffset;
}
for (int offset = 0x40; offset < 0xc0; offset++) {
memoryReadOffset[offset] = ramReadOffset;
memoryWriteOffset[offset] = ramWriteOffset;
}
}
private void updateIOMemoryMap() {
int romOffset;
if (isRomInternal) {
romOffset = (MEM_ROM_INTERNAL - MEM_PHYS_IO);
} else {
romOffset = (MEM_ROM_EXTERNAL - MEM_PHYS_IO);
}
for (int offset = 0xc1; offset < 0xd0; offset++) {
memoryReadOffset[offset] = romOffset;
}
if (isRomC3External) {
memoryReadOffset[0xc3] = (MEM_ROM_EXTERNAL - MEM_PHYS_IO);
} else {
memoryReadOffset[0xc3] = (MEM_ROM_INTERNAL - MEM_PHYS_IO);
}
}
private void initIOMemoryMap() {
for (int offset = 0xc1; offset < 0xd0; offset++) {
memoryWriteOffset[offset] = (MEM_WASTE - MEM_PHYS_IO);
}
}
private void updateLCMemoryMap() {
int lcReadOffset, lcReadOffsetHigh;
int lcWriteOffset, lcWriteOffsetHigh;
if (!isLcReadEnable) {
lcReadOffset = (MEM_ROM_MAIN_LOW - MEM_PHYS_ROM_LOW);
lcReadOffsetHigh = (MEM_ROM_MAIN_LOW - MEM_PHYS_ROM_LOW);
} else if (isAuxZeroPage) {
lcReadOffset = isLcBank2 ? (MEM_AUX_LC2 - MEM_PHYS_ROM_LOW) : (MEM_AUX_LC1 - MEM_PHYS_ROM_LOW);
lcReadOffsetHigh = (MEM_AUX_LC_HIGH - MEM_PHYS_ROM_HIGH);
} else {
lcReadOffset = isLcBank2 ? (MEM_MAIN_LC2 - MEM_PHYS_ROM_LOW) : (MEM_MAIN_LC1 - MEM_PHYS_ROM_LOW);
lcReadOffsetHigh = (MEM_MAIN_LC_HIGH - MEM_PHYS_ROM_HIGH);
}
if (!isLcWriteEnable) {
lcWriteOffset = (MEM_WASTE - MEM_PHYS_ROM_LOW);
lcWriteOffsetHigh = (MEM_WASTE - MEM_PHYS_ROM_HIGH);
} else if (isAuxZeroPage) {
lcWriteOffset = isLcBank2 ? (MEM_AUX_LC2 - MEM_PHYS_ROM_LOW) : (MEM_AUX_LC1 - MEM_PHYS_ROM_LOW);
lcWriteOffsetHigh = (MEM_AUX_LC_HIGH - MEM_PHYS_ROM_HIGH);
} else {
lcWriteOffset = isLcBank2 ? (MEM_MAIN_LC2 - MEM_PHYS_ROM_LOW) : (MEM_MAIN_LC1 - MEM_PHYS_ROM_LOW);
lcWriteOffsetHigh = (MEM_MAIN_LC_HIGH - MEM_PHYS_ROM_HIGH);
}
for (int offset = 0xd0; offset < 0xe0; offset++) {
memoryReadOffset[offset] = lcReadOffset;
memoryWriteOffset[offset] = lcWriteOffset;
}
for (int offset = 0xe0; offset < 0x100; offset++) {
memoryReadOffset[offset] = lcReadOffsetHigh;
memoryWriteOffset[offset] = lcWriteOffsetHigh;
}
}
void initMemoryMap() {
initIOMemoryMap();
updateMainMemoryMap();
updateIOMemoryMap();
updateLCMemoryMap();
}
/**
* Apple I/O reads
*
* @param address Address
*/
private int ioRead(int address) {
address &= 0xff;
if (address >= 0x90) {
return slots[(address & 0x70) >> 4].ioRead(address);
}
switch (address) {
case 0x00: case 0x01: case 0x02: case 0x03:
case 0x04: case 0x05: case 0x06: case 0x07:
case 0x08: case 0x09: case 0x0a: case 0x0b:
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
// Keyboard data
//System.err.println("io: " + address + ": " + keyboardLatch);
return keyboardLatch;
case 0x10:
// Keyboard strobe
keyboardLatch &= 0x7f;
return keyboardLatch;
case 0x11:
// Reading from LC Bank 2?
return (keyboardLatch & 0x7f) | (isLcBank2 ? 0x80 : 0x00);
case 0x12:
// Reading from LC?
return (keyboardLatch & 0x7f) | (isLcReadEnable ? 0x80 : 0x00);
case 0x13:
// Reading aux memory?
return (keyboardLatch & 0x7f) | (isAuxRead ? 0x80 : 0x00);
case 0x14:
// Writing aux memory?
return (keyboardLatch & 0x7f) | (isAuxWrite ? 0x80 : 0x00);
case 0x15:
// Using internal slot ROM?
return (keyboardLatch & 0x7f) | (isRomInternal ? 0x80 : 0x00);
case 0x16:
// Using slot zero page+stack+LC?
return (keyboardLatch & 0x7f) | (isAuxZeroPage ? 0x80 : 0x00);
case 0x17:
// Using external slot 3 ROM?
return (keyboardLatch & 0x7f) | (isRomC3External ? 0x80 : 0x00);
case 0x18:
// 80STORE?
return (keyboardLatch & 0x7f) | (((graphicsMode & GR_80STORE) != 0) ? 0x80 : 0x00);
case 0x19:
// VBL Signal low?
isVideoVBL = !isVideoVBL;
return (keyboardLatch & 0x7f) | (isVideoVBL ? 0x80 : 0x00);
case 0x1a:
// Using text mode?
return (keyboardLatch & 0x7f) | (((graphicsMode & GR_TEXT) != 0) ? 0x80 : 0x00);
case 0x1b:
// Using mixed mode?
return (keyboardLatch & 0x7f) | (((graphicsMode & GR_MIXMODE) != 0) ? 0x80 : 0x00);
case 0x1c:
// Using page 2?
return (keyboardLatch & 0x7f) | (((graphicsMode & GR_PAGE2) != 0) ? 0x80 : 0x00);
case 0x1d:
// Using hires?
return (keyboardLatch & 0x7f) | (((graphicsMode & GR_HIRES) != 0) ? 0x80 : 0x00);
case 0x1e:
// Using alt charset?
return (keyboardLatch & 0x7f) | (((graphicsMode & GR_ALTCHAR) != 0) ? 0x80 : 0x00);
case 0x1f:
// Using 80-column display mode?
return (keyboardLatch & 0x7f) | (((graphicsMode & GR_80CHAR) != 0) ? 0x80 : 0x00);
case 0x20: case 0x21: case 0x22: case 0x23:
case 0x24: case 0x25: case 0x26: case 0x27:
case 0x28: case 0x29: case 0x2a: case 0x2b:
case 0x2c: case 0x2d: case 0x2e: case 0x2f:
// Cassette output
break;
case 0x30: case 0x31: case 0x32: case 0x33:
case 0x34: case 0x35: case 0x36: case 0x37:
case 0x38: case 0x39: case 0x3a: case 0x3b:
case 0x3c: case 0x3d: case 0x3e: case 0x3f:
// Speaker
speakerFlips[speakerFlipsPointer] = clock;
speakerFlipsPointer = (speakerFlipsPointer + 1) & SPEAKER_FLIPS_MASK;
break;
case 0x40: case 0x41: case 0x42: case 0x43:
case 0x44: case 0x45: case 0x46: case 0x47:
case 0x48: case 0x49: case 0x4a: case 0x4b:
case 0x4c: case 0x4d: case 0x4e: case 0x4f:
// Game strobe
break;
case 0x50:
graphicsMode &= ~GR_TEXT;
break;
case 0x51:
graphicsMode |= GR_TEXT;
break;
case 0x52:
graphicsMode &= ~GR_MIXMODE;
break;
case 0x53:
graphicsMode |= GR_MIXMODE;
break;
case 0x54:
graphicsMode &= ~GR_PAGE2;
updateMainMemoryMap();
break;
case 0x55:
graphicsMode |= GR_PAGE2;
updateMainMemoryMap();
break;
case 0x56:
graphicsMode &= ~GR_HIRES;
updateMainMemoryMap();
break;
case 0x57:
graphicsMode |= GR_HIRES;
updateMainMemoryMap();
break;
case 0x58: case 0x59: case 0x5a: case 0x5b:
case 0x5c: case 0x5d:
// Annunciators
break;
case 0x5e:
graphicsMode |= GR_DHIRES;
break;
case 0x5f:
graphicsMode &= ~GR_DHIRES;
break;
case 0x60:
case 0x68:
// (Also cassette input)
return paddle.getButtonRegister(3);
case 0x61:
case 0x69:
return paddle.getButtonRegister(0);
case 0x62:
case 0x6a:
return paddle.getButtonRegister(1);
case 0x63:
case 0x6b:
return paddle.getButtonRegister(2);
case 0x64:
case 0x6c:
return paddle.getPaddleRegister(0);
case 0x65:
case 0x6d:
return paddle.getPaddleRegister(1);
case 0x66:
case 0x6e:
return paddle.getPaddleRegister(2);
case 0x67:
case 0x6f:
return paddle.getPaddleRegister(3);
case 0x70: case 0x71: case 0x72: case 0x73:
case 0x74: case 0x75: case 0x76: case 0x77:
case 0x78: case 0x79: case 0x7a: case 0x7b:
case 0x7c: case 0x7d: case 0x7e: case 0x7f:
paddle.triggerRegister();
break;
case 0x80:
case 0x84:
isLcBank2 = true;
isLcReadEnable = true;
isLcWriteEnable = false;
updateLCMemoryMap();
break;
case 0x81:
case 0x85:
isLcBank2 = true;
isLcReadEnable = false;
isLcWriteEnable = true;
updateLCMemoryMap();
break;
case 0x82:
case 0x86:
isLcBank2 = true;
isLcReadEnable = false;
isLcWriteEnable = false;
updateLCMemoryMap();
break;
case 0x83:
case 0x87:
isLcBank2 = true;
isLcReadEnable = true;
isLcWriteEnable = true;
updateLCMemoryMap();
break;
case 0x88:
case 0x8c:
isLcBank2 = false;
isLcReadEnable = true;
isLcWriteEnable = false;
updateLCMemoryMap();
break;
case 0x89:
case 0x8d:
isLcBank2 = false;
isLcReadEnable = false;
isLcWriteEnable = true;
updateLCMemoryMap();
break;
case 0x8a:
case 0x8e:
isLcBank2 = false;
isLcReadEnable = false;
isLcWriteEnable = false;
updateLCMemoryMap();
break;
case 0x8b:
case 0x8f:
isLcBank2 = false;
isLcReadEnable = true;
isLcWriteEnable = true;
updateLCMemoryMap();
break;
case 0x90: case 0x91: case 0x92: case 0x93:
case 0x94: case 0x95: case 0x96: case 0x97:
case 0x98: case 0x99: case 0x9a: case 0x9b:
case 0x9c: case 0x9d: case 0x9e: case 0x9f:
return slots[1].ioRead(address);
case 0xa0: case 0xa1: case 0xa2: case 0xa3:
case 0xa4: case 0xa5: case 0xa6: case 0xa7:
case 0xa8: case 0xa9: case 0xaa: case 0xab:
case 0xac: case 0xad: case 0xae: case 0xaf:
return slots[2].ioRead(address);
case 0xb0: case 0xb1: case 0xb2: case 0xb3:
case 0xb4: case 0xb5: case 0xb6: case 0xb7:
case 0xb8: case 0xb9: case 0xba: case 0xbb:
case 0xbc: case 0xbd: case 0xbe: case 0xbf:
return slots[3].ioRead(address);
case 0xc0: case 0xc1: case 0xc2: case 0xc3:
case 0xc4: case 0xc5: case 0xc6: case 0xc7:
case 0xc8: case 0xc9: case 0xca: case 0xcb:
case 0xcc: case 0xcd: case 0xce: case 0xcf:
return slots[4].ioRead(address);
case 0xd0: case 0xd1: case 0xd2: case 0xd3:
case 0xd4: case 0xd5: case 0xd6: case 0xd7:
case 0xd8: case 0xd9: case 0xda: case 0xdb:
case 0xdc: case 0xdd: case 0xde: case 0xdf:
return slots[5].ioRead(address);
case 0xe0: case 0xe1: case 0xe2: case 0xe3:
case 0xe4: case 0xe5: case 0xe6: case 0xe7:
case 0xe8: case 0xe9: case 0xea: case 0xeb:
case 0xec: case 0xed: case 0xee: case 0xef:
return slots[6].ioRead(address);
case 0xf0: case 0xf1: case 0xf2: case 0xf3:
case 0xf4: case 0xf5: case 0xf6: case 0xf7:
case 0xf8: case 0xf9: case 0xfa: case 0xfb:
case 0xfc: case 0xfd: case 0xfe: case 0xff:
return slots[7].ioRead(address);
}
return noise();
}
/**
* Apple I/O writes
*
* @param address Address
* @param value Value
*/
private void ioWrite(int address, int value) {
address &= 0xff;
if (address >= 0x90) {
slots[(address & 0x70) >> 4].ioWrite(address, value);
return;
}
switch (address) {
case 0x00:
// 80STORE off
graphicsMode &= ~GR_80STORE;
updateMainMemoryMap();
return;
case 0x01:
// 80STORE on
graphicsMode |= GR_80STORE;
updateMainMemoryMap();
return;
case 0x02:
// Read aux mem off
isAuxRead = false;
updateMainMemoryMap();
return;
case 0x03:
// Read aux mem on
isAuxRead = true;
updateMainMemoryMap();
return;
case 0x04:
// Write aux mem off
isAuxWrite = false;
updateMainMemoryMap();
return;
case 0x05:
// Write aux mem on
isAuxWrite = true;
updateMainMemoryMap();
return;
case 0x06:
// Do not use internal ROM
isRomInternal = false;
updateIOMemoryMap();
return;
case 0x07:
// Use internal ROM
isRomInternal = true;
updateIOMemoryMap();
return;
case 0x08:
if (isAuxZeroPage) {
// Physically get main zero page
System.arraycopy(mem, MEM_PHYS_ZP, mem, MEM_AUX_ZP, 0x200);
System.arraycopy(mem, MEM_MAIN_ZP, mem, MEM_PHYS_ZP, 0x200);
}
// Aux zero page off
isAuxZeroPage = false;
updateLCMemoryMap();
return;
case 0x09:
if (!isAuxZeroPage) {
// Physically get aux zero page
System.arraycopy(mem, MEM_PHYS_ZP, mem, MEM_MAIN_ZP, 0x200);
System.arraycopy(mem, MEM_AUX_ZP, mem, MEM_PHYS_ZP, 0x200);
}
// Aux zero page on
isAuxZeroPage = true;
updateLCMemoryMap();
return;
case 0x0a:
// Do not use external slot 3 ROM
isRomC3External = false;
updateIOMemoryMap();
return;
case 0x0b:
// Use external slot 3 ROM
isRomC3External = true;
updateIOMemoryMap();
return;
case 0x0c:
// 80c off
graphicsMode &= ~GR_80CHAR;
return;
case 0x0d:
// 80c on
graphicsMode |= GR_80CHAR;
return;
case 0x0e:
// Alt charset off
graphicsMode &= ~GR_ALTCHAR;
return;
case 0x0f:
// Alt charset on
graphicsMode |= GR_ALTCHAR;
return;
case 0x10: case 0x11: case 0x12: case 0x13:
case 0x14: case 0x15: case 0x16: case 0x17:
case 0x18: case 0x19: case 0x1a: case 0x1b:
case 0x1c: case 0x1d: case 0x1e: case 0x1f:
// Keyboard strobe
keyboardLatch &= 0x7f;
//System.err.println("io: " + address + ": " + keyboardLatch);
return;
case 0x20: case 0x21: case 0x22: case 0x23:
case 0x24: case 0x25: case 0x26: case 0x27:
case 0x28: case 0x29: case 0x2a: case 0x2b:
case 0x2c: case 0x2d: case 0x2e: case 0x2f:
// Cassette output
return;
case 0x30: case 0x31: case 0x32: case 0x33:
case 0x34: case 0x35: case 0x36: case 0x37:
case 0x38: case 0x39: case 0x3a: case 0x3b:
case 0x3c: case 0x3d: case 0x3e: case 0x3f:
// Speaker
speakerFlips[speakerFlipsPointer] = clock;
speakerFlipsPointer = (speakerFlipsPointer + 1) & SPEAKER_FLIPS_MASK;
return;
case 0x40: case 0x41: case 0x42: case 0x43:
case 0x44: case 0x45: case 0x46: case 0x47:
case 0x48: case 0x49: case 0x4a: case 0x4b:
case 0x4c: case 0x4d: case 0x4e: case 0x4f:
// Game strobe
return;
case 0x50:
graphicsMode &= ~GR_TEXT;
return;
case 0x51:
graphicsMode |= GR_TEXT;
return;
case 0x52:
graphicsMode &= ~GR_MIXMODE;
return;
case 0x53:
graphicsMode |= GR_MIXMODE;
return;
case 0x54:
graphicsMode &= ~GR_PAGE2;
updateMainMemoryMap();
return;
case 0x55:
graphicsMode |= GR_PAGE2;
updateMainMemoryMap();
return;
case 0x56:
graphicsMode &= ~GR_HIRES;
updateMainMemoryMap();
return;
case 0x57:
graphicsMode |= GR_HIRES;
updateMainMemoryMap();
return;
case 0x58: case 0x59: case 0x5a: case 0x5b:
case 0x5c: case 0x5d:
// Annunciators
return;
case 0x5e:
graphicsMode |= GR_DHIRES;
break;
case 0x5f:
graphicsMode &= ~GR_DHIRES;
break;
case 0x60: case 0x61: case 0x62: case 0x63:
case 0x64: case 0x65: case 0x66: case 0x67:
case 0x68: case 0x69: case 0x6a: case 0x6b:
case 0x6c: case 0x6d: case 0x6e: case 0x6f:
// Cassette input/paddle data
return;
case 0x70: case 0x71: case 0x72: case 0x73:
case 0x74: case 0x75: case 0x76: case 0x77:
case 0x78: case 0x79: case 0x7a: case 0x7b:
case 0x7c: case 0x7d: case 0x7e: case 0x7f:
paddle.triggerRegister();
return;
case 0x80:
case 0x84:
isLcBank2 = true;
isLcReadEnable = true;
isLcWriteEnable = false;
updateLCMemoryMap();
break;
case 0x81:
case 0x85:
isLcBank2 = true;
isLcReadEnable = false;
isLcWriteEnable = true;
updateLCMemoryMap();
break;
case 0x82:
case 0x86:
isLcBank2 = true;
isLcReadEnable = false;
isLcWriteEnable = false;
updateLCMemoryMap();
break;
case 0x83:
case 0x87:
isLcBank2 = true;
isLcReadEnable = true;
isLcWriteEnable = true;
updateLCMemoryMap();
break;
case 0x88:
case 0x8c:
isLcBank2 = false;
isLcReadEnable = true;
isLcWriteEnable = false;
updateLCMemoryMap();
break;
case 0x89:
case 0x8d:
isLcBank2 = false;
isLcReadEnable = false;
isLcWriteEnable = true;
updateLCMemoryMap();
break;
case 0x8a:
case 0x8e:
isLcBank2 = false;
isLcReadEnable = false;
isLcWriteEnable = false;
updateLCMemoryMap();
break;
case 0x8b:
case 0x8f:
isLcBank2 = false;
isLcReadEnable = true;
isLcWriteEnable = true;
updateLCMemoryMap();
break;
}
}
/**
* Emulator thread
*/
public void run() {
try {
while (!isPaused) {
long refreshStart = System.currentTimeMillis();
long refreshDelay;
checkInterrupts();
if (isStepMode) {
if (isNextStep) {
isNextStep = false;
executeInstructions(stepCount);
}
} else {
int clocksNeeded = clocksPerInterval;
while (clocksNeeded > 0) {
clocksNeeded -= executeInstructions(1 + (clocksNeeded >> 3));
}
}
refreshDelay = System.currentTimeMillis() - refreshStart;
refreshDelayCumulative += refreshDelay;
refreshCycle++;
if (refreshCycle >= refreshRate) {
refreshDelayPerSecond = refreshDelayCumulative;
refreshDelayCumulative = refreshCycle = 0;
}
if (refreshDelay < refreshInterval) {
Thread.sleep(refreshInterval - refreshDelay);
}
}
} catch (Throwable e) {
view.debug(e);
view.repaint();
}
}
/**
* Reset assertion code
*/
protected void onReset() {
// Reset IOU + MMU
ioWrite(0x00, 0);
ioWrite(0x02, 0);
ioWrite(0x04, 0);
ioWrite(0x06, 0);
ioWrite(0x08, 0);
ioWrite(0x0a, 0);
ioWrite(0x0c, 0);
ioWrite(0x0e, 0);
ioWrite(0x50, 0);
ioWrite(0x52, 0);
ioWrite(0x54, 0);
ioWrite(0x56, 0);
ioWrite(0x5F, 0);
ioWrite(0x82, 0);
if (isRestart) {
// Clear RAM
for (int i = 0; i < MEM_ROM_MAIN_LOW; i++) {
mem[i] = 0;
}
setRandomSeed();
}
// Reset devices
for (int slot = 1; slot < 8; slot++) {
slots[slot].reset();
}
}
}