CPU emulator

This commit is contained in:
Denis Molony 2017-01-09 10:36:10 +11:00
parent abdaa41060
commit e74e2f4a11
5 changed files with 659 additions and 0 deletions

View File

@ -3,6 +3,8 @@ package com.bytezone.diskbrowser.applefile;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import com.bytezone.diskbrowser.utilities.HexFormatter;
public class DoubleHiResImage extends HiResImage
{
private static final int BLACK = 0x000000;
@ -26,6 +28,8 @@ public class DoubleHiResImage extends HiResImage
GRAY, PINK, MEDIUM_BLUE, LIGHT_BLUE, AQUA, WHITE };
private final byte[] auxBuffer;
private DoubleScrunch doubleScrunch;
byte[] packedBuffer;
public DoubleHiResImage (String name, byte[] buffer, byte[] auxBuffer)
{
@ -36,6 +40,44 @@ public class DoubleHiResImage extends HiResImage
createImage ();
}
public DoubleHiResImage (String name, byte[] buffer)
{
super (name, buffer);
assert name.endsWith (".PAC");
packedBuffer = buffer;
doubleScrunch = new DoubleScrunch ();
doubleScrunch.unscrunch (buffer);
auxBuffer = doubleScrunch.auxBuffer;
this.buffer = doubleScrunch.primaryBuffer;
createImage ();
// testes
if (false)
{
byte b = -1;
System.out.printf ("%02X %d%n", b, b);
if (b < 0)
System.out.println ("negative");
b = -122;
for (int i = 0; i < 10; i++)
{
--b;
System.out.printf ("%4d %02X%n", b, b);
}
System.out.println ();
b = (byte) 250;
for (int i = 0; i < 10; i++)
{
++b;
System.out.printf ("%4d %02X%n", b, b);
}
}
}
@Override
protected void createMonochromeImage ()
{
@ -95,4 +137,22 @@ public class DoubleHiResImage extends HiResImage
}
}
}
@Override
public String getHexDump ()
{
StringBuilder text = new StringBuilder ();
if (packedBuffer != null)
{
text.append ("Packed buffer:\n\n");
text.append (HexFormatter.format (packedBuffer));
}
text.append ("\n\nAuxilliary buffer:\n\n");
text.append (HexFormatter.format (auxBuffer));
text.append ("\n\nPrimary buffer:\n\n");
text.append (HexFormatter.format (buffer));
return text.toString ();
}
}

View File

