From 92f55a0cfe8289d905680b03837ec21105d9a2b2 Mon Sep 17 00:00:00 2001 From: Toni Wilen Date: Mon, 30 Dec 2019 17:24:31 +0200 Subject: [PATCH] Optional basic check for exception 2 and 3. Ignores possible register and CCR partial modifications (SR part is checked), stack frame opcode field accepts both current and following opcode, I/N field is ignored. --- cputest.cpp | 37 ++++++++--- cputest/cputest_defines.h | 2 +- cputest/main.c | 131 +++++++++++++++++++++++++++++--------- cputest/readme.txt | 17 +++-- 4 files changed, 137 insertions(+), 50 deletions(-) diff --git a/cputest.cpp b/cputest.cpp index bf13c8f1..344e9b3b 100644 --- a/cputest.cpp +++ b/cputest.cpp @@ -2357,7 +2357,7 @@ static uaecptr handle_specials_extra(uae_u16 opcode, uaecptr pc, struct instr *d 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 { @@ -2366,7 +2366,7 @@ static uae_u32 generate_stack_return(int cnt) { case 0: case 3: - v = opcode_memory_start + 128; + v = opcode_memory_start + 32768; break; case 1: v &= 0xffff; @@ -2569,8 +2569,8 @@ static void execute_ins(uaecptr endpc, uaecptr targetpc, struct instr *dp) 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; @@ -2754,10 +2754,20 @@ static uae_u8 *save_exception(uae_u8 *p, struct instr *dp) 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); } @@ -3348,7 +3358,14 @@ static void test_mnemo(const TCHAR *path, const TCHAR *mnemo, const TCHAR *ovrfi } - 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); @@ -3479,13 +3496,13 @@ static void test_mnemo(const TCHAR *path, const TCHAR *mnemo, const TCHAR *ovrfi 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; @@ -3493,7 +3510,7 @@ static void test_mnemo(const TCHAR *path, const TCHAR *mnemo, const TCHAR *ovrfi 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)); diff --git a/cputest/cputest_defines.h b/cputest/cputest_defines.h index 33d15780..539901e0 100644 --- a/cputest/cputest_defines.h +++ b/cputest/cputest_defines.h @@ -1,5 +1,5 @@ -#define DATA_VERSION 11 +#define DATA_VERSION 12 #define CT_FPREG 0 #define CT_DREG 0 diff --git a/cputest/main.c b/cputest/main.c index 0ee3e55f..95b6005b 100644 --- a/cputest/main.c +++ b/cputest/main.c @@ -117,6 +117,7 @@ static uae_u8 ccr_mask; 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 @@ -1061,8 +1062,8 @@ static void addinfo(void) 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 @@ -1187,6 +1188,9 @@ static void hexdump(uae_u8 *p, int len) 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) { @@ -1198,6 +1202,7 @@ static uae_u8 *validate_exception(struct registers *regs, uae_u8 *p, int excnum, uae_u8 excdatalen = *p++; int size; int excrwp = 0; + int alts = 0; if (!excdatalen) { return p; @@ -1225,7 +1230,7 @@ static uae_u8 *validate_exception(struct registers *regs, uae_u8 *p, int excnum, } 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; @@ -1277,14 +1282,22 @@ static uae_u8 *validate_exception(struct registers *regs, uae_u8 *p, int excnum, 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); @@ -1298,6 +1311,23 @@ static uae_u8 *validate_exception(struct registers *regs, uae_u8 *p, int excnum, // 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 @@ -1378,7 +1408,7 @@ static uae_u8 *validate_exception(struct registers *regs, uae_u8 *p, int excnum, 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 { @@ -1389,7 +1419,19 @@ static uae_u8 *validate_exception(struct registers *regs, uae_u8 *p, int excnum, 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: "); @@ -1448,6 +1490,10 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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) { @@ -1467,7 +1513,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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; } @@ -1475,6 +1521,8 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) if (exc) { p = validate_exception(&test_regs, p, exc, &cpuexc, &experr); } + errflag_orig = errflag; + errflag = 0; break; } if (exc == 0 && cpuexc == 4) { @@ -1482,12 +1530,17 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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(); @@ -1502,7 +1555,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) experr = 1; } outbp += strlen(outbp); - errors++; + errflag |= 1 << 16; } break; } @@ -1518,7 +1571,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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; @@ -1533,7 +1586,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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)); @@ -1543,7 +1596,6 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) // 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) { @@ -1554,20 +1606,29 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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) { @@ -1585,7 +1646,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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; @@ -1599,7 +1660,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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; @@ -1612,7 +1673,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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; @@ -1636,7 +1697,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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; @@ -1648,7 +1709,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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; @@ -1661,7 +1722,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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; @@ -1681,7 +1742,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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) { @@ -1690,7 +1751,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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++) { @@ -1702,7 +1763,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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) { @@ -1711,7 +1772,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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(); @@ -1719,7 +1780,7 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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(); @@ -1727,10 +1788,10 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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"); @@ -1758,6 +1819,12 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) 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; } @@ -2303,6 +2370,8 @@ int main(int argc, char *argv[]) printf("all = test all, starting from \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; } @@ -2343,6 +2412,8 @@ int main(int argc, char *argv[]) cpu_lvl = 5; } else if (!_stricmp(s, "nodisasm")) { disasm = 0; + } else if (!_stricmp(s, "basicexc")) { + basicexcept = 1; } } diff --git a/cputest/readme.txt b/cputest/readme.txt index 48517c69..9baedfa0 100644 --- a/cputest/readme.txt +++ b/cputest/readme.txt @@ -11,14 +11,14 @@ Verifies: - Generated exception and stack frame contents (if any) - Memory writes, including stack modifications (if any) - Loop mode for JIT testing. (generates , 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.. @@ -29,20 +29,19 @@ If 68000/68010 and address error testing is enabled: 2 extra test rounds are gen 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. @@ -76,7 +75,7 @@ Build instructions: 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) -- 2.47.3