1
0
mirror of https://github.com/sethm/symon.git synced 2024-06-01 08:41:32 +00:00

Our first test program is running!

This commit is contained in:
sethm 2008-12-12 00:37:54 -08:00
parent 7ccde7dc1b
commit fb3db6b65f
8 changed files with 766 additions and 61 deletions

View File

@ -22,7 +22,7 @@ public class Bus {
}
public Bus(int startAddress, int endAddress) {
this.devices = new TreeSet();
this.devices = new TreeSet<Device>();
this.startAddress = startAddress;
this.endAddress = endAddress;
}
@ -41,8 +41,9 @@ public class Bus {
MemoryRange memRange = device.getMemoryRange();
for (Device d : devices) {
if (d.getMemoryRange().overlaps(memRange)) {
throw new MemoryRangeException("The device being added overlaps " +
"with an existing device.");
throw new MemoryRangeException("The device being added " +
"overlaps with an existing " +
"device.");
}
}
@ -52,6 +53,7 @@ public class Bus {
}
public void addCpu(Cpu cpu) {
cpu.setBus(this);
this.cpu = cpu;
}
@ -123,8 +125,8 @@ public class Bus {
throw new RuntimeException("Write failed! Device not found.");
}
public SortedSet getDevices() {
public SortedSet<Device> getDevices() {
// Expose a copy of the device list, not the original
return new TreeSet(devices);
return new TreeSet<Device>(devices);
}
}

View File

@ -18,11 +18,15 @@ public class Cpu implements InstructionTable {
private int sp; // Stack Pointer register
private int ir; // Instruction register
/* Operands for the current instruction */
private int[] operands = new int[2];
private int addr; // The address the most recent instruction
// was fetched from
/**
* Construct a new CPU.
*/
public Cpu() {
reset();
}
/**
@ -43,8 +47,13 @@ public class Cpu implements InstructionTable {
* Reset the CPU to known initial values.
*/
public void reset() {
// Registers
sp = 0x01ff;
pc = 0xfffc;
// Set the PC to the address stored in 0xfffc
pc = CpuUtils.address(bus.read(0xfffc), bus.read(0xfffd));
// Clear instruction register.
ir = 0;
}
@ -52,15 +61,572 @@ public class Cpu implements InstructionTable {
* Performs an individual machine cycle.
*/
public void step() {
// 1. Fetch memory
// Store the address from which the IR was read, for debugging
addr = pc;
// Fetch memory location for this instruction.
ir = bus.read(pc);
// 2. Decode the instruction and execute.
// x. Increment PC
// TODO: The way we increment the PC may need
// to change when interrupts are implemented
// Increment PC
incProgramCounter();
}
// Decode the instruction and operands
int size = instructionSizes[ir];
for (int i = 0; i < size-1; i++) {
operands[i] = bus.read(pc);
// Increment PC after reading
incProgramCounter();
}
// Execute
switch(ir) {
case 0x00: // HLT
break;
case 0x01: // n/a
break;
case 0x02: // n/a
break;
case 0x03: // n/a
break;
case 0x04: // n/a
break;
case 0x05: // n/a
break;
case 0x06: // n/a
break;
case 0x07: // n/a
break;
case 0x08: // n/a
break;
case 0x09: // n/a
break;
case 0x0a: // n/a
break;
case 0x0b: // n/a
break;
case 0x0c: // n/a
break;
case 0x0d: // n/a
break;
case 0x0e: // n/a
break;
case 0x0f: // n/a
break;
case 0x10: // n/a
break;
case 0x11: // n/a
break;
case 0x12: // n/a
break;
case 0x13: // n/a
break;
case 0x14: // n/a
break;
case 0x15: // n/a
break;
case 0x16: // n/a
break;
case 0x17: // n/a
break;
case 0x18: // n/a
break;
case 0x19: // n/a
break;
case 0x1a: // n/a
break;
case 0x1b: // n/a
break;
case 0x1c: // n/a
break;
case 0x1d: // n/a
break;
case 0x1e: // n/a
break;
case 0x1f: // n/a
break;
case 0x20: // n/a
break;
case 0x21: // n/a
break;
case 0x22: // n/a
break;
case 0x23: // n/a
break;
case 0x24: // n/a
break;
case 0x25: // n/a
break;
case 0x26: // n/a
break;
case 0x27: // n/a
break;
case 0x28: // n/a
break;
case 0x29: // n/a
break;
case 0x2a: // n/a
break;
case 0x2b: // n/a
break;
case 0x2c: // n/a
break;
case 0x2d: // n/a
break;
case 0x2e: // n/a
break;
case 0x2f: // n/a
break;
case 0x30: // n/a
break;
case 0x31: // n/a
break;
case 0x32: // n/a
break;
case 0x33: // n/a
break;
case 0x34: // n/a
break;
case 0x35: // n/a
break;
case 0x36: // n/a
break;
case 0x37: // n/a
break;
case 0x38: // n/a
break;
case 0x39: // n/a
break;
case 0x3a: // n/a
break;
case 0x3b: // n/a
break;
case 0x3c: // n/a
break;
case 0x3d: // n/a
break;
case 0x3e: // n/a
break;
case 0x3f: // n/a
break;
case 0x40: // n/a
break;
case 0x41: // n/a
break;
case 0x42: // n/a
break;
case 0x43: // n/a
break;
case 0x44: // n/a
break;
case 0x45: // n/a
break;
case 0x46: // n/a
break;
case 0x47: // n/a
break;
case 0x48: // n/a
break;
case 0x49: // n/a
break;
case 0x4a: // n/a
break;
case 0x4b: // n/a
break;
case 0x4c: // JMP - Absolute
pc = CpuUtils.address(operands[0], operands[1]);
break;
case 0x4d: // n/a
break;
case 0x4e: // n/a
break;
case 0x4f: // n/a
break;
case 0x50: // n/a
break;
case 0x51: // n/a
break;
case 0x52: // n/a
break;
case 0x53: // n/a
break;
case 0x54: // n/a
break;
case 0x55: // n/a
break;
case 0x56: // n/a
break;
case 0x57: // n/a
break;
case 0x58: // n/a
break;
case 0x59: // n/a
break;
case 0x5a: // n/a
break;
case 0x5b: // n/a
break;
case 0x5c: // n/a
break;
case 0x5d: // n/a
break;
case 0x5e: // n/a
break;
case 0x5f: // n/a
break;
case 0x60: // n/a
break;
case 0x61: // n/a
break;
case 0x62: // n/a
break;
case 0x63: // n/a
break;
case 0x64: // n/a
break;
case 0x65: // n/a
break;
case 0x66: // n/a
break;
case 0x67: // n/a
break;
case 0x68: // n/a
break;
case 0x69: // n/a
break;
case 0x6a: // n/a
break;
case 0x6b: // n/a
break;
case 0x6c: // n/a
break;
case 0x6d: // n/a
break;
case 0x6e: // n/a
break;
case 0x6f: // n/a
break;
case 0x70: // n/a
break;
case 0x71: // n/a
break;
case 0x72: // n/a
break;
case 0x73: // n/a
break;
case 0x74: // n/a
break;
case 0x75: // n/a
break;
case 0x76: // n/a
break;
case 0x77: // n/a
break;
case 0x78: // n/a
break;
case 0x79: // n/a
break;
case 0x7a: // n/a
break;
case 0x7b: // n/a
break;
case 0x7c: // n/a
break;
case 0x7d: // n/a
break;
case 0x7e: // n/a
break;
case 0x7f: // n/a
break;
case 0x80: // n/a
break;
case 0x81: // n/a
break;
case 0x82: // n/a
break;
case 0x83: // n/a
break;
case 0x84: // n/a
break;
case 0x85: // n/a
break;
case 0x86: // n/a
break;
case 0x87: // n/a
break;
case 0x88: // n/a
break;
case 0x89: // n/a
break;
case 0x8a: // n/a
break;
case 0x8b: // n/a
break;
case 0x8c: // n/a
break;
case 0x8d: // n/a
break;
case 0x8e: // n/a
break;
case 0x8f: // n/a
break;
case 0x90: // n/a
break;
case 0x91: // n/a
break;
case 0x92: // n/a
break;
case 0x93: // n/a
break;
case 0x94: // n/a
break;
case 0x95: // n/a
break;
case 0x96: // n/a
break;
case 0x97: // n/a
break;
case 0x98: // n/a
break;
case 0x99: // n/a
break;
case 0x9a: // n/a
break;
case 0x9b: // n/a
break;
case 0x9c: // n/a
break;
case 0x9d: // n/a
break;
case 0x9e: // n/a
break;
case 0x9f: // n/a
break;
case 0xa0: // n/a
break;
case 0xa1: // n/a
break;
case 0xa2: // n/a
break;
case 0xa3: // n/a
break;
case 0xa4: // n/a
break;
case 0xa5: // n/a
break;
case 0xa6: // n/a
break;
case 0xa7: // n/a
break;
case 0xa8: // n/a
break;
case 0xa9: // LDA - Immediate
a = operands[0];
break;
case 0xaa: // n/a
break;
case 0xab: // n/a
break;
case 0xac: // n/a
break;
case 0xad: // n/a
break;
case 0xae: // n/a
break;
case 0xaf: // n/a
break;
case 0xb0: // n/a
break;
case 0xb1: // n/a
break;
case 0xb2: // n/a
break;
case 0xb3: // n/a
break;
case 0xb4: // n/a
break;
case 0xb5: // n/a
break;
case 0xb6: // n/a
break;
case 0xb7: // n/a
break;
case 0xb8: // n/a
break;
case 0xb9: // n/a
break;
case 0xba: // n/a
break;
case 0xbb: // n/a
break;
case 0xbc: // n/a
break;
case 0xbd: // n/a
break;
case 0xbe: // n/a
break;
case 0xbf: // n/a
break;
case 0xc0: // n/a
break;
case 0xc1: // n/a
break;
case 0xc2: // n/a
break;
case 0xc3: // n/a
break;
case 0xc4: // n/a
break;
case 0xc5: // n/a
break;
case 0xc6: // n/a
break;
case 0xc7: // n/a
break;
case 0xc8: // n/a
break;
case 0xc9: // n/a
break;
case 0xca: // n/a
break;
case 0xcb: // n/a
break;
case 0xcc: // n/a
break;
case 0xcd: // n/a
break;
case 0xce: // n/a
break;
case 0xcf: // n/a
break;
case 0xd0: // n/a
break;
case 0xd1: // n/a
break;
case 0xd2: // n/a
break;
case 0xd3: // n/a
break;
case 0xd4: // n/a
break;
case 0xd5: // n/a
break;
case 0xd6: // n/a
break;
case 0xd7: // n/a
break;
case 0xd8: // n/a
break;
case 0xd9: // n/a
break;
case 0xda: // n/a
break;
case 0xdb: // n/a
break;
case 0xdc: // n/a
break;
case 0xdd: // n/a
break;
case 0xde: // n/a
break;
case 0xdf: // n/a
break;
case 0xe0: // n/a
break;
case 0xe1: // n/a
break;
case 0xe2: // n/a
break;
case 0xe3: // n/a
break;
case 0xe4: // n/a
break;
case 0xe5: // n/a
break;
case 0xe6: // n/a
break;
case 0xe7: // n/a
break;
case 0xe8: // n/a
break;
case 0xe9: // n/a
break;
case 0xea: // NOP
break;
case 0xeb: // n/a
break;
case 0xec: // n/a
break;
case 0xed: // n/a
break;
case 0xee: // n/a
break;
case 0xef: // n/a
break;
case 0xf0: // n/a
break;
case 0xf1: // n/a
break;
case 0xf2: // n/a
break;
case 0xf3: // n/a
break;
case 0xf4: // n/a
break;
case 0xf5: // n/a
break;
case 0xf6: // n/a
break;
case 0xf7: // n/a
break;
case 0xf8: // n/a
break;
case 0xf9: // n/a
break;
case 0xfa: // n/a
break;
case 0xfb: // n/a
break;
case 0xfc: // n/a
break;
case 0xfd: // n/a
break;
case 0xfe: // n/a
break;
case 0xff: // n/a
break;
}
}
public String statusString() {
String opcode = CpuUtils.opcode(ir, operands[0], operands[1]);
StringBuffer sb = new StringBuffer(String.format("$%04X", addr) + " ");
sb.append(String.format("%-12s", opcode));
sb.append("A=" + String.format("$%02X", a) + "; ");
sb.append("X=" + String.format("$%02X", x) + "; ");
sb.append("Y=" + String.format("$%02X", y) + "; ");
sb.append("PC=" + String.format("$%04X", pc));
return sb.toString();
}
/*
* Increment the PC, rolling over if necessary.
*/
@ -71,6 +637,5 @@ public class Cpu implements InstructionTable {
++pc;
}
}
}
}

View File

@ -0,0 +1,37 @@
package com.loomcom.lm6502;
import com.loomcom.lm6502.InstructionTable.Mode;
public class CpuUtils {
/**
* Given two bytes, return an address.
*/
public static int address(int lowByte, int hiByte) {
return ((hiByte<<8)|lowByte);
}
/**
* Given an opcode and its operands, return a formatted name.
*
* @param opcode
* @param operands
* @return
*/
public static String opcode(int opcode, int op1, int op2) {
String opcodeName = Cpu.opcodeNames[opcode];
if (opcodeName == null) { return "??"; }
StringBuffer sb = new StringBuffer(opcodeName);
switch (Cpu.instructionModes[opcode]) {
case ABS:
sb.append(String.format(" $%04X", address(op1, op2)));
break;
case IMM:
sb.append(String.format(" #$%02X", op1));
}
return sb.toString();
}
}

View File

@ -92,6 +92,10 @@ public interface InstructionTable {
}
// 6502 opcodes. No 65C02 opcodes implemented.
/**
* Instruction opcode names.
*/
public static final String[] opcodeNames = {
"BRK", "ORA", null, null, null, "ORA", "ASL", null,
"PHP", "ORA", "ASL", null, null, "ORA", "ASL", null,
@ -127,7 +131,10 @@ public interface InstructionTable {
"SED", "SBC", null, null, null, "SBC", "INC", null
};
public static final Mode[] opcodeModes = {
/**
* Instruction addressing modes.
*/
public static final Mode[] instructionModes = {
Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b
@ -193,5 +200,50 @@ public interface InstructionTable {
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff
};
/**
* Size, in bytes, required for each instruction.
*/
public static final int[] instructionSizes = {
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0,
2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
0, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0
};
/**
* Number of clock cycles required for each instruction
*/
public static final int[] instructionClocks = {
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0,
2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0,
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0,
0, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0
};
}

View File

@ -5,7 +5,6 @@
package com.loomcom.lm6502;
import java.util.*;
import com.loomcom.lm6502.devices.*;
import com.loomcom.lm6502.exceptions.*;
@ -21,7 +20,7 @@ public class Profiler implements InstructionTable {
public void dumpOpCodes() {
for (int i = 0; i < 0x100; i++) {
String name = opcodeNames[i];
Mode mode = opcodeModes[i];
Mode mode = instructionModes[i];
System.out.print(String.format("0x%02x: ", i));
@ -34,12 +33,10 @@ public class Profiler implements InstructionTable {
}
public void profileMemoryReads() {
// Create a bus.
Bus b = new Bus(0, 65535);
try {
// Create a bus.
Bus b = new Bus(0, 65535);
// Create eight devices, each 8KB, to fill the bus.
b.addDevice(new Memory(0x0000, 0x2000)); // 8KB @ $0000-$1fff
b.addDevice(new Memory(0x2000, 0x2000)); // 8KB @ $2000-$3fff
@ -49,40 +46,37 @@ public class Profiler implements InstructionTable {
b.addDevice(new Memory(0xa000, 0x2000)); // 8KB @ $a000-$bfff
b.addDevice(new Memory(0xc000, 0x2000)); // 8KB @ $c000-$dfff
b.addDevice(new Memory(0xe000, 0x2000)); // 8KB @ $e000-$ffff
// Read memory
long sum = 0;
long average = 0;
long iters = 500;
for (int i = 0; i < iters; i++) {
long startTime = System.nanoTime();
// Read and assign to a buffer
int buf = 0;
for (int j = 0; j < 0xffff; j++) {
buf = b.read(j);
if (buf != 0xff) {
System.out.println("WARNING! MEMORY SHOULD HAVE " +
"BEEN $FF, WAS: " + buf);
System.exit(0);
}
}
long endTime = System.nanoTime();
long diff = endTime - startTime;
sum += diff;
average = sum / (i + 1);
}
System.out.println("Average time to read 64KB: " + average +
" ns (" + (average / 1000) + " us)");
System.out.println("Average time to read one byte: " +
sum / (64 * 1024 * iters) + " ns");
} catch (MemoryRangeException ex) {
System.out.println("Memory Access Exception! " + ex.getMessage());
System.out.println("Memory Range Exception! " + ex.getMessage());
}
// Read memory
long sum = 0;
long average = 0;
long iters = 500;
for (int i = 0; i < iters; i++) {
long startTime = System.nanoTime();
// Read and assign to a buffer
int buf = 0;
for (int j = 0; j < 0xffff; j++) {
buf = b.read(j);
if (buf != 0xff) {
System.out.println("WARNING! MEMORY SHOULD HAVE " +
"BEEN $FF, WAS: " + buf);
System.exit(0);
}
}
long endTime = System.nanoTime();
long diff = endTime - startTime;
sum += diff;
average = sum / (i + 1);
}
System.out.println("Average time to read 64KB: " + average +
" ns (" + (average / 1000) + " us)");
System.out.println("Average time to read one byte: " +
sum / (64 * 1024 * iters) + " ns");
}
}

View File

@ -1,6 +1,7 @@
package com.loomcom.lm6502;
import java.io.IOException;
import com.loomcom.lm6502.devices.*;
import com.loomcom.lm6502.exceptions.*;
/**
* Main control class for the J6502 Simulator.
@ -23,8 +24,11 @@ public class Simulator {
*/
private Bus bus;
public Simulator() {
public Simulator() throws MemoryRangeException {
cpu = new Cpu();
bus = new Bus(0x0000, 0xffff);
bus.addCpu(cpu);
bus.addDevice(new Memory(0x0000, 0x10000));
parser = new CommandParser(System.in, System.out, this);
}
@ -41,12 +45,51 @@ public class Simulator {
public void write(int address, int value) {
}
/**
* A test method.
*/
public void runTest() {
// Start at 0x0300
bus.write(0xfffc, 0x00);
bus.write(0xfffd, 0x03);
bus.write(0x0300, 0xa9); // LDA #$FF
bus.write(0x0301, 0xff);
bus.write(0x0302, 0xea); // NOP
bus.write(0x0303, 0xea); // NOP
bus.write(0x0304, 0xea); // NOP
bus.write(0x0305, 0xea); // NOP
bus.write(0x0306, 0xa9); // LDA #$1A
bus.write(0x0307, 0x1a);
bus.write(0x0308, 0xea); // NOP
bus.write(0x0309, 0xea); // NOP
bus.write(0x030a, 0xa9); // LDA #$03
bus.write(0x030b, 0x03);
bus.write(0x030c, 0x4c); // JMP #$0300
bus.write(0x030d, 0x00);
bus.write(0x030e, 0x03);
cpu.reset();
for (int i = 0; i < 60; i++) {
cpu.step();
System.out.println(cpu.statusString());
}
}
/**
* Main simulator routine.
*/
public static void main(String[] args) {
new Simulator().run();
try {
new Simulator().runTest();
} catch (MemoryRangeException ex) {
System.err.println("Error: " + ex.toString());
}
}
}

View File

@ -2,9 +2,6 @@ package com.loomcom.lm6502;
import junit.framework.*;
import java.util.HashMap;
import java.util.Map;
/**
*
*/

View File

@ -0,0 +1,15 @@
package com.loomcom.lm6502;
import junit.framework.TestCase;
public class CpuUtilsTest extends TestCase {
public void testAddress() {
assertEquals(0xf1ea, CpuUtils.address(0xea, 0xf1));
assertEquals(0x00ea, CpuUtils.address(0xea, 0x00));
assertEquals(0xf100, CpuUtils.address(0x00, 0xf1));
assertEquals(0x1234, CpuUtils.address(0x34, 0x12));
assertEquals(0xffff, CpuUtils.address(0xff, 0xff));
}
}