From: Toni Wilen Date: Sat, 18 Jan 2020 09:55:50 +0000 (+0200) Subject: CPU tester cycle counting support, multi test set support and more. X-Git-Tag: 4400~173 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=39b314860c772453cd0b35fd6464d7628eb031eb;p=francis%2Fwinuae.git CPU tester cycle counting support, multi test set support and more. --- diff --git a/cputest.cpp b/cputest.cpp index 838fc726..e8464a08 100644 --- a/cputest.cpp +++ b/cputest.cpp @@ -57,6 +57,7 @@ static struct cputbl_data cpudatatbl[65536]; struct regstruct regs; struct flag_struct regflags; +int cpu_cycles; static int verbose = 1; static int feature_exception3_data = 0; @@ -70,6 +71,7 @@ static int feature_test_rounds = 2; static int feature_flag_mode = 0; static int feature_usp = 0; static int feature_exception_vectors = 0; +static int feature_interrupts = 0; static TCHAR *feature_instruction_size = NULL; static uae_u32 feature_addressing_modes[2]; static int feature_gzip = 0; @@ -153,6 +155,7 @@ static int test_memory_accessed; static uae_u16 extra_or, extra_and; static uae_u32 cur_registers[MAX_REGISTERS]; static uae_u16 read_buffer_prev; +static int interrupt_count; struct uae_prefs currprefs; @@ -313,10 +316,20 @@ static void check_bus_error(uaecptr addr, int write, int fc) } } +static void add_memory_cycles(int c) +{ + if (!testing_active) + return; + if (trace_store_pc != 0xffffffff) + return; + cpu_cycles += c * 4; +} + static uae_u8 get_ibyte_test(uaecptr addr) { check_bus_error(addr, 0, regs.s ? 5 : 1); uae_u8 *p = get_addr(addr, 1, 0); + add_memory_cycles(1); return *p; } @@ -327,6 +340,7 @@ static uae_u16 get_iword_test(uaecptr addr) return (get_ibyte_test(addr + 0) << 8) | (get_ibyte_test(addr + 1) << 0); } else { uae_u8 *p = get_addr(addr, 2, 0); + add_memory_cycles(1); return (p[0] << 8) | (p[1]); } } @@ -336,6 +350,7 @@ uae_u16 get_word_test_prefetch(int o) // no real prefetch if (cpu_lvl < 2) o -= 2; + add_memory_cycles(-1); regs.irc = get_iword_test(m68k_getpci() + o + 2); read_buffer_prev = regs.read_buffer; regs.read_buffer = regs.irc; @@ -374,6 +389,7 @@ void put_byte_test(uaecptr addr, uae_u32 v) } regs.write_buffer = v; *p = v; + add_memory_cycles(1); } void put_word_test(uaecptr addr, uae_u32 v) { @@ -401,6 +417,7 @@ void put_word_test(uaecptr addr, uae_u32 v) p[1] = v & 0xff; } regs.write_buffer = v; + add_memory_cycles(1); } void put_long_test(uaecptr addr, uae_u32 v) { @@ -432,6 +449,7 @@ void put_long_test(uaecptr addr, uae_u32 v) p[1] = v >> 16; p[2] = v >> 8; p[3] = v >> 0; + add_memory_cycles(2); } regs.write_buffer = v; } @@ -469,6 +487,7 @@ uae_u32 get_byte_test(uaecptr addr) uae_u8 *p = get_addr(addr, 1, 0); read_buffer_prev = regs.read_buffer; regs.read_buffer = *p; + add_memory_cycles(1); return *p; } uae_u32 get_word_test(uaecptr addr) @@ -483,6 +502,7 @@ uae_u32 get_word_test(uaecptr addr) } read_buffer_prev = regs.read_buffer; regs.read_buffer = v; + add_memory_cycles(1); return v; } uae_u32 get_long_test(uaecptr addr) @@ -501,6 +521,7 @@ uae_u32 get_long_test(uaecptr addr) } else { uae_u8 *p = get_addr(addr, 4, 0); v = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]); + add_memory_cycles(2); } read_buffer_prev = regs.read_buffer; regs.read_buffer = v; @@ -641,10 +662,33 @@ bool mmu_op30(uaecptr pc, uae_u32 opcode, uae_u16 extra, uaecptr extraa) return true; } +int is_cycle_ce(void) +{ + return 0; +} + +void ipl_fetch(void) +{ +} + +int intlev(void) +{ + return 0; +} + +void do_cycles_test(int cycles) +{ + if (!testing_active) + return; + cpu_cycles += cycles; +} + uae_u32(*x_get_long)(uaecptr); uae_u32(*x_get_word)(uaecptr); +uae_u32(*x_get_byte)(uaecptr); void (*x_put_long)(uaecptr, uae_u32); void (*x_put_word)(uaecptr, uae_u32); +void (*x_put_byte)(uaecptr, uae_u32); uae_u32(*x_cp_get_long)(uaecptr); uae_u32(*x_cp_get_word)(uaecptr); @@ -661,6 +705,31 @@ void (*x_do_cycles)(unsigned long); uae_u32(REGPARAM3 *x_cp_get_disp_ea_020)(uae_u32 base, int idx) REGPARAM; +void m68k_do_rts_ce(void) +{ + uaecptr pc; + pc = x_get_word(m68k_areg(regs, 7)) << 16; + pc |= x_get_word(m68k_areg(regs, 7) + 2); + m68k_areg(regs, 7) += 4; + m68k_setpci(pc); +} + +void m68k_do_bsr_ce(uaecptr oldpc, uae_s32 offset) +{ + m68k_areg(regs, 7) -= 4; + x_put_word(m68k_areg(regs, 7), oldpc >> 16); + x_put_word(m68k_areg(regs, 7) + 2, oldpc); + m68k_incpci(offset); +} + +void m68k_do_jsr_ce(uaecptr oldpc, uaecptr dest) +{ + m68k_areg(regs, 7) -= 4; + x_put_word(m68k_areg(regs, 7), oldpc >> 16); + x_put_word(m68k_areg(regs, 7) + 2, oldpc); + m68k_setpci(dest); +} + static int SPCFLAG_TRACE, SPCFLAG_DOTRACE; uae_u32 get_disp_ea_test(uae_u32 base, uae_u32 dp) @@ -823,6 +892,7 @@ static void doexcstack2(void) int noac = noaccesshistory; int ta = testing_active; + int cycs = cpu_cycles; noaccesshistory = 1; testing_active = -1; @@ -881,6 +951,7 @@ static void doexcstack2(void) m68k_areg(regs, 7) = tmp; testing_active = ta; noaccesshistory = noac; + cpu_cycles = cycs; } static void doexcstack(void) @@ -1558,7 +1629,8 @@ static void save_data(uae_u8 *dst, const TCHAR *dir) fwrite(data, 1, 4, f); pl(data, (uae_u32)starttime); fwrite(data, 1, 4, f); - pl(data, ((hmem_rom | (test_high_memory_start == 0xffffffff ? 0x8000 : 0x0000)) << 16) | (lmem_rom | (test_low_memory_start == 0xffffffff ? 0x8000 : 0x0000))); + pl(data, ((hmem_rom | (test_high_memory_start == 0xffffffff ? 0x8000 : 0x0000)) << 16) | + (lmem_rom | (test_low_memory_start == 0xffffffff ? 0x8000 : 0x0000))); fwrite(data, 1, 4, f); pl(data, test_memory_start); fwrite(data, 1, 4, f); @@ -1566,7 +1638,8 @@ static void save_data(uae_u8 *dst, const TCHAR *dir) fwrite(data, 1, 4, f); pl(data, opcode_memory_start); fwrite(data, 1, 4, f); - pl(data, (cpu_lvl << 16) | sr_undefined_mask | (addressing_mask == 0xffffffff ? 0x80000000 : 0) | (feature_min_interrupt_mask << 20) | (safe_memory_mode << 23)); + pl(data, (cpu_lvl << 16) | sr_undefined_mask | (addressing_mask == 0xffffffff ? 0x80000000 : 0) | + (feature_min_interrupt_mask << 20) | (safe_memory_mode << 23) | (feature_interrupts << 26)); fwrite(data, 1, 4, f); pl(data, currprefs.fpu_model); fwrite(data, 1, 4, f); @@ -1881,13 +1954,17 @@ static int create_ea_random(uae_u16 *opcodep, uaecptr pc, int mode, int reg, str } case absw: { - uae_u16 v; + uae_u32 v; + if (!high_memory && !low_memory) + return -1; for (;;) { v = rand16(); + if (v >= 0x8000) + v |= 0xffff0000; if (other_targetea_same(srcdst, (uae_s32)(uae_s16)v)) continue; if (analyze_address(dp, srcdst, v)) - break; + break; } put_word_test(pc, v); *isconstant = 16; @@ -2622,6 +2699,11 @@ static int handle_specials_stack(uae_u16 opcode, uaecptr pc, struct instr *dp, i return offset; } +static const int interrupt_levels[] = +{ + 1, 1, 1, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 0 +}; + static void execute_ins(uaecptr endpc, uaecptr targetpc, struct instr *dp) { uae_u16 opc = regs.ir; @@ -2659,6 +2741,7 @@ static void execute_ins(uaecptr endpc, uaecptr targetpc, struct instr *dp) regs.read_buffer = regs.irc; regs.write_buffer = 0xf00d; exception_extra_frame_size = 0; + cpu_cycles = 0; int cnt = (feature_loop_mode + 1) * 2; if (multi_mode) @@ -2677,8 +2760,20 @@ static void execute_ins(uaecptr endpc, uaecptr targetpc, struct instr *dp) abort(); } - if (SPCFLAG_TRACE) + if (SPCFLAG_TRACE) { do_trace(); + } + + if (feature_interrupts) { + int ic = interrupt_count; + interrupt_count++; + interrupt_count &= 15; + int lvl = interrupt_levels[ic]; + if (lvl > 0 && lvl > feature_min_interrupt_mask) { + Exception(lvl + 24); + break; + } + } regs.instruction_pc = regs.pc; uaecptr a7 = regs.regs[15]; @@ -3115,6 +3210,7 @@ static void test_mnemo(const TCHAR *path, const TCHAR *mnemo, const TCHAR *ovrfi int rounds = feature_test_rounds; int subtest_count = 0; int data_saved = 0; + int first_cycles = 1; int count = 0; @@ -3194,6 +3290,7 @@ static void test_mnemo(const TCHAR *path, const TCHAR *mnemo, const TCHAR *ovrfi full_format_cnt = 0; last_exception_len = -1; + interrupt_count = 0; int sr_override = 0; @@ -3721,6 +3818,7 @@ static void test_mnemo(const TCHAR *path, const TCHAR *mnemo, const TCHAR *ovrfi uae_u32 last_sr = 0; uae_u32 last_pc = 0; + uae_u32 last_cpu_cycles = 0; uae_u32 last_registers[MAX_REGISTERS]; floatx80 last_fpuregisters[8]; uae_u32 last_fpiar = 0; @@ -4058,6 +4156,11 @@ static void test_mnemo(const TCHAR *path, const TCHAR *mnemo, const TCHAR *ovrfi last_fpcr = regs.fpcr; } } + if (cpu_lvl <= 1 && (last_cpu_cycles != cpu_cycles || first_cycles)) { + dst = store_reg(dst, CT_CYCLES, last_cpu_cycles, cpu_cycles, first_cycles ? 0 : -1); + last_cpu_cycles = cpu_cycles; + first_cycles = 0; + } // store test instruction generated changes dst = store_mem_writes(dst, 0); // save exception, possible combinations: @@ -4401,20 +4504,71 @@ static const TCHAR *addrmodes[] = #define INISECTION _T("cputest") -int __cdecl main(int argc, char *argv[]) +static bool ini_getvalx(struct ini_data *ini, const TCHAR *sections, const TCHAR *key, int *val) +{ + bool ret = false; + while (*sections) { + const TCHAR *sect = sections; + if (_tcsicmp(sections, INISECTION)) { + TCHAR *tout = NULL; + if (ini_getstring(ini, sections, key, &tout)) { + if (!_tcsicmp(tout, _T("*"))) { + sect = INISECTION; + } + xfree(tout); + } + } + if (ini_getval(ini, sect, key, val)) + ret = true; + sections += _tcslen(sections) + 1; + } + if (ret) + wprintf(_T("%s=%08x (%d)\n"), key, *val, *val); + return ret; +} +static bool ini_getstringx(struct ini_data *ini, const TCHAR *sections, const TCHAR *key, TCHAR **out) +{ + bool ret = false; + *out = NULL; + while (*sections) { + TCHAR *tout = NULL; + if (ini_getstring(ini, sections, key, &tout)) { + if (!_tcsicmp(tout, _T("*"))) { + xfree(tout); + if (!ini_getstring(ini, INISECTION, key, &tout)) { + tout = my_strdup(_T("")); + } + } + ret = true; + if (*out) { + free(*out); + } + *out = tout; + } + sections += _tcslen(sections) + 1; + } + if (ret) + wprintf(_T("%s=%s\n"), key, *out); + return ret; +} + +static int test(struct ini_data *ini, const TCHAR *sections, const TCHAR *testname) { const struct cputbl *tbl = NULL; TCHAR path[1000], *vs; int v; - struct ini_data *ini = ini_load(_T("cputestgen.ini"), false); - if (!ini) { - wprintf(_T("Couldn't open cputestgen.ini")); - return 0; + wprintf(_T("Generating test '%s'\n"), testname); + + v = 0; + ini_getvalx(ini, sections, _T("enabled"), &v); + if (!v) { + wprintf(_T("Test disabled\n")); + return 1; } currprefs.cpu_model = 68000; - ini_getval(ini, INISECTION, _T("cpu"), &currprefs.cpu_model); + ini_getvalx(ini, sections, _T("cpu"), &currprefs.cpu_model); if (currprefs.cpu_model != 68000 && currprefs.cpu_model != 68010 && currprefs.cpu_model != 68020 && currprefs.cpu_model != 68030 && currprefs.cpu_model != 68040 && currprefs.cpu_model != 68060) { wprintf(_T("Unsupported CPU model.\n")); @@ -4424,7 +4578,7 @@ int __cdecl main(int argc, char *argv[]) currprefs.address_space_24 = 1; addressing_mask = 0x00ffffff; v = 24; - ini_getval(ini, INISECTION, _T("cpu_address_space"), &v); + ini_getvalx(ini, sections, _T("cpu_address_space"), &v); if (v == 32 || currprefs.cpu_model >= 68030) { currprefs.address_space_24 = 0; addressing_mask = 0xffffffff; @@ -4432,7 +4586,7 @@ int __cdecl main(int argc, char *argv[]) currprefs.fpu_model = 0; currprefs.fpu_mode = 1; - ini_getval(ini, INISECTION, _T("fpu"), &currprefs.fpu_model); + ini_getvalx(ini, sections, _T("fpu"), &currprefs.fpu_model); if (currprefs.fpu_model && currprefs.cpu_model < 68020) { wprintf(_T("FPU requires 68020 or 68040 CPU.\n")); return 0; @@ -4443,10 +4597,10 @@ int __cdecl main(int argc, char *argv[]) } verbose = 1; - ini_getval(ini, INISECTION, _T("verbose"), &verbose); + ini_getvalx(ini, sections, _T("verbose"), &verbose); feature_gzip = 0; - ini_getval(ini, INISECTION, _T("feature_gzip"), &feature_gzip); + ini_getvalx(ini, sections, _T("feature_gzip"), &feature_gzip); feature_addressing_modes[0] = 0xffffffff; feature_addressing_modes[1] = 0xffffffff; @@ -4454,18 +4608,18 @@ int __cdecl main(int argc, char *argv[]) pc8r[0] = pc8r[1] = 1; feature_exception3_data = 0; - ini_getval(ini, INISECTION, _T("feature_exception3_data"), &feature_exception3_data); + ini_getvalx(ini, sections, _T("feature_exception3_data"), &feature_exception3_data); feature_exception3_instruction = 0; - ini_getval(ini, INISECTION, _T("feature_exception3_instruction"), &feature_exception3_instruction); + ini_getvalx(ini, sections, _T("feature_exception3_instruction"), &feature_exception3_instruction); safe_memory_start = 0xffffffff; - if (ini_getval(ini, INISECTION, _T("feature_safe_memory_start"), &v)) + if (ini_getvalx(ini, sections, _T("feature_safe_memory_start"), &v)) safe_memory_start = v; safe_memory_end = 0xffffffff; - if (ini_getval(ini, INISECTION, _T("feature_safe_memory_size"), &v)) + if (ini_getvalx(ini, sections, _T("feature_safe_memory_size"), &v)) safe_memory_end = safe_memory_start + v; safe_memory_mode = 7; - if (ini_getstring(ini, INISECTION, _T("feature_safe_memory_mode"), &vs)) { + if (ini_getstringx(ini, sections, _T("feature_safe_memory_mode"), &vs)) { safe_memory_mode = 0; if (_totupper(vs[0]) == 'R') safe_memory_mode |= 1; @@ -4488,7 +4642,7 @@ int __cdecl main(int argc, char *argv[]) feature_target_ea[i][2] = 0xffffffff; } for (int i = 0; i < 3; i++) { - if (ini_getstring(ini, INISECTION, i == 2 ? _T("feature_target_opcode_offset") : (i ? _T("feature_target_dst_ea") : _T("feature_target_src_ea")), &vs)) { + if (ini_getstringx(ini, sections, i == 2 ? _T("feature_target_opcode_offset") : (i ? _T("feature_target_dst_ea") : _T("feature_target_src_ea")), &vs)) { int cnt = 0; TCHAR *p = vs; int exp3cnt = 0; @@ -4536,25 +4690,27 @@ int __cdecl main(int argc, char *argv[]) } feature_sr_mask = 0; - ini_getval(ini, INISECTION, _T("feature_sr_mask"), &feature_sr_mask); + ini_getvalx(ini, sections, _T("feature_sr_mask"), &feature_sr_mask); feature_min_interrupt_mask = 0; - ini_getval(ini, INISECTION, _T("feature_min_interrupt_mask"), &feature_min_interrupt_mask); + ini_getvalx(ini, sections, _T("feature_min_interrupt_mask"), &feature_min_interrupt_mask); feature_loop_mode = 0; - ini_getval(ini, INISECTION, _T("feature_loop_mode"), &feature_loop_mode); + ini_getvalx(ini, sections, _T("feature_loop_mode"), &feature_loop_mode); if (feature_loop_mode) { feature_loop_mode_register = 7; } feature_flag_mode = 0; - ini_getval(ini, INISECTION, _T("feature_flags_mode"), &feature_flag_mode); + ini_getvalx(ini, sections, _T("feature_flags_mode"), &feature_flag_mode); feature_usp = 0; - ini_getval(ini, INISECTION, _T("feature_usp"), &feature_usp); + ini_getvalx(ini, sections, _T("feature_usp"), &feature_usp); feature_exception_vectors = 0; - ini_getval(ini, INISECTION, _T("feature_exception_vectors"), &feature_exception_vectors); + ini_getvalx(ini, sections, _T("feature_exception_vectors"), &feature_exception_vectors); + feature_interrupts = 0; + ini_getvalx(ini, sections, _T("feature_interrupts"), &feature_interrupts); feature_full_extension_format = 0; if (currprefs.cpu_model >= 68020) { - ini_getval(ini, INISECTION, _T("feature_full_extension_format"), &feature_full_extension_format); + ini_getvalx(ini, sections, _T("feature_full_extension_format"), &feature_full_extension_format); if (feature_full_extension_format) { ad8r[0] |= 2; ad8r[1] |= 2; @@ -4565,7 +4721,7 @@ int __cdecl main(int argc, char *argv[]) for (int j = 0; j < 2; j++) { TCHAR *am = NULL; - if (ini_getstring(ini, INISECTION, j ? _T("feature_addressing_modes_dst") : _T("feature_addressing_modes_src"), &am)) { + if (ini_getstringx(ini, sections, j ? _T("feature_addressing_modes_dst") : _T("feature_addressing_modes_src"), &am)) { if (_tcslen(am) > 0) { feature_addressing_modes[j] = 0; ad8r[j] = 0; @@ -4606,10 +4762,10 @@ int __cdecl main(int argc, char *argv[]) TCHAR *mode = NULL; - ini_getstring(ini, INISECTION, _T("mode"), &mode); + ini_getstringx(ini, sections, _T("mode"), &mode); TCHAR *ipath = NULL; - ini_getstring(ini, INISECTION, _T("path"), &ipath); + ini_getstringx(ini, sections, _T("path"), &ipath); if (!ipath) { _tcscpy(path, _T("data/")); } else { @@ -4617,21 +4773,21 @@ int __cdecl main(int argc, char *argv[]) } free(ipath); - _stprintf(path + _tcslen(path), _T("%lu/"), currprefs.cpu_model); + _stprintf(path + _tcslen(path), _T("%lu_%s/"), currprefs.cpu_model, testname); _wmkdir(path); xorshiftstate = 1; feature_test_rounds = 2; - ini_getval(ini, INISECTION, _T("test_rounds"), &feature_test_rounds); + ini_getvalx(ini, sections, _T("test_rounds"), &feature_test_rounds); feature_instruction_size = NULL; - ini_getstring(ini, INISECTION, _T("feature_instruction_size"), &feature_instruction_size); + ini_getstringx(ini, sections, _T("feature_instruction_size"), &feature_instruction_size); - ini_getval(ini, INISECTION, _T("feature_instruction_size"), &feature_test_rounds); + ini_getvalx(ini, sections, _T("feature_instruction_size"), &feature_test_rounds); v = 0; - ini_getval(ini, INISECTION, _T("test_memory_start"), &v); + ini_getvalx(ini, sections, _T("test_memory_start"), &v); if (!v) { wprintf(_T("test_memory_start is required\n")); return 0; @@ -4639,7 +4795,7 @@ int __cdecl main(int argc, char *argv[]) test_memory_start = v; v = 0; - ini_getval(ini, INISECTION, _T("test_memory_size"), &v); + ini_getvalx(ini, sections, _T("test_memory_size"), &v); if (!v) { wprintf(_T("test_memory_start is required\n")); return 0; @@ -4650,19 +4806,19 @@ int __cdecl main(int argc, char *argv[]) test_low_memory_start = 0xffffffff; test_low_memory_end = 0xffffffff; v = 0; - if (ini_getval(ini, INISECTION, _T("test_low_memory_start"), &v)) + if (ini_getvalx(ini, sections, _T("test_low_memory_start"), &v)) test_low_memory_start = v; v = 0; - if (ini_getval(ini, INISECTION, _T("test_low_memory_end"), &v)) + if (ini_getvalx(ini, sections, _T("test_low_memory_end"), &v)) test_low_memory_end = v; test_high_memory_start = 0xffffffff; test_high_memory_end = 0xffffffff; v = 0; - if (ini_getval(ini, INISECTION, _T("test_high_memory_start"), &v)) + if (ini_getvalx(ini, sections, _T("test_high_memory_start"), &v)) test_high_memory_start = v; v = 0; - if (ini_getval(ini, INISECTION, _T("test_high_memory_end"), &v)) + if (ini_getvalx(ini, sections, _T("test_high_memory_end"), &v)) test_high_memory_end = v; if (addressing_mask == 0xffffffff && test_high_memory_end <= 0x01000000) { @@ -4678,7 +4834,7 @@ int __cdecl main(int argc, char *argv[]) } v = 0; - if (ini_getval(ini, INISECTION, _T("opcode_memory_start"), &v)) { + if (ini_getvalx(ini, sections, _T("opcode_memory_start"), &v)) { opcode_memory_start = v; opcode_memory = test_memory + (opcode_memory_start - test_memory_start); } else { @@ -4689,7 +4845,7 @@ int __cdecl main(int argc, char *argv[]) wprintf(_T("Opcode memory out of bounds\n")); return 0; } - if (ini_getval(ini, INISECTION, _T("feature_stack_memory"), &v)) { + if (ini_getvalx(ini, sections, _T("feature_stack_memory"), &v)) { super_stack_memory = v; user_stack_memory = super_stack_memory - (RESERVED_SUPERSTACK + RESERVED_USERSTACK_EXTRA); } else { @@ -4714,7 +4870,7 @@ int __cdecl main(int argc, char *argv[]) if (test_low_memory_start != 0xffffffff) { TCHAR *lmem_rom_name = NULL; - ini_getstring(ini, INISECTION, _T("low_rom"), &lmem_rom_name); + ini_getstringx(ini, sections, _T("low_rom"), &lmem_rom_name); if (lmem_rom_name) { if (load_file(NULL, lmem_rom_name, low_memory_temp, low_memory_size, 0)) { wprintf(_T("Low test memory ROM loaded\n")); @@ -4727,7 +4883,7 @@ int __cdecl main(int argc, char *argv[]) if (test_high_memory_start != 0xffffffff) { TCHAR *hmem_rom_name = NULL; - ini_getstring(ini, INISECTION, _T("high_rom"), &hmem_rom_name); + ini_getstringx(ini, sections, _T("high_rom"), &hmem_rom_name); if (hmem_rom_name) { if (load_file(NULL, hmem_rom_name, high_memory_temp, high_memory_size, -1)) { wprintf(_T("High test memory ROM loaded\n")); @@ -4849,8 +5005,10 @@ int __cdecl main(int argc, char *argv[]) x_get_long = get_long_test; x_get_word = get_word_test; + x_get_byte = get_byte_test; x_put_long = put_long_test; x_put_word = put_word_test; + x_put_byte = put_byte_test; x_next_iword = next_iword_test; x_cp_next_iword = next_iword_test; @@ -4943,7 +5101,49 @@ int __cdecl main(int argc, char *argv[]) modep = sp; } + xfree(low_memory); + xfree(low_memory_temp); + xfree(high_memory); + xfree(high_memory_temp); + xfree(test_memory); + xfree(test_memory_temp); + xfree(storage_buffer); + wprintf(_T("%d total tests generated\n"), test_count); - return 0; + return 1; } + +static TCHAR sections[1000]; + +int __cdecl main(int argc, char *argv[]) +{ + const struct cputbl *tbl = NULL; + TCHAR path[1000], *vs; + int v; + + struct ini_data *ini = ini_load(_T("cputestgen.ini"), false); + if (!ini) { + wprintf(_T("Couldn't open cputestgen.ini")); + return 0; + } + + TCHAR *sptr = sections; + _tcscpy(sections, INISECTION); + sptr += _tcslen(sptr) + 1; + + int idx = 0; + for (;;) { + TCHAR *section = NULL; + if (!ini_getsection(ini, idx, §ion)) + break; + if (!_tcsnicmp(section, _T("test="), 5)) { + _tcscpy(sptr, section); + if (!test(ini, sections, section + 5)) + break; + } + idx++; + } + +} + diff --git a/cputest/amiga.S b/cputest/amiga.S index a088abd1..b8698509 100644 --- a/cputest/amiga.S +++ b/cputest/amiga.S @@ -2,6 +2,7 @@ .text .equ ACTIVITYREG,0xdff180 + .equ CYCLEREG,0xdff006 .globl _allocate_absolute .globl _free_absolute @@ -9,6 +10,19 @@ .globl _tosuper .globl _testexit .globl _get_cpu_model + .globl sync + +sync: + move.b 0xdff006,d0 + beq.s sync + cmp.b #0xff,d0 + beq.s sync + cmp.b #0x38,d0 + beq.s sync + | above prevent wraparound between + | dff004.w and dff006.w reads + move.l 0xdff004,d0 + rts | check left mouse button/joystick fire _testexit: @@ -26,6 +40,7 @@ _tosuper: jsr -0x78(a6) | Disable jsr -0x96(a6) | SuperState move.w #0x0200,0xdff096 + move.w #0x2700,sr move.l (sp)+,a6 rts diff --git a/cputest/asm.S b/cputest/asm.S index a00440b2..3e5bab70 100644 --- a/cputest/asm.S +++ b/cputest/asm.S @@ -36,14 +36,16 @@ S_FPCR = S_FPIAR+4 S_FPSR = S_FPCR+4 S_TRACECNT = S_FPSR+4 S_TRACESTACK = S_TRACECNT+4 -S_NEXT = S_TRACESTACK+12 +S_CYCLES = S_TRACESTACK+12 +S_CYCLES2 = S_CYCLES+4 +S_CYCLEST = S_CYCLES2+4 +S_NEXT = S_CYCLEST+4 _callinflate: - movem.l a4-a5,-(sp) - move.l 4+2*4(sp),a4 - move.l 8+2*4(sp),a5 + movem.l a4-a6,-(sp) + movem.l 4+3*4(sp),a4-a6 bsr _inflate - movem.l (sp)+,a4-a5 + movem.l (sp)+,a4-a6 rts | set CPU special registers @@ -135,7 +137,10 @@ _execute_test000: move.w S_SR+2(a0),-(sp) move.l S_AREG+7*4(a0),a1 move.l a1,USP + bsr sync + move.l d0,S_CYCLES2(a0) movem.l (a0),d0-d7/a0-a6 + move.w CYCLEREG,cycles rte | 68010+ test entrypoint @@ -154,7 +159,10 @@ _execute_test010: move.w S_SR+2(a0),-(sp) move.l S_AREG+7*4(a0),a1 move.l a1,USP + bsr sync + move.l d0,S_CYCLES2(a0) movem.l (a0),d0-d7/a0-a6 + move.w CYCLEREG,cycles | clear data output buffer. | we don't want random DOB contents in bus/address error frame move.w #0xf00d,dummy @@ -214,12 +222,16 @@ _msp_address2: rte exception_trace000: + move.w sr,-(sp) + move.w CYCLEREG,cycles+4 + addq.l #2+4,sp move.l a0,-(sp) move.l datapointer(pc),a0 tst.l S_TRACECNT(a0) bne.s .nexttrace000 move.l 4(sp),S_TRACESTACK(a0) move.l 8(sp),S_TRACESTACK+4(a0) + move.w cycles+4(pc),S_CYCLEST+2(a0) .nexttrace000: addq.l #1,S_TRACECNT(a0) move.l (sp)+,a0 @@ -233,7 +245,7 @@ _exceptiontable000: bsr.s exception | 6 bsr.s exception | 7 bsr.s exception | 8 - bra.s exception_trace000 | 9 + bsr.s exception_trace000 | 9 bsr.s exception | 10 bsr.s exception | 11 bsr.s exception | 12 @@ -275,6 +287,7 @@ _exceptiontable000: nop exception: move.w sr,-(sp) + move.w CYCLEREG,cycles+2 move.w #0,ACTIVITYREG move.l a0,-(sp) move.l datapointer(pc),a0 @@ -302,6 +315,7 @@ exception: move.l USP,a1 move.l a1,S_AREG+7*4(a0) + move.l cycles(pc),S_CYCLES(a0) move.w #0x222,ACTIVITYREG move.l superstack(pc),sp @@ -310,6 +324,9 @@ exception: rts exception_trace010: + move.w sr,-(sp) + move.w CYCLEREG,cycles+4 + addq.l #2+4,sp move.l a0,-(sp) move.l datapointer(pc),a0 tst.l S_TRACECNT(a0) @@ -317,20 +334,21 @@ exception_trace010: move.l 4(sp),S_TRACESTACK(a0) move.l 8(sp),S_TRACESTACK+4(a0) move.l 12(sp),S_TRACESTACK+8(a0) + move.w cycles+4(pc),S_CYCLEST+2(a0) .nexttrace010: addq.l #1,S_TRACECNT(a0) move.l (sp)+,a0 rte _exceptiontable010: - bsr.s exception010 | 2 - bsr.s exception010 | 3 + bsr.s exception010 | 2 + bsr.s exception010 | 3 bsr.s exception010 | 4 bsr.s exception010 | 5 bsr.s exception010 | 6 bsr.s exception010 | 7 bsr.s exception010 | 8 - bra.s exception_trace010 | 9 + bsr.s exception_trace010 | 9 bsr.s exception010 | 10 bsr.s exception010 | 11 bsr.s exception010 | 12 @@ -372,6 +390,7 @@ _exceptiontable010: nop exception010: move.w sr,-(sp) + move.w CYCLEREG,cycles+2 move.w #0,ACTIVITYREG move.l a0,-(sp) move.l datapointer(pc),a0 @@ -396,7 +415,8 @@ exception010: move.l USP,a1 move.l a1,S_AREG+7*4(a0) - + move.l cycles(pc),S_CYCLES(a0) + move.w #0x222,ACTIVITYREG move.l superstack(pc),sp move.w (sp)+,sr @@ -600,5 +620,7 @@ datapointer: dc.l 0 superstack: dc.l 0 +cycles: + dc.l 0,0 dummy: dc.l 0 diff --git a/cputest/cputest_defines.h b/cputest/cputest_defines.h index 9677bb27..19ef7081 100644 --- a/cputest/cputest_defines.h +++ b/cputest/cputest_defines.h @@ -1,5 +1,5 @@ -#define DATA_VERSION 14 +#define DATA_VERSION 15 #define CT_FPREG 0 #define CT_DREG 0 @@ -11,6 +11,7 @@ #define CT_FPIAR 20 #define CT_FPSR 21 #define CT_FPCR 22 +#define CT_CYCLES 25 #define CT_ENDPC 26 #define CT_BRANCHTARGET 27 #define CT_SRCADDR 28 diff --git a/cputest/cputestgen.ini b/cputest/cputestgen.ini index d60e03da..24007c9b 100644 --- a/cputest/cputestgen.ini +++ b/cputest/cputestgen.ini @@ -1,4 +1,3 @@ - [cputest] ; CPU model (68000, 68020, 68030, 68040 or 68060). @@ -27,6 +26,7 @@ feature_gzip=0 ; Low address space limits. Real hardware must have RAM in this space. Comment out to disable. ; Start should be zero if Amiga, set to 0x0800 if Atari ST. +; Must be disabled if cycle counting, cycle count tests must only access real Fast RAM. test_low_memory_start=0x0000 test_low_memory_end=0x8000 @@ -39,12 +39,11 @@ test_high_memory_end=0x01000000 high_rom=D:\amiga\roms\Kickstart v3.1 rev 40.63 (1993)(Commodore)(A500-A600-A2000)[!].rom ; main test memory start and size (real hardware must have RAM in this address space) -test_memory_start=0x800000 +test_memory_start=0x860000 ;test_memory_start=0x68800000 ;test_memory_start=0x07800000 -;test_memory_start=0x00800000 ;test_memory_start=0x340000 -test_memory_size=0x1c0000 +test_memory_size=0xa0000 ; address where test instructions are located ; if not defined: mid point of test memory @@ -72,19 +71,23 @@ feature_exception3_instruction=0 ; Supports 68000 addressing modes only. ; If instruction only has destination EA, source Areg, Dreg or immediate is generated. feature_target_src_ea= -;feature_target_dst_ea=0x87fffa,0x87fffb,0x87fffc,0x87fffd,0x87fffe,0x87ffff,0x880000,0x880001,0x880002,0x880003,0x880004 +feature_target_dst_ea= + +; addresses where test instruction is located, use for bus error prefetch testing +; automatically enables RP bus error mode, data read bus errors are skipped. +;feature_target_opcode_offset=90,92,94,96,98,100,102,104 ; Memory region that generates bus error (both read and write). ; Must be inside any test memory region. ; Can be used to verify bus errors if ea above is inside this memory region. -feature_safe_memory_start=0x880000 -feature_safe_memory_size=0x80000 +feature_safe_memory_start= +feature_safe_memory_size= ; R = data read only bus error ; W = data write only bus error ; P = prefetch bus error ; empty or RWP = both. ; if enabled, all tests that don't generate matching bus error are skipped. -feature_safe_memory_mode=R +feature_safe_memory_mode= ; user stack modes ; 0 = normal even stack @@ -110,6 +113,12 @@ feature_flags_mode=1 ; Skips all tests that would set lower interrupt mask. feature_min_interrupt_mask=0 +; Interrupt test +; If enabled, interrupt request is set before test. +; Tests all INTREQ bits one by one. Compatible with cycle count mode. +; Amiga only +feature_interrupts=0 + ; SR extra mask. ; 0x8000 = T1 ; 0x4000 = T0 (68020-68040) @@ -117,8 +126,9 @@ feature_min_interrupt_mask=0 ; 0x1000 = M (68020-68060) ; Other bits are ignored. ; For example 0xa000 adds 3 extra test rounds: S=1/T1=0, S=0/T1=1 and S=1/T1=1 +; For example 0x8000 adds 1 extra test round: T1=1 ; Note: instructions that generate privilege violation exception will automatically add extra S=1 round. -feature_sr_mask=0xa000 +feature_sr_mask=0x0000 ; generate loop test: label: dbf dn,label ; value: 0 = disabled, >0 = number of loops @@ -144,4 +154,46 @@ feature_instruction_size= ; all = generate all CPU tests. tst = generate tst.b, tst.w and tst.l. tst.l = generate only tst.l ; fall = generate all FPU tests. ; branch = all branch instructions (branchj = non-stack only, branchs = stack using) +mode= + +; test groups +; use key=* to restore default value + +[test=Default] +enabled=1 +mode=all + +[test=IRQ] +enabled=0 +mode=nop,ext,swap +feature_interrupts=1 + +[test=AE_SRC] +enabled=0 +feature_target_src_ea=0x87fff1,0x7111 +feature_target_dst_ea= +mode=all + +[test=AE_DST] +enabled=0 +feature_target_src_ea= +feature_target_dst_ea=0x87fff1,0x7111 +mode=all + +[test=BERR_SRC] +enabled=0 +feature_safe_memory_start=0x880000 +feature_safe_memory_size=0x80000 +feature_safe_memory_mode=R +feature_target_src_ea=0x87fffc,0x87fffd,0x87fffe,0x87ffff,0x880000,0x880001,0x880002,0x880003,0x880004 +feature_target_dst_ea= +mode=all + +[test=BERR_DST] +enabled=0 +feature_safe_memory_start=0x880000 +feature_safe_memory_size=0x80000 +feature_safe_memory_mode=R +feature_target_src_ea= +feature_target_dst_ea=0x87fffc,0x87fffd,0x87fffe,0x87ffff,0x880000,0x880001,0x880002,0x880003,0x880004 mode=all diff --git a/cputest/inflate.S b/cputest/inflate.S index bdd5a099..c2f7eafd 100644 --- a/cputest/inflate.S +++ b/cputest/inflate.S @@ -66,7 +66,7 @@ * on whether OPT_TABLE_LOOKUP is enabled). * SPEEDUP: none; COST: -2 bytes code (makes code slightly smaller) */ #ifndef OPT_STORAGE_OFFSTACK -#define OPT_STORAGE_OFFSTACK 0 +#define OPT_STORAGE_OFFSTACK 1 #endif /* By default all lookup/conversion tables are generated on-the-fly on every diff --git a/cputest/main.c b/cputest/main.c index 6e85a55d..6b0aabaa 100644 --- a/cputest/main.c +++ b/cputest/main.c @@ -27,7 +27,7 @@ typedef signed char uae_s8; #include "cputest_defines.h" -extern void callinflate(uae_u8*, uae_u8*); +extern void callinflate(uae_u8*, uae_u8*,uae_u8*); struct fpureg { @@ -51,6 +51,7 @@ struct registers uae_u32 fpiar, fpcr, fpsr; uae_u32 tracecnt; uae_u16 tracedata[6]; + uae_u32 cycles, cycles2, cyclest; uae_u32 srcaddr, dstaddr, branchtarget; uae_u8 branchtarget_mode; uae_u32 endpc; @@ -72,7 +73,7 @@ static uae_u32 test_memory_addr, test_memory_end; static uae_u32 test_memory_size; static uae_u8 *test_data; static uae_u8 *safe_memory_start, *safe_memory_end; -static int safe_memory_mode; +static short safe_memory_mode; static uae_u32 user_stack_memory, super_stack_memory; static uae_u32 exception_vectors; static int test_data_size; @@ -114,18 +115,26 @@ static char tmpbuffer[1024]; static char path[256]; static char *outbp; -static int infoadded; +static short infoadded; static int errors; static int testcnt; -static int dooutput = 1; -static int quit; +static short dooutput = 1; +static short quit; static uae_u8 ccr_mask; static uae_u32 addressing_mask = 0x00ffffff; static uae_u32 interrupt_mask; -static int disasm; -static int basicexcept; -static int askifmissing; -static int nextall; +static short disasm; +static short basicexcept; +static short askifmissing; +static short nextall; +static int exitcnt; +static short cycles, cycles_range, cycles_adjust; +static short gotcycles; +static short interrupttest; +#ifdef AMIGA +static short interrupt_count; +static uae_u16 main_intena; +#endif #define SIZE_STORED_ADDRESS_OFFSET 8 #define SIZE_STORED_ADDRESS 16 @@ -135,6 +144,8 @@ static uae_u8 branchtarget[SIZE_STORED_ADDRESS]; static uae_u8 stackaddr[SIZE_STORED_ADDRESS]; static uae_u32 stackaddr_ptr; +static char opcode[32], group[32], cpustr[10]; + #ifndef M68K #define xmemcpy memcpy @@ -349,19 +360,27 @@ static void start_test(void) enable_data = tosuper(0); - safe_memcpy(low_memory_back + low_memory_offset, low_memory + low_memory_offset, low_memory_size - low_memory_offset); +#ifdef AMIGA + main_intena = *((volatile uae_u16*)0xdff01c); +#endif + + if (test_low_memory_start != 0xffffffff) + safe_memcpy(low_memory_back + low_memory_offset, low_memory + low_memory_offset, low_memory_size - low_memory_offset); + // always copy exception vectors if 68000 if (cpu_lvl == 0 && low_memory_offset > 0x08) safe_memcpy(low_memory_back + 8, low_memory + 8, (192 - 2) * 4); - if (!hmem_rom) + if (!hmem_rom && test_high_memory_start != 0xffffffff) safe_memcpy(high_memory_back, high_memory + high_memory_offset, high_memory_size - high_memory_offset); - safe_memcpy(low_memory + low_memory_offset, low_memory_temp + low_memory_offset, low_memory_size - low_memory_offset); + if (test_low_memory_start != 0xffffffff) + safe_memcpy(low_memory + low_memory_offset, low_memory_temp + low_memory_offset, low_memory_size - low_memory_offset); + if (cpu_lvl == 0 && low_memory_offset > 0x08) safe_memcpy(low_memory + 8, low_memory_temp + 8, (192 - 2) * 4); - if (!hmem_rom) + if (!hmem_rom && test_high_memory_start != 0xffffffff) safe_memcpy(high_memory + high_memory_offset, high_memory_temp, high_memory_size - high_memory_offset); if (cpu_lvl == 0) { @@ -375,6 +394,12 @@ static void start_test(void) error_vectors[i - 2] = p[i]; } } + for (int i = 24; i < 24 + 8; i++) { + p[i] = (uae_u32)(((uae_u32)&exceptiontable000) + (i - 2) * 2); + if (exception_vectors) { + p[i] = exception_vectors; + } + } for (int i = 32; i < 48; i++) { p[i] = (uae_u32)(((uae_u32)&exceptiontable000) + (i - 2) * 2); if (exception_vectors) { @@ -412,11 +437,13 @@ static void end_test(void) return; test_active = 0; - safe_memcpy(low_memory + low_memory_offset, low_memory_back + low_memory_offset, low_memory_size - low_memory_offset); + if (test_low_memory_start != 0xffffffff) + safe_memcpy(low_memory + low_memory_offset, low_memory_back + low_memory_offset, low_memory_size - low_memory_offset); + if (cpu_lvl == 0 && low_memory_offset > 0x08) safe_memcpy(low_memory + 8, low_memory_back + 8, (192 - 2) * 4); - if (!hmem_rom) + if (!hmem_rom && test_high_memory_start != 0xffffffff) safe_memcpy(high_memory + high_memory_offset, high_memory_back, high_memory_size - high_memory_offset); if (cpu_lvl > 0) { @@ -424,6 +451,12 @@ static void end_test(void) } setcpu(cpu_lvl, cpustatearraystore, NULL); +#ifdef AMIGA + *((volatile uae_u16*)0xdff09a) = 0x7fff; + *((volatile uae_u16*)0xdff09c) = 0x7fff; + *((volatile uae_u16*)0xdff09a) = main_intena | 0x8000; +#endif + touser(enable_data); } @@ -473,6 +506,9 @@ static uae_u8 *parse_gzip(uae_u8 *gzbuf, int *sizep) return gzbuf; } +#define INFLATE_STACK_SIZE 3000 +static uae_u8 *inflatestack; + static uae_u8 *load_file(const char *path, const char *file, uae_u8 *p, int *sizep, int exiterror, int candirect) { char fname[256]; @@ -503,6 +539,14 @@ static uae_u8 *load_file(const char *path, const char *file, uae_u8 *p, int *siz exit(0); } f = NULL; + if (!inflatestack) { + inflatestack = malloc(INFLATE_STACK_SIZE); + if (!inflatestack) { + printf("Couldn't allocate %ld bytes (inflate stack)\n", INFLATE_STACK_SIZE); + exit(0); + } + inflatestack += INFLATE_STACK_SIZE; + } if (!p) { p = calloc(1, size); if (!p) { @@ -510,12 +554,12 @@ static uae_u8 *load_file(const char *path, const char *file, uae_u8 *p, int *siz exit(0); } printf("Decompressing '%s' (%ld -> %ld)\n", fname, gsize, size); - callinflate(p, gzdata); + callinflate(p, gzdata, inflatestack); *sizep = size; return p; } else if (candirect) { printf("Decompressing '%s' (%ld -> %ld)\n", fname, gsize, size); - callinflate(p, gzdata); + callinflate(p, gzdata, inflatestack); *sizep = size; return p; } else { @@ -525,7 +569,7 @@ static uae_u8 *load_file(const char *path, const char *file, uae_u8 *p, int *siz exit(0); } printf("Decompressing '%s' (%ld -> %ld)\n", fname, gsize, size); - callinflate(unpack, gzdata); + callinflate(unpack, gzdata, inflatestack); *sizep = size; } } @@ -747,9 +791,9 @@ static uae_u8 *restore_rel(uae_u8 *p, uae_u32 *vp, int nocheck) val |= *p++; v = val; if (!nocheck) { - if ((val & addressing_mask) < low_memory_size) { + if (test_low_memory_start != 0xffffffff && (val & addressing_mask) < low_memory_size) { ; // low memory - } else if ((val & ~addressing_mask) == ~addressing_mask && val >= 0xfff80000) { + } else if (test_high_memory_start != 0xffffffff && (val & ~addressing_mask) == ~addressing_mask && val >= 0xfff80000) { ; // high memory } else if ((val & addressing_mask) < test_memory_addr || (val & addressing_mask) >= test_memory_addr + test_memory_size) { end_test(); @@ -1023,6 +1067,10 @@ static uae_u8 *restore_data(uae_u8 *p) } else if (mode == CT_SR) { int size; p = restore_value(p, ®s.sr, &size); + } else if (mode == CT_CYCLES) { + int size; + p = restore_value(p, ®s.cycles, &size); + gotcycles = 1; } else if (mode == CT_FPIAR) { int size; p = restore_value(p, ®s.fpiar, &size); @@ -1616,6 +1664,156 @@ static uae_u8 *validate_exception(struct registers *regs, uae_u8 *p, int excnum, return p; } +static int getexceptioncycles(int exc) +{ + if (cpu_lvl == 0) { + switch (exc) + { + case 2: + case 3: + return 58; + case 4: + case 5: + case 6: + case 8: + case 9: + case 10: + case 11: + return 34; + case 7: + return 30; + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + return 44; + default: + return 34; + } + } else { + switch (exc) + { + case 2: + case 3: + return 126; + case 4: + case 10: + case 11: + return 38; + case 5: + return 42; + case 6: + return 44; + case 7: + return 40; + case 8: + return 38; + case 9: + return 38; + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + return 48; + default: + return 38; + } + } +} + +static int check_cycles(int exc) +{ + // 7MHz 68000 PAL A500 only! + uae_u16 vstart = (test_regs.cycles >> 24) & 0xff; + uae_u16 vend = (test_regs.cycles >> 8) & 0xff; + uae_u16 hstart = (test_regs.cycles >> 16) & 0xff; + uae_u16 hend = (test_regs.cycles >> 0) & 0xff; + + // trace exception? + if (test_regs.cyclest != 0xffffffff) { + vend = (test_regs.cyclest >> 8) & 0xff; + hend = (test_regs.cyclest >> 0) & 0xff; + } + + if (test_regs.cycles2 & 0x00010000) { + if (vstart > vend) { + vstart += 0x100; + vend += 0x138 + 1; + } else { + vstart += 0x100; + vend += 0x100; + } + } else { + if (vstart > vend) { + vend += 0x100; + } + } + + // hpos 0-1: vertical count hasn't been increased yet + if (hstart <= 1) { + vstart++; + } + if (hend <= 1) { + vend++; + } + + if (hstart >= hend) { + hend += 227; + vend--; + } + int startcycle = vstart * 227 + hstart; + int endcycle = vend * 227 + hend; + int gotcycles = (endcycle - startcycle) * 2; + int expectedcycles = last_registers.cycles; + if (cpu_lvl == 0) { + // move.w CYCLEREG,cycles + gotcycles -= 20; + // RTE + gotcycles -= 20; + // + // EXCEPTION + expectedcycles += getexceptioncycles(exc); + // bsr.b + gotcycles -= 18; + // move.w sr,-(sp) + gotcycles -= 14; + // move.w CYCLEREG,cycles + gotcycles -= 8; + } else { + // move.w CYCLEREG,cycles + gotcycles -= 20; + // move.w #x,dummy + gotcycles -= 20; + // RTE + gotcycles -= 24; + // + // ILLEGAL + expectedcycles += getexceptioncycles(exc); + // bsr.b + gotcycles -= 18; + // move.w sr,-(sp) + gotcycles -= 12; + // move.w CYCLEREG,cycles + gotcycles -= 8; + } + gotcycles += cycles_adjust; + + if (0 || abs(gotcycles - expectedcycles) > cycles_range) { + addinfo(); + sprintf(outbp, "Got %ld cycles but expected %ld cycles (%08x %08x)\n", gotcycles, expectedcycles, test_regs.cycles, test_regs.cycles2); + outbp += strlen(outbp); + return 0; + } + return 1; +} // regs: registers before execution of test code // test_reg: registers used during execution of test code, also modified by test code. @@ -1811,6 +2009,12 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) p = restore_rel(p, &val, 0); pc_changed = 0; last_registers.pc = val; + } else if (mode == CT_CYCLES) { + uae_u32 val = last_registers.cycles; + int size; + p = restore_value(p, &val, &size); + last_registers.cycles = val; + gotcycles = 1; } else if (mode == CT_FPCR) { uae_u32 val = last_registers.fpcr; int size; @@ -1965,6 +2169,18 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) } errflag |= 1 << 5; } + if (cycles && cpu_lvl <= 1) { + if (!gotcycles && errflag) { + if (dooutput) { + sprintf(outbp, "No Cycle count data available.\n"); + outbp += strlen(outbp); + } + } else { + if (!check_cycles(exc)) { + errflag |= 1 << 8; + } + } + } } if (errflag && dooutput) { addinfo(); @@ -1984,6 +2200,8 @@ static uae_u8 *validate_test(uae_u8 *p, int ignore_errors, int ignore_sr) outbp += strlen(outbp); if (exception_stored) { hexdump(last_exception, exception_stored); + } else { + *outbp++ = '\n'; } } if ((exc == 3 || exc == 2) && cpu_lvl == 0) { @@ -2021,6 +2239,34 @@ static void store_addr(uae_u32 s, uae_u8 *d) } } +#ifdef AMIGA +static const int interrupt_levels[] = +{ + 0, 1, 1, 1, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, -1 +}; + +static void set_interrupt(void) +{ + if (interrupt_count < 15) { + volatile uae_u16 *intena = (uae_u16*)0xdff09a; + volatile uae_u16 *intreq = (uae_u16*)0xdff09c; + uae_u16 mask = 1 << interrupt_count; + *intena = mask | 0x8000 | 0x4000; + *intreq = mask | 0x8000; + } + interrupt_count++; + interrupt_count &= 15; +} + +static void clear_interrupt(void) +{ + volatile uae_u16 *intena = (uae_u16*)0xdff09a; + volatile uae_u16 *intreq = (uae_u16*)0xdff09c; + *intena = 0x7fff; + *intreq = 0x7fff; +} +#endif + static void process_test(uae_u8 *p) { outbp = outbuffer; @@ -2036,10 +2282,14 @@ static void process_test(uae_u8 *p) endpc = opcode_memory_addr; startpc = opcode_memory_addr; - start_test(); test_ccrignoremask = 0xffff; + +#ifdef AMIGA + interrupt_count = 0; + clear_interrupt(); +#endif ahcnt = 0; for (;;) { @@ -2134,6 +2384,7 @@ static void process_test(uae_u8 *p) regs.msp = super_stack_memory; regs.pc = startpc; regs.fpiar = startpc; + regs.cyclest = 0xffffffff; #ifdef M68K if (stackcopysize > 0) @@ -2168,6 +2419,20 @@ static void process_test(uae_u8 *p) old_super = super; } + if (exitcnt >= 0) { + exitcnt--; + if (exitcnt < 0) { + addinfo(); + strcat(outbp, "Registers before:\n"); + outbp += strlen(outbp); + out_regs(®s, 1); + end_test(); + printf(outbuffer); + printf("\nExit count expired\n"); + exit(0); + } + } + if ((*p) == CT_END_SKIP) { p++; @@ -2180,10 +2445,18 @@ static void process_test(uae_u8 *p) if ((ccr_mask & ccr) || (ccr == 0)) { reset_error_vectors(); + #if 0 volatile int *tn = (volatile int*)0x100; *tn = testcnt; #endif + +#ifdef AMIGA + if (interrupttest) { + set_interrupt(); + } +#endif + if (cpu_lvl == 1) { execute_test010(&test_regs); } else if (cpu_lvl >= 2) { @@ -2196,6 +2469,12 @@ static void process_test(uae_u8 *p) execute_test000(&test_regs); } +#ifdef AMIGA + if (interrupttest) { + clear_interrupt(); + } +#endif + if (ccr_mask == 0 && ccr == 0) ignore_sr = 1; @@ -2326,6 +2605,7 @@ static int test_mnemo(const char *opcode) lvl = (lvl_mask >> 16) & 15; interrupt_mask = (lvl_mask >> 20) & 7; addressing_mask = (lvl_mask & 0x80000000) ? 0xffffffff : 0x00ffffff; + interrupttest = (lvl_mask >> 26) & 1; sr_undefined_mask = lvl_mask & 0xffff; safe_memory_mode = (lvl_mask >> 23) & 3; fpu_model = read_u32(headerfile, &headoffset); @@ -2372,11 +2652,19 @@ static int test_mnemo(const char *opcode) } low_memory_offset = 0; - high_memory_offset = 0; - if (test_low_memory_start != 0xffffffff) + if (test_low_memory_start != 0xffffffff) { low_memory_offset = test_low_memory_start; - if (test_high_memory_start != 0xffffffff) + } else { + low_memory_offset = 0x100; + test_low_memory_end = 0xffffffff; + } + + high_memory_offset = 0; + if (test_high_memory_start != 0xffffffff) { high_memory_offset = test_high_memory_start & 0x7fff; + } else { + test_high_memory_end = 0xffffffff; + } if (!absallocated) { test_memory = allocate_absolute(test_memory_addr, test_memory_size); @@ -2407,14 +2695,14 @@ static int test_mnemo(const char *opcode) printf("Test: %08lx-%08lx Safe: %08lx-%08lx\n", test_memory_addr, test_memory_end, safe_memory_start, safe_memory_end); - printf("%s:\n", inst_name); + printf("%s (%s):\n", inst_name, group); testcnt = 0; memset(exceptioncount, 0, sizeof(exceptioncount)); supercnt = 0; for (;;) { - printf("%s. %lu...\n", tfname, testcnt); + printf("%s (%s). %lu...\n", tfname, group, testcnt); sprintf(tfname, "%s/%04ld.dat", opcode, filecnt); @@ -2502,15 +2790,13 @@ static int getparamval(const char *p) static int isdir(const char *dirpath, const char *name) { struct stat buf; - char path[FILENAME_MAX]; - snprintf(path, sizeof(path), "%s%s", dirpath, name); - return stat(path, &buf) == 0 && S_ISDIR(buf.st_mode); + snprintf(tmpbuffer, sizeof(tmpbuffer), "%s%s", dirpath, name); + return stat(tmpbuffer, &buf) == 0 && S_ISDIR(buf.st_mode); } int main(int argc, char *argv[]) { - char opcode[16]; int stop_on_error = 1; atexit(freestuff); @@ -2554,153 +2840,233 @@ int main(int argc, char *argv[]) #endif if (argc < 2) { - printf("cputest () (continue)\n"); + printf("cputest ()/ () (other params)\n"); printf("mnemonic = test single mnemonic\n"); printf("all = test all\n"); printf("all = test all, starting from \n"); - printf("all next = test all, starting after \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"); - printf("askifmissing = ask for new path if dat file is missing.\n"); + printf("all -next = test all, starting after \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"); + printf("-askifmissing = ask for new path if dat file is missing.\n"); + printf("-exit n = exit after n tests.\n"); + printf("-cycles [range adjust] = check cycle counts.\n"); return 0; } - if (strlen(argv[1]) >= sizeof(opcode) - 1) - return 0; - - strcpy(opcode, argv[1]); + opcode[0] = 0; + strcpy(group, "default"); check_undefined_sr = 1; ccr_mask = 0xff; disasm = 1; + exitcnt = -1; + for (int i = 1; i < argc; i++) { char *s = argv[i]; char *next = i + 1 < argc ? argv[i + 1] : NULL; - if (!_stricmp(s, "continue")) { + if (s[0] != '-' && opcode[0] == 0 && strlen(s) < sizeof(opcode) - 1) { + strcpy(opcode, s); + continue; + } + if (!_stricmp(s, "-continue")) { stop_on_error = 0; - } else if (!_stricmp(s, "noundefined")) { + } else if (!_stricmp(s, "-noundefined")) { check_undefined_sr = 0; - } else if (!_stricmp(s, "ccrmask")) { + } else if (!_stricmp(s, "-ccrmask")) { ccr_mask = 0; if (next) { ccr_mask = ~getparamval(next); i++; } - } else if (!_stricmp(s, "silent")) { + } else if (!_stricmp(s, "-silent")) { dooutput = 0; - } else if (!_stricmp(s, "68000")) { + } else if (!_stricmp(s, "-68000")) { cpu_lvl = 0; - } else if (!_stricmp(s, "68010")) { + } else if (!_stricmp(s, "-68010")) { cpu_lvl = 1; - } else if (!_stricmp(s, "68020")) { + } else if (!_stricmp(s, "-68020")) { cpu_lvl = 2; - } else if (!_stricmp(s, "68030")) { + } else if (!_stricmp(s, "-68030")) { cpu_lvl = 3; - } else if (!_stricmp(s, "68040")) { + } else if (!_stricmp(s, "-68040")) { cpu_lvl = 4; - } else if (!_stricmp(s, "68060")) { + } else if (!_stricmp(s, "-68060")) { cpu_lvl = 5; - } else if (!_stricmp(s, "nodisasm")) { + } else if (!_stricmp(s, "-nodisasm")) { disasm = 0; - } else if (!_stricmp(s, "basicexc")) { + } else if (!_stricmp(s, "-basicexc")) { basicexcept = 1; - } else if (!_stricmp(s, "askifmissing")) { + } else if (!_stricmp(s, "-askifmissing")) { askifmissing = 1; - } else if (!_stricmp(s, "next")) { + } else if (!_stricmp(s, "-next")) { nextall = 1; + } else if (!_stricmp(s, "-exit")) { + if (next) { + exitcnt = atoi(next); + i++; + } + } else if (!_stricmp(s, "-cycles")) { + cycles = 1; + cycles_range = 2; + if (i + 1 < argc) { + i++; + cycles_range = atoi(argv[i]); + if (i + 1 < argc) { + i++; + cycles_adjust = atoi(argv[i]); + } + } } } - sprintf(path + strlen(path), "%lu/", 68000 + (cpu_lvl == 5 ? 6 : cpu_lvl) * 10); + DIR *groupd = NULL; + + char *p = strchr(opcode, '/'); + if (p) { + strcpy(tmpbuffer, opcode); + strcpy(group, opcode); + group[p - opcode] = 0; + strcpy(opcode, tmpbuffer + (p - opcode) + 1); + } + + if (!strcmp(group, "all")) { + groupd = opendir(path); + } + + int cpumodel = 68000 + (cpu_lvl == 5 ? 6 : cpu_lvl) * 10; + sprintf(cpustr, "%lu_", cpumodel); + char *pathptr = path + strlen(path); + + for (;;) { + + if (groupd) { + pathptr[0] = 0; + struct dirent *groupdr = readdir(groupd); + if (!groupdr) + break; + if (!isdir(path, groupdr->d_name)) + continue; + if (groupdr->d_name[0] == '.') + continue; + if (strnicmp(cpustr, groupdr->d_name, strlen(cpustr))) + continue; + sprintf(pathptr, "%s/", groupdr->d_name); + strcpy(group, groupdr->d_name + strlen(cpustr)); + } else { + sprintf(pathptr, "%lu_%s/",cpumodel, group); + } - low_memory_size = -1; - low_memory_temp = load_file(path, "lmem.dat", NULL, &low_memory_size, 0, 1); - high_memory_size = -1; - high_memory_temp = load_file(path, "hmem.dat", NULL, &high_memory_size, 0, 1); + low_memory_size = -1; + low_memory_temp = load_file(path, "lmem.dat", NULL, &low_memory_size, 0, 1); + high_memory_size = -1; + high_memory_temp = load_file(path, "hmem.dat", NULL, &high_memory_size, 0, 1); #ifndef M68K - if (low_memory_size > 0) - low_memory = calloc(1, low_memory_size); - if (high_memory_size > 0) - high_memory = calloc(1, high_memory_size); + low_memory = calloc(1, 32768); + if (high_memory_size > 0) + high_memory = calloc(1, high_memory_size); #endif - if (low_memory_size > 0) - low_memory_back = calloc(1, low_memory_size); - if (high_memory_size > 0) - high_memory_back = calloc(1, high_memory_size); + low_memory_back = calloc(1, 32768); + if (!low_memory_temp) { + low_memory_temp = calloc(1, 32768); + } - if (!_stricmp(opcode, "all")) { - DIR *d = opendir(path); - if (!d) { - printf("Couldn't list directory '%s'\n", path); - return 0; + if (high_memory_size > 0) { + high_memory_back = calloc(1, high_memory_size); } + + if (!_stricmp(opcode, "all")) { + DIR *d = opendir(path); + if (!d) { + printf("Couldn't list directory '%s'\n", path); + return 0; + } #define MAX_FILE_LEN 128 #define MAX_MNEMOS 256 - char *dirs = calloc(MAX_MNEMOS, MAX_FILE_LEN); - int diroff = 0; - if (!dirs) - return 0; + char *dirs = calloc(MAX_MNEMOS, MAX_FILE_LEN); + int diroff = 0; + if (!dirs) + return 0; - for (;;) { - struct dirent *dr = readdir(d); - if (!dr) - break; - int d = isdir(path, dr->d_name); - if (d && dr->d_name[0] != '.') { - strcpy(dirs + diroff, dr->d_name); - diroff += MAX_FILE_LEN; - if (diroff >= MAX_FILE_LEN * MAX_MNEMOS) { - printf("too many directories!?\n"); - return 0; + for (;;) { + struct dirent *dr = readdir(d); + if (!dr) + break; + int d = isdir(path, dr->d_name); + if (d && dr->d_name[0] != '.') { + strcpy(dirs + diroff, dr->d_name); + diroff += MAX_FILE_LEN; + if (diroff >= MAX_FILE_LEN * MAX_MNEMOS) { + printf("too many directories!?\n"); + return 0; + } } } - } - closedir(d); + closedir(d); - for (int i = 0; i < diroff; i += MAX_FILE_LEN) { - for (int j = i + MAX_FILE_LEN; j < diroff; j += MAX_FILE_LEN) { - if (_stricmp(dirs + i, dirs + j) > 0) { - char tmp[MAX_FILE_LEN]; - strcpy(tmp, dirs + j); - strcpy(dirs + j, dirs + i); - strcpy(dirs + i, tmp); + for (int i = 0; i < diroff; i += MAX_FILE_LEN) { + for (int j = i + MAX_FILE_LEN; j < diroff; j += MAX_FILE_LEN) { + if (_stricmp(dirs + i, dirs + j) > 0) { + char tmp[MAX_FILE_LEN]; + strcpy(tmp, dirs + j); + strcpy(dirs + j, dirs + i); + strcpy(dirs + i, tmp); + } } } - } - int first = 0; - if (argc >= 3) { - first = -1; - for (int i = 0; i < diroff; i += MAX_FILE_LEN) { - if (!_stricmp(dirs + i, argv[2])) { - first = i; - break; + int first = 0; + if (argc >= 3 && argv[2][0] != '-') { + first = -1; + for (int i = 0; i < diroff; i += MAX_FILE_LEN) { + if (!_stricmp(dirs + i, argv[2])) { + first = i; + break; + } + } + if (first < 0) { + printf("Couldn't find '%s'\n", argv[2]); + return 0; } } - if (first < 0) { - printf("Couldn't find '%s'\n", argv[2]); - return 0; + if (nextall) { + first += MAX_FILE_LEN; } - } - if (nextall) { - first += MAX_FILE_LEN; - } - for (int i = first; i < diroff; i += MAX_FILE_LEN) { - if (test_mnemo(dirs + i)) { + int err = 0; + for (int i = first; i < diroff; i += MAX_FILE_LEN) { + if (test_mnemo(dirs + i)) { + err = 1; + if (stop_on_error) + break; + } + } + + free(dirs); + + if (err && stop_on_error) + break; + + } else { + if (test_mnemo(opcode)) { if (stop_on_error) break; } } - free(dirs); + free(low_memory_temp); + free(high_memory_temp); + free(low_memory_back); + free(high_memory_back); - } else { - test_mnemo(opcode); + if (!groupd) + break; } + if (groupd) + closedir(groupd); + return 0; } diff --git a/cputest/readme.txt b/cputest/readme.txt index 9baedfa0..1c553904 100644 --- a/cputest/readme.txt +++ b/cputest/readme.txt @@ -1,5 +1,5 @@ -UAE CPU Tester +UAE 680x0 CPU Tester I finally wrote utility (This was my "Summer 2019" project) that can be used to verify operation of for example software emulated or FPGA 680x0 CPUs. It is based on UAE CPU core (gencpu generated special test core). All the CPU logic comes from UAE CPU core. @@ -12,6 +12,7 @@ Verifies: - Memory writes, including stack modifications (if any) - Loop mode for JIT testing. (generates , dbf dn,loop) - Supports 68000, 68010, 68020, 68030 (only difference between 020 and 030 seems to be data cache and MMU), 68040 and 68060. +- Cycle counts (68000 Amiga only) Tests executed for each tested instruction: @@ -24,9 +25,11 @@ Tests executed for each tested instruction: Test generation details: -Instruction's effective address is randomized. It is accepted if it points to any of 3 test memory regions. If it points outside of test memory, it will be re-randomized few times. Test will be skipped if current EA makes it impossible to point to any of 3 test regions. +If normal mode: Instruction's effective address is randomized. It is accepted if it points to any of 3 test memory regions. If it points outside of test memory, it will be re-randomized few times. Test will be skipped if current EA makes it impossible to point to any of 3 test regions. If 68000/68010 and address error testing is enabled: 2 extra test rounds are generated, one with even and another with odd EAs to test and verify address errors. +If target EA mode: instruction's effective address always points to configured target address(es). Very useful when testing address or bus errors. + Notes and limitations: - 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. @@ -35,20 +38,34 @@ Notes and limitations: - 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 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. - 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. Including bus and address error stack frame/register/CCR modification undocumented behavior. -68010: Partially supported. +68000: Complete. Including bus and address error stack frame/register/CCR modification undocumented behavior. Cycle count support. +68010: Almost complete. Bus errors are only partially supported. DIVS undefined condition codes are not yet supported. 68020: Almost complete (DIV undocumented behavior is not yet known) 68030: Same as 68020. 68040: Almost complete (Weird unaligned MOVE16 behavior which may be board specific). 68060: Same as 68040. -More CPU details in WinUAE changelog. +68000 cycle count testing: + +Cycle counting requires 100% accurate timing also for following instructions: +- BSR.B +- NOP +- MOVE.W ABS.L,ABS.L +- MOVE SR,-(SP) +- RTE +- Illegal instruction exception +- If instruction internally generates exception, internal exception also needs to be cycle-accurate. + +0xDFF006 is used for cycle counting = accuracy will be +-2 CPU cycles. 0xDFF006 behavior must be accurate. +Currently only supported hardware for cycle counting is 7MHz 68000 PAL Amiga with real Fast RAM. + +Bus error cycle counting is not yet supported. + +-- Not implemented or only partially implemented: @@ -60,7 +77,7 @@ Not implemented or only partially implemented: All models: -- Interrupts (stack frames and STOP) +- STOP test only checks immediate values that do not stop the CPU. - MMU instructions (Not going to happen) - 68020+ cache related instructions. - FPU FSAVE/FRESTORE, FPU support also isn't fully implemented yet. @@ -72,6 +89,9 @@ Build instructions: - build cputestgen project. - build native Amiga project (cputest directory). Assembly files probably only compiles with Bebbo's GCC. +More CPU details in WinUAE changelog. + +-- Test generator quick instructions: @@ -99,3 +119,12 @@ cputest tst.b = run tst.b tests only cputest all tst.b = run tst.b, then tst.w and so on in alphabetical order until end or mismatch is detected. If mismatch is detected, opcode word(s), instruction disassembly, registers before and after and reason message is shown on screen. If difference is in exception stack frame, both expected and returned stack frame is shown in hexadecimal. + +-- + +Change log: + +- Cycle count validation (Amiga, 68000 only), including exceptions (except bus errors). +- Interrupt testing (Amiga only, INTREQ bits set one by one, validate correct exception). +- Multiple test sets can be generated and tested in single step. +- Stack usage reduced, gzip decompression works with default 4096 byte stack. diff --git a/gencpu.cpp b/gencpu.cpp index acfe3100..e24a7544 100644 --- a/gencpu.cpp +++ b/gencpu.cpp @@ -6255,6 +6255,10 @@ static void gen_opcode (unsigned int opcode) if (curi->smode >= Ad16 && cpu_level == 1 && using_prefetch) { dummy_prefetch("srca", NULL); } + if (curi->smode == Ad16 || curi->smode == absw || curi->smode == PC16) + addcycles000(2); + if (curi->smode == Ad8r || curi->smode == PC8r) + addcycles000(6); printf("\t\texception3i(opcode, srca);\n"); write_return_cycles("\t\t", 0); printf("\t}\n"); @@ -6339,6 +6343,10 @@ static void gen_opcode (unsigned int opcode) if (curi->smode >= Ad16 && cpu_level == 1 && using_prefetch) { dummy_prefetch("srca", NULL); } + if (curi->smode == Ad16 || curi->smode == absw || curi->smode == PC16) + addcycles000(2); + if (curi->smode == Ad8r || curi->smode == PC8r) + addcycles000(6); printf("\t\texception3i(opcode, srca);\n"); write_return_cycles("\t\t", 0); printf("\t}\n"); @@ -8288,7 +8296,12 @@ static void generate_cpu_test(int mode) using_exception_3 = 1; using_simple_cycles = 1; - if (mode == 1) { + if (mode == 0) { + using_simple_cycles = 0; + using_ce = 1; + } else if (mode == 1) { + using_simple_cycles = 0; + using_ce = 1; cpu_level = 1; } else if (mode == 2) { cpu_level = 2; diff --git a/include/cputest.h b/include/cputest.h index 550065f0..bbfd0e31 100644 --- a/include/cputest.h +++ b/include/cputest.h @@ -38,6 +38,10 @@ extern int movem_index1[256]; extern int movem_index2[256]; extern int movem_next[256]; +void ipl_fetch(void); +void do_cycles_test(int); +int intlev(void); + uae_u16 get_word_test_prefetch(int); uae_u16 get_wordi_test(int); @@ -55,6 +59,9 @@ uaecptr m68k_getpci(void); void m68k_setpci_j(uaecptr); void m68k_do_rtsi(void); void m68k_do_bsri(uaecptr, uae_s32); +void m68k_do_bsr_ce(uaecptr, uae_s32); +void m68k_do_bsr_ce(uaecptr oldpc, uae_s32 offset); +void m68k_do_jsr_ce(uaecptr oldpc, uaecptr dest); void m68k_setstopped(void); void check_t0_trace(void); diff --git a/ini.cpp b/ini.cpp index 4e555ff0..af3c45d6 100644 --- a/ini.cpp +++ b/ini.cpp @@ -446,6 +446,30 @@ bool ini_getdata(struct ini_data *ini, const TCHAR *section, const TCHAR *key, u return ini_getdata_multi(ini, section, key, out, size, NULL); } +bool ini_getsection(struct ini_data *ini, int idx, TCHAR **section) +{ + const TCHAR *sptr = NULL; + for (int c = 0; c < ini->inilines; c++) { + struct ini_line *il = ini->inidata[c]; + if (il) { + if (!sptr) { + sptr = il->section; + } + if (!sptr) + continue; + if (_tcsicmp(sptr, il->section)) { + idx--; + if (idx < 0) { + *section = my_strdup(il->section); + return true; + } + sptr = il->section; + } + } + } + return false; +} + bool ini_getsectionstring(struct ini_data *ini, const TCHAR *section, int idx, TCHAR **keyout, TCHAR **valout) { for (int c = 0; c < ini->inilines; c++) {