@ -0,0 +1,237 @@
package com.bytezone.diskbrowser.applefile;
import com.bytezone.diskbrowser.utilities.CPU;
public class DoubleScrunch extends CPU
{
private byte mem_71D0;
private byte mem_71D2;
private byte mem_71D3;
private byte mem_71D4;
private int src; // base address of packed buffer
private int dst; // base address of main/aux memory
private byte zp_00;
private byte zp_01;
private byte zp_26;
private byte zp_27;
private byte zp_E6;
final byte[] auxBuffer = new byte[0x2000];
final byte[] primaryBuffer = new byte[0x2000];
private byte[] packedBuffer;
private final boolean debug = false;
public DoubleScrunch ()
{
setDebug (debug);
}
void unscrunch (byte[] buffer)
{
if (false)
{
ldx ((byte) 0x81);
calculateScreenAddress ();
return;
}
packedBuffer = buffer;
src = 0x4000; // packed picture data
dst = 0x2000; // main/aux picture data
zp_00 = 0x00;
zp_01 = 0x40;
zp_E6 = 0x20; // hi-res page 1
lda (zp_E6);
ora ((byte) 0x04);
zp_27 = sta ();
ldx ((byte) 0x01);
mem_71D0 = stx ();
ldy ((byte) 0x00);
mem_71D2 = sty ();
zp_26 = sty ();
while (true)
{
lda (packedBuffer, indirectY (src, zp_00, zp_01)); // LDA ($00),Y)
php ();
zp_00 = inc (zp_00);
if (zero)
zp_01 = inc (zp_01);
and ((byte) 0x7F);
mem_71D4 = sta ();
plp ();
if (negative)
{
boolean finished = blockB ();
if (finished)
break;
}
else
{
boolean finished = blockA ();
if (finished)
break;
}
zp_00 = inc (zp_00);
if (zero)
zp_01 = inc (zp_01);
}
}
// $7144
// repeat a single byte X times
private boolean blockA ()
{
lda (packedBuffer, indirectY (src, zp_00, zp_01)); // LDA ($00),Y)
mem_71D3 = sta ();
do
{
lda (mem_71D3);
storeByte ();
function_71B1 ();
if (carry)
return true; // completely finished
calculateScreenAddress ();
ldy (mem_71D4);
} while (!zero);
return false; // not finished
}
// $717D
// copy X single bytes
private boolean blockB ()
{
while (true)
{
ldy ((byte) 0x00);
lda (packedBuffer, indirectY (src, zp_00, zp_01)); // LDA ($00),Y)
storeByte ();
function_71B1 ();
if (carry)
return true; // completely finished
calculateScreenAddress ();
ldy (mem_71D4);
if (zero)
return false; // not finished
zp_00 = inc (zp_00);
if (zero)
zp_01 = inc (zp_01);
assert !zero;
}
}
private void storeByte ()
{
mem_71D4 = dec (mem_71D4); // decrement counter
ldy (mem_71D2); // load byte to store
php ();
sei ();
pha ();
tya ();
lsr (); // divide by 2 ?
tay ();
pla ();
// switch page
byte[] target = carry ? primaryBuffer : auxBuffer;
sta (target, indirectY (dst, zp_26, zp_27));
plp ();
}
// $70E8
private void calculateScreenAddress ()
{
txa ();
and ((byte) 0xC0); // clear bits 5-0
zp_26 = sta ();
lsr ();
lsr ();
ora (zp_26);
zp_26 = sta ();
txa ();
zp_27 = sta ();
asl ();
asl ();
asl ();
zp_27 = rol (zp_27);
asl ();
zp_27 = rol (zp_27);
asl ();
zp_26 = ror (zp_26);
lda (zp_27);
and ((byte) 0x1F); // clear bits 7-5
ora (zp_E6);
zp_27 = sta ();
}
// $71B1
private void function_71B1 ()
{
inx ();
inx ();
cpx ((byte) 0xC0);
if (!carry) // BCC $71CE (if X < 0xC0)
{
clc ();
return;
}
mem_71D0 = dec (mem_71D0);
if (!negative) // BPL $71C9
{
ldx ((byte) 0);
mem_71D0 = stx ();
clc ();
return;
}
mem_71D2 = inc (mem_71D2);
ldy (mem_71D2);
cpy ((byte) 0x50);
if (carry) // BCS $71CF
return; // program finished
ldx ((byte) 0x01);
// bit ((byte) 0xA2); // ??
mem_71D0 = stx ();
clc ();
}
@Override
protected String debugString ()
{
return String.format ("0: %02X %02X 26: %02X %02X %02X %02X %02X %02X", zp_00,
zp_01, zp_26, zp_27, mem_71D0, mem_71D2, mem_71D3, mem_71D4);
}
}

View File

@ -207,6 +207,8 @@ abstract class AbstractCatalogEntry implements AppleFileSource
appleFile = new MerlinSource (name, exactBuffer);
else if (HiResImage.isGif (exactBuffer))
appleFile = new OriginalHiResImage (name, exactBuffer, loadAddress);
else if (name.endsWith (".PAC"))
appleFile = new DoubleHiResImage (name, exactBuffer);
else if (link != null)
{
byte[] auxBuffer = link.disk.readSectors (link.dataSectors);

View File

@ -275,6 +275,8 @@ class FileEntry extends CatalogEntry implements ProdosConstants
else
file = new DoubleHiResImage (name, exactBuffer, link.getBuffer ());
}
else if (name.endsWith (".PAC"))
file = new DoubleHiResImage (name, exactBuffer);
else if ((endOfFile == 0x1FF8 || endOfFile == 0x1FFF || endOfFile == 0x2000
|| endOfFile == 0x4000)
&& (auxType == 0x1FFF || auxType == 0x2000 || auxType == 0x4000))

View File

@ -0,0 +1,358 @@
package com.bytezone.diskbrowser.utilities;
public abstract class CPU
{
// registers
private byte xReg;
private byte yReg;
private byte aReg;
// status register
protected boolean carry;
protected boolean zero;
protected boolean interrupt;
protected boolean decimal;
protected boolean breakFlag;
protected boolean overflow; // Clancy
protected boolean negative;
// stack
private final byte[] stack = new byte[0x100];
private int sp = stack.length;
private boolean debug = false;
protected void setDebug (boolean value)
{
debug = value;
}
protected void and (byte mask) // AND
{
aReg &= mask;
zero = aReg == 0;
negative = (aReg & 0x80) != 0;
debug ("AND");
}
protected void asl () // ASL
{
carry = (aReg & 0x80) != 0; // move bit 7 into the carry flag
aReg = (byte) (aReg << 1); // shift left
zero = aReg == 0;
negative = (aReg & 0x80) != 0;
debug ("ASL");
}
// unfinished
protected void bit (byte value) // BIT
{
byte b = (byte) (aReg & value);
zero = b == 0;
overflow = (value & 0x40) != 0;
negative = (value & 0x80) != 0;
debug ("BIT");
}
protected void clc () // CLC
{
carry = false;
debug ("CLC");
}
protected void cli () // CLI
{
interrupt = false;
debug ("CLI");
}
protected void clv () // CLV
{
overflow = false;
debug ("CLV");
}
protected void cmp (byte value) // CMP
{
int tmp = (aReg & 0xFF) - (value & 0xFF);
zero = tmp == 0;
negative = (tmp & 0x80) != 0;
carry = (aReg & 0xFF) >= (value & 0xFF);
debug ("CMP");
if (debug)
System.out.printf (" cmp a: %02X v: %02X%n", aReg, value);
}
protected void cpx (byte value) // CPX
{
int tmp = (xReg & 0xFF) - (value & 0xFF);
zero = tmp == 0;
negative = (tmp & 0x80) != 0;
carry = (xReg & 0xFF) >= (value & 0xFF);
debug ("CPX");
if (debug)
System.out.printf (" cpx x: %02X v: %02X%n", xReg, value);
}
protected void cpy (byte value) // CPY
{
int tmp = (yReg & 0xFF) - (value & 0xFF);
zero = tmp == 0;
negative = (tmp & 0x80) != 0;
carry = (yReg & 0xFF) >= (value & 0xFF);
debug ("CPY");
if (debug)
System.out.printf (" cpy y: %02X v: %02X%n", yReg, value);
}
protected byte dec (byte value) // DEC
{
value = (byte) ((value & 0xFF) - 1);
zero = value == 0;
negative = (value & 0x80) != 0;
debug ("DEC");
return value;
}
protected byte inc (byte value) // INC
{
value = (byte) ((value & 0xFF) + 1);
zero = value == 0;
negative = (value & 0x80) != 0;
debug ("INC");
return value;
}
protected void inx () // INX
{
xReg = (byte) ((xReg & 0xFF) + 1);
xReg &= 0xFF;
zero = xReg == 0;
negative = (xReg & 0x80) != 0;
debug ("INX");
}
protected void lda (byte value) // LDA
{
aReg = value;
zero = aReg == 0;
negative = (aReg & 0x80) != 0;
debug ("LDA");
}
protected void lda (byte[] buffer, int offset) // LDA
{
aReg = buffer[offset];
zero = aReg == 0;
negative = (aReg & 0x80) != 0;
debug ("LDA");
}
protected void ldx (byte value) // LDX
{
xReg = value;
zero = xReg == 0;
negative = (xReg & 0x80) != 0;
debug ("LDX");
}
protected void ldy (byte value) // LDY
{
yReg = value;
zero = yReg == 0;
negative = (yReg & 0x80) != 0;
debug ("LDY");
}
protected void lsr () // LSR
{
negative = false;
carry = (aReg & 0x01) != 0;
aReg = (byte) ((aReg & 0xFF) >>> 1);
zero = aReg == 0;
debug ("LSR");
}
protected void ora (byte mask) // ORA
{
aReg |= mask;
zero = aReg == 0;
negative = (aReg & 0x80) != 0;
debug ("ORA");
}
protected void php () // PHP
{
byte flags = 0;
if (negative)
flags |= 0x80;
if (overflow)
flags |= 0x40;
if (breakFlag)
flags |= 0x10;
if (decimal)
flags |= 0x08;
if (interrupt)
flags |= 0x04;
if (zero)
flags |= 0x02;
if (carry)
flags |= 0x01;
stack[--sp] = flags;
debug ("PHP");
}
protected void plp () // PLP
{
byte flags = stack[sp++];
negative = (flags & 0x80) != 0;
overflow = (flags & 0x40) != 0;
breakFlag = (flags & 0x10) != 0;
decimal = (flags & 0x08) != 0;
interrupt = (flags & 0x04) != 0;
zero = (flags & 0x02) != 0;
carry = (flags & 0x01) != 0;
debug ("PLP");
}
protected void pha () // PHA
{
stack[--sp] = aReg;
debug ("PHA");
}
protected void pla () // PLA
{
aReg = stack[sp++];
zero = aReg == 0;
negative = (aReg & 0x80) != 0;
debug ("PLA");
}
protected void rol () // ROL
{
boolean tempCarry = carry;
carry = (aReg & 0x80) != 0; // move bit 7 into the carry flag
aReg = (byte) (aReg << 1); // shift left
if (tempCarry)
aReg |= 0x01; // move old carry into bit 0
zero = aReg == 0;
negative = (aReg & 0x80) != 0;
debug ("ROL");
}
protected byte rol (byte value) // ROL
{
boolean tempCarry = carry;
carry = (value & 0x80) != 0; // move bit 7 into the carry flag
value = (byte) (value << 1);
if (tempCarry)
value |= 0x01; // move old carry into bit 0
zero = value == 0;
negative = (value & 0x80) != 0;
debug ("ROL");
return value;
}
protected byte ror (byte value) // ROR
{
boolean tempCarry = carry;
carry = (value & 0x01) != 0; // move bit 0 into the carry flag
value = (byte) ((value & 0xFF) >>> 1);
if (tempCarry)
value |= 0x80; // move old carry into bit 7
zero = value == 0;
negative = (value & 0x80) != 0;
debug ("ROR");
return value;
}
protected byte sta () // STA
{
debug ("STA");
return aReg;
}
protected void sta (byte[] buffer, int offset) // STA
{
buffer[offset] = aReg;
zero = aReg == 0;
negative = (aReg & 0x80) != 0;
debug ("STA");
}
protected byte stx () // STX
{
debug ("STX");
return xReg;
}
protected byte sty () // STY
{
debug ("STY");
return yReg;
}
protected void txa () // TXA
{
aReg = xReg;
zero = aReg == 0;
negative = (aReg & 0x80) != 0;
debug ("TXA");
}
protected void tya () // TYA
{
aReg = yReg;
zero = aReg == 0;
negative = (aReg & 0x80) != 0;
debug ("TYA");
}
protected void tax () // TAX
{
xReg = aReg;
zero = xReg == 0;
negative = (xReg & 0x80) != 0;
debug ("TAX");
}
protected void tay () // TAY
{
yReg = aReg;
zero = yReg == 0;
negative = (yReg & 0x80) != 0;
debug ("TAY");
}
protected void sei () // SEI
{
interrupt = true;
debug ("SEI");
}
protected abstract String debugString ();
protected void debug (String cmd)
{
if (debug)
{
String flags = String.format ("%s %s - %s %s %s %s %s", negative ? "1" : ".",
overflow ? "1" : ".", breakFlag ? "1" : ".", decimal ? "1" : ".",
interrupt ? "1" : ".", zero ? "1" : ".", carry ? "1" : ".");
System.out.printf ("%3s A: %02X X: %02X Y: %02X %s %s%n", cmd, aReg, xReg,
yReg, flags, debugString ());
}
}
protected int indirectY (int base, byte offset, byte page)
{
if (debug)
System.out.printf ("base: %,6d, page: %02X, offset: %02X, yReg: %02X%n", base, page,
offset, yReg);
return ((page & 0xFF) * 256 + (offset & 0xFF)) - base + (yReg & 0xFF);
}
}