struct regstruct regs;
struct flag_struct regflags;
+int cpu_cycles;
static int verbose = 1;
static int feature_exception3_data = 0;
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;
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;
}
}
+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;
}
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]);
}
}
// 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;
}
regs.write_buffer = v;
*p = v;
+ add_memory_cycles(1);
}
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)
{
p[1] = v >> 16;
p[2] = v >> 8;
p[3] = v >> 0;
+ add_memory_cycles(2);
}
regs.write_buffer = v;
}
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)
}
read_buffer_prev = regs.read_buffer;
regs.read_buffer = v;
+ add_memory_cycles(1);
return v;
}
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;
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);
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)
int noac = noaccesshistory;
int ta = testing_active;
+ int cycs = cpu_cycles;
noaccesshistory = 1;
testing_active = -1;
m68k_areg(regs, 7) = tmp;
testing_active = ta;
noaccesshistory = noac;
+ cpu_cycles = cycs;
}
static void doexcstack(void)
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);
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);
}
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;
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;
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)
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];
int rounds = feature_test_rounds;
int subtest_count = 0;
int data_saved = 0;
+ int first_cycles = 1;
int count = 0;
full_format_cnt = 0;
last_exception_len = -1;
+ interrupt_count = 0;
int sr_override = 0;
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;
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:
#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"));
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;
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;
}
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;
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;
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;
}
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;
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;
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 {
}
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;
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;
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) {
}
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 {
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 {
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"));
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"));
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;
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++;
+ }
+
+}
+
.text
.equ ACTIVITYREG,0xdff180
+ .equ CYCLEREG,0xdff006
.globl _allocate_absolute
.globl _free_absolute
.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:
jsr -0x78(a6) | Disable
jsr -0x96(a6) | SuperState
move.w #0x0200,0xdff096
+ move.w #0x2700,sr
move.l (sp)+,a6
rts
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
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
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
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
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
nop
exception:
move.w sr,-(sp)
+ move.w CYCLEREG,cycles+2
move.w #0,ACTIVITYREG
move.l a0,-(sp)
move.l datapointer(pc),a0
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
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)
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
nop
exception010:
move.w sr,-(sp)
+ move.w CYCLEREG,cycles+2
move.w #0,ACTIVITYREG
move.l a0,-(sp)
move.l datapointer(pc),a0
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
dc.l 0
superstack:
dc.l 0
+cycles:
+ dc.l 0,0
dummy:
dc.l 0
-#define DATA_VERSION 14
+#define DATA_VERSION 15
#define CT_FPREG 0
#define CT_DREG 0
#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
-
[cputest]
; CPU model (68000, 68020, 68030, 68040 or 68060).
; 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
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
; 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
; 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)
; 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: <test instruction> dbf dn,label
; value: 0 = disabled, >0 = number of loops
; 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
* 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
#include "cputest_defines.h"
-extern void callinflate(uae_u8*, uae_u8*);
+extern void callinflate(uae_u8*, uae_u8*,uae_u8*);
struct fpureg
{
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;
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;
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
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
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) {
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) {
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) {
}
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);
}
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];
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) {
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 {
exit(0);
}
printf("Decompressing '%s' (%ld -> %ld)\n", fname, gsize, size);
- callinflate(unpack, gzdata);
+ callinflate(unpack, gzdata, inflatestack);
*sizep = size;
}
}
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();
} 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);
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;
+ // <test instruction>
+ // 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;
+ // <test instruction>
+ // 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.
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;
}
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();
outbp += strlen(outbp);
if (exception_stored) {
hexdump(last_exception, exception_stored);
+ } else {
+ *outbp++ = '\n';
}
}
if ((exc == 3 || exc == 2) && cpu_lvl == 0) {
}
}
+#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;
endpc = opcode_memory_addr;
startpc = opcode_memory_addr;
-
start_test();
test_ccrignoremask = 0xffff;
+
+#ifdef AMIGA
+ interrupt_count = 0;
+ clear_interrupt();
+#endif
ahcnt = 0;
for (;;) {
regs.msp = super_stack_memory;
regs.pc = startpc;
regs.fpiar = startpc;
+ regs.cyclest = 0xffffffff;
#ifdef M68K
if (stackcopysize > 0)
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++;
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) {
execute_test000(&test_regs);
}
+#ifdef AMIGA
+ if (interrupttest) {
+ clear_interrupt();
+ }
+#endif
+
if (ccr_mask == 0 && ccr == 0)
ignore_sr = 1;
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);
}
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);
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);
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);
#endif
if (argc < 2) {
- printf("cputest <all/mnemonic> (<start mnemonic>) (continue)\n");
+ printf("cputest (<group>)/<all/mnemonic> (<start mnemonic>) (other params)\n");
printf("mnemonic = test single mnemonic\n");
printf("all = test all\n");
printf("all <mnemonic> = test all, starting from <mnemonic>\n");
- printf("all <mnemonic> next = test all, starting after <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");
- printf("askifmissing = ask for new path if dat file is missing.\n");
+ printf("all <mnemonic> -next = test all, starting after <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");
+ 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;
}
-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.
- Memory writes, including stack modifications (if any)
- Loop mode for JIT testing. (generates <test instruction>, 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:
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.
- 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:
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.
- 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:
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.
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");
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");
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;
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);
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);
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++) {