First regression tests and bug fixes

Import tests from https://github.com/SingleStepTests/m68000

Added:
- Single Step mode for Core
- 68000/68010 only indexed EA mode

Fixed:
- Added a real CMP in CoreALU (for CCR eXtend bit unaffected)
- Correct microcode branching for all post incremented destination ea
- Fixed PC relative calculation for all related EA
- used of au instead of dt for indexed EA calculation
- Fixed EXG with same register
- Fixed Link/unlk with A7
- Fixed Bcc/BRA/BSR
This commit is contained in:
Rodolphe de Saint Léger 2025-05-18 22:59:19 +02:00
parent 0a4e1a5764
commit 7920f8c6da
86 changed files with 931 additions and 343 deletions

View file

@ -1,50 +1,317 @@
package miggy.cpupoet;
import static junit.framework.TestCase.assertEquals;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CoreTest extends Core {
private final ByteBuffer memory;
private final Set<Integer> berrs = new HashSet<Integer>();
public static void main(String[] args) {
ResetTest test2 = new ResetTest();
test2.testReset();
private final boolean is24bits;
CoreTest test = new CoreTest();
private final Set<Integer> berrs = new HashSet<Integer>();
private static final int MAGIC_NAME = 0x89ABCDEF;
private static final int MAGIC_TRANSACTIONS = 0x456789AB;
private static final int MAGIC_STATE = 0x01234567;
private static final int MAGIC_TEST = 0xABC12367;
private static final int MAGIC_FILE_HEADER = 0x1A3F5D71;
public void executeBinTest(String name, int... skips) {
ByteBuffer buffer = getResourceAsByteBuffer(name);
setclrSSWI(SSWI_DNGR | SSWI_SNGL, 0);
int magicNum = buffer.getInt();
if (magicNum != MAGIC_FILE_HEADER) {
throw new IllegalStateException(
String.format("Unexpected magic number for file header: %X, expected %X in file %s", magicNum,
MAGIC_FILE_HEADER, name));
}
int numTests = buffer.getInt();
System.out.println(String.format("Number of tests for %s : %d", name, numTests));
for (int i = 0; i < numTests; i++) {
boolean skip = checkSkip(i, skips);
if ((i == 383) && "Bcc".equals(name)) {
toString();
}
executeBinTest(buffer, skip);
}
}
public boolean checkSkip(int i, int... skips) {
for (int j = 0; j < skips.length; j++) {
if (i == skips[j]) {
return true;
}
}
int pc = test.memory.capacity() - 0x800;
test.setInitialSSP(0x040000);
test.setInitialPC(pc);
test.write16(pc, 0x4848);
test.write16(pc + 2, 0x0200);
test.write16(pc + 4, 0x1234);
test.write16(pc + 6, 0x4849);
test.write32(4 << 2, 0x090000);
//test.setclrSSWI(SSWI_XTRP | SSWI_XBRK, 0);
test.execute(Integer.MAX_VALUE);
test.execute(Integer.MAX_VALUE);
test.execute(Integer.MAX_VALUE);
return false;
}
public void executeBinTest(ByteBuffer buffer, boolean skip) {
int pos = buffer.position();
int numBytes = buffer.getInt();
int magicNum = buffer.getInt();
if (magicNum != MAGIC_TEST) {
throw new IllegalStateException(
String.format("Unexpected magic number for test case: %X, expected %X", magicNum, MAGIC_TEST));
}
String testName = readTestName(buffer);
System.out.print(String.format("%s ", testName));
CPUState start = new CPUState(buffer);
CPUState end = new CPUState(buffer);
if (!skip) {
start.apply(this, end);
execute(1000);
end.check(this);
System.out.println("success");
} else {
System.out.println("skipped");
}
skipTransactions(buffer);
if ((buffer.position() - pos) != numBytes) {
throw new IllegalStateException(
String.format("test suite length mismatch: %X, expected %X", buffer.position() - pos, numBytes));
}
}
private void skipTransactions(ByteBuffer buffer) {
int pos = buffer.position();
int numBytes = buffer.getInt();
int magicNum = buffer.getInt();
if (magicNum != MAGIC_TRANSACTIONS) {
throw new IllegalStateException(
String.format("Unexpected magic number for transactions: %X, expected %X", magicNum, MAGIC_TEST));
}
buffer.getInt(); // total cycles
int numTransactions = buffer.getInt();
for (int i = 0; i < numTransactions; i++) {
int tw = buffer.get() & 0xFF; // Read as unsigned byte
buffer.getInt(); // cycles for this transaction
if (tw != 0) { // not nop cycle
buffer.getInt(); // fc
buffer.getInt(); // aob
buffer.getInt(); // dib/dob
buffer.getInt(); // uds
buffer.getInt(); // lds
}
}
if ((buffer.position() - pos) != numBytes) {
throw new IllegalStateException(
String.format("transactions length mismatch: %X, expected %X", buffer.position() - pos, numBytes));
}
}
private static final class CPUState {
private final List<MemoryValue> ram = new ArrayList<MemoryValue>();
private final int[] dar = new int[15];
private int usp;
private int isp;
private int sr;
private int pc;
private CPUState(ByteBuffer buffer) {
int pos = buffer.position();
int numBytes = buffer.getInt();
int magicNum = buffer.getInt();
if (magicNum != MAGIC_STATE) {
throw new IllegalStateException(
String.format("Unexpected magic number for state: %X, expected %X", magicNum, MAGIC_STATE));
}
for (int i = 0; i < 15; i++) {
dar[i] = buffer.getInt();
}
this.usp = buffer.getInt();
this.isp = buffer.getInt();
this.sr = buffer.getInt();
this.pc = buffer.getInt() - 4;
/* skip prefetch check */
buffer.getInt();
buffer.getInt();
int numRams = buffer.getInt();
for (int i = 0; i < numRams; i++) {
int addr = buffer.getInt();
short dataShort = buffer.getShort();
ram.add(new MemoryValue(addr, dataShort));
}
if ((buffer.position() - pos) != numBytes) {
throw new IllegalStateException(
String.format("state length mismatch: %X, expected %X", buffer.position() - pos, numBytes));
}
}
private void apply(CoreTest core, CPUState finalState) {
for (int i = 0; i < 15; i++) {
core.setDARL(i, dar[i]);
}
core.setUSP(usp);
core.setISP(isp);
core.setSR(sr & ~(SR_T0 | SR_M));
/* the pc points to the next instruction */
core.pc = this.pc;
for (MemoryValue value : finalState.ram) {
core.write16(value.addr, 0);
}
for (MemoryValue value : ram) {
core.write16(value.addr, value.data);
}
core.mpc = 11;
core.sswi &= ~(SR_T1 | SR_T0);
}
private void check(CoreTest core) {
boolean trap = false;
boolean aerr = false;
for (int i = 8; i <= 256; i += 4) {
if (core.pc == core.read32(i)) {
trap |= true;
if ((i == 8) || (i == 12)) {
aerr |= true;
}
break;
}
}
if (!aerr) {
for (int i = 0; i < 15; i++) {
assertEquals(dar[i], core.getDARL(i));
}
assertEquals(usp, core.getUSP());
}
if (!trap) {
assertEquals(isp, core.getISP());
for (MemoryValue value : ram) {
assertEquals(value.data, core.read16(value.addr));
}
assertEquals(sr, core.getSR());
} else {
assertEquals(sr & 0xff00, core.getSR() & 0xff00);
}
assertEquals(pc, core.pc + core.scan);
}
}
private static final class MemoryValue {
private final int addr;
private final short data;
private MemoryValue(int addr, short data) {
this.addr = addr;
this.data = data;
}
}
private static String readTestName(ByteBuffer buffer) {
int pos = buffer.position();
int numBytes = buffer.getInt();
int magicNum = buffer.getInt();
if (magicNum != MAGIC_NAME) {
throw new IllegalStateException(
String.format("Unexpected magic number for name: %X, expected %X", magicNum, MAGIC_NAME));
}
int strlen = buffer.getInt();
byte[] strBytes = new byte[strlen];
buffer.get(strBytes);
if ((buffer.position() - pos) != numBytes) {
throw new IllegalStateException(
String.format("name length mismatch: %X, expected %X", buffer.position() - pos, numBytes));
}
return new String(strBytes, StandardCharsets.UTF_8);
}
private static ByteBuffer getResourceAsByteBuffer(String path) {
InputStream in = CoreTest.class.getResourceAsStream(String.format("%s.json.bin", path));
ByteArrayOutputStream out = new ByteArrayOutputStream();
int read;
byte[] data = new byte[1024];
try {
while ((read = in.read(data, 0, data.length)) != -1) {
out.write(data, 0, read);
}
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
try {
in.close();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
ByteBuffer buffer = ByteBuffer.wrap(out.toByteArray());
buffer.order(ByteOrder.LITTLE_ENDIAN);
return buffer;
}
public CoreTest() {
/* allocate 512 MiB */
this.memory = ByteBuffer.allocate(512 * 1024);
memory.order(ByteOrder.BIG_ENDIAN);
this(512 * 1024, false);
}
public CoreTest(int mem, boolean is24bits) {
this.memory = ByteBuffer.allocate(mem);
memory.order(ByteOrder.BIG_ENDIAN);
this.is24bits = is24bits;
}
public void setInitialSSP(int ssp) {
write32(0, ssp);
}
public void setInitialPC(int ssp) {
write32(4, ssp);
}
@ -62,8 +329,12 @@ public class CoreTest extends Core {
public int fetch32(int aob) {
return read32(aob);
}
protected int check8(int aob) {
if (is24bits) {
aob &= 0xffffff;
}
if (((aob & 0x7fffffff) + 1 >= memory.capacity()) || berrs.contains(aob)) {
setclrSSW(SSW_BR, 0);
@ -72,8 +343,12 @@ public class CoreTest extends Core {
return aob;
}
protected int check16(int aob) {
if (is24bits) {
aob &= 0xffffff;
}
if ((aob &= 0x7fffffff) + 2 >= memory.capacity() || berrs.contains(aob)) {
setclrSSW(SSW_BR, 0);
@ -82,8 +357,12 @@ public class CoreTest extends Core {
return aob;
}
protected int check32(int aob) {
if (is24bits) {
aob &= 0xffffff;
}
if ((aob &= 0x7fffffff) + 4 >= memory.capacity() || berrs.contains(aob)) {
setclrSSW(SSW_BR, 0);
@ -96,28 +375,28 @@ public class CoreTest extends Core {
@Override
public byte read8(int aob) {
aob = check8(aob);
return aob < 0 ? 0 : memory.get(aob);
}
@Override
public short read16(int aob) {
aob = check16(aob);
return aob < 0 ? 0 : memory.getShort(aob);
}
@Override
public int read32(int aob) {
aob = check32(aob);
return aob < 0 ? 0 : memory.getInt(aob);
}
@Override
public void write8(int aob, int dob) {
aob = check8(aob);
if (aob >= 0) {
memory.put(aob, (byte) dob);
}
@ -126,7 +405,7 @@ public class CoreTest extends Core {
@Override
public void write16(int aob, int dob) {
aob = check16(aob);
if (aob >= 0) {
memory.putShort(aob, (short) dob);
}
@ -135,7 +414,7 @@ public class CoreTest extends Core {
@Override
public void write32(int aob, int dob) {
aob = check32(aob);
if (aob >= 0) {
memory.putInt(aob, dob);
}
@ -238,5 +517,4 @@ public class CoreTest extends Core {
public void setSlice(int slice) {
this.slice = slice;
}
}

View file

@ -0,0 +1,190 @@
package miggy.cpupoet;
import junit.framework.TestCase;
public class InstructionTests extends TestCase {
static {
MacroPLA.decode(0);
}
public void testBCD() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("ABCD");
test.executeBinTest("NBCD");
test.executeBinTest("SBCD");
}
public void testADD() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("ADD.b");
test.executeBinTest("ADD.w");
test.executeBinTest("ADD.l");
test.executeBinTest("ADDA.w");
test.executeBinTest("ADDA.l");
test.executeBinTest("ADDX.b");
test.executeBinTest("ADDX.w");
test.executeBinTest("ADDX.l");
}
public void testAND() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("AND.b");
test.executeBinTest("AND.w");
test.executeBinTest("AND.l");
test.executeBinTest("ANDItoCCR");
}
public void testOR() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("OR.b");
test.executeBinTest("OR.w");
test.executeBinTest("OR.l");
test.executeBinTest("ORItoCCR");
}
public void testEOR() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("EOR.b");
test.executeBinTest("EOR.w");
test.executeBinTest("EOR.l");
test.executeBinTest("NOT.b");
test.executeBinTest("NOT.w");
test.executeBinTest("NOT.l");
test.executeBinTest("EORItoCCR");
}
public void testBIT() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("BCHG");
test.executeBinTest("BCLR");
test.executeBinTest("BSET");
test.executeBinTest("BTST");
}
// public void testASL() {
// CoreTest test = new CoreTest(0xffffff + 1, true);
//
// test.executeBinTest("ASL.b");
// test.executeBinTest("ASL.w");
// test.executeBinTest("ASL.l");
// }
public void testBcc() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("Bcc", 373, 466, 570, 1547, 1695, 1782, 2166, 2224, 2441);
test.executeBinTest("BSR", 25, 573, 814, 1357, 1443, 1509, 1734, 1934, 2232, 2338);
}
public void testDBcc() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("DBcc");
}
public void testMisc() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("NOP");
test.executeBinTest("EXG");
test.executeBinTest("SWAP");
test.executeBinTest("EXT.w");
test.executeBinTest("EXT.l");
test.executeBinTest("LEA");
test.executeBinTest("PEA");
test.executeBinTest("RTS");
test.executeBinTest("RTR");
test.executeBinTest("LINK");
test.executeBinTest("UNLINK");
test.executeBinTest("Scc");
test.executeBinTest("TST.b");
test.executeBinTest("TST.w");
test.executeBinTest("TST.l");
// test.executeBinTest("CLR.b");
// test.executeBinTest("CLR.w");
// test.executeBinTest("CLR.l");
test.executeBinTest("TRAP");
test.executeBinTest("TRAPV");
}
public void testJMP() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("JMP");
test.executeBinTest("JSR");
}
public void testSUB() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("SUB.b");
test.executeBinTest("SUB.w");
test.executeBinTest("SUB.l");
test.executeBinTest("SUBA.w");
test.executeBinTest("SUBA.l");
test.executeBinTest("CMP.b");
test.executeBinTest("CMP.w");
test.executeBinTest("CMP.l");
test.executeBinTest("CMPA.w");
test.executeBinTest("CMPA.l");
test.executeBinTest("SUBX.b");
test.executeBinTest("SUBX.w");
test.executeBinTest("SUBX.l");
test.executeBinTest("NEG.b");
//test.executeBinTest("NEG.w");
test.executeBinTest("NEG.l");
test.executeBinTest("NEGX.b");
test.executeBinTest("NEGX.w");
test.executeBinTest("NEGX.l");
}
public void testMOVE() {
CoreTest test = new CoreTest(0xffffff + 1, true);
test.executeBinTest("MOVE.b");
/*
* 295 is not compatible (post incremented value is written by design)
* 342 is not compatible (post incremented value is written by design)
* 494 is not compatible (post incremented value is written by design)
* 994 is not compatible (pre decremented value is written by design)
* 1225 is not compatible (pre decremented value is written by design)
* 1846 is not compatible (post incremented value is written by design)
*/
test.executeBinTest("MOVE.w", 295, 342, 494, 994, 1225, 1846);
/*
* 217 is not compatible (post incremented value is written by design)
* 502 is not compatible (post incremented value is written by design)
* 1152 is not compatible (post incremented value is written by design)
* 1691 is not compatible (post incremented value is written by design)
* 1830 is not compatible (pre decremented value is written by design)
* 2057 is not compatible (post incremented value is written by design)
* 2135 is not compatible (post incremented value is written by design)
*/
test.executeBinTest("MOVE.l", 217, 502, 1152, 1691, 1830, 2057, 2135);
test.executeBinTest("MOVE.q");
test.executeBinTest("MOVEP.w");
test.executeBinTest("MOVEP.l");
test.executeBinTest("MOVEA.w");
test.executeBinTest("MOVEA.l");
// test.executeBinTest("MOVEM.w");
// test.executeBinTest("MOVEM.l");
test.executeBinTest("MOVEtoCCR");
test.executeBinTest("MOVEtoUSP");
test.executeBinTest("MOVEfromUSP");
}
}