static uae_u32 generate_stack_return(int cnt)
{
uae_u32 v;
- // if targer sp mode: generate random return address
+ // if target sp mode: always generate valid address
if (target_ea[0] != 0xffffffff && feature_usp < 3) {
v = target_ea[0];
} else {
{
case 0:
case 3:
- v = opcode_memory_start + 128;
+ v = opcode_memory_start + 32768;
break;
case 1:
v &= 0xffff;
break;
}
- // Supervisor mode and A7 was modified: skip this test round.
- if (s && regs.regs[15] != a7) {
+ // Supervisor mode and A7 was modified and not RTE+stack mode: skip this test round.
+ if (s && regs.regs[15] != a7 && (dp->mnemo != i_RTE || feature_usp < 3)) {
// but not if RTE
if (!is_superstack_use_required()) {
test_exception = -1;
if (cpu_lvl == 0) {
if (test_exception == 2 || test_exception == 3) {
// status
- *p++ = sf[1];
+ uae_u8 st = sf[1];
+ // save also current opcode if different than stacked opcode
+ // used by basic exception check
+ if (((sf[6] << 8) | sf[7]) != regs.opcode) {
+ st |= 0x20;
+ }
+ *p++ = st;
// opcode (which is not necessarily current opcode!)
*p++ = sf[6];
*p++ = sf[7];
+ if (st & 0x20) {
+ *p++ = regs.opcode >> 8;
+ *p++ = regs.opcode;
+ }
// access address
p = store_rel(p, 0, opcode_memory_start, gl(sf + 2), 1);
}
}
- put_word_test(opcode_memory_address, opc);
+ // if bus error stack checking and RTE: copy USP to ISP before RTE
+ if (dp->mnemo == i_RTE && feature_usp == 3) {
+ put_word_test(opcode_memory_address, 0x4e6f); // MOVE USP,A7
+ put_word_test(opcode_memory_address + 2, opc);
+ pc += 2;
+ } else {
+ put_word_test(opcode_memory_address, opc);
+ }
if (extra_or || extra_and) {
uae_u16 ew = get_word_test(opcode_memory_address + 2);
srcaddr = get_long_test(regs.regs[15] + stackoffset);
}
// branch target is not accessible? skip.
- if ((srcaddr >= cur_registers[15] - 16 && srcaddr <= cur_registers[15] + 16) || ((srcaddr & 1) && !feature_exception3_instruction && feature_usp < 2)) {
+ if (((srcaddr >= cur_registers[15] - 16 && srcaddr <= cur_registers[15] + 16) && dp->mnemo != i_RTE) || ((srcaddr & 1) && !feature_exception3_instruction && feature_usp < 2)) {
// lets not jump directly to stack..
if (verbose) {
if (srcaddr & 1)
- wprintf(_T(" Branch target is odd\n"));
+ wprintf(_T(" Branch target is odd (%08x)\n"), srcaddr);
else
- wprintf(_T(" Branch target is stack\n"));
+ wprintf(_T(" Branch target is stack (%08x)\n"), srcaddr);
}
memcpy(opcode_memory, oldcodebytes, sizeof(oldcodebytes));
continue;
testing_active = 1;
if (!valid_address(srcaddr, 2, 1) || srcaddr + 2 == opcode_memory_start) {
if (verbose) {
- wprintf(_T(" Branch target inaccessible\n"));
+ wprintf(_T(" Branch target inaccessible (%08x)\n"), srcaddr);
}
testing_active = 0;
memcpy(opcode_memory, oldcodebytes, sizeof(oldcodebytes));
static uae_u32 addressing_mask = 0x00ffffff;
static uae_u32 interrupt_mask;
static int disasm;
+static int basicexcept;
#define SIZE_STORED_ADDRESS_OFFSET 8
#define SIZE_STORED_ADDRESS 16
uae_u8 *b = (uae_u8 *)regs.branchtarget - SIZE_STORED_ADDRESS_OFFSET;
addinfo_bytes("B", b, regs.branchtarget, -SIZE_STORED_ADDRESS_OFFSET, SIZE_STORED_ADDRESS);
}
- sprintf(outbp, "STARTPC=%08lx ENDPC=%08lx\n", startpc, endpc);
- outbp += strlen(outbp);
+// sprintf(outbp, "STARTPC=%08lx ENDPC=%08lx\n", startpc, endpc);
+// outbp += strlen(outbp);
}
struct srbit
static uae_u8 last_exception[256], last_exception_extra;
static int last_exception_len;
+static uae_u8 alternate_exception1[256];
+static uae_u8 alternate_exception2[256];
+static uae_u8 alternate_exception3[256];
static uae_u8 *validate_exception(struct registers *regs, uae_u8 *p, int excnum, int *gotexcnum, int *experr)
{
uae_u8 excdatalen = *p++;
int size;
int excrwp = 0;
+ int alts = 0;
if (!excdatalen) {
return p;
}
uae_u32 retv = exceptiontableinuse + (excnum - 2) * 2;
if (ret != retv) {
- sprintf(outbp, "Trace (%d stacked) PC mismatch: %08lx != %08lx (%ld)\n", excnum, ret, retv);
+ sprintf(outbp, "Trace (%d stacked) PC mismatch: %08lx != %08lx\n", excnum, ret, retv);
outbp += strlen(outbp);
errors = 1;
*experr = 1;
if (cpu_lvl == 0) {
if (excnum == 2 || excnum == 3) {
// status (with undocumented opcode part)
+ uae_u8 status = p[0];
uae_u8 opcode0 = p[1];
uae_u8 opcode1 = p[2];
+ p += 1 + 2;
+ uae_u8 opcode0b = opcode0;
+ uae_u8 opcode1b = opcode1;
+ if (status & 0x20) {
+ opcode0b = p[0];
+ opcode1b = p[1];
+ p += 2;
+ }
exc[0] = opcode0;
- exc[1] = (opcode1 & ~0x1f) | p[0];
- excrwp = ((p[0] & 0x10) == 0) ? 1 : 0;
- if (p[0] & 2)
+ exc[1] = (opcode1 & ~0x1f) | (status & 0x1f);
+ excrwp = ((status & 0x10) == 0) ? 1 : 0;
+ if (status & 2)
excrwp = 2;
- p += 3;
// access address
v = opcode_memory_addr;
p = restore_rel_ordered(p, &v);
// pc
pl(exc + 10, regs->pc);
exclen = 14;
+ if (basicexcept) {
+ // I/N field is not always as documented
+ memcpy(alternate_exception1, exc, exclen);
+ alternate_exception1[1] ^= 0x08; // I/N
+ alts = 1;
+ if (status & 0x80) {
+ // opcode field is not always current opcode
+ memcpy(alternate_exception2, exc, exclen);
+ alternate_exception2[0] = opcode0b;
+ alternate_exception2[1] = (opcode1b & ~0x1f) | (status & 0x1f);
+ alternate_exception2[6] = opcode0b;
+ alternate_exception2[7] = opcode1b;
+ memcpy(alternate_exception3, alternate_exception2, exclen);
+ alternate_exception3[1] ^= 0x08; // I/N
+ alts += 2;
+ }
+ }
}
} else if (cpu_lvl > 0) {
// sr
last_exception_len = exclen;
if (p != op + excdatalen + 1) {
end_test();
- printf("Exception length mismatch %d != %d\n", excdatalen, p - op - 1);
+ printf("Exception %d length mismatch %d != %d\n", excnum, excdatalen, p - op - 1);
exit(0);
}
} else {
if (exclen == 0 || *gotexcnum != excnum)
return p;
+ int err = 0;
if (memcmp(exc, sp, exclen)) {
+ err = 1;
+ if (err && alts > 0) {
+ if (alts >= 1 && !memcmp(alternate_exception1, sp, exclen))
+ err = 0;
+ if (alts >= 2 && !memcmp(alternate_exception2, sp, exclen))
+ err = 0;
+ if (alts >= 3 && !memcmp(alternate_exception3, sp, exclen))
+ err = 0;
+ }
+ }
+ if (err) {
sprintf(outbp, "Exception %ld stack frame mismatch:\n", excnum);
outbp += strlen(outbp);
strcpy(outbp, "Expected: ");
return p + 1;
int experr = 0;
+ int errflag = 0;
+ int errflag_orig = 0;
+ uae_u8 *outbp_old = outbp;
+
for (;;) {
uae_u8 v = *p;
if (v & CT_END) {
sprintf(outbp, "Exception: vector number does not match vector offset! (%d <> %d)\n", exc, cpuexc010);
experr = 1;
outbp += strlen(outbp);
- errors++;
+ errflag |= 1 << 16;
}
break;
}
if (exc) {
p = validate_exception(&test_regs, p, exc, &cpuexc, &experr);
}
+ errflag_orig = errflag;
+ errflag = 0;
break;
}
if (exc == 0 && cpuexc == 4) {
if (last_registers.pc != test_regs.pc && dooutput) {
sprintf(outbp, "PC: expected %08lx but got %08lx\n", last_registers.pc, test_regs.pc);
outbp += strlen(outbp);
- errors++;
+ errflag |= 1 << 16;
}
break;
}
if (exc) {
p = validate_exception(&test_regs, p, exc, &cpuexc, &experr);
+ if (basicexcept && (cpuexc == 2 || cpuexc == 3)) {
+ errflag_orig = errflag;
+ errflag &= ~(1 << 0);
+ errflag &= ~(1 << 7);
+ }
}
if (exc != cpuexc && exc >= 2) {
addinfo();
experr = 1;
}
outbp += strlen(outbp);
- errors++;
+ errflag |= 1 << 16;
}
break;
}
sprintf(outbp, "%c%d: expected %08lx but got %08lx\n", mode < CT_AREG ? 'D' : 'A', mode & 7, val, test_regs.regs[mode]);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 0;
}
regs_changed[mode] = 0;
last_registers.regs[mode] = val;
test_regs.fpuregs[mode].exp, test_regs.fpuregs[mode].m[0], test_regs.fpuregs[mode].m[1]);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 1;
}
regs_fpuchanged[mode] = 0;
xmemcpy(&last_registers.fpuregs[mode], &val, sizeof(struct fpureg));
// High 16 bit: ignore mask, low 16 bit: SR/CCR
p = restore_value(p, &val, &size);
test_ccrignoremask = ~(val >> 16);
-
if ((val & (sr_undefined_mask & test_ccrignoremask)) != (test_regs.sr & (sr_undefined_mask & test_ccrignoremask)) && !ignore_errors && !ignore_sr) {
addinfo();
if (dooutput) {
outbp += strlen(outbp);
}
*outbp++ = '\n';
+ errflag |= 1 << 2;
+ errflag |= 1 << 7;
+ }
+ uae_u16 mask = test_ccrignoremask & 0xff00;
+ if ((val & (sr_undefined_mask & mask)) == (test_regs.sr & (sr_undefined_mask & mask))) {
+ errflag &= ~(1 << 2);
+ }
+ mask = test_ccrignoremask & 0x00ff;
+ if ((val & (sr_undefined_mask & mask)) == (test_regs.sr & (sr_undefined_mask & mask))) {
+ errflag &= ~(1 << 7);
}
- errors++;
}
sr_changed = 0;
last_registers.sr = val;
if (!(test_regs.expsr & 0x2000)) {
sprintf(outbp, "SR S-bit is not set at start of exception handler!\n");
outbp += strlen(outbp);
- errors++;
+ errflag |= 1 << 16;
}
if ((test_regs.expsr & 0xff) != (test_regs.sr & 0xff)) {
sprintf(outbp, "Exception stacked CCR != CCR at start of exception handler!\n");
outbp += strlen(outbp);
- errors++;
+ errflag |= 1 << 16;
}
} else if (mode == CT_PC) {
sprintf(outbp, "FPCR: expected %08lx -> %08lx but got %08lx\n", test_fpcr, val, test_regs.fpcr);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 3;
}
fpcr_changed = 0;
last_registers.fpcr = val;
sprintf(outbp, "FPSR: expected %08lx -> %08lx but got %08lx\n", test_fpsr, val, test_regs.fpsr);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 4;
}
fpsr_changed = 0;
last_registers.fpsr = val;
sprintf(outbp, "FPIAR: expected %08x but got %08x\n", val, test_regs.fpiar);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 5;
}
fpiar_changed = 0;
last_registers.fpiar = val;
sprintf(outbp, "Memory byte write: address %08lx, expected %02x but got %02x\n", (uae_u32)addr, val, mval);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 6;
}
addr[0] = oldval;
break;
sprintf(outbp, "Memory word write: address %08lx, expected %04x but got %04x\n", (uae_u32)addr, val, mval);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 6;
}
addr[0] = oldval >> 8;
addr[1] = oldval;
sprintf(outbp, "Memory long write: address %08lx, expected %08lx but got %08x\n", (uae_u32)addr, val, mval);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 6;
}
pl(addr, oldval);
break;
sprintf(outbp, "%c%d: modified %08lx -> %08lx but expected no modifications\n", i < 8 ? 'D' : 'A', i & 7, last_registers.regs[i], test_regs.regs[i]);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 0;
}
}
if (sr_changed) {
sprintf(outbp, "SR: modified %04x -> %04x but expected no modifications\n", last_registers.sr & 0xffff, test_regs.sr & 0xffff);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 2;
}
}
for (int i = 0; i < 8; i++) {
test_regs.fpuregs[i].exp, test_regs.fpuregs[i].m[0], test_regs.fpuregs[i].m[1]);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 1;
}
}
if (fpsr_changed) {
sprintf(outbp, "FPSR: modified %08x -> %08x but expected no modifications\n", last_registers.fpsr, test_regs.fpsr);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 3;
}
if (fpcr_changed) {
addinfo();
sprintf(outbp, "FPCR: modified %08x -> %08x but expected no modifications\n", last_registers.fpcr, test_regs.fpcr);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 4;
}
if (fpiar_changed) {
addinfo();
sprintf(outbp, "FPIAR: modified %08x -> %08x but expected no modifications\n", last_registers.fpiar, test_regs.fpiar);
outbp += strlen(outbp);
}
- errors++;
+ errflag |= 1 << 5;
}
}
- if (errors && dooutput) {
+ if (errflag && dooutput) {
addinfo();
if (!fpu_model) {
strcat(outbp, "Registers before:\n");
sprintf(outbp, "OK: No exception generated\n");
outbp += strlen(outbp);
}
+ errors++;
+ }
+ if (!errflag && errflag_orig && dooutput) {
+ outbp = outbp_old;
+ *outbp = 0;
+ infoadded = 0;
}
return p;
}
printf("all <mnemonic> = test all, starting from <mnemonic>\n");
printf("continue = don't stop on error (all mode only)\n");
printf("ccrmask = ignore CCR bits that are not set.\n");
+ printf("nodisasm = do not disassemble failed test.\n");
+ printf("basicexc = do only basic checks when exception is 2 or 3.\n");
return 0;
}
cpu_lvl = 5;
} else if (!_stricmp(s, "nodisasm")) {
disasm = 0;
+ } else if (!_stricmp(s, "basicexc")) {
+ basicexcept = 1;
}
}
- Generated exception and stack frame contents (if any)
- Memory writes, including stack modifications (if any)
- Loop mode for JIT testing. (generates <test instruction>, dbf dn,loop)
-- Supports 68000, 68020, 68030 (only difference between 020 and 030 seems to be data cache and MMU), 68040 and 68060. (I don't currently have real 68010)
+- Supports 68000, 68010, 68020, 68030 (only difference between 020 and 030 seems to be data cache and MMU), 68040 and 68060.
Tests executed for each tested instruction:
-- Every CCR combination (32 tests)
+- Every CCR combination or optionally only all zeros, all ones CCR (32 or 2 tests)
- Every FPU condition combination (4 bits) + 2 precision bits + 2 rounding bits (256 tests)
- Every addressing mode, including optionally 68020+ addressing modes.
-- If instruction generated privilege violation exception, extra test round is run in supervisor mode (Total 64 tests).
+- If instruction generated privilege violation exception, extra test round is run in supervisor mode.
- Optionally can do any combination of T0, T1, S and M -bit SR register extra test rounds.
- Every opcode value is tested. Total number of tests per opcode depends on available addressing modes etc. It can be hundreds of thousands or even millions..
Notes and limitations:
-- Test generator is very brute force based, it should be more intelligent..
-- Address error testing is optional, if disabled, generated tests never cause address errors.
+- Test generator is very brute force based, it should be more intelligent.. Now has optional target src/dst/opcode modes for better bus/address error testing.
+- Bus and address error testing is optional, if disabled, generated tests never cause bus/address errors.
- RTE test only tests stack frame types 0 and 2 (if 68020+)
- All tests that would halt or reset the CPU are skipped (RESET in supervisor mode, STOP parameter that would stop the CPU etc)
- Single instruction test set will take long time to run on real 68000. Few minutes to much longer...
-- Undefined flags (for example DIV and CHK) are also verified. It probably would be good idea to optionally filter them out.
+- Undefined flags (for example DIV and CHK or 68000/010 bus address error) are also verified. It probably would be good idea to optionally filter them out.
- Instruction cycle order or timing is ignored. It is not possible without extra hardware.
-- 68000 bus errors are not tested but there are some plans for future version with custom hardware. (Hatari also uses UAE CPU core)
- FPU testing is not yet fully implemented.
- Sometimes reported old and new condition code state does not match error report..
Tester compatibility (integer instructions only):
-68000: Complete. Bus errors are only supported partially.
+68000: Complete. Including bus and address error stack frame/register/CCR modification undocumented behavior.
68010: Partially supported.
68020: Almost complete (DIV undocumented behavior is not yet known)
68030: Same as 68020.
Test generator quick instructions:
-Update cputestgen.ini to match your CPU model, memory settings etc...
+Update cputestgen.ini to match your CPU model, memory settings etc.
"Low memory" = memory accessible using absolute word addressing mode, positive value (0x0000 to 0x7fff). Can be larger.
"High memory" = memory accessible using absolute word addressing mode, negative value (0xFFF8000 to 0xFFFFFFFF)