mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
4297 lines
105 KiB
C
4297 lines
105 KiB
C
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <memory.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#include "msc_dirent.h"
|
|
#else
|
|
#include <dirent.h>
|
|
#endif
|
|
|
|
typedef unsigned int uae_u32;
|
|
typedef int uae_s32;
|
|
typedef unsigned short uae_u16;
|
|
typedef short uae_s16;
|
|
typedef unsigned char uae_u8;
|
|
typedef signed char uae_s8;
|
|
|
|
#include "cputest_defines.h"
|
|
|
|
extern void callinflate(uae_u8*, uae_u8*,uae_u8*);
|
|
|
|
struct fpureg
|
|
{
|
|
uae_u16 exp;
|
|
uae_u16 dummy;
|
|
uae_u32 m[2];
|
|
};
|
|
|
|
// must match asm.S
|
|
struct registers
|
|
{
|
|
uae_u32 regs[16];
|
|
uae_u32 ssp;
|
|
uae_u32 msp;
|
|
uae_u32 pc;
|
|
uae_u32 sr;
|
|
uae_u32 expsr;
|
|
uae_u32 exc, exc010;
|
|
uae_u32 excframe;
|
|
uae_u32 tracecnt;
|
|
uae_u16 tracedata[6];
|
|
uae_u32 cycles, cycles2, cyclest;
|
|
uae_u32 blttemp;
|
|
|
|
struct fpureg fpuregs[8];
|
|
uae_u32 fpiar, fpcr, fpsr;
|
|
uae_u32 fsave[216 / 4];
|
|
|
|
uae_u32 srcaddr, dstaddr, branchtarget;
|
|
uae_u8 branchtarget_mode;
|
|
uae_u32 endpc;
|
|
|
|
uae_u16 fpeaset;
|
|
};
|
|
|
|
static short continue_on_error;
|
|
static struct registers test_regs;
|
|
static struct registers last_regs;
|
|
static struct registers cur_regs;
|
|
static uae_u8 *opcode_memory;
|
|
static uae_u32 opcode_memory_addr;
|
|
static uae_u8 *low_memory;
|
|
static uae_u8 *high_memory;
|
|
static int low_memory_size;
|
|
static int high_memory_size;
|
|
static uae_u32 test_low_memory_start, test_low_memory_end;
|
|
static uae_u32 test_high_memory_start, test_high_memory_end;
|
|
static uae_u8 *test_memory;
|
|
static uae_u32 test_memory_addr, test_memory_end;
|
|
static uae_u32 test_memory_size;
|
|
static uae_u8 *test_data;
|
|
#define BLT_TEMP_SIZE 8000
|
|
static uae_u32 blt_data;
|
|
static uae_u8 *safe_memory_start, *safe_memory_end;
|
|
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 uae_u32 oldvbr;
|
|
static uae_u8 *vbr_zero = 0;
|
|
static int hmem_rom, lmem_rom;
|
|
static uae_u8 *absallocated;
|
|
static int cpu_lvl, fpu_model;
|
|
static uae_u16 sr_undefined_mask;
|
|
static int check_undefined_sr;
|
|
static short is_fpu_adjust;
|
|
static short fpu_adjust_exp;
|
|
struct fpureg fpu_adjust;
|
|
static uae_u32 cpustatearraystore[16];
|
|
static uae_u32 cpustatearraynew[] = {
|
|
0x00000005, // SFC
|
|
0x00000005, // DFC
|
|
0x00000000, // CACR
|
|
0x00000000, // CAAR
|
|
0x00000000, // MSP
|
|
};
|
|
|
|
static uae_u8 *low_memory_temp;
|
|
static uae_u8 *high_memory_temp;
|
|
static uae_u8 *low_memory_back;
|
|
static uae_u8 *high_memory_back;
|
|
static int low_memory_offset;
|
|
static int high_memory_offset;
|
|
|
|
static uae_u32 vbr[256];
|
|
static int exceptioncount[3][128];
|
|
static int supercnt;
|
|
static uae_u32 startpc, endpc;
|
|
|
|
static char inst_name[16+1];
|
|
#define PAGE_OUTBUFFER_SIZE 3000
|
|
static int outbuffer_size;
|
|
static char *stored_outbuffer;
|
|
static char outbuffer[6000];
|
|
static char outbuffer2[6000];
|
|
static char tmpbuffer[1024];
|
|
static char path[256];
|
|
|
|
static char *outbp;
|
|
static short infoadded;
|
|
static int errors;
|
|
static int totalerrors;
|
|
static int testcnt;
|
|
static short testcntsub, testcntsubmax;
|
|
static int invalidcycles;
|
|
static int fpu_approx, fpu_approxcnt;
|
|
static short dooutput = 1;
|
|
static short quit;
|
|
static uae_u8 ccr_mask;
|
|
static uae_u32 fpsr_ignore_mask;
|
|
static uae_u32 addressing_mask = 0x00ffffff;
|
|
static uae_u32 interrupt_mask;
|
|
static short initial_interrupt_mask;
|
|
static short initial_interrupt;
|
|
static short loop_mode_jit, loop_mode_68010, loop_mode_cnt;
|
|
static short instructionsize;
|
|
static short disasm;
|
|
static short basicexcept;
|
|
static short excskipccr;
|
|
static short skipmemwrite;
|
|
static short skipregchange;
|
|
static short skipccrchange;
|
|
static short askifmissing;
|
|
static short nextall;
|
|
static short exitmode;
|
|
static int exitcnt, exitcnt2, irqcnt;
|
|
static short cycles, cycles_range, cycles_adjust;
|
|
static short gotcycles;
|
|
static short interrupttest;
|
|
static short interrupt_delay_cnt;
|
|
static short interrupttest_diff_cnt;
|
|
static uae_u32 cyclecounter_addr;
|
|
static int errorcnt;
|
|
static short uaemode;
|
|
#ifdef AMIGA
|
|
static short interrupt_count;
|
|
static uae_u16 main_intena;
|
|
#endif
|
|
static int prealloc_test_data_size = 1001000;
|
|
static int prealloc_gzip_size = 500000;
|
|
static int prealloc;
|
|
static uae_u8 *debug_item_data;
|
|
static short debug_item_count;
|
|
|
|
#define SIZE_STORED_ADDRESS_OFFSET 6
|
|
#define SIZE_STORED_ADDRESS 20
|
|
static uae_u8 srcaddr[SIZE_STORED_ADDRESS];
|
|
static uae_u8 dstaddr[SIZE_STORED_ADDRESS];
|
|
static uae_u8 branchtarget[SIZE_STORED_ADDRESS];
|
|
static uae_u8 stackaddr[SIZE_STORED_ADDRESS];
|
|
static uae_u32 stackaddr_ptr;
|
|
static uae_u8 *opcode_memory_end;
|
|
|
|
#define MAX_IPL_PC_VALUES 4
|
|
static uae_u32 ipl_pc[MAX_IPL_PC_VALUES];
|
|
static int ipl_pc_cnt[MAX_IPL_PC_VALUES];
|
|
|
|
static char opcode[32], group[32], cpustr[10];
|
|
|
|
void *x_opendir(char *path);
|
|
void x_closedir(void *v);
|
|
int x_readdir(char *path, void *v, char *s);
|
|
|
|
#ifndef M68K
|
|
|
|
#define xmemcpy memcpy
|
|
|
|
static uae_u8 *allocate_absolute(uae_u32 addr, uae_u32 size)
|
|
{
|
|
return calloc(1, size);
|
|
}
|
|
static void free_absolute(uae_u32 addr, uae_u32 size)
|
|
{
|
|
}
|
|
static uae_u8 *allocate_blttemp(uae_u32 size)
|
|
{
|
|
return calloc(1, size);
|
|
}
|
|
static void free_blttemp(uae_u32 addr, uae_u32 size)
|
|
{
|
|
}
|
|
static void execute_test000(struct registers *regs)
|
|
{
|
|
}
|
|
static void execute_test010(struct registers *regs)
|
|
{
|
|
}
|
|
static void execute_test020(struct registers *regs)
|
|
{
|
|
}
|
|
static void execute_testfpu(struct registers *regs)
|
|
{
|
|
}
|
|
static uae_u32 tosuper(uae_u32 v, uae_u32 vv)
|
|
{
|
|
return 0;
|
|
}
|
|
static void touser(uae_u32 v, uae_u32 vv)
|
|
{
|
|
}
|
|
static uae_u32 exceptiontable000, exceptiontable010, exceptiontable020, exceptiontablefpu;
|
|
static uae_u32 testexit(void)
|
|
{
|
|
return 0;
|
|
}
|
|
static uae_u32 setvbr(uae_u32 v)
|
|
{
|
|
return 0;
|
|
}
|
|
static uae_u32 get_cpu_model(void)
|
|
{
|
|
return 0;
|
|
}
|
|
static void setcpu(uae_u32 v, uae_u32 *s, uae_u32 *d)
|
|
{
|
|
}
|
|
static void flushcache(uae_u32 v)
|
|
{
|
|
}
|
|
static void berrcopy(void *src, void *dst, uae_u32 size, uae_u32 hasvbr)
|
|
{
|
|
}
|
|
static uae_u32 fpucomp(void *v)
|
|
{
|
|
return 0;
|
|
}
|
|
static uae_u32 fpucompzero(void *v)
|
|
{
|
|
return 0;
|
|
}
|
|
static void initfpu(void)
|
|
{
|
|
}
|
|
static void *error_vector;
|
|
#else
|
|
|
|
static void xmemcpy(void *d, void *s, int size)
|
|
{
|
|
__builtin_memcpy(d, s, size);
|
|
}
|
|
|
|
extern uae_u8 *allocate_absolute(uae_u32, uae_u32);
|
|
extern void free_absolute(uae_u32, uae_u32);
|
|
extern uae_u32 allocate_blttemp(uae_u32);
|
|
extern void free_blttemp(uae_u32, uae_u32);
|
|
extern void execute_test000(struct registers*);
|
|
extern void execute_test010(struct registers *);
|
|
extern void execute_test020(struct registers *);
|
|
extern void execute_testfpu(struct registers *);
|
|
extern uae_u32 tosuper(uae_u32, uae_u32);
|
|
extern void touser(uae_u32, uae_u32);
|
|
extern uae_u32 exceptiontable000, exceptiontable010, exceptiontable020, exceptiontablefpu;
|
|
extern uae_u32 testexit(void);
|
|
extern uae_u32 setvbr(uae_u32);
|
|
extern uae_u32 get_cpu_model(void);
|
|
extern void setcpu(uae_u32, uae_u32*, uae_u32*);
|
|
extern void flushcache(uae_u32);
|
|
extern void *error_vector;
|
|
extern void berrcopy(void*, void*, uae_u32, uae_u32);
|
|
extern uae_u32 fpucomp(void *);
|
|
extern uae_u32 fpucompzero(void *);
|
|
extern void initfpu(void);
|
|
|
|
#endif
|
|
static uae_u32 exceptiontableinuse;
|
|
|
|
struct accesshistory
|
|
{
|
|
uae_u8 *addr;
|
|
uae_u32 val;
|
|
uae_u32 oldval;
|
|
int size;
|
|
};
|
|
static int ahcnt;
|
|
|
|
#define MAX_ACCESSHIST 64
|
|
static struct accesshistory ahist[MAX_ACCESSHIST];
|
|
|
|
static int is_valid_test_addr_read(uae_u32 a)
|
|
{
|
|
a &= addressing_mask;
|
|
if ((uae_u8*)a >= safe_memory_start && (uae_u8*)a < safe_memory_end && (safe_memory_mode & 1))
|
|
return 0;
|
|
return (a >= test_low_memory_start && a < test_low_memory_end && test_low_memory_start != 0xffffffff) ||
|
|
(a >= test_high_memory_start && a < test_high_memory_end && test_high_memory_start != 0xffffffff) ||
|
|
(a >= test_memory_addr && a < test_memory_end);
|
|
}
|
|
|
|
static int is_valid_test_addr_readwrite(uae_u32 a)
|
|
{
|
|
a &= addressing_mask;
|
|
if ((uae_u8*)a >= safe_memory_start && (uae_u8*)a < safe_memory_end)
|
|
return 0;
|
|
return (a >= test_low_memory_start && a < test_low_memory_end && test_low_memory_start != 0xffffffff) ||
|
|
(a >= test_high_memory_start && a < test_high_memory_end && test_high_memory_start != 0xffffffff) ||
|
|
(a >= test_memory_addr && a < test_memory_end);
|
|
}
|
|
|
|
uae_u16 lw(uae_u16 *p)
|
|
{
|
|
if (!is_valid_test_addr_read((uae_u32)p) || !is_valid_test_addr_read(((uae_u32)p) + 1))
|
|
return 0;
|
|
return *p;
|
|
}
|
|
uae_u32 llu(uae_u16 *p)
|
|
{
|
|
if (!is_valid_test_addr_read((uae_u32)p) || !is_valid_test_addr_read(((uae_u32)p) + 1))
|
|
return 0;
|
|
if (!is_valid_test_addr_read(((uae_u32)p) + 2) || !is_valid_test_addr_read(((uae_u32)p) + 3))
|
|
return p[0] << 16;
|
|
return *(uae_u32*)p;
|
|
}
|
|
uae_s32 lls(uae_u16 *p)
|
|
{
|
|
return (uae_s32)llu(p);
|
|
}
|
|
|
|
static void endinfo(void)
|
|
{
|
|
printf("Last test: %u\n", testcnt);
|
|
uae_u8 *p = opcode_memory;
|
|
for (int i = 0; i < 4 * 2; i += 2) {
|
|
if (!is_valid_test_addr_read((uae_u32)(&p[i])))
|
|
break;
|
|
uae_u16 v = (p[i] << 8) | (p[i + 1]);
|
|
printf("%08x %04x\n", (uae_u32)&p[i], v);
|
|
if (v == ILLG_OPCODE && i > 0)
|
|
break;
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static void safe_memcpy(uae_u8 *d, uae_u8 *s, int size)
|
|
{
|
|
if (safe_memory_start == (uae_u8*)0xffffffff && safe_memory_end == (uae_u8*)0xffffffff) {
|
|
xmemcpy(d, s, size);
|
|
return;
|
|
}
|
|
if (safe_memory_end <= d || safe_memory_start >= d + size) {
|
|
if (safe_memory_end <= s || safe_memory_start >= s + size) {
|
|
xmemcpy(d, s, size);
|
|
return;
|
|
}
|
|
}
|
|
while (size > 0) {
|
|
int size2 = size > sizeof(tmpbuffer) ? sizeof(tmpbuffer) : size;
|
|
if ((d + size2 > safe_memory_start && d < safe_memory_end) ||
|
|
(s + size2 > safe_memory_start && s < safe_memory_end)) {
|
|
for (int i = 0; i < size2; i++) {
|
|
if ((d >= safe_memory_start && d < safe_memory_end) ||
|
|
(s >= safe_memory_start && s < safe_memory_end)) {
|
|
s++;
|
|
d++;
|
|
continue;
|
|
}
|
|
*d++ = *s++;
|
|
}
|
|
} else {
|
|
xmemcpy(d, s, size2);
|
|
d += size2;
|
|
s += size2;
|
|
}
|
|
size -= size2;
|
|
}
|
|
}
|
|
|
|
static int test_active;
|
|
static uae_u32 enable_data;
|
|
static uae_u32 error_vectors[12];
|
|
|
|
// if exception happens outside of test code, jump to
|
|
// infinite loop and flash colors.
|
|
static void reset_error_vectors(void)
|
|
{
|
|
uae_u32 *p;
|
|
if (cpu_lvl == 0) {
|
|
p = (uae_u32*)vbr_zero;
|
|
} else {
|
|
p = vbr;
|
|
}
|
|
for (int i = 2; i < 4; i++) {
|
|
p[i] = error_vectors[i - 2];
|
|
}
|
|
}
|
|
|
|
static void set_error_vectors(void)
|
|
{
|
|
uae_u32 *p;
|
|
if (cpu_lvl == 0) {
|
|
p = (uae_u32 *)vbr_zero;
|
|
} else {
|
|
p = vbr;
|
|
}
|
|
for (int i = 2; i < 4; i++) {
|
|
p[i] = (uae_u32)&error_vector;
|
|
}
|
|
}
|
|
|
|
static void start_test(void)
|
|
{
|
|
if (test_active)
|
|
return;
|
|
|
|
#ifdef M68K
|
|
if (lmem_rom > 0) {
|
|
if (memcmp(low_memory, low_memory_temp, low_memory_size)) {
|
|
printf("Low memory ROM mismatch!\n");
|
|
exit(0);
|
|
}
|
|
}
|
|
if (hmem_rom > 0) {
|
|
if (memcmp(high_memory, high_memory_temp, high_memory_size)) {
|
|
printf("High memory ROM mismatch!\n");
|
|
exit(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
test_active = 1;
|
|
|
|
enable_data = tosuper(0, 1);
|
|
|
|
#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 || test_low_memory_start == 0xffffffff))
|
|
safe_memcpy(low_memory_back + 8, low_memory + 8, (192 - 2) * 4);
|
|
|
|
if (!hmem_rom && test_high_memory_start != 0xffffffff)
|
|
safe_memcpy(high_memory_back, high_memory + high_memory_offset, high_memory_size - high_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 && 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) {
|
|
uae_u32 *p = (uae_u32 *)vbr_zero;
|
|
for (int i = 2; i < 12; i++) {
|
|
p[i] = (uae_u32)(((uae_u32)&exceptiontable000) + (i - 2) * 2);
|
|
if (exception_vectors && i >= 4) {
|
|
p[i] = exception_vectors;
|
|
}
|
|
if (i < 12 + 2) {
|
|
error_vectors[i - 2] = p[i];
|
|
}
|
|
}
|
|
if (interrupttest || initial_interrupt_mask || initial_interrupt) {
|
|
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) {
|
|
p[i] = exception_vectors;
|
|
}
|
|
}
|
|
exceptiontableinuse = (uae_u32)&exceptiontable000;
|
|
} else {
|
|
oldvbr = setvbr((uae_u32)vbr);
|
|
for (int i = 2; i < 64; i++) {
|
|
if (fpu_model) {
|
|
vbr[i] = (uae_u32)(((uae_u32)&exceptiontablefpu) + (i - 2) * 2);
|
|
exceptiontableinuse = (uae_u32)&exceptiontablefpu;
|
|
} else if (cpu_lvl == 1) {
|
|
vbr[i] = (uae_u32)(((uae_u32)&exceptiontable010) + (i - 2) * 2);
|
|
exceptiontableinuse = (uae_u32)&exceptiontable010;
|
|
} else {
|
|
vbr[i] = (uae_u32)(((uae_u32)&exceptiontable020) + (i - 2) * 2);
|
|
exceptiontableinuse = (uae_u32)&exceptiontable020;
|
|
}
|
|
if (i >= 2 && i < 12) {
|
|
error_vectors[i - 2] = vbr[i];
|
|
}
|
|
if (exception_vectors && i >= 4) {
|
|
vbr[i] = exception_vectors;
|
|
}
|
|
}
|
|
}
|
|
if (cpu_lvl >= 4) {
|
|
// 68040/060 CACR (IE=1)
|
|
cpustatearraynew[2] = 0x00008000;
|
|
} else {
|
|
// 68020/30 CACR (CI=1,IE=1)
|
|
cpustatearraynew[2] = 0x0009;
|
|
if (cpu_lvl == 3) {
|
|
// 68030 CACR CD=1
|
|
cpustatearraynew[2] |= 0x0800;
|
|
}
|
|
}
|
|
|
|
setcpu(cpu_lvl, cpustatearraynew, cpustatearraystore);
|
|
}
|
|
|
|
static void end_test(void)
|
|
{
|
|
if (!test_active)
|
|
return;
|
|
test_active = 0;
|
|
|
|
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 || test_low_memory_start == 0xffffffff))
|
|
safe_memcpy(low_memory + 8, low_memory_back + 8, (192 - 2) * 4);
|
|
|
|
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) {
|
|
setvbr(oldvbr);
|
|
}
|
|
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, 1);
|
|
}
|
|
|
|
static int readdata(uae_u8 *p, int size, FILE *f, uae_u8 *unpack, int *offsetp)
|
|
{
|
|
if (!unpack) {
|
|
return fread(p, 1, size, f);
|
|
} else {
|
|
memcpy(p, unpack + (*offsetp), size);
|
|
(*offsetp) += size;
|
|
return size;
|
|
}
|
|
}
|
|
static void seekdata(int seek, FILE *f, uae_u8 *unpack, int *offsetp)
|
|
{
|
|
if (!unpack) {
|
|
fseek(f, seek, SEEK_CUR);
|
|
} else {
|
|
(*offsetp) += seek;
|
|
}
|
|
}
|
|
|
|
static uae_u8 *parse_gzip(uae_u8 *gzbuf, int *sizep)
|
|
{
|
|
uae_u8 *end = gzbuf + (*sizep);
|
|
uae_u8 flags = gzbuf[3];
|
|
uae_u16 v;
|
|
if (gzbuf[0] != 0x1f && gzbuf[1] != 0x8b)
|
|
return NULL;
|
|
gzbuf += 10;
|
|
if (flags & 2) /* multipart not supported */
|
|
return NULL;
|
|
if (flags & 32) /* encryption not supported */
|
|
return NULL;
|
|
if (flags & 4) { /* skip extra field */
|
|
v = *gzbuf++;
|
|
v |= (*gzbuf++) << 8;
|
|
gzbuf += v + 2;
|
|
}
|
|
if (flags & 8) { /* get original file name */
|
|
while (*gzbuf++);
|
|
}
|
|
if (flags & 16) { /* skip comment */
|
|
while (*gzbuf++);
|
|
}
|
|
*sizep = (end[-4] << 0) | (end[-3] << 8) | (end[-2] << 16) | (end[-1] << 24);
|
|
return gzbuf;
|
|
}
|
|
|
|
#define INFLATE_STACK_SIZE 3000
|
|
static uae_u8 *inflatestack;
|
|
static uae_u8 *prealloc_gzip;
|
|
|
|
#ifdef AMIGA
|
|
extern void uae_command(char*);
|
|
#endif
|
|
|
|
static int set_berr(int mask, int ask)
|
|
{
|
|
#ifdef AMIGA
|
|
if (uaemode) {
|
|
if (!mask) {
|
|
sprintf(tmpbuffer, "dbg \"w 0\"");
|
|
} else {
|
|
sprintf(tmpbuffer, "dbg \"w 0 %08x %08x B%s%s%s\"", (uae_u32)safe_memory_start, safe_memory_end - safe_memory_start, (mask & 1) ? "R" : "", (mask & 2) ? "W" : "", (mask & 4) ? "P" : "");
|
|
}
|
|
uae_command(tmpbuffer);
|
|
return 0;
|
|
}
|
|
#endif
|
|
if (!ask) {
|
|
return 0;
|
|
}
|
|
if (mask) {
|
|
printf("Re-enable write bus error mode and press any key (ESC=abort)\n");
|
|
} else {
|
|
printf("Disable write bus error mode and press any key (SPACE=skip,ESC=abort)\n");
|
|
}
|
|
return getchar();
|
|
}
|
|
|
|
static int load_file_offset(FILE *f, int *foffsetp)
|
|
{
|
|
int size = -1;
|
|
unsigned char buf[4] = { 0 };
|
|
fseek(f, *foffsetp, SEEK_SET);
|
|
fread(buf, 1, sizeof(buf), f);
|
|
if (buf[0] == 0xaf && buf[1] == 'M' && buf[2] == 'R' && buf[3] == 'G') {
|
|
fread(buf, 1, sizeof(buf), f);
|
|
size = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3] << 0);
|
|
if (size == 0) {
|
|
*foffsetp = -1;
|
|
return 0;
|
|
}
|
|
*foffsetp = *foffsetp + size + 8;
|
|
return size;
|
|
}
|
|
*foffsetp = -1;
|
|
return -1;
|
|
}
|
|
|
|
static uae_u8 *load_file(const char *path, const char *file, uae_u8 *p, int *sizep, int *foffsetp, int exiterror, int candirect)
|
|
{
|
|
char fname[256];
|
|
uae_u8 *unpack = NULL;
|
|
int unpackoffset = 0;
|
|
int size = 0;
|
|
int foffset = foffsetp ? *foffsetp : 0;
|
|
|
|
if (path) {
|
|
strcpy(fname, path);
|
|
strcat(fname, file);
|
|
if (strchr(file, '.')) {
|
|
fname[strlen(fname) - 1] = 'z';
|
|
} else {
|
|
strcat(fname, ".gz");
|
|
}
|
|
} else {
|
|
strcpy(fname, file);
|
|
}
|
|
FILE *f = fopen(fname, "rb");
|
|
if (f) {
|
|
int gsize = 0;
|
|
if (foffsetp) {
|
|
gsize = load_file_offset(f, foffsetp);
|
|
if (gsize == 0) {
|
|
return NULL;
|
|
}
|
|
if (gsize > 0) {
|
|
*sizep = gsize;
|
|
}
|
|
}
|
|
if (*sizep <= 0 || foffsetp == NULL) {
|
|
fseek(f, 0, SEEK_END);
|
|
gsize = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
}
|
|
|
|
uae_u8 *gzbuf = NULL;
|
|
if (prealloc) {
|
|
if (!prealloc_gzip) {
|
|
prealloc_gzip = malloc(prealloc_gzip_size);
|
|
if (!prealloc_gzip) {
|
|
printf("Couldn't preallocate gzip temp memory, %d bytes.\n", prealloc_gzip_size);
|
|
exit(0);
|
|
}
|
|
}
|
|
if (gsize > prealloc_gzip_size) {
|
|
printf("Preallocated gzip temp memory space is too small %d < %d\n", prealloc_gzip_size, gsize);
|
|
exit(0);
|
|
}
|
|
gzbuf = prealloc_gzip;
|
|
} else {
|
|
gzbuf = malloc(gsize);
|
|
}
|
|
if (!gzbuf) {
|
|
printf("Couldn't allocate %d bytes (packed), file '%s'\n", gsize, fname);
|
|
exit(0);
|
|
}
|
|
if (fread(gzbuf, 1, gsize, f) != gsize) {
|
|
printf("Couldn't read file '%s'\n", fname);
|
|
exit(0);
|
|
}
|
|
fclose(f);
|
|
size = gsize;
|
|
uae_u8 *gzdata = parse_gzip(gzbuf, &size);
|
|
if (!gzdata) {
|
|
printf("Couldn't parse gzip file '%s'\n", fname);
|
|
exit(0);
|
|
}
|
|
f = NULL;
|
|
if (!inflatestack) {
|
|
inflatestack = malloc(INFLATE_STACK_SIZE);
|
|
if (!inflatestack) {
|
|
printf("Couldn't allocate %d bytes (inflate stack)\n", INFLATE_STACK_SIZE);
|
|
exit(0);
|
|
}
|
|
inflatestack += INFLATE_STACK_SIZE;
|
|
}
|
|
if (!p) {
|
|
p = calloc(1, size);
|
|
if (!p) {
|
|
printf("Couldn't allocate %d bytes, file '%s'\n", size, fname);
|
|
exit(0);
|
|
}
|
|
printf("Decompressing '%s' (%d -> %d)\n", fname, gsize, size);
|
|
callinflate(p, gzdata, inflatestack);
|
|
*sizep = size;
|
|
if (!prealloc_gzip) {
|
|
free(gzbuf);
|
|
}
|
|
return p;
|
|
} else if (candirect) {
|
|
printf("Decompressing '%s' (%d -> %d)\n", fname, gsize, size);
|
|
callinflate(p, gzdata, inflatestack);
|
|
*sizep = size;
|
|
if (!prealloc_gzip) {
|
|
free(gzbuf);
|
|
}
|
|
return p;
|
|
} else {
|
|
unpack = calloc(1, size);
|
|
if (!unpack) {
|
|
printf("Couldn't allocate %d bytes (unpack), file '%s'\n", size, fname);
|
|
exit(0);
|
|
}
|
|
printf("Decompressing '%s' (%d -> %d)\n", fname, gsize, size);
|
|
callinflate(unpack, gzdata, inflatestack);
|
|
*sizep = size;
|
|
}
|
|
if (!prealloc_gzip) {
|
|
free(gzbuf);
|
|
}
|
|
}
|
|
if (!unpack) {
|
|
if (path) {
|
|
sprintf(fname, "%s%s", path, file);
|
|
} else {
|
|
strcpy(fname, file);
|
|
}
|
|
f = fopen(fname, "rb");
|
|
if (!f) {
|
|
if (exiterror) {
|
|
printf("Couldn't open '%s'\n", fname);
|
|
exit(0);
|
|
}
|
|
return NULL;
|
|
}
|
|
if (foffsetp) {
|
|
size = load_file_offset(f, foffsetp);
|
|
if (size == 0) {
|
|
return NULL;
|
|
}
|
|
if (size > 0) {
|
|
*sizep = size;
|
|
}
|
|
}
|
|
size = *sizep;
|
|
if (size <= 0) {
|
|
fseek(f, 0, SEEK_END);
|
|
size = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
}
|
|
}
|
|
if (!p) {
|
|
p = calloc(1, size);
|
|
if (!p) {
|
|
printf("Couldn't allocate %d bytes, file '%s'\n", size, fname);
|
|
exit(0);
|
|
}
|
|
} else {
|
|
if (prealloc_test_data_size < size) {
|
|
printf("Preallocated memory space is too small %d < %d\n", prealloc_test_data_size, size);
|
|
exit(0);
|
|
}
|
|
}
|
|
if (safe_memory_end < p || safe_memory_start >= p + size) {
|
|
*sizep = readdata(p, size, f, unpack, &unpackoffset);
|
|
} else {
|
|
if (size > 0 && p < safe_memory_start) {
|
|
int size2 = safe_memory_start - p;
|
|
if (size2 > size)
|
|
size2 = size;
|
|
if (readdata(p, size2, f, unpack, &unpackoffset) != size2)
|
|
goto end;
|
|
p += size2;
|
|
size -= size2;
|
|
}
|
|
if ((safe_memory_mode & 2) && (safe_memory_mode & (1 | 4))) {
|
|
// if writing causes bus error and other bit(s) are also active: skip it
|
|
if (size > 0 && p >= safe_memory_start && p < safe_memory_end) {
|
|
int size2 = safe_memory_end - p;
|
|
if (size2 > size)
|
|
size2 = size;
|
|
seekdata(size2, f, unpack, &unpackoffset);
|
|
p += size2;
|
|
size -= size2;
|
|
}
|
|
} else if (safe_memory_mode == 2) {
|
|
// if only writes generate bus error: load data if different
|
|
if (size > 0 && p >= safe_memory_start && p < safe_memory_end) {
|
|
int size2 = safe_memory_end - p;
|
|
if (size2 > size)
|
|
size2 = size;
|
|
uae_u8 *tmp = malloc(size2);
|
|
if (!tmp) {
|
|
printf("Couldn't allocate safe tmp memory (%d bytes)\n", size2);
|
|
exit(0);
|
|
}
|
|
readdata(tmp, size2, f, unpack, &unpackoffset);
|
|
if (memcmp(tmp, p, size2)) {
|
|
uae_u32 t = tosuper(0, 0);
|
|
berrcopy(tmp, p, size2 / 2, cpu_lvl > 0);
|
|
touser(t, 0);
|
|
if (memcmp(tmp, p, size2)) {
|
|
// data was still different, do it manually.
|
|
int ch = set_berr(0, 1);
|
|
if (ch == 27) {
|
|
exit(0);
|
|
} else if (ch == 32) {
|
|
seekdata(size2, f, unpack, &unpackoffset);
|
|
p += size2;
|
|
size -= size2;
|
|
} else {
|
|
memcpy(p, tmp, size2);
|
|
p += size2;
|
|
size -= size2;
|
|
ch = set_berr(2, 1);
|
|
if (ch == 27) {
|
|
exit(0);
|
|
}
|
|
}
|
|
} else {
|
|
p += size2;
|
|
size -= size2;
|
|
}
|
|
} else {
|
|
// data was identical
|
|
printf("Write-only bus error mode, data already correct. Skipping read.\n");
|
|
p += size2;
|
|
size -= size2;
|
|
}
|
|
free(tmp);
|
|
}
|
|
}
|
|
if (size > 0) {
|
|
if (readdata(p, size, f, unpack, &unpackoffset) != size)
|
|
goto end;
|
|
}
|
|
size = *sizep;
|
|
}
|
|
if (*sizep != size) {
|
|
end:
|
|
printf("Couldn't read file '%s' (%d <> %d)\n", fname, *sizep, size);
|
|
exit(0);
|
|
}
|
|
if (f) {
|
|
fclose(f);
|
|
}
|
|
if (unpack) {
|
|
free(unpack);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static void endinfo_debug(uae_u8 *p)
|
|
{
|
|
printf("PTR %08x, %08x, count %d\n", debug_item_data, p, debug_item_count);
|
|
}
|
|
|
|
static void pl(uae_u8 *p, uae_u32 v)
|
|
{
|
|
p[0] = v >> 24;
|
|
p[1] = v >> 16;
|
|
p[2] = v >> 8;
|
|
p[3] = v >> 0;
|
|
}
|
|
static void pw(uae_u8 *p, uae_u32 v)
|
|
{
|
|
p[0] = v >> 8;
|
|
p[1] = v >> 0;
|
|
}
|
|
|
|
static uae_u32 gl(uae_u8 *p)
|
|
{
|
|
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
|
|
}
|
|
|
|
static uae_u16 gw(uae_u8 *p)
|
|
{
|
|
return (p[0] << 8) | (p[1] << 0);
|
|
}
|
|
|
|
static uae_u8 *restore_fpvalue(uae_u8 *p, struct fpureg *fp)
|
|
{
|
|
uae_u8 v = *p++;
|
|
if ((v & CT_SIZE_MASK) != CT_SIZE_FPU) {
|
|
end_test();
|
|
printf("Expected CT_SIZE_FPU, got %02x\n", v);
|
|
endinfo_debug(p);
|
|
endinfo();
|
|
exit(0);
|
|
}
|
|
fp->dummy = 0;
|
|
uae_u8 size = *p++;
|
|
if (size == 0x00) {
|
|
fp->exp = 0;
|
|
fp->m[0] = fp->m[1] = 0;
|
|
} else if (size == 0xff) {
|
|
fp->exp = gw(p);
|
|
p += 2;
|
|
fp->m[0] = gl(p);
|
|
p += 4;
|
|
fp->m[1] = gl(p);
|
|
p += 4;
|
|
} else {
|
|
uae_u8 f[10];
|
|
f[0] = fp->exp >> 8;
|
|
f[1] = fp->exp;
|
|
f[2] = fp->m[0] >> 24;
|
|
f[3] = fp->m[0] >> 16;
|
|
f[4] = fp->m[0] >> 8;
|
|
f[5] = fp->m[0] >> 0;
|
|
f[6] = fp->m[1] >> 24;
|
|
f[7] = fp->m[1] >> 16;
|
|
f[8] = fp->m[1] >> 8;
|
|
f[9] = fp->m[1] >> 0;
|
|
uae_u8 size1 = (size >> 4) & 15;
|
|
for (uae_u8 i = 0; i < size1; i++) {
|
|
f[i] = *p++;
|
|
}
|
|
uae_u8 size2 = (size >> 0) & 15;
|
|
for (uae_u8 i = 0; i < size2; i++) {
|
|
f[9 - i] = *p++;
|
|
}
|
|
fp->exp = (f[0] << 8) | (f[1] << 0);
|
|
fp->m[0] = (f[2] << 24) | (f[3] << 16) | (f[4] << 8) | (f[5] << 0);
|
|
fp->m[1] = (f[6] << 24) | (f[7] << 16) | (f[8] << 8) | (f[9] << 0);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static uae_u8 *restore_value(uae_u8 *p, uae_u32 *vp, int *sizep)
|
|
{
|
|
uae_u32 val = *vp;
|
|
uae_u8 v = *p++;
|
|
switch(v & CT_SIZE_MASK)
|
|
{
|
|
case CT_SIZE_BYTE:
|
|
val &= 0xffffff00;
|
|
val |= *p++;
|
|
*sizep = 0;
|
|
break;
|
|
case CT_SIZE_WORD:
|
|
val &= 0xffff0000;
|
|
val |= (*p++) << 8;
|
|
val |= *p++;
|
|
*sizep = 1;
|
|
break;
|
|
case CT_SIZE_LONG:
|
|
val = (*p++) << 24;
|
|
val |= (*p++) << 16;
|
|
val |= (*p++) << 8;
|
|
val |= *p++;
|
|
*sizep = 2;
|
|
break;
|
|
case CT_SIZE_FPU:
|
|
end_test();
|
|
printf("Unexpected CT_SIZE_FPU (%02x)\n", v);
|
|
endinfo_debug(p);
|
|
endinfo();
|
|
exit(0);
|
|
break;
|
|
}
|
|
*vp = val;
|
|
return p;
|
|
}
|
|
|
|
static uae_u8 *restore_rel(uae_u8 *p, uae_u32 *vp, int nocheck)
|
|
{
|
|
uae_u32 v = *vp;
|
|
switch ((*p++) & CT_SIZE_MASK)
|
|
{
|
|
case CT_RELATIVE_START_BYTE:
|
|
{
|
|
uae_u8 val;
|
|
val = *p++;
|
|
v += (uae_s8)val;
|
|
break;
|
|
}
|
|
case CT_RELATIVE_START_WORD:
|
|
{
|
|
uae_u16 val;
|
|
val = (*p++) << 8;
|
|
val |= *p++;
|
|
v += (uae_s16)val;
|
|
break;
|
|
}
|
|
case CT_ABSOLUTE_WORD:
|
|
{
|
|
uae_u16 val;
|
|
val = (*p++) << 8;
|
|
val |= *p++;
|
|
v = (uae_s32)(uae_s16)val;
|
|
break;
|
|
}
|
|
case CT_ABSOLUTE_LONG:
|
|
{
|
|
uae_u32 val;
|
|
val = (*p++) << 24;
|
|
val |= (*p++) << 16;
|
|
val |= (*p++) << 8;
|
|
val |= *p++;
|
|
v = val;
|
|
if (!nocheck) {
|
|
if (test_low_memory_start != 0xffffffff && (val & addressing_mask) < low_memory_size) {
|
|
; // low memory
|
|
} 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();
|
|
printf("restore_rel CT_ABSOLUTE_LONG outside of test memory! %08x\n", v);
|
|
endinfo_debug(p);
|
|
endinfo();
|
|
exit(0);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
*vp = v;
|
|
return p;
|
|
}
|
|
|
|
static uae_u8 *restore_rel_ordered(uae_u8 *p, uae_u32 *vp)
|
|
{
|
|
if (*p == CT_EMPTY)
|
|
return p + 1;
|
|
return restore_rel(p, vp, 1);
|
|
}
|
|
|
|
|
|
static void validate_mode(uae_u8 *p, uae_u8 v)
|
|
{
|
|
uae_u8 mode = p[0];
|
|
if ((mode & CT_DATA_MASK) != v) {
|
|
end_test();
|
|
printf("CT_MEMWRITE expected but got %02X\n", mode);
|
|
endinfo_debug(p);
|
|
endinfo();
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
static uae_u8 *get_memory_addr(uae_u8 *p, uae_u8 **addrp)
|
|
{
|
|
uae_u8 v = *p++;
|
|
switch(v & CT_SIZE_MASK)
|
|
{
|
|
case CT_ABSOLUTE_WORD:
|
|
{
|
|
uae_u16 val;
|
|
val = (*p++) << 8;
|
|
val |= *p++;
|
|
uae_u8 *addr;
|
|
uae_s16 offset = (uae_s16)val;
|
|
if (offset < 0) {
|
|
addr = high_memory + 32768 + offset;
|
|
} else {
|
|
addr = low_memory + offset;
|
|
}
|
|
validate_mode(p, CT_MEMWRITE);
|
|
*addrp = addr;
|
|
return p;
|
|
}
|
|
case CT_ABSOLUTE_LONG:
|
|
{
|
|
uae_u32 val;
|
|
val = (*p++) << 24;
|
|
val |= (*p++) << 16;
|
|
val |= (*p++) << 8;
|
|
val |= *p++;
|
|
if (val < low_memory_size) {
|
|
#ifndef M68K
|
|
uae_u8 *addr = low_memory + val;
|
|
#else
|
|
uae_u8 *addr = (uae_u8*)val;
|
|
#endif
|
|
validate_mode(p, CT_MEMWRITE);
|
|
*addrp = addr;
|
|
return p;
|
|
} else if (val >= test_memory_addr && val < test_memory_addr + test_memory_size) {
|
|
#ifndef M68K
|
|
uae_u8 *addr = test_memory + (val - test_memory_addr);
|
|
#else
|
|
uae_u8 *addr = (uae_u8*)val;
|
|
#endif
|
|
validate_mode(p, CT_MEMWRITE);
|
|
*addrp = addr;
|
|
return p;
|
|
} else {
|
|
end_test();
|
|
printf("get_memory_addr CT_ABSOLUTE_LONG outside of test memory! %08x\n", val);
|
|
endinfo_debug(p);
|
|
endinfo();
|
|
exit(0);
|
|
}
|
|
}
|
|
case CT_RELATIVE_START_WORD:
|
|
{
|
|
uae_u16 val;
|
|
val = (*p++) << 8;
|
|
val |= *p++;
|
|
uae_s16 offset = (uae_s16)val;
|
|
uae_u8 *addr = opcode_memory + offset;
|
|
validate_mode(p, CT_MEMWRITE);
|
|
*addrp = addr;
|
|
return p;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
end_test();
|
|
printf("get_memory_addr unknown size %02x\n", v);
|
|
endinfo_debug(p);
|
|
endinfo();
|
|
exit(0);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void tomem(uae_u8 *p, uae_u32 v, uae_u32 oldv, int size, int storedata)
|
|
{
|
|
if (ahcnt >= MAX_ACCESSHIST) {
|
|
end_test();
|
|
printf("History index too large! %d >= %d\n", ahcnt, MAX_ACCESSHIST);
|
|
exit(0);
|
|
}
|
|
|
|
if (storedata) {
|
|
struct accesshistory *ah = &ahist[ahcnt++];
|
|
ah->oldval = oldv;
|
|
ah->val = v;
|
|
ah->size = size;
|
|
ah->addr = p;
|
|
}
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
p[0] = (uae_u8)v;
|
|
break;
|
|
case 1:
|
|
p[0] = (uae_u8)(v >> 8);
|
|
p[1] = (uae_u8)(v >> 0);
|
|
break;
|
|
case 2:
|
|
p[0] = (uae_u8)(v >> 24);
|
|
p[1] = (uae_u8)(v >> 16);
|
|
p[2] = (uae_u8)(v >> 8);
|
|
p[3] = (uae_u8)(v >> 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void restoreahist(void)
|
|
{
|
|
if (!ahcnt)
|
|
return;
|
|
for (int i = ahcnt - 1; i >= 0; i--) {
|
|
struct accesshistory *ah = &ahist[i];
|
|
tomem(ah->addr, ah->oldval, 0, ah->size, 0);
|
|
}
|
|
ahcnt = 0;
|
|
}
|
|
|
|
static uae_u8 *restore_bytes(uae_u8 *mem, uae_u8 *p)
|
|
{
|
|
uae_u8 *addr = mem;
|
|
uae_u16 len, offset;
|
|
if (*p == 0xff) {
|
|
p++;
|
|
offset = *p++;
|
|
len = *p++;
|
|
if (len == 0) {
|
|
len = 256;
|
|
}
|
|
} else {
|
|
uae_u8 v = *p++;
|
|
offset = v >> 5;
|
|
len = v & 31;
|
|
if (len == 0) {
|
|
len = 32;
|
|
}
|
|
}
|
|
addr += offset;
|
|
#ifndef _MSC_VER
|
|
xmemcpy(addr, p, len);
|
|
#endif
|
|
p += len;
|
|
return p;
|
|
}
|
|
|
|
static uae_u8 *restore_memory(uae_u8 *p, int storedata)
|
|
{
|
|
uae_u8 v = *p;
|
|
switch (v & CT_SIZE_MASK)
|
|
{
|
|
case CT_ABSOLUTE_WORD:
|
|
{
|
|
uae_u8 *addr;
|
|
int size;
|
|
p = get_memory_addr(p, &addr);
|
|
uae_u32 mv = 0;
|
|
uae_u32 oldv = 0;
|
|
p = restore_value(p, &oldv, &size);
|
|
p = restore_value(p, &mv, &size);
|
|
tomem(addr, mv, oldv, size, storedata);
|
|
return p;
|
|
}
|
|
case CT_ABSOLUTE_LONG:
|
|
{
|
|
uae_u8 *addr;
|
|
int size;
|
|
p = get_memory_addr(p, &addr);
|
|
uae_u32 mv = 0;
|
|
uae_u32 oldv = 0;
|
|
p = restore_value(p, &oldv, &size);
|
|
p = restore_value(p, &mv, &size);
|
|
tomem(addr, mv, oldv, size, storedata);
|
|
return p;
|
|
}
|
|
}
|
|
if ((v & CT_DATA_MASK) == CT_MEMWRITES) {
|
|
switch (v & CT_SIZE_MASK)
|
|
{
|
|
case CT_PC_BYTES:
|
|
{
|
|
p = restore_bytes(opcode_memory, p + 1);
|
|
break;
|
|
}
|
|
default:
|
|
end_test();
|
|
printf("Unknown restore_memory type!?\n");
|
|
endinfo_debug(p);
|
|
endinfo();
|
|
exit(0);
|
|
break;
|
|
}
|
|
} else {
|
|
switch (v & CT_SIZE_MASK)
|
|
{
|
|
case CT_RELATIVE_START_WORD:
|
|
{
|
|
uae_u8 *addr;
|
|
int size;
|
|
p = get_memory_addr(p, &addr);
|
|
uae_u32 mv = 0, oldv = 0;
|
|
p = restore_value(p, &oldv, &size);
|
|
p = restore_value(p, &mv, &size);
|
|
tomem(addr, mv, oldv, size, storedata);
|
|
return p;
|
|
}
|
|
default:
|
|
end_test();
|
|
printf("Unknown restore_memory type!?\n");
|
|
endinfo_debug(p);
|
|
endinfo();
|
|
exit(0);
|
|
break;
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static uae_u8 *restore_edata(uae_u8 *p)
|
|
{
|
|
p++;
|
|
uae_u8 v = *p++;
|
|
switch(v)
|
|
{
|
|
case CT_EDATA_IRQ_CYCLES:
|
|
interrupt_delay_cnt = *p++;
|
|
break;
|
|
default:
|
|
end_test();
|
|
printf("Unexpected CT_EDATA 0x%02x\n", *p);
|
|
endinfo_debug(p);
|
|
endinfo();
|
|
exit(0);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static uae_u8 *restore_data(uae_u8 *p, struct registers *r)
|
|
{
|
|
uae_u8 v = *p;
|
|
if (v & CT_END) {
|
|
end_test();
|
|
printf("Unexpected end bit!? 0x%02x offset %d\n", v, p - test_data);
|
|
endinfo_debug(p);
|
|
endinfo();
|
|
exit(0);
|
|
}
|
|
int mode = v & CT_DATA_MASK;
|
|
if (mode == CT_SRCADDR) {
|
|
int size;
|
|
p = restore_value(p, &r->srcaddr, &size);
|
|
} else if (mode == CT_DSTADDR) {
|
|
int size;
|
|
p = restore_value(p, &r->dstaddr, &size);
|
|
} else if (mode == CT_ENDPC) {
|
|
int size;
|
|
p = restore_value(p, &r->endpc, &size);
|
|
} else if (mode == CT_PC) {
|
|
int size;
|
|
p = restore_value(p, &r->pc, &size);
|
|
} else if (mode == CT_BRANCHTARGET) {
|
|
int size;
|
|
p = restore_value(p, &r->branchtarget, &size);
|
|
r->branchtarget_mode = *p++;
|
|
} else if (mode < CT_AREG + 8) {
|
|
int size;
|
|
if ((v & CT_SIZE_MASK) == CT_SIZE_FPU) {
|
|
p = restore_fpvalue(p, &r->fpuregs[mode]);
|
|
} else {
|
|
p = restore_value(p, &r->regs[mode], &size);
|
|
}
|
|
} else if (mode == CT_SR) {
|
|
int size;
|
|
p = restore_value(p, &r->sr, &size);
|
|
} else if (mode == CT_CYCLES) {
|
|
int size;
|
|
p = restore_value(p, &r->cycles, &size);
|
|
gotcycles = 1;
|
|
} else if (mode == CT_FPIAR) {
|
|
int size;
|
|
p = restore_value(p, &r->fpiar, &size);
|
|
} else if (mode == CT_FPCR) {
|
|
int size;
|
|
p = restore_value(p, &r->fpcr, &size);
|
|
} else if (mode == CT_FPSR) {
|
|
int size;
|
|
p = restore_value(p, &r->fpsr, &size);
|
|
} else if (mode == CT_MEMWRITE) {
|
|
// if memwrite, store old data
|
|
p = restore_memory(p, 1);
|
|
} else if (mode == CT_MEMWRITES) {
|
|
p = restore_memory(p, 0);
|
|
} else if (mode == CT_EDATA) {
|
|
p = restore_edata(p);
|
|
} else {
|
|
end_test();
|
|
printf("Unexpected mode %02x\n", v);
|
|
endinfo();
|
|
exit(0);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static uae_u16 test_ccrignoremask;
|
|
|
|
static int addr_diff(uae_u8 *ap, uae_u8 *bp, int size)
|
|
{
|
|
for (int i = 0; i < size; i++) {
|
|
if (is_valid_test_addr_read((uae_u32)bp)) {
|
|
if (*ap != *bp)
|
|
return 1;
|
|
}
|
|
ap++;
|
|
bp++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void addinfo_bytes(char *name, uae_u8 *src, uae_u32 address, int offset, int len)
|
|
{
|
|
sprintf(outbp, "%s %08x ", name, address);
|
|
address += offset;
|
|
outbp += strlen(outbp);
|
|
int cnt = 0;
|
|
while (len-- > 0) {
|
|
if (offset == 0)
|
|
*outbp++ = '*';
|
|
else if (cnt > 0)
|
|
*outbp++ = '.';
|
|
if ((uae_u8*)address >= safe_memory_start && (uae_u8*)address < safe_memory_end && (safe_memory_mode & (1 | 4))) {
|
|
outbp[0] = '?';
|
|
outbp[1] = '?';
|
|
} else {
|
|
sprintf(outbp, "%02x", src[cnt]);
|
|
}
|
|
outbp += 2;
|
|
offset++;
|
|
address++;
|
|
cnt++;
|
|
}
|
|
*outbp++ = '\n';
|
|
}
|
|
|
|
extern uae_u16 disasm_instr(uae_u16 *, char *, int);
|
|
|
|
static short is_valid_word(uae_u8 *p)
|
|
{
|
|
if (!is_valid_test_addr_read((uae_u32)p) || !is_valid_test_addr_read((uae_u32)p + 1))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
#define MAX_DISM_LINES 15
|
|
|
|
static void out_disasm(uae_u8 *mem)
|
|
{
|
|
uae_u16 *code;
|
|
#ifndef M68K
|
|
uae_u16 swapped[16];
|
|
for (int i = 0; i < 16; i++) {
|
|
swapped[i] = (mem[i * 2 + 0] << 8) | (mem[i * 2 + 1] << 0);
|
|
}
|
|
code = swapped;
|
|
#else
|
|
code = (uae_u16*)mem;
|
|
#endif
|
|
uae_u8 *p = mem;
|
|
int offset = 0;
|
|
int lines = 0;
|
|
short last = 0;
|
|
while (lines++ < MAX_DISM_LINES) {
|
|
int v = 0;
|
|
if (!is_valid_word(p)) {
|
|
sprintf(outbp, "%08x -- INACCESSIBLE --\n", (uae_u32)p);
|
|
outbp += strlen(outbp);
|
|
break;
|
|
}
|
|
tmpbuffer[0] = 0;
|
|
if (!(((uae_u32)code) & 1)) {
|
|
v = disasm_instr(code + offset, tmpbuffer, cpu_lvl);
|
|
sprintf(outbp, "%08x ", (uae_u32)p);
|
|
outbp += strlen(outbp);
|
|
for (int i = 0; i < v; i++) {
|
|
uae_u8 *pb = &p[i * 2 + 0];
|
|
uae_u16 v = 0;
|
|
if (is_valid_word(pb)) {
|
|
v = (pb[0] << 8) | (pb[1]);
|
|
}
|
|
sprintf(outbp, "%04x ", v);
|
|
outbp += strlen(outbp);
|
|
if (v == NOP_OPCODE)
|
|
lines--;
|
|
}
|
|
sprintf(outbp, " %s\n", tmpbuffer);
|
|
outbp += strlen(outbp);
|
|
if (!is_valid_word((uae_u8*)(code + offset)))
|
|
break;
|
|
if (v <= 0)
|
|
break;
|
|
if (last)
|
|
break;
|
|
if (code[offset] == ILLG_OPCODE)
|
|
last = 1;
|
|
while (v > 0) {
|
|
offset++;
|
|
p += 2;
|
|
v--;
|
|
}
|
|
} else {
|
|
if (!is_valid_test_addr_read((uae_u32)code))
|
|
break;
|
|
sprintf(outbp, "%08x %02x\n", (uae_u32)code, *((uae_u8*)code));
|
|
code = (uae_u16*)(((uae_u32)code) + 1);
|
|
p++;
|
|
outbp += strlen(outbp);
|
|
}
|
|
if (v < 0)
|
|
break;
|
|
}
|
|
*outbp = 0;
|
|
}
|
|
|
|
static void addinfo(void)
|
|
{
|
|
struct registers *r = &cur_regs;
|
|
if (infoadded)
|
|
return;
|
|
infoadded = 1;
|
|
if (!dooutput)
|
|
return;
|
|
|
|
if (disasm) {
|
|
out_disasm(opcode_memory);
|
|
if (r->branchtarget != 0xffffffff && !(r->branchtarget & 1)) {
|
|
out_disasm((uae_u8*)r->branchtarget);
|
|
}
|
|
}
|
|
|
|
uae_u16 *code = (uae_u16*)opcode_memory;
|
|
if (code[0] == 0x4e73 || code[0] == 0x4e74 || code[0] == 0x4e75 || code[0] == 0x4e77) {
|
|
addinfo_bytes("P", stackaddr, stackaddr_ptr, -SIZE_STORED_ADDRESS_OFFSET, SIZE_STORED_ADDRESS);
|
|
addinfo_bytes(" ", (uae_u8*)stackaddr_ptr - SIZE_STORED_ADDRESS_OFFSET, stackaddr_ptr, -SIZE_STORED_ADDRESS_OFFSET, SIZE_STORED_ADDRESS);
|
|
}
|
|
if (r->srcaddr != 0xffffffff) {
|
|
uae_u8 *a = srcaddr;
|
|
uae_u8 *b = (uae_u8*)r->srcaddr - SIZE_STORED_ADDRESS_OFFSET;
|
|
addinfo_bytes("S", a, r->srcaddr, -SIZE_STORED_ADDRESS_OFFSET, SIZE_STORED_ADDRESS);
|
|
if (addr_diff(a, b, SIZE_STORED_ADDRESS)) {
|
|
addinfo_bytes(" ", b, r->srcaddr, -SIZE_STORED_ADDRESS_OFFSET, SIZE_STORED_ADDRESS);
|
|
}
|
|
}
|
|
if (r->dstaddr != 0xffffffff) {
|
|
uae_u8 *a = dstaddr;
|
|
uae_u8 *b = (uae_u8*)r->dstaddr - SIZE_STORED_ADDRESS_OFFSET;
|
|
addinfo_bytes("D", a, r->dstaddr, -SIZE_STORED_ADDRESS_OFFSET, SIZE_STORED_ADDRESS);
|
|
if (addr_diff(a, b, SIZE_STORED_ADDRESS)) {
|
|
addinfo_bytes(" ", b, r->dstaddr, -SIZE_STORED_ADDRESS_OFFSET, SIZE_STORED_ADDRESS);
|
|
}
|
|
}
|
|
if (r->branchtarget != 0xffffffff && r->srcaddr != r->branchtarget && r->dstaddr != r->branchtarget) {
|
|
uae_u8 *b = (uae_u8*)r->branchtarget - SIZE_STORED_ADDRESS_OFFSET;
|
|
addinfo_bytes("B", b, r->branchtarget, -SIZE_STORED_ADDRESS_OFFSET, SIZE_STORED_ADDRESS);
|
|
}
|
|
// sprintf(outbp, "STARTPC=%08x ENDPC=%08x\n", startpc, endpc);
|
|
// outbp += strlen(outbp);
|
|
}
|
|
|
|
struct srbit
|
|
{
|
|
char *name;
|
|
int bit;
|
|
int size;
|
|
};
|
|
static const struct srbit srbits[] = {
|
|
{ "T1", 15, 1 },
|
|
{ "T0", 14, 1 },
|
|
{ "S", 13, 1 },
|
|
{ "M", 12, 1 },
|
|
{ "IM", 8, 3 },
|
|
{ "X", 4, 1 },
|
|
{ "N", 3, 1 },
|
|
{ "Z", 2, 1 },
|
|
{ "V", 1, 1 },
|
|
{ "C", 0, 1 },
|
|
{ NULL, 0, 0 }
|
|
};
|
|
|
|
// r = registers to output
|
|
// r1 = test results
|
|
// r2 = expected results
|
|
// sreg = original registers
|
|
|
|
static void out_regs(struct registers *r, struct registers *r1, struct registers *r2, struct registers *sreg, short before, short branched)
|
|
{
|
|
if (before) {
|
|
for (int i = 0; i < 16; i++) {
|
|
if (i > 0 && (i % 4) == 0) {
|
|
strcat(outbp, "\n");
|
|
} else if ((i % 8) != 0) {
|
|
strcat(outbp, " ");
|
|
}
|
|
uae_u32 v1 = r1->regs[i];
|
|
uae_u32 v2 = r2->regs[i];
|
|
uae_u32 sv = sreg->regs[i];
|
|
outbp += strlen(outbp);
|
|
sprintf(outbp, "%c%d:%c%08x", i < 8 ? 'D' : 'A', i & 7, v1 == v2 && v1 != sv ? '*' : (v1 != v2 ? '!' : ' '), r->regs[i]);
|
|
outbp += strlen(outbp);
|
|
}
|
|
*outbp++ = '\n';
|
|
sprintf(outbp, "SR:%c%04x PC: %08x ISP: %08x", r1->sr == r2->sr && r1->sr != sreg->sr ? '*' : (r1->sr != r2->sr ? '!' : ' '), r->sr, r->pc, r->ssp);
|
|
} else {
|
|
// output only lines that have at least one modified register to save screen space
|
|
for (int i = 0; i < 4; i++) {
|
|
int diff = 0;
|
|
for (int j = 0; j < 4; j++) {
|
|
int idx = i * 4 + j;
|
|
if (r1->regs[idx] != sreg->regs[idx]) {
|
|
diff = 1;
|
|
}
|
|
}
|
|
if (diff) {
|
|
for (int j = 0; j < 4; j++) {
|
|
int idx = i * 4 + j;
|
|
if (j > 0)
|
|
*outbp++ = ' ';
|
|
uae_u32 v1 = r1->regs[idx];
|
|
uae_u32 v2 = r2->regs[idx];
|
|
uae_u32 sv = sreg->regs[idx];
|
|
sprintf(outbp, "%c%d:%c%08x", idx < 8 ? 'D' : 'A', idx & 7, v1 == v2 && v1 != sv ? '*' : ((v1 != v2) ? '!' : ' '), r->regs[idx]);
|
|
outbp += strlen(outbp);
|
|
}
|
|
*outbp++ = '\n';
|
|
}
|
|
}
|
|
sprintf(outbp, "SR:%c%04x/%04x PC: %08x ISP: %08x", r1->sr == r2->sr && r1->sr != sreg->sr ? '*' : (r1->sr != r2->sr ? '!' : ' '), r->sr, r->expsr, r->pc, r->ssp);
|
|
}
|
|
outbp += strlen(outbp);
|
|
if (cpu_lvl >= 2 && cpu_lvl <= 4) {
|
|
sprintf(outbp, " MSP: %08x", r->msp);
|
|
outbp += strlen(outbp);
|
|
}
|
|
if (branched) {
|
|
sprintf(outbp, " B: %d", branched);
|
|
outbp += strlen(outbp);
|
|
}
|
|
*outbp++ = '\n';
|
|
*outbp = 0;
|
|
|
|
if (before >= 0) {
|
|
uae_u16 s = r->sr; // current value
|
|
uae_u16 s1 = sreg->sr; // original value
|
|
uae_u16 s2 = r1->sr; // test result value
|
|
uae_u16 s3 = r2->sr; // expected result value
|
|
for (int i = 0; srbits[i].name; i++) {
|
|
if (i > 0)
|
|
*outbp++ = ' ';
|
|
uae_u16 mask = 0;
|
|
for (int j = 0; j < srbits[i].size; j++) {
|
|
mask <<= 1;
|
|
mask |= 1 << srbits[i].bit;
|
|
}
|
|
sprintf(outbp, "%s%c%d", srbits[i].name,
|
|
(s2 & mask) != (s3 & mask) ? '!' : ((s1 & mask) != (s2 & mask) ? '*' : '='), (s & mask) >> srbits[i].bit);
|
|
outbp += strlen(outbp);
|
|
}
|
|
*outbp++ = '\n';
|
|
*outbp = 0;
|
|
}
|
|
|
|
if (!fpu_model)
|
|
return;
|
|
|
|
if (before) {
|
|
for (int i = 0; i < 8; i++) {
|
|
if (i > 0 && (i % 2) == 0) {
|
|
strcat(outbp, "\n");
|
|
} else if ((i % 4) != 0) {
|
|
strcat(outbp, " ");
|
|
}
|
|
outbp += strlen(outbp);
|
|
struct fpureg *f = &r->fpuregs[i];
|
|
void *f1 = &r->fpuregs[i];
|
|
void *f2 = &r1->fpuregs[i];
|
|
sprintf(outbp, "FP%d:%c%04x-%08x%08x %f",
|
|
i,
|
|
memcmp(f1, f2, sizeof(struct fpureg)) ? '*' : ' ',
|
|
f->exp, f->m[0], f->m[1],
|
|
*((long double *)f));
|
|
outbp += strlen(outbp);
|
|
}
|
|
*outbp++ = '\n';
|
|
*outbp = 0;
|
|
} else {
|
|
for (int i = 0; i < 8; i += 2) {
|
|
struct fpureg *f = &r->fpuregs[i];
|
|
void *f1 = &r1->fpuregs[i];
|
|
void *f2 = &r2->fpuregs[i];
|
|
void *f1b = &r1->fpuregs[i + 1];
|
|
void *f2b = &r2->fpuregs[i + 1];
|
|
|
|
if (memcmp(f1, f2, sizeof(struct fpureg)) || memcmp(f1b, f2b, sizeof(struct fpureg))) {
|
|
for (int j = 0; j < 2; j++) {
|
|
f1 = &r->fpuregs[i + j];
|
|
f2 = &test_regs.fpuregs[i + j];
|
|
f = &test_regs.fpuregs[i + j];
|
|
if (j > 0)
|
|
*outbp++ = ' ';
|
|
sprintf(outbp, "FP%d:%c%04x-%08x%08x %f",
|
|
i + j,
|
|
memcmp(f1, f2, sizeof(struct fpureg)) ? '*' : ' ',
|
|
f->exp, f->m[0], f->m[1],
|
|
*((long double *)f));
|
|
outbp += strlen(outbp);
|
|
}
|
|
*outbp++ = '\n';
|
|
*outbp = 0;
|
|
}
|
|
}
|
|
}
|
|
sprintf(outbp, "FPSR:%c%08x FPCR:%c%08x FPIAR:%c%08x\n",
|
|
r->fpsr != r2->fpsr ? '*' : ' ', r->fpsr,
|
|
r->fpcr != r2->fpcr ? '*' : ' ', r->fpcr,
|
|
r->fpiar != r2->fpiar ? '*' : ' ', r->fpiar);
|
|
|
|
outbp += strlen(outbp);
|
|
|
|
}
|
|
|
|
static void hexdump(uae_u8 *p, int len, short lf)
|
|
{
|
|
for (int i = 0; i < len; i++) {
|
|
if (i > 0)
|
|
*outbp++ = '.';
|
|
sprintf(outbp, "%02x", p[i]);
|
|
outbp += strlen(outbp);
|
|
}
|
|
if (lf)
|
|
*outbp++ = '\n';
|
|
}
|
|
|
|
static uae_u8 last_exception[256], last_exception_extra;
|
|
static int last_exception_len;
|
|
static uae_u8 alternate_exception1[256];
|
|
static uae_u8 alternate_exception2[256];
|
|
static uae_u8 alternate_exception3[256];
|
|
static uae_u8 masked_exception[256];
|
|
static int mask_exception;
|
|
static int exception_stored;
|
|
|
|
static int compare_exception(uae_u8 *s1, uae_u8 *s2, int len, int domask, uae_u8 *mask)
|
|
{
|
|
if (!domask) {
|
|
return memcmp(s1, s2, len);
|
|
} else {
|
|
for (int i = 0; i < len; i++) {
|
|
uae_u8 m = mask[i];
|
|
if (m == 0)
|
|
continue;
|
|
if ((s1[i] & m) != (s2[i] & m))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static uae_u8 *get_exceptionframe(struct registers *regs, short excnum, int *sizep)
|
|
{
|
|
uae_u8 *frame = (uae_u8*)regs->excframe;
|
|
short size = 0;
|
|
if (cpu_lvl == 0) {
|
|
if (excnum == 2 || excnum == 3) {
|
|
size = 14;
|
|
} else {
|
|
size = 6;
|
|
}
|
|
*sizep = size;
|
|
return frame;
|
|
}
|
|
uae_u16 type = (frame[4] << 8) | frame[5];
|
|
switch (type >> 12)
|
|
{
|
|
case 0:
|
|
size = 8;
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
size = 12;
|
|
break;
|
|
case 4:
|
|
size = 16;
|
|
break;
|
|
case 7:
|
|
size = 60;
|
|
break;
|
|
case 8:
|
|
size = 82;
|
|
break;
|
|
case 9:
|
|
size = 20;
|
|
break;
|
|
case 10:
|
|
size = 32;
|
|
break;
|
|
case 11:
|
|
size = 92;
|
|
break;
|
|
}
|
|
*sizep = size;
|
|
return frame;
|
|
}
|
|
|
|
static uae_u8 *validate_exception(struct registers *regs, uae_u8 *p, short excnum, short *gotexcnum, short *experr, short *extratrace, short *group2with1)
|
|
{
|
|
int exclen = 0;
|
|
uae_u8 *exc;
|
|
uae_u8 *op = p;
|
|
uae_u8 *sp = (uae_u8*)regs->excframe;
|
|
uae_u32 v;
|
|
uae_u8 excdatalen = *p++;
|
|
int excrwp = 0;
|
|
int alts = 0;
|
|
|
|
mask_exception = 0;
|
|
if (!excdatalen) {
|
|
return p;
|
|
}
|
|
|
|
if (excdatalen != 0xff) {
|
|
// check possible extra trace
|
|
last_exception_extra = *p++;
|
|
if (last_exception_extra & 0x40) {
|
|
*group2with1 = *p++;
|
|
exceptioncount[0][*group2with1]++;
|
|
last_exception_extra &= ~0x40;
|
|
}
|
|
if ((last_exception_extra & 0x3f) == 9) {
|
|
exceptioncount[0][last_exception_extra & 0x3f]++;
|
|
uae_u32 ret = (regs->tracedata[1] << 16) | regs->tracedata[2];
|
|
uae_u16 sr = regs->tracedata[0];
|
|
if (regs->tracecnt == 0) {
|
|
uae_u32 pc;
|
|
if (!(last_exception_extra & 0x80)) {
|
|
pc = exceptiontableinuse + (excnum - 2) * 2;
|
|
} else {
|
|
pc = opcode_memory_addr;
|
|
p = restore_rel_ordered(p + 2, &pc);
|
|
}
|
|
sprintf(outbp, "Expected trace exception (PC=%08x) but got none.\n", pc);
|
|
outbp += strlen(outbp);
|
|
*experr |= 2;
|
|
} else if (!(last_exception_extra & 0x80)) {
|
|
// Trace stacked with group 2 exception
|
|
if (!(sr & 0x2000) || (sr | 0x2000 | 0xc000) != (regs->sr | 0x2000 | 0xc000)) {
|
|
sprintf(outbp, "Trace (%d stacked) SR mismatch: %04x != %04x\n", excnum, sr, regs->sr);
|
|
outbp += strlen(outbp);
|
|
*experr |= 2;
|
|
}
|
|
uae_u32 retv = exceptiontableinuse + (excnum - 2) * 2;
|
|
if (ret != retv) {
|
|
sprintf(outbp, "Trace (%d stacked) PC mismatch: %08x != %08x\n", excnum, ret, retv);
|
|
outbp += strlen(outbp);
|
|
*experr |= 2;
|
|
}
|
|
*extratrace = 1;
|
|
} else {
|
|
// Standalone Trace
|
|
uae_u16 vsr = (p[0] << 8) | (p[1]);
|
|
p += 2;
|
|
v = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &v);
|
|
if ((vsr & test_ccrignoremask) != (sr & test_ccrignoremask)) {
|
|
sprintf(outbp, "Trace (non-stacked) SR mismatch: %04x != %04x (PC=%08x)\n", sr, vsr, v);
|
|
outbp += strlen(outbp);
|
|
*experr |= 2;
|
|
}
|
|
if (v != ret) {
|
|
sprintf(outbp, "Trace (non-stacked) PC mismatch: %08x != %08x (SR=%04x)\n", ret, v, vsr);
|
|
outbp += strlen(outbp);
|
|
*experr |= 2;
|
|
}
|
|
*extratrace = -1;
|
|
}
|
|
} else if (!last_exception_extra && excnum != 9) {
|
|
if (regs->tracecnt > 0) {
|
|
uae_u32 ret = (regs->tracedata[1] << 16) | regs->tracedata[2];
|
|
uae_u16 sr = regs->tracedata[0];
|
|
sprintf(outbp, "Got unexpected trace exception: SR=%04x PC=%08x.\n", sr, ret);
|
|
outbp += strlen(outbp);
|
|
if (excnum >= 2) {
|
|
sprintf(outbp, "Exception %d also pending.\n", excnum);
|
|
outbp += strlen(outbp);
|
|
}
|
|
*experr |= 2;
|
|
}
|
|
} else if (last_exception_extra) {
|
|
end_test();
|
|
printf("Unsupported exception extra %d\n", last_exception_extra);
|
|
exit(0);
|
|
}
|
|
// trace only
|
|
if (excnum == 9 && *gotexcnum == 4) {
|
|
sp = (uae_u8*)regs->tracedata;
|
|
*gotexcnum = 9;
|
|
}
|
|
}
|
|
|
|
last_exception_len = 0;
|
|
if (excnum == 1)
|
|
return p;
|
|
|
|
exc = last_exception;
|
|
if (excdatalen != 0xff) {
|
|
if (cpu_lvl == 0) {
|
|
if (excnum == 2 || excnum == 3) {
|
|
// status (with undocumented opcode part)
|
|
uae_u8 status = p[0];
|
|
uae_u8 opcode0 = p[1];
|
|
uae_u8 opcode1 = p[2];
|
|
p += 1 + 2;
|
|
uae_u8 opcode0b = opcode0;
|
|
uae_u8 opcode1b = opcode1;
|
|
if (status & 0x20) {
|
|
opcode0b = p[0];
|
|
opcode1b = p[1];
|
|
p += 2;
|
|
}
|
|
exc[0] = opcode0;
|
|
exc[1] = (opcode1 & ~0x1f) | (status & 0x1f);
|
|
excrwp = ((status & 0x10) == 0) ? 1 : 0;
|
|
if (status & 2)
|
|
excrwp = 2;
|
|
// access address
|
|
v = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &v);
|
|
pl(exc + 2, v);
|
|
// opcode
|
|
exc[6] = opcode0;
|
|
exc[7] = opcode1;
|
|
// sr
|
|
exc[8] = regs->sr >> 8;
|
|
exc[9] = regs->sr;
|
|
// program counter
|
|
v = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &v);
|
|
pl(exc + 10, v);
|
|
exclen = 14;
|
|
if (basicexcept) {
|
|
// I/N field is not always as documented
|
|
memcpy(alternate_exception1, exc, exclen);
|
|
alternate_exception1[1] ^= 0x08; // I/N
|
|
alts = 1;
|
|
if (status & 0x80) {
|
|
// opcode field is not always current opcode
|
|
memcpy(alternate_exception2, exc, exclen);
|
|
alternate_exception2[0] = opcode0b;
|
|
alternate_exception2[1] = (opcode1b & ~0x1f) | (status & 0x1f);
|
|
alternate_exception2[6] = opcode0b;
|
|
alternate_exception2[7] = opcode1b;
|
|
memcpy(alternate_exception3, alternate_exception2, exclen);
|
|
alternate_exception3[1] ^= 0x08; // I/N
|
|
alts += 2;
|
|
}
|
|
}
|
|
} else {
|
|
// sr
|
|
exc[0] = regs->sr >> 8;
|
|
exc[1] = regs->sr;
|
|
// program counter
|
|
v = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &v);
|
|
pl(exc + 2, v);
|
|
exclen = 6;
|
|
}
|
|
} else if (cpu_lvl > 0) {
|
|
// sr
|
|
exc[0] = regs->sr >> 8;
|
|
exc[1] = regs->sr;
|
|
// program counter
|
|
v = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &v);
|
|
pl(exc + 2, v);
|
|
const uae_u16 t0 = *p++;
|
|
const uae_u16 t1 = *p++;
|
|
// frame type
|
|
uae_u16 frame = (t0 << 8) | t1;
|
|
exc[6] = frame >> 8;
|
|
exc[7] = frame >> 0;
|
|
|
|
switch (frame >> 12)
|
|
{
|
|
case 0:
|
|
exclen = 8;
|
|
break;
|
|
case 2:
|
|
v = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &v);
|
|
pl(exc + 8, v);
|
|
exclen = 12;
|
|
if (excnum == 11 || excnum == 55) {
|
|
regs->fpeaset = (*p++) & 1;
|
|
if (!regs->fpeaset) {
|
|
mask_exception = 1;
|
|
memset(masked_exception, 0xff, exclen);
|
|
memset(masked_exception + 8, 0, 4);
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
v = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &v);
|
|
pl(exc + 8, v);
|
|
exclen = 12;
|
|
regs->fpeaset = (*p++) & 1;
|
|
if (!regs->fpeaset) {
|
|
mask_exception = 1;
|
|
memset(masked_exception, 0xff, exclen);
|
|
memset(masked_exception + 8, 0, 4);
|
|
}
|
|
break;
|
|
case 4:
|
|
v = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &v);
|
|
pl(exc + 8, v);
|
|
v = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &v);
|
|
pl(exc + 12, v);
|
|
exclen = 16;
|
|
regs->fpeaset = (*p++) & 1;
|
|
if (!regs->fpeaset) {
|
|
mask_exception = 1;
|
|
memset(masked_exception, 0xff, exclen);
|
|
memset(masked_exception + 8, 0, 4);
|
|
}
|
|
break;
|
|
case 8: // 68010 bus/address error
|
|
{
|
|
// program counter
|
|
v = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &v);
|
|
pl(exc + 2, v);
|
|
uae_u16 ssw = (p[0] << 8) | p[1];
|
|
exc[8] = *p++;
|
|
exc[9] = *p++;
|
|
excrwp = ((exc[8] & 1) == 0) ? 1 : 0;
|
|
if (exc[9] & 2)
|
|
excrwp = 2;
|
|
uae_u32 fault_addr = opcode_memory_addr;
|
|
p = restore_rel_ordered(p, &fault_addr);
|
|
pl(exc + 10, fault_addr);
|
|
// data out
|
|
exc[16] = *p++;
|
|
exc[17] = *p++;
|
|
// data in
|
|
exc[20] = *p++;
|
|
exc[21] = *p++;
|
|
// inst
|
|
exc[24] = *p++;
|
|
exc[25] = *p++;
|
|
exc[14] = exc[15] = 0;
|
|
sp[14] = sp[15] = 0;
|
|
exc[18] = exc[19] = 0;
|
|
sp[18] = sp[19] = 0;
|
|
exc[22] = exc[23] = 0;
|
|
sp[22] = sp[23] = 0;
|
|
if (basicexcept) {
|
|
exclen = 14;
|
|
mask_exception = 1;
|
|
memset(masked_exception, 0xff, sizeof(masked_exception));
|
|
masked_exception[8] = ~0x30;
|
|
} else {
|
|
// ignore undocumented data
|
|
exclen = 26;
|
|
// read input buffer may contain either actual data read from memory or previous read data
|
|
// this depends on hardware, cpu does dummy read cycle and some hardware returns memory data, some ignore it.
|
|
memcpy(alternate_exception1, exc, exclen);
|
|
memcpy(alternate_exception2, exc, exclen);
|
|
alternate_exception1[20] = p[0];
|
|
alternate_exception1[21] = p[1];
|
|
memcpy(alternate_exception3, alternate_exception1, exclen);
|
|
// same with instruction input buffer if branch instruction generates address error
|
|
alternate_exception2[24] = p[0];
|
|
alternate_exception2[25] = p[1];
|
|
alternate_exception3[24] = p[0];
|
|
alternate_exception3[25] = p[1];
|
|
if (excnum == 2 || !is_valid_test_addr_readwrite(fault_addr - 4) || !is_valid_test_addr_readwrite(fault_addr + 4)) {
|
|
// bus error read: cpu may still read the data, depends on hardware.
|
|
// ignore input buffer contents
|
|
mask_exception = 1;
|
|
memset(masked_exception, 0xff, sizeof(masked_exception));
|
|
masked_exception[20] = 0;
|
|
masked_exception[21] = 0;
|
|
}
|
|
alts = 3;
|
|
}
|
|
if (instructionsize == 0) {
|
|
// if byte wide bus error, ignore high byte of input and output buffer
|
|
// contents of high byte depends on used alu operation and instruction type and more..
|
|
masked_exception[16] = 0;
|
|
masked_exception[20] = 0;
|
|
}
|
|
p += 2;
|
|
}
|
|
break;
|
|
case 0x0a:
|
|
case 0x0b:
|
|
exclen = 8;
|
|
break;
|
|
default:
|
|
end_test();
|
|
printf("Unknown frame %04x\n", frame);
|
|
exit(0);
|
|
break;
|
|
}
|
|
}
|
|
last_exception_len = exclen;
|
|
if (p != op + excdatalen + 1) {
|
|
end_test();
|
|
printf("Exception %d length mismatch %d != %d\n", excnum, excdatalen, p - op - 1);
|
|
exit(0);
|
|
}
|
|
} else {
|
|
exclen = last_exception_len;
|
|
}
|
|
|
|
exceptioncount[excrwp][*gotexcnum]++;
|
|
|
|
if (exclen == 0 || *gotexcnum != excnum)
|
|
return p;
|
|
int err = 0;
|
|
if (compare_exception(exc, sp, exclen, mask_exception, masked_exception)) {
|
|
err = 1;
|
|
if (err && alts > 0) {
|
|
if (alts >= 1 && !compare_exception(alternate_exception1, sp, exclen, mask_exception, masked_exception))
|
|
err = 0;
|
|
if (alts >= 2 && !compare_exception(alternate_exception2, sp, exclen, mask_exception, masked_exception))
|
|
err = 0;
|
|
if (alts >= 3 && !compare_exception(alternate_exception3, sp, exclen, mask_exception, masked_exception))
|
|
err = 0;
|
|
}
|
|
}
|
|
if (err) {
|
|
sprintf(outbp, "Exception %d stack frame mismatch:\n", excnum);
|
|
outbp += strlen(outbp);
|
|
strcpy(outbp, "Expected: ");
|
|
outbp += strlen(outbp);
|
|
hexdump(exc, exclen, 1);
|
|
strcpy(outbp, "Got : ");
|
|
outbp += strlen(outbp);
|
|
hexdump(sp, exclen, 1);
|
|
*experr |= 1;
|
|
}
|
|
exception_stored = exclen;
|
|
return p;
|
|
}
|
|
|
|
static int getexceptioncycles(int exc)
|
|
{
|
|
if (cpu_lvl == 0) {
|
|
switch (exc)
|
|
{
|
|
case 2:
|
|
return 58;
|
|
case 3:
|
|
return 54;
|
|
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:
|
|
return 134;
|
|
case 3:
|
|
return 130;
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 14:
|
|
return 38;
|
|
case 7:
|
|
return 38;
|
|
case 24:
|
|
case 25:
|
|
case 26:
|
|
case 27:
|
|
case 28:
|
|
case 29:
|
|
case 30:
|
|
case 31:
|
|
return 48;
|
|
default:
|
|
return 38;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef AMIGA
|
|
// 7MHz 68000 PAL A500 only!
|
|
static int get_cycles_amiga(void)
|
|
{
|
|
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;
|
|
return gotcycles;
|
|
}
|
|
|
|
static uae_u16 test_intena, test_intreq;
|
|
|
|
static const uae_u16 itable[] =
|
|
{
|
|
0,
|
|
1 << 2,
|
|
1 << 3,
|
|
1 << 4,
|
|
1 << 7,
|
|
1 << 11,
|
|
1 << 13,
|
|
0
|
|
};
|
|
|
|
static void set_interrupt(void)
|
|
{
|
|
if (interrupttest == 1) {
|
|
if (interrupt_count < 15) {
|
|
volatile uae_u16 *intena = (uae_u16*)0xdff09a;
|
|
volatile uae_u16 *intreq = (uae_u16*)0xdff09c;
|
|
uae_u16 mask = 1 << interrupt_count;
|
|
test_intena = mask | 0x8000 | 0x4000;
|
|
test_intreq = mask | 0x8000;
|
|
*intena = test_intena;
|
|
*intreq = test_intreq;
|
|
}
|
|
}
|
|
if (interrupttest == 2) {
|
|
volatile uae_u16 *intena = (uae_u16*)0xdff09a;
|
|
volatile uae_u16 *intreq = (uae_u16*)0xdff09c;
|
|
volatile uae_u16 *serper = (uae_u16*)0xdff032;
|
|
*serper = IPL_TRIGGER_SERPER;
|
|
*intena = 0x8000 | 0x4000 | IPL_TRIGGER_INTMASK;
|
|
*intreq = IPL_TRIGGER_INTMASK;
|
|
}
|
|
if (initial_interrupt) {
|
|
uae_u16 mask = itable[initial_interrupt];
|
|
volatile uae_u16 *intena = (uae_u16*)0xdff09a;
|
|
volatile uae_u16 *intreq = (uae_u16*)0xdff09c;
|
|
*intena = 0x8000 | 0x4000 | mask;
|
|
*intreq = 0x8000 | mask;
|
|
}
|
|
}
|
|
|
|
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 int check_cycles(int exc, short extratrace, short extrag2w1, struct registers *lregs)
|
|
{
|
|
int gotcycles = 0;
|
|
|
|
if ((test_regs.cycles >> 16) == 0xffff) {
|
|
// cycle counter got exception (interrupt) during exception handling
|
|
// -> cycle counter was invalidated. Ignore cycle count.
|
|
invalidcycles++;
|
|
return 1;
|
|
}
|
|
|
|
if (cyclecounter_addr != 0xffffffff) {
|
|
gotcycles = (test_regs.cycles & 0xffff) - (test_regs.cycles >> 16);
|
|
if (gotcycles == 0) {
|
|
end_test();
|
|
printf("Cycle counter hardware address 0x%08x returned zero cycles.\n", cyclecounter_addr);
|
|
exit(0);
|
|
}
|
|
} else {
|
|
#ifdef AMIGA
|
|
gotcycles = get_cycles_amiga();
|
|
// if write bus error, decrease cycles by 2. Tester hardware side-effect.
|
|
if (exc == 2) {
|
|
if (cpu_lvl == 0 && (last_exception[1] & 0x10) == 0) {
|
|
gotcycles -= 2;
|
|
}
|
|
if (cpu_lvl == 1 && (last_exception[8] & 0x01) == 0) {
|
|
gotcycles -= 2;
|
|
}
|
|
}
|
|
#else
|
|
end_test();
|
|
printf("No cycle count support\n");
|
|
exit(0);
|
|
#endif
|
|
}
|
|
|
|
int expectedcycles = lregs->cycles;
|
|
int exceptioncycles = getexceptioncycles(exc);
|
|
if (cpu_lvl == 0) {
|
|
// move.w CYCLEREG,cycles
|
|
gotcycles -= 20;
|
|
// RTE
|
|
gotcycles -= 20;
|
|
// <test instruction>
|
|
// EXCEPTION
|
|
expectedcycles += exceptioncycles;
|
|
// 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 += exceptioncycles;
|
|
// bsr.b
|
|
gotcycles -= 18;
|
|
// move.w sr,-(sp)
|
|
gotcycles -= 12;
|
|
// move.w CYCLEREG,cycles
|
|
gotcycles -= 8;
|
|
}
|
|
gotcycles += cycles_adjust;
|
|
|
|
if (extratrace > 0) {
|
|
expectedcycles += getexceptioncycles(9);
|
|
}
|
|
// address error during group 2 exception stacking (=odd exception vector)
|
|
if (extrag2w1) {
|
|
// 4 idle, write pc low, write sr, write pc high, read vector high, read vector low,
|
|
// exception vector fetch that generates address error
|
|
expectedcycles += 7 * 4;
|
|
if (extrag2w1 == 7) {
|
|
// TRAPV has no startup idle cycles
|
|
expectedcycles -= 4;
|
|
}
|
|
if (cpu_lvl == 1) {
|
|
// 68010: frame type
|
|
expectedcycles += 4;
|
|
}
|
|
if (extrag2w1 >= 24 && extrag2w1 < 24 + 8) {
|
|
// interrupt
|
|
expectedcycles += 2 + 4 + 4;
|
|
}
|
|
}
|
|
|
|
if (0 || abs(gotcycles - expectedcycles) > cycles_range) {
|
|
sprintf(outbp, "Got %d cycles (%d + %d) but expected %d (%d + %d) %08x\n",
|
|
gotcycles, gotcycles - exceptioncycles, exceptioncycles,
|
|
expectedcycles, expectedcycles - exceptioncycles, exceptioncycles, test_regs.cycles);
|
|
outbp += strlen(outbp);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// mostly ugly workarounds for logarithmic/trigonometric functions
|
|
// not returning identical values (6888x algorithms are unknown)
|
|
static short fpucheckextra(struct fpureg *f1, struct fpureg *f2)
|
|
{
|
|
uae_u32 vx[9];
|
|
|
|
if (!is_fpu_adjust)
|
|
return 0;
|
|
|
|
uae_u32 m1[2];
|
|
uae_u32 m2[2];
|
|
m1[0] = f1->m[0];
|
|
m1[1] = f1->m[1];
|
|
m2[0] = f2->m[0];
|
|
m2[1] = f2->m[1];
|
|
uae_u16 exp1 = f1->exp & 0x7fff;
|
|
uae_u16 exp2 = f2->exp & 0x7fff;
|
|
// NaN or Infinite
|
|
if (exp1 == 0x7fff || exp2 == 0x7fff) {
|
|
if (m1[0] == 0 && m1[1] == 0) {
|
|
if (m2[0] != 0 || m2[1] != 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (m2[0] == 0 && m2[1] == 0) {
|
|
if (m1[0] != 0 || m1[1] != 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// One zero: other must be close enough to zero
|
|
if ((!exp1 && !m1[0] && !m1[1]) || (!exp2 && !m2[0] && !m2[1])) {
|
|
vx[0] = f1->exp << 16;
|
|
vx[1] = f1->m[0];
|
|
vx[2] = f1->m[1];
|
|
vx[3] = f2->exp << 16;
|
|
vx[4] = f2->m[0];
|
|
vx[5] = f2->m[1];
|
|
vx[6] = (16383 - 10) << 16;
|
|
vx[7] = 0x80000000;
|
|
vx[8] = 0x00000000;
|
|
if (fpucompzero(vx)) {
|
|
fpu_approx++;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
vx[0] = f1->exp << 16;
|
|
vx[1] = f1->m[0];
|
|
vx[2] = f1->m[1];
|
|
vx[3] = f2->exp << 16;
|
|
vx[4] = f2->m[0];
|
|
vx[5] = f2->m[1];
|
|
vx[6] = fpu_adjust_exp << 16;
|
|
vx[7] = 0x80000000;
|
|
vx[8] = 0x00000000;
|
|
|
|
if (fpucomp(vx)) {
|
|
fpu_approx++;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void loop_mode_error(uae_u32 ov, uae_u32 nv)
|
|
{
|
|
strcpy(outbp, " expected ");
|
|
outbp += strlen(outbp);
|
|
for (short i = loop_mode_cnt - 1; i >= 0; i--) {
|
|
if ((ov >> i) & 1) {
|
|
*outbp++ = '1';
|
|
} else {
|
|
*outbp++ = '0';
|
|
}
|
|
}
|
|
strcpy(outbp, " got ");
|
|
outbp += strlen(outbp);
|
|
for (short i = loop_mode_cnt - 1; i >= 0; i--) {
|
|
if ((nv >> i) & 1) {
|
|
*outbp++ = '1';
|
|
} else {
|
|
*outbp++ = '0';
|
|
}
|
|
}
|
|
*outbp++ = '\n';
|
|
}
|
|
|
|
static const char cvzn[] = { "CVZN" };
|
|
static void loop_mode_error_CVZN(uae_u32 ov, uae_u32 nv)
|
|
{
|
|
// Swap V<>Z
|
|
ov = (ov & 0xff0000ff) | ((ov >> 8) & 0x0000ff00) | ((ov << 8) & 0x00ff0000);
|
|
nv = (nv & 0xff0000ff) | ((nv >> 8) & 0x0000ff00) | ((nv << 8) & 0x00ff0000);
|
|
|
|
for (short i = 0; i < 4; i++) {
|
|
if ((ov & 0xff) != (nv & 0xff)) {
|
|
sprintf(outbp, "Loop mode %c:", cvzn[i]);
|
|
outbp += strlen(outbp);
|
|
loop_mode_error(ov, nv);
|
|
}
|
|
ov >>= 8;
|
|
nv >>= 8;
|
|
}
|
|
}
|
|
static void loop_mode_error_X(uae_u32 ov, uae_u32 nv)
|
|
{
|
|
sprintf(outbp, "Loop mode X:");
|
|
outbp += strlen(outbp);
|
|
ov >>= 4;
|
|
nv >>= 4;
|
|
loop_mode_error(ov, nv);
|
|
}
|
|
|
|
static uae_u16 lmtable1[LM_BUFFER / 2];
|
|
static uae_u16 lmtable2[LM_BUFFER / 2];
|
|
|
|
// sregs: registers before execution of test code
|
|
// tregs: registers used during execution of test code, also modified by test code.
|
|
// lregs: registers after modifications from data files. Test ok if tregs == lregs.
|
|
|
|
static uae_u8 *validate_test(uae_u8 *p, short ignore_errors, short ignore_sr, struct registers *sregs, struct registers *tregs, struct registers *lregs, int opcodeendsizeextra)
|
|
{
|
|
uae_u8 regs_changed[16] = { 0 }, regs_changed_any = 0;
|
|
uae_u8 regs_fpuchanged[8] = { 0 }, regs_fpuchanged_any = 0;
|
|
uae_u8 sr_changed = 0, ccr_changed = 0, pc_changed = 0;
|
|
uae_u8 fpiar_changed = 0, fpsr_changed = 0, fpcr_changed = 0;
|
|
short exc = -1;
|
|
|
|
if (loop_mode_jit) {
|
|
memset(lmtable1, 0xff, sizeof(lmtable1));
|
|
memset(lmtable2, 0xff, sizeof(lmtable2));
|
|
}
|
|
|
|
// Register modified in test? (start register != test register)
|
|
for (short i = 0; i < 16; i++) {
|
|
if (sregs->regs[i] != tregs->regs[i]) {
|
|
regs_changed[i] = 1;
|
|
regs_changed_any = 1;
|
|
}
|
|
}
|
|
if ((sregs->sr & test_ccrignoremask & 0xff00) != (tregs->sr & test_ccrignoremask & 0xff00)) {
|
|
sr_changed = 1;
|
|
}
|
|
if ((sregs->sr & test_ccrignoremask & ccr_mask & 0x00ff) != (tregs->sr & test_ccrignoremask & ccr_mask & 0x00ff)) {
|
|
ccr_changed = 1;
|
|
}
|
|
if (sregs->pc + opcodeendsizeextra != tregs->pc) {
|
|
pc_changed = 1;
|
|
}
|
|
if (fpu_model) {
|
|
for (short i = 0; i < 8; i++) {
|
|
if (memcmp(&sregs->fpuregs[i], &tregs->fpuregs[i], sizeof(struct fpureg))) {
|
|
regs_fpuchanged[i] = 1;
|
|
regs_fpuchanged_any = 1;
|
|
}
|
|
}
|
|
if (sregs->fpsr != tregs->fpsr) {
|
|
fpsr_changed = 1;
|
|
}
|
|
if (sregs->fpcr != tregs->fpcr) {
|
|
fpcr_changed = 1;
|
|
}
|
|
if (sregs->fpiar != tregs->fpiar) {
|
|
fpiar_changed = 1;
|
|
}
|
|
}
|
|
|
|
if (*p == CT_END_SKIP)
|
|
return p + 1;
|
|
|
|
short experr = 0;
|
|
int errflag = 0;
|
|
int errflag_orig = 0;
|
|
short extratrace = 0;
|
|
short extrag2w1 = 0;
|
|
short exceptionnum = 0;
|
|
uae_u8 *outbp_old = outbp;
|
|
short branched = 0, branched2 = 0;
|
|
exception_stored = 0;
|
|
|
|
for (;;) {
|
|
uae_u8 v = *p;
|
|
if (v & CT_END) {
|
|
exc = v & CT_EXCEPTION_MASK;
|
|
short cpuexc = tregs->exc & 65535;
|
|
short cpuexc010 = tregs->exc010 & 65535;
|
|
branched = (v & CT_BRANCHED) != 0;
|
|
branched2 = branched;
|
|
p++;
|
|
if (cpu_lvl > 0 && exc >= 2 && cpuexc010 != cpuexc) {
|
|
if (dooutput) {
|
|
sprintf(outbp, "Exception: vector number does not match vector offset! (%d <> %d) %d\n", exc, cpuexc010, cpuexc);
|
|
experr |= 1;
|
|
outbp += strlen(outbp);
|
|
errflag |= 1 << 16;
|
|
}
|
|
break;
|
|
}
|
|
if (ignore_errors) {
|
|
if (exc) {
|
|
p = validate_exception(&test_regs, p, exc, &cpuexc, &experr, &extratrace, &extrag2w1);
|
|
}
|
|
errflag_orig |= errflag;
|
|
errflag = 0;
|
|
break;
|
|
}
|
|
if (exc == 0 && cpuexc == 4) {
|
|
// successful complete generates exception 4 with matching PC
|
|
if (lregs->pc + opcodeendsizeextra != tregs->pc) {
|
|
branched2 = lregs->pc < opcode_memory_addr || lregs->pc >= opcode_memory_addr + OPCODE_AREA;
|
|
if (dooutput) {
|
|
int excsize;
|
|
uae_u8 *excp;
|
|
sprintf(outbp, "PC (%c): expected %08x but got %08x ", branched ? 'B' : '-', lregs->pc, tregs->pc);
|
|
outbp += strlen(outbp);
|
|
if (tregs->pc == opcode_memory_addr) {
|
|
sprintf(outbp, "Got unexpected exception %d (unsupported instruction?) ", cpuexc);
|
|
} else {
|
|
sprintf(outbp, "Got unexpected exception %d ", cpuexc);
|
|
}
|
|
outbp += strlen(outbp);
|
|
excp = get_exceptionframe(&test_regs, cpuexc, &excsize);
|
|
if (excp && excsize) {
|
|
hexdump(excp, excsize, 1);
|
|
}
|
|
}
|
|
errflag |= 1 << 16;
|
|
}
|
|
break;
|
|
}
|
|
if (exc) {
|
|
p = validate_exception(&test_regs, p, exc, &cpuexc, &experr, &extratrace, &extrag2w1);
|
|
if (experr) {
|
|
errflag |= 1 << 16;
|
|
errflag_orig |= errflag;
|
|
}
|
|
if (basicexcept && (cpuexc == 2 || cpuexc == 3)) {
|
|
errflag &= ~(1 << 0);
|
|
errflag &= ~(1 << 7);
|
|
}
|
|
exceptionnum = cpuexc;
|
|
}
|
|
if (exc != cpuexc && exc >= 2) {
|
|
if (dooutput) {
|
|
if (cpuexc == 4 && lregs->pc + opcodeendsizeextra == tregs->pc) {
|
|
sprintf(outbp, "Exception: expected %d but got no exception.\n", exc);
|
|
outbp += strlen(outbp);
|
|
} else if (cpuexc == 4) {
|
|
sprintf(outbp, "Exception: expected %d but got %d (or no exception)\n", exc, cpuexc);
|
|
outbp += strlen(outbp);
|
|
} else {
|
|
int excsize;
|
|
uae_u8 *excp;
|
|
sprintf(outbp, "Exception: expected %d but got %d\n", exc, cpuexc);
|
|
outbp += strlen(outbp);
|
|
excp = get_exceptionframe(&test_regs, cpuexc, &excsize);
|
|
if (excp && excsize) {
|
|
hexdump(excp, excsize, 1);
|
|
}
|
|
}
|
|
if ((experr & 2) && !(experr & 1) && extratrace) {
|
|
sprintf(outbp, "Valid trace exception found\n");
|
|
outbp += strlen(outbp);
|
|
}
|
|
experr |= 2;
|
|
}
|
|
errflag |= 1 << 16;
|
|
}
|
|
break;
|
|
}
|
|
short mode = v & CT_DATA_MASK;
|
|
|
|
if (mode < CT_AREG + 8 && (v & CT_SIZE_MASK) != CT_SIZE_FPU) {
|
|
uae_u32 val = lregs->regs[mode];
|
|
int size;
|
|
p = restore_value(p, &val, &size);
|
|
if (val != tregs->regs[mode]) {
|
|
if (!ignore_errors && !skipregchange) {
|
|
if (dooutput) {
|
|
if (sregs->regs[mode] == tregs->regs[mode]) {
|
|
sprintf(outbp, "%c%d: expected %08x but register was not modified\n", mode < CT_AREG ? 'D' : 'A', mode & 7, val);
|
|
} else {
|
|
sprintf(outbp, "%c%d: expected %08x but got %08x\n", mode < CT_AREG ? 'D' : 'A', mode & 7, val, tregs->regs[mode]);
|
|
}
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 0;
|
|
}
|
|
}
|
|
lregs->regs[mode] = val;
|
|
regs_changed[mode] = 0;
|
|
} else if (mode < CT_AREG && (v & CT_SIZE_MASK) == CT_SIZE_FPU) {
|
|
struct fpureg val;
|
|
memcpy(&val, &lregs->fpuregs[mode], sizeof(struct fpureg));
|
|
p = restore_fpvalue(p, &val);
|
|
if (memcmp(&val, &tregs->fpuregs[mode], sizeof(struct fpureg))) {
|
|
if (!ignore_errors && !skipregchange && !fpucheckextra(&val, &tregs->fpuregs[mode])) {
|
|
if (dooutput) {
|
|
if (!memcmp(&sregs->fpuregs, &tregs->fpuregs[mode], sizeof(struct fpureg))) {
|
|
sprintf(outbp, "FP%d: expected %04x-%08x%08x but register was not modified\n", mode,
|
|
val.exp, val.m[0], val.m[1]);
|
|
} else {
|
|
sprintf(outbp, "FP%d: expected %04x-%08x%08x but got %04x-%08x%08x\n", mode,
|
|
val.exp, val.m[0], val.m[1],
|
|
tregs->fpuregs[mode].exp, tregs->fpuregs[mode].m[0], tregs->fpuregs[mode].m[1]);
|
|
}
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 1;
|
|
}
|
|
}
|
|
memcpy(&lregs->fpuregs[mode], &val, sizeof(struct fpureg));
|
|
regs_fpuchanged[mode] = 0;
|
|
} else if (mode == CT_SR) {
|
|
uae_u32 val = lregs->sr;
|
|
int size;
|
|
// High 16 bit: ignore mask, low 16 bit: SR/CCR
|
|
p = restore_value(p, &val, &size);
|
|
test_ccrignoremask = ~(val >> 16);
|
|
if ((val & ccr_mask & (sr_undefined_mask & test_ccrignoremask)) != (tregs->sr & ccr_mask & (sr_undefined_mask & test_ccrignoremask)) && !ignore_errors && !ignore_sr) {
|
|
if (dooutput) {
|
|
sprintf(outbp, "SR: expected %04x -> %04x but got %04x", sregs->sr & 0xffff, val & 0xffff, tregs->sr & 0xffff);
|
|
outbp += strlen(outbp);
|
|
if (test_ccrignoremask != 0xffff) {
|
|
sprintf(outbp, " (%04x)", test_ccrignoremask);
|
|
outbp += strlen(outbp);
|
|
}
|
|
*outbp++ = '\n';
|
|
errflag |= 1 << 2;
|
|
errflag |= 1 << 7;
|
|
}
|
|
// SR check
|
|
uae_u16 mask = test_ccrignoremask & 0xff00;
|
|
if ((val & (sr_undefined_mask & mask)) == (tregs->sr & (sr_undefined_mask & mask))) {
|
|
errflag &= ~(1 << 2);
|
|
}
|
|
// CCR check
|
|
mask = test_ccrignoremask & 0x00ff;
|
|
if (skipccrchange || ((val & (sr_undefined_mask & mask)) == (tregs->sr & (sr_undefined_mask & mask)))) {
|
|
errflag &= ~(1 << 7);
|
|
}
|
|
}
|
|
sr_changed = 0;
|
|
ccr_changed = 0;
|
|
if (!(tregs->expsr & 0x2000)) {
|
|
sprintf(outbp, "SR S-bit is not set at start of exception handler!\n");
|
|
outbp += strlen(outbp);
|
|
errflag |= 1 << 16;
|
|
}
|
|
if ((tregs->expsr & 0xff) != (tregs->sr & 0xff)) {
|
|
sprintf(outbp, "Exception stacked CCR (%04x) != CCR (%04x) at start of exception handler!\n", tregs->sr, tregs->expsr);
|
|
outbp += strlen(outbp);
|
|
errflag |= 1 << 16;
|
|
}
|
|
lregs->sr = val;
|
|
} else if (mode == CT_PC) {
|
|
uae_u32 val = lregs->pc;
|
|
p = restore_rel(p, &val, 1);
|
|
pc_changed = 0;
|
|
lregs->pc = val;
|
|
} else if (mode == CT_CYCLES) {
|
|
uae_u32 val = lregs->cycles;
|
|
int size;
|
|
p = restore_value(p, &val, &size);
|
|
lregs->cycles = val;
|
|
gotcycles = 1;
|
|
} else if (mode == CT_FPCR) {
|
|
uae_u32 val = lregs->fpcr;
|
|
int size;
|
|
p = restore_value(p, &val, &size);
|
|
if (val != tregs->fpcr) {
|
|
if (!ignore_errors) {
|
|
if (dooutput) {
|
|
if (sregs->fpcr == tregs->fpcr) {
|
|
sprintf(outbp, "FPCR: expected %08x but register was not modified\n", val);
|
|
} else {
|
|
sprintf(outbp, "FPCR: expected %08x -> %08x but got %08x\n", sregs->fpcr, val, tregs->fpcr);
|
|
}
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 3;
|
|
}
|
|
}
|
|
lregs->fpcr = val;
|
|
fpcr_changed = 0;
|
|
} else if (mode == CT_FPSR) {
|
|
uae_u32 val = lregs->fpsr;
|
|
int size;
|
|
p = restore_value(p, &val, &size);
|
|
if (val != tregs->fpsr) {
|
|
if (!ignore_errors && ((val & fpsr_ignore_mask) != (tregs->fpsr & fpsr_ignore_mask))) {
|
|
if (dooutput) {
|
|
if (sregs->fpsr == tregs->fpsr) {
|
|
sprintf(outbp, "FPSR: expected %08x but register was not modified\n", val);
|
|
} else {
|
|
sprintf(outbp, "FPSR: expected %08x -> %08x but got %08x\n", sregs->fpsr, val, tregs->fpsr);
|
|
}
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 4;
|
|
}
|
|
}
|
|
lregs->fpsr = val;
|
|
fpsr_changed = 0;
|
|
} else if (mode == CT_FPIAR) {
|
|
uae_u32 val = lregs->fpiar;
|
|
int size;
|
|
p = restore_value(p, &val, &size);
|
|
if (val != tregs->fpiar) {
|
|
if (!ignore_errors) {
|
|
if (dooutput) {
|
|
if (sregs->fpiar == tregs->fpiar) {
|
|
sprintf(outbp, "FPIAR: expected %08x but register was not modified\n", val);
|
|
} else {
|
|
sprintf(outbp, "FPIAR: expected %08x but got %08x\n", val, tregs->fpiar);
|
|
}
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 5;
|
|
}
|
|
}
|
|
lregs->fpiar = val;
|
|
fpiar_changed = 0;
|
|
} else if (mode == CT_MEMWRITES) {
|
|
p = restore_memory(p, 0);
|
|
} else if (mode == CT_MEMWRITE) {
|
|
uae_u8 *addr;
|
|
uae_u32 val = 0, mval = 0, oldval = 0;
|
|
int size;
|
|
short lm = 0;
|
|
p = get_memory_addr(p, &addr);
|
|
p = restore_value(p, &oldval, &size);
|
|
p = restore_value(p, &val, &size);
|
|
switch(size)
|
|
{
|
|
case 0:
|
|
mval = addr[0];
|
|
if (mval != val && !ignore_errors && !skipmemwrite) {
|
|
if (dooutput) {
|
|
sprintf(outbp, "Memory byte write: address %08x, expected %02x but got %02x\n", (uae_u32)addr, val, mval);
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 6;
|
|
}
|
|
addr[0] = oldval;
|
|
break;
|
|
case 1:
|
|
mval = (addr[0] << 8) | (addr[1]);
|
|
if (loop_mode_jit && addr >= test_memory && addr < test_memory + LM_BUFFER) {
|
|
lmtable1[(addr - test_memory) / 2] = val;
|
|
lmtable2[(addr - test_memory) / 2] = mval;
|
|
lm = 1;
|
|
}
|
|
if (mval != val && !ignore_errors && !skipmemwrite) {
|
|
if (dooutput) {
|
|
if (!lm || (lm && (val == 0xffff || mval == 0xffff))) {
|
|
sprintf(outbp, "Memory word write: address %08x, expected %04x but got %04x\n", (uae_u32)addr, val, mval);
|
|
}
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 6;
|
|
}
|
|
addr[0] = oldval >> 8;
|
|
addr[1] = oldval;
|
|
break;
|
|
case 2:
|
|
mval = gl(addr);
|
|
if (mval != val && !ignore_errors && !skipmemwrite) {
|
|
if (dooutput) {
|
|
sprintf(outbp, "Memory long write: address %08x, expected %08x but got %08x\n", (uae_u32)addr, val, mval);
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 6;
|
|
}
|
|
pl(addr, oldval);
|
|
break;
|
|
}
|
|
} else {
|
|
end_test();
|
|
printf("Unknown test data %02x mode %d\n", v, mode);
|
|
exit(0);
|
|
}
|
|
}
|
|
if (!ignore_errors) {
|
|
if (!skipregchange && regs_changed_any) {
|
|
for (short mode = 0; mode < 16; mode++) {
|
|
if (regs_changed[mode] && tregs->regs[mode] != lregs->regs[mode]) {
|
|
if (dooutput) {
|
|
uae_u32 val = lregs->regs[mode];
|
|
sprintf(outbp, "%c%d: expected %08x but got %08x\n", mode < CT_AREG ? 'D' : 'A', mode & 7, val, tregs->regs[mode]);
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 0;
|
|
}
|
|
}
|
|
}
|
|
if (!ignore_sr) {
|
|
if (sr_changed && tregs->sr != lregs->sr) {
|
|
if (dooutput) {
|
|
sprintf(outbp, "SR: modified %04x -> %04x but expected %04x\n", sregs->sr & 0xffff, tregs->sr & 0xffff, lregs->sr & 0xffff);
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 2;
|
|
} else if (ccr_changed && !skipccrchange && (tregs->sr & ccr_mask & 0xff) != (lregs->sr & ccr_mask & 0xff)) {
|
|
if (dooutput) {
|
|
sprintf(outbp, "SR: modified %04x -> %04x but expected %04x\n", sregs->sr & 0xffff, tregs->sr & 0xffff, lregs->sr & 0xffff);
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 2;
|
|
}
|
|
}
|
|
if (!skipregchange && regs_fpuchanged_any) {
|
|
for (short mode = 0; mode < 8; mode++) {
|
|
if (regs_fpuchanged[mode] && memcmp(&tregs->fpuregs[mode], &lregs->fpuregs[mode], sizeof(struct fpureg))) {
|
|
if (!fpucheckextra(&tregs->fpuregs[mode], &lregs->fpuregs[mode])) {
|
|
if (dooutput) {
|
|
struct fpureg *val = &lregs->fpuregs[mode];
|
|
sprintf(outbp, "FP%d: expected %04x-%08x%08x but got %04x-%08x%08x\n", mode,
|
|
val->exp, val->m[0], val->m[1],
|
|
tregs->fpuregs[mode].exp, tregs->fpuregs[mode].m[0], tregs->fpuregs[mode].m[1]);
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!ignore_sr) {
|
|
if (fpsr_changed && (tregs->fpsr & fpsr_ignore_mask) != (lregs->fpsr & fpsr_ignore_mask)) {
|
|
if (dooutput) {
|
|
uae_u32 val = lregs->fpsr;
|
|
sprintf(outbp, "FPSR: expected %08x -> %08x but got %08x\n", sregs->fpsr, val, tregs->fpsr);
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 3;
|
|
}
|
|
}
|
|
if (fpcr_changed && tregs->fpcr != lregs->fpcr) {
|
|
if (dooutput) {
|
|
uae_u32 val = lregs->fpcr;
|
|
sprintf(outbp, "FPCR: expected %08x -> %08x but got %08x\n", sregs->fpcr, val, tregs->fpcr);
|
|
outbp += strlen(outbp);
|
|
}
|
|
errflag |= 1 << 4;
|
|
}
|
|
if (fpiar_changed && tregs->fpiar != lregs->fpiar) {
|
|
if (dooutput) {
|
|
uae_u32 val = lregs->fpiar;
|
|
sprintf(outbp, "FPIAR: expected %08x but got %08x\n", val, tregs->fpiar);
|
|
outbp += strlen(outbp);
|
|
}
|
|
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, extratrace, extrag2w1, lregs)) {
|
|
errflag |= 1 << 8;
|
|
}
|
|
}
|
|
}
|
|
errflag_orig |= errflag;
|
|
}
|
|
|
|
if (loop_mode_jit) {
|
|
short idx = 0, cnt = 0, end = loop_mode_cnt;
|
|
for (;;) {
|
|
uae_u16 v1a = lmtable1[idx];
|
|
uae_u16 v2a = lmtable2[idx];
|
|
uae_u16 v1b = lmtable1[idx + 1];
|
|
uae_u16 v2b = lmtable2[idx + 1];
|
|
idx++;
|
|
if (loop_mode_jit == 3 || loop_mode_jit == 4) {
|
|
idx++;
|
|
}
|
|
if (idx >= LM_BUFFER / 2) {
|
|
break;
|
|
}
|
|
if (v1a == 0xffff && v2a == 0xffff) {
|
|
continue;
|
|
}
|
|
cnt++;
|
|
if (v1a != v2a) {
|
|
if (loop_mode_jit == 3 || loop_mode_jit == 4) {
|
|
static const char *constss[] = { "C", "Z", "V", "N", "X" };
|
|
const char *c = "?";
|
|
if (v1b < 5 && v1b == v2b) {
|
|
c = constss[v1b];
|
|
}
|
|
sprintf(outbp, "LM %02d/%02d %s: %d %c %d (%04x %04x - %04x %04x)\n",
|
|
cnt, end, c, (v1a & 0xff) != 0, v1a != v2a ? '!' : '=', (v2a & 0xff) != 0,
|
|
v1a, v1b, v2a, v2b);
|
|
outbp += strlen(outbp);
|
|
} else if (loop_mode_jit == 1 || loop_mode_jit == 2) {
|
|
sprintf(outbp, "LM %02d/%02d CCR: %02x != %02x",
|
|
cnt, end, v1a, v2a);
|
|
outbp += strlen(outbp);
|
|
sprintf(outbp, " X%c%d N%c%d Z%c%d V%c%d C%c%d\n",
|
|
(v1a & 0x10) != (v2a & 0x10) ? '!' : '=', (v2a & 0x10) != 0,
|
|
(v1a & 0x08) != (v2a & 0x08) ? '!' : '=', (v2a & 0x08) != 0,
|
|
(v1a & 0x04) != (v2a & 0x04) ? '!' : '=', (v2a & 0x04) != 0,
|
|
(v1a & 0x02) != (v2a & 0x02) ? '!' : '=', (v2a & 0x02) != 0,
|
|
(v1a & 0x01) != (v2a & 0x01) ? '!' : '=', (v2a & 0x01) != 0);
|
|
outbp += strlen(outbp);
|
|
}
|
|
}
|
|
if (cnt >= end) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if excskipccr + bus, address, divide by zero or chk + only CCR mismatch detected: clear error
|
|
if (excskipccr && errflag == (1 << 7) && (exceptionnum == 2 || exceptionnum == 3 || exceptionnum == 5 || exceptionnum == 6)) {
|
|
errflag = 0;
|
|
}
|
|
|
|
if (exitcnt == 0 && !exitmode && !errflag) {
|
|
errflag = 1;
|
|
}
|
|
|
|
if (errflag && dooutput) {
|
|
outbp = outbuffer;
|
|
addinfo();
|
|
strcpy(outbp, outbuffer2);
|
|
strcat(outbp, "Registers before:\n");
|
|
outbp += strlen(outbp);
|
|
out_regs(sregs, tregs, lregs, sregs, 1, branched);
|
|
strcat(outbp, "Registers after:\n");
|
|
outbp += strlen(outbp);
|
|
out_regs(tregs, tregs, lregs, sregs, 0, branched2);
|
|
#ifdef AMIGA
|
|
if (interrupttest == 1) {
|
|
sprintf(outbp, "INTREQ: %04x INTENA: %04x\n", test_intreq, test_intena);
|
|
outbp += strlen(outbp);
|
|
}
|
|
#endif
|
|
if (exc > 1) {
|
|
if (!(experr & 1)) {
|
|
sprintf(outbp, "OK: exception %d ", exc);
|
|
outbp += strlen(outbp);
|
|
if (exception_stored) {
|
|
hexdump(last_exception, exception_stored, 0);
|
|
}
|
|
if (extrag2w1) {
|
|
sprintf(outbp, " (+EXC %d)", extrag2w1);
|
|
outbp += strlen(outbp);
|
|
}
|
|
*outbp++ = '\n';
|
|
}
|
|
if ((exc == 3 || exc == 2) && cpu_lvl == 0) {
|
|
sprintf(outbp, "RW=%d IN=%d FC=%d\n",
|
|
((test_regs.exc >> (16 + 4)) & 1),
|
|
((test_regs.exc >> (16 + 3)) & 1),
|
|
((test_regs.exc >> (16 + 0)) & 7));
|
|
outbp += strlen(outbp);
|
|
}
|
|
} else if (exc == 0 && (test_regs.exc & 65535) == 4 && lregs->pc == test_regs.pc) {
|
|
sprintf(outbp, "OK: No exception generated\n");
|
|
outbp += strlen(outbp);
|
|
}
|
|
errors++;
|
|
}
|
|
if (!errflag && errflag_orig && dooutput) {
|
|
outbp = outbp_old;
|
|
*outbp = 0;
|
|
infoadded = 0;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static void out_endinfo(void)
|
|
{
|
|
sprintf(outbp, "%u (%u/%u/%u) ", testcnt, testcntsub, testcntsubmax, interrupt_delay_cnt);
|
|
outbp += strlen(outbp);
|
|
sprintf(outbp, "S=%d", supercnt);
|
|
outbp += strlen(outbp);
|
|
for (short i = 0; i < 128; i++) {
|
|
if (exceptioncount[0][i] || exceptioncount[1][i] || exceptioncount[2][i]) {
|
|
if (i == 2 || i == 3) {
|
|
sprintf(outbp, " E%02d=%d/%d/%d", i, exceptioncount[0][i], exceptioncount[1][i], exceptioncount[2][i]);
|
|
} else {
|
|
sprintf(outbp, " E%02d=%d", i, exceptioncount[0][i]);
|
|
}
|
|
outbp += strlen(outbp);
|
|
}
|
|
}
|
|
if (invalidcycles) {
|
|
sprintf(outbp, " INVCYC=%d", invalidcycles);
|
|
outbp += strlen(outbp);
|
|
}
|
|
strcat(outbp, "\n");
|
|
outbp += strlen(outbp);
|
|
if (interrupttest == 2) {
|
|
for (short i = 0; i < MAX_IPL_PC_VALUES; i++) {
|
|
if (ipl_pc[i]) {
|
|
sprintf(outbp, "%08x:%d ", ipl_pc[i], ipl_pc_cnt[i]);
|
|
outbp += strlen(outbp);
|
|
}
|
|
}
|
|
strcat(outbp, "\n");
|
|
outbp += strlen(outbp);
|
|
}
|
|
if (fpu_approxcnt) {
|
|
sprintf(outbp, "FPU approximate matches: %d\n", fpu_approxcnt);
|
|
}
|
|
supercnt = 0;
|
|
memset(exceptioncount, 0, sizeof(exceptioncount));
|
|
}
|
|
|
|
static void store_addr(uae_u32 s, uae_u8 *d)
|
|
{
|
|
if (s == 0xffffffff)
|
|
return;
|
|
for (int i = 0; i < SIZE_STORED_ADDRESS; i++) {
|
|
uae_u32 ss = s + (i - SIZE_STORED_ADDRESS_OFFSET);
|
|
if (is_valid_test_addr_read(ss)) {
|
|
*d++ = *((uae_u8*)ss);
|
|
} else {
|
|
*d++ = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static uae_u32 xorshiftstate;
|
|
static uae_u32 xorshift32(void)
|
|
{
|
|
uae_u32 x = xorshiftstate;
|
|
x ^= x << 13;
|
|
x ^= x >> 17;
|
|
x ^= x << 5;
|
|
xorshiftstate = x;
|
|
return xorshiftstate;
|
|
}
|
|
|
|
static void copyregs(struct registers *d, struct registers *s, short fpumode)
|
|
{
|
|
if (fpumode) {
|
|
memcpy(d->regs, s->regs, offsetof(struct registers, fsave));
|
|
} else {
|
|
memcpy(d->regs, s->regs, offsetof(struct registers, fpuregs));
|
|
}
|
|
}
|
|
|
|
static void process_test(uae_u8 *p)
|
|
{
|
|
outbp = outbuffer2;
|
|
outbp[0] = 0;
|
|
infoadded = 0;
|
|
errors = 0;
|
|
|
|
memset(&cur_regs, 0, sizeof(struct registers));
|
|
cur_regs.sr = interrupt_mask << 8;
|
|
cur_regs.srcaddr = 0xffffffff;
|
|
cur_regs.dstaddr = 0xffffffff;
|
|
cur_regs.branchtarget = 0xffffffff;
|
|
|
|
if (safe_memory_mode) {
|
|
set_berr(safe_memory_mode, 0);
|
|
}
|
|
|
|
endpc = opcode_memory_addr;
|
|
startpc = opcode_memory_addr;
|
|
start_test();
|
|
|
|
test_ccrignoremask = 0xffff;
|
|
|
|
#ifdef AMIGA
|
|
interrupt_count = 0;
|
|
clear_interrupt();
|
|
#endif
|
|
ahcnt = 0;
|
|
|
|
short doopcodeswap = 1;
|
|
|
|
if (interrupttest >= 2) {
|
|
doopcodeswap = 0;
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
cur_regs.endpc = endpc;
|
|
cur_regs.pc = startpc;
|
|
|
|
debug_item_data = p;
|
|
debug_item_count = 0;
|
|
for (;;) {
|
|
uae_u8 v = *p;
|
|
if (v == CT_END_INIT || v == CT_END_FINISH)
|
|
break;
|
|
p = restore_data(p, &cur_regs);
|
|
debug_item_count++;
|
|
}
|
|
debug_item_count = -1;
|
|
if (*p == CT_END_FINISH)
|
|
break;
|
|
p++;
|
|
|
|
int stackcopysize = 0;
|
|
for (short i = 0; i < 32; i += 2) {
|
|
if (!is_valid_test_addr_readwrite(cur_regs.regs[15] + i))
|
|
break;
|
|
stackcopysize += 2;
|
|
}
|
|
|
|
store_addr(cur_regs.srcaddr, srcaddr);
|
|
store_addr(cur_regs.dstaddr, dstaddr);
|
|
store_addr(cur_regs.branchtarget, branchtarget);
|
|
startpc = cur_regs.pc;
|
|
endpc = cur_regs.endpc;
|
|
opcode_memory_end = (uae_u8*)endpc;
|
|
|
|
int fpumode = fpu_model && (opcode_memory[0] & 0xf0) == 0xf0;
|
|
if (fpumode) {
|
|
initfpu();
|
|
}
|
|
copyregs(&last_regs, &cur_regs, fpumode);
|
|
|
|
uae_u32 originalopcodeend = (NOP_OPCODE << 16) | ILLG_OPCODE;
|
|
short opcodeendsizeextra = 0;
|
|
uae_u32 opcodeend = originalopcodeend;
|
|
int extraccr = 0;
|
|
int validendsize = 0;
|
|
if (is_valid_test_addr_readwrite((uae_u32)opcode_memory_end + 2)) {
|
|
validendsize = 2;
|
|
} else if (is_valid_test_addr_readwrite((uae_u32)opcode_memory_end)) {
|
|
validendsize = 1;
|
|
}
|
|
|
|
int old_super = -1;
|
|
|
|
for (;;) {
|
|
uae_u16 sr_mask = 0;
|
|
|
|
if (extraccr & 1)
|
|
sr_mask |= 0x2000; // S
|
|
if (extraccr & 2)
|
|
sr_mask |= 0x4000; // T0
|
|
if (extraccr & 4)
|
|
sr_mask |= 0x8000; // T1
|
|
if (extraccr & 8)
|
|
sr_mask |= 0x1000; // M
|
|
|
|
uae_u8 ccrmode = *p++;
|
|
short maxccr = ccrmode & 0x3f;
|
|
short ccrshift = 0;
|
|
while ((maxccr - 1) & (1 << ccrshift)) {
|
|
ccrshift++;
|
|
}
|
|
ccrshift--;
|
|
testcntsubmax = maxccr;
|
|
testcntsub = 0;
|
|
for (short ccrcnt = 0; ccrcnt < maxccr; ccrcnt++, testcntsub++) {
|
|
short ccr = ccrcnt & (maxccr - 1);
|
|
fpu_approx = 0;
|
|
|
|
if (doopcodeswap) {
|
|
opcodeend = (opcodeend >> 16) | (opcodeend << 16);
|
|
}
|
|
opcodeendsizeextra = (opcodeend >> 16) == NOP_OPCODE ? 2 : 0;
|
|
if (validendsize == 2) {
|
|
pl(opcode_memory_end, opcodeend);
|
|
} else if (validendsize == 1) {
|
|
pw(opcode_memory_end, opcodeend >> 16);
|
|
}
|
|
|
|
if (cur_regs.branchtarget != 0xffffffff && !(cur_regs.branchtarget & 1)) {
|
|
if (cur_regs.branchtarget_mode == 1) {
|
|
uae_u32 bv = gl((uae_u8*)cur_regs.branchtarget);
|
|
bv = (bv >> 16) | (bv << 16);
|
|
pl((uae_u8*)cur_regs.branchtarget, bv);
|
|
} else if (cur_regs.branchtarget_mode == 2) {
|
|
uae_u16 bv = gw((uae_u8*)cur_regs.branchtarget);
|
|
if (bv == NOP_OPCODE)
|
|
bv = ILLG_OPCODE;
|
|
else
|
|
bv = NOP_OPCODE;
|
|
pw((uae_u8*)cur_regs.branchtarget, bv);
|
|
}
|
|
}
|
|
|
|
cur_regs.ssp = super_stack_memory - 0x80;
|
|
cur_regs.msp = super_stack_memory;
|
|
cur_regs.fpiar = 0xffffffff;
|
|
|
|
copyregs(&test_regs, &cur_regs, fpumode);
|
|
|
|
test_regs.pc = startpc;
|
|
test_regs.cyclest = 0xffffffff;
|
|
test_regs.fpeaset = 0;
|
|
test_regs.blttemp = blt_data;
|
|
|
|
#ifdef M68K
|
|
if (stackcopysize > 0)
|
|
xmemcpy((void*)test_regs.ssp, (void*)test_regs.regs[15], stackcopysize);
|
|
#endif
|
|
if (maxccr >= 32) {
|
|
test_regs.sr = ccr & 0xff;
|
|
} else {
|
|
test_regs.sr = (ccr & 1) ? 31 : 0;
|
|
}
|
|
test_regs.sr |= sr_mask | (initial_interrupt_mask << 8);
|
|
if (fpumode) {
|
|
test_regs.fpcr = 0;
|
|
test_regs.fpsr = 0;
|
|
if (maxccr < 16) {
|
|
// all on/all off
|
|
test_regs.fpsr = ((ccr & 1) ? 15 : 0) << 24;
|
|
test_regs.fpcr = ((ccr & 1) ? 15 : 0) << 4;
|
|
} else {
|
|
if (ccrmode & 0x40) {
|
|
// condition codes
|
|
test_regs.fpsr = (ccr & 15) << 24;
|
|
} else {
|
|
// precision and rounding
|
|
test_regs.fpcr = (ccr & 15) << 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef AMIGA
|
|
if (interrupttest == 1) {
|
|
interrupt_count = *p++;
|
|
}
|
|
#endif
|
|
|
|
while ((*p) == CT_OVERRIDE_REG) {
|
|
p++;
|
|
uae_u8 v = *p++;
|
|
uae_u8 r = v & CT_DATA_MASK;
|
|
uae_u8 size = v & CT_SIZE_MASK;
|
|
if (r == CT_SR) {
|
|
if (size == CT_SIZE_BYTE) {
|
|
test_regs.sr &= 0xff00;
|
|
test_regs.sr |= *p++;
|
|
} else {
|
|
test_regs.sr = (*p++) << 8;
|
|
test_regs.sr |= *p++;
|
|
}
|
|
cur_regs.sr = test_regs.sr;
|
|
} else if (r == CT_FPSR) {
|
|
cur_regs.fpsr = test_regs.fpsr = gl(p);
|
|
p += 4;
|
|
} else if (r == CT_FPCR) {
|
|
cur_regs.fpcr = test_regs.fpcr = gl(p);
|
|
p += 4;
|
|
} else if (r == CT_FPIAR) {
|
|
cur_regs.fpiar = test_regs.fpiar = gl(p);
|
|
p += 4;
|
|
} else if (r >= CT_DREG && r < CT_DREG + 16) {
|
|
if (size == CT_SIZE_FPU) {
|
|
struct fpureg *f = &test_regs.fpuregs[r - CT_DREG];
|
|
uae_u32 val = gl(p);
|
|
f->exp = val >> 16;
|
|
f->dummy = val;
|
|
f->m[0] = gl(p + 4);
|
|
f->m[1] = gl(p + 8);
|
|
p += 12;
|
|
} else {
|
|
cur_regs.regs[r - CT_DREG] = gl(p);
|
|
p += 4;
|
|
}
|
|
} else {
|
|
end_test();
|
|
printf("Unknown override register %02x!\n", v);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
test_regs.expsr = test_regs.sr | 0x2000;
|
|
|
|
// internally modified registers become part of cur_regs
|
|
cur_regs.sr = test_regs.sr;
|
|
cur_regs.expsr = test_regs.expsr;
|
|
cur_regs.fpsr = test_regs.fpsr;
|
|
cur_regs.fpcr = test_regs.fpcr;
|
|
if (!sr_mask && !ccr) {
|
|
last_regs.sr = test_regs.sr;
|
|
last_regs.expsr = test_regs.expsr;
|
|
last_regs.fpsr = test_regs.fpsr;
|
|
last_regs.fpcr = test_regs.fpcr;
|
|
}
|
|
|
|
int super = (test_regs.sr & 0x2000) != 0;
|
|
|
|
if (super != old_super) {
|
|
stackaddr_ptr = super ? test_regs.ssp : test_regs.regs[15];
|
|
store_addr(stackaddr_ptr, stackaddr);
|
|
old_super = super;
|
|
}
|
|
|
|
if (exitcnt >= 0) {
|
|
exitcnt--;
|
|
if (!exitcnt && exitmode) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
if ((*p) == CT_END_SKIP) {
|
|
|
|
p++;
|
|
|
|
} else {
|
|
|
|
short ignore_errors = 0;
|
|
short ignore_sr = 0;
|
|
|
|
if ((ccr_mask & ccr) || (ccr == 0)) {
|
|
|
|
reset_error_vectors();
|
|
|
|
#if 0
|
|
volatile int *tn = (volatile int*)0x100;
|
|
*tn = testcnt;
|
|
#endif
|
|
#if 0
|
|
uae_u32 *sx = (uae_u32*)startpc;
|
|
if (*sx == 0xf2364c98) {
|
|
volatile uae_u16 *tn = (volatile uae_u16*)0x70000;
|
|
*tn = 0x1234;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef AMIGA
|
|
if (interrupttest || initial_interrupt_mask || initial_interrupt) {
|
|
set_interrupt();
|
|
}
|
|
#endif
|
|
|
|
if (cpu_lvl == 1) {
|
|
execute_test010(&test_regs);
|
|
} else if (cpu_lvl >= 2) {
|
|
flushcache(cpu_lvl);
|
|
if (fpu_model)
|
|
execute_testfpu(&test_regs);
|
|
else
|
|
execute_test020(&test_regs);
|
|
} else {
|
|
execute_test000(&test_regs);
|
|
}
|
|
|
|
#ifdef AMIGA
|
|
if (interrupttest || initial_interrupt_mask || initial_interrupt) {
|
|
clear_interrupt();
|
|
}
|
|
#endif
|
|
|
|
if (ccr_mask == 0 && ccr == 0)
|
|
ignore_sr = 1;
|
|
|
|
set_error_vectors();
|
|
|
|
} else {
|
|
|
|
ignore_errors = 1;
|
|
ignore_sr = 1;
|
|
|
|
}
|
|
|
|
#if 0
|
|
if ((*p) == CT_SKIP_REGS) {
|
|
p++;
|
|
for (int i = 0; i < 16; i++) {
|
|
test_regs.regs[i] = regs.regs[i];
|
|
}
|
|
if (fpu_model) {
|
|
for (int i = 0; i < 8; i++) {
|
|
test_regs.fpuregs[i] = regs.fpuregs[i];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
p = validate_test(p, ignore_errors, ignore_sr, &cur_regs, &test_regs, &last_regs, opcodeendsizeextra);
|
|
|
|
if (interrupttest == 2) {
|
|
short found = 0;
|
|
for (short i = 0; i < MAX_IPL_PC_VALUES; i++) {
|
|
if (ipl_pc[i] == test_regs.pc) {
|
|
ipl_pc_cnt[i]++;
|
|
found = 1;
|
|
|
|
}
|
|
}
|
|
if (!found) {
|
|
for (short i = 0; i < MAX_IPL_PC_VALUES; i++) {
|
|
if (ipl_pc[i] == 0) {
|
|
ipl_pc[i] = test_regs.pc;
|
|
ipl_pc_cnt[i] = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
testcnt++;
|
|
if (super)
|
|
supercnt++;
|
|
|
|
}
|
|
|
|
if (testexit()) {
|
|
end_test();
|
|
printf("\nAborted (%d)\n", testcnt);
|
|
exit(0);
|
|
}
|
|
|
|
if (fpu_approx) {
|
|
fpu_approxcnt++;
|
|
}
|
|
|
|
if (quit || errors) {
|
|
if (!quit && errorcnt > 0 && totalerrors < errorcnt) {
|
|
if (totalerrors > 0) {
|
|
strcat(stored_outbuffer, "----------------------------------------\n");
|
|
}
|
|
if (strlen(stored_outbuffer) + strlen(outbuffer) + 40 >= outbuffer_size) {
|
|
goto end;
|
|
}
|
|
strcat(stored_outbuffer, outbuffer);
|
|
outbp = stored_outbuffer + strlen(stored_outbuffer);
|
|
out_endinfo();
|
|
infoadded = 0;
|
|
errors = 0;
|
|
outbuffer[0] = 0;
|
|
outbuffer2[0] = 0;
|
|
outbp = outbuffer2;
|
|
} else {
|
|
goto end;
|
|
}
|
|
totalerrors++;
|
|
}
|
|
}
|
|
|
|
if (*p == CT_END) {
|
|
p++;
|
|
break;
|
|
}
|
|
|
|
extraccr = *p++;
|
|
|
|
}
|
|
|
|
if (validendsize == 2) {
|
|
pl(opcode_memory_end, originalopcodeend);
|
|
} else if (validendsize == 1) {
|
|
pw(opcode_memory_end, originalopcodeend >> 16);
|
|
}
|
|
|
|
restoreahist();
|
|
}
|
|
|
|
end:
|
|
end_test();
|
|
set_berr(0, 0);
|
|
|
|
if (errorcnt > 0) {
|
|
printf("%s", stored_outbuffer);
|
|
} else {
|
|
if (infoadded) {
|
|
printf("\n");
|
|
printf("%s", outbuffer);
|
|
}
|
|
}
|
|
if (exitcnt == 0 && exitmode) {
|
|
printf("Exit count expired\n");
|
|
}
|
|
}
|
|
|
|
static void freestuff(void)
|
|
{
|
|
if (test_memory && test_memory_addr) {
|
|
free_absolute(test_memory_addr, test_memory_size);
|
|
}
|
|
if (blt_data) {
|
|
free_blttemp(blt_data, BLT_TEMP_SIZE);
|
|
}
|
|
#ifdef WAITEXIT
|
|
getchar();
|
|
#endif
|
|
}
|
|
|
|
static uae_u32 read_u32(uae_u8 *headerfile, int *poffset)
|
|
{
|
|
uae_u8 data[4] = { 0 };
|
|
memcpy(data, headerfile + (*poffset), 4);
|
|
(*poffset) += 4;
|
|
return gl(data);
|
|
}
|
|
|
|
static int test_mnemo(const char *opcode)
|
|
{
|
|
int size;
|
|
uae_u32 v;
|
|
char tfname[256];
|
|
int filecnt = 0;
|
|
uae_u32 starttimeid;
|
|
int lvl;
|
|
|
|
errors = 0;
|
|
quit = 0;
|
|
|
|
|
|
sprintf(tfname, "%s/0000.dat", opcode);
|
|
size = -1;
|
|
int foffset = 0;
|
|
uae_u8 *headerfile = load_file(path, tfname, NULL, &size, &foffset, 1, 1);
|
|
if (!headerfile) {
|
|
printf("Data file failed to open\n");
|
|
exit(0);
|
|
}
|
|
int headoffset = 0;
|
|
v = read_u32(headerfile, &headoffset);
|
|
if (v != DATA_VERSION) {
|
|
printf("Invalid test data file (header %08x<>%08x)\n", v, DATA_VERSION);
|
|
exit(0);
|
|
}
|
|
|
|
starttimeid = read_u32(headerfile, &headoffset);
|
|
uae_u32 hmem_lmem = read_u32(headerfile, &headoffset);
|
|
hmem_rom = (uae_s16)(hmem_lmem >> 16);
|
|
lmem_rom = (uae_s16)(hmem_lmem & 65535);
|
|
test_memory_addr = read_u32(headerfile, &headoffset);
|
|
test_memory_size = read_u32(headerfile, &headoffset);
|
|
test_memory_end = test_memory_addr + test_memory_size;
|
|
opcode_memory_addr = read_u32(headerfile, &headoffset);
|
|
opcode_memory = (uae_u8*)opcode_memory_addr;
|
|
v = read_u32(headerfile, &headoffset);
|
|
lvl = (v >> 16) & 15;
|
|
interrupt_mask = (v >> 20) & 7;
|
|
addressing_mask = (v & 0x80000000) ? 0xffffffff : 0x00ffffff;
|
|
interrupttest = (v >> 26) & 3;
|
|
sr_undefined_mask = v & 0xffff;
|
|
safe_memory_mode = (v >> 23) & 7;
|
|
loop_mode_jit = (v >> 28) & 1;
|
|
loop_mode_68010 = (v >> 29) & 1;
|
|
v = read_u32(headerfile, &headoffset);
|
|
initial_interrupt_mask = v & 7;
|
|
initial_interrupt = (v >> 3) & 7;
|
|
v = read_u32(headerfile, &headoffset);
|
|
v = read_u32(headerfile, &headoffset);
|
|
fpu_model = read_u32(headerfile, &headoffset);
|
|
test_low_memory_start = read_u32(headerfile, &headoffset);
|
|
test_low_memory_end = read_u32(headerfile, &headoffset);
|
|
test_high_memory_start = read_u32(headerfile, &headoffset);
|
|
test_high_memory_end = read_u32(headerfile, &headoffset);
|
|
safe_memory_start = (uae_u8*)read_u32(headerfile, &headoffset);
|
|
safe_memory_end = (uae_u8*)read_u32(headerfile, &headoffset);
|
|
user_stack_memory = read_u32(headerfile, &headoffset);
|
|
super_stack_memory = read_u32(headerfile, &headoffset);
|
|
exception_vectors = read_u32(headerfile, &headoffset);
|
|
v = read_u32(headerfile, &headoffset);
|
|
if (loop_mode_jit || loop_mode_68010) {
|
|
loop_mode_cnt = v & 0xff;
|
|
}
|
|
if ((v >> 8) & 15) {
|
|
loop_mode_jit = (v >> 8) & 15;
|
|
}
|
|
read_u32(headerfile, &headoffset);
|
|
read_u32(headerfile, &headoffset);
|
|
memcpy(inst_name, headerfile + headoffset, sizeof(inst_name) - 1);
|
|
inst_name[sizeof(inst_name) - 1] = 0;
|
|
free(headerfile);
|
|
headerfile = NULL;
|
|
|
|
int lvl2 = cpu_lvl;
|
|
if (lvl2 == 5 && lvl2 != lvl)
|
|
lvl2 = 4;
|
|
|
|
if (lvl != lvl2) {
|
|
printf("Mismatched CPU model: %u <> %u\n",
|
|
68000 + 10 * (cpu_lvl < 5 ? cpu_lvl : 6), 68000 + (lvl < 5 ? lvl : 6) * 10);
|
|
return 0;
|
|
}
|
|
|
|
if (!check_undefined_sr) {
|
|
sr_undefined_mask = ~sr_undefined_mask;
|
|
} else {
|
|
sr_undefined_mask = 0xffff;
|
|
}
|
|
|
|
if (lmem_rom >= 0 && (low_memory_size <= 0 || !low_memory_temp)) {
|
|
printf("lmem.dat required but it was not loaded or was missing.\n");
|
|
return 0;
|
|
}
|
|
if (hmem_rom >= 0 && (high_memory_size <= 0 || !high_memory_temp)) {
|
|
printf("hmem.dat required but it was not loaded or was missing.\n");
|
|
return 0;
|
|
}
|
|
|
|
low_memory_offset = 0;
|
|
if (test_low_memory_start != 0xffffffff) {
|
|
low_memory_offset = test_low_memory_start;
|
|
} 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);
|
|
if (!test_memory) {
|
|
printf("Couldn't allocate tmem area %08x-%08x\n", test_memory_addr, test_memory_addr + test_memory_size - 1);
|
|
exit(0);
|
|
}
|
|
absallocated = test_memory;
|
|
}
|
|
if (absallocated != test_memory) {
|
|
printf("tmem area changed!?\n");
|
|
exit(0);
|
|
}
|
|
|
|
size = test_memory_size;
|
|
load_file(path, "tmem.dat", test_memory, &size, NULL, 1, 0);
|
|
if (size != test_memory_size) {
|
|
printf("tmem.dat size mismatch\n");
|
|
exit(0);
|
|
}
|
|
|
|
printf("CPUlvl=%d, Mask=%08x Code=%08x SP=%08x ISP=%08x\n",
|
|
cpu_lvl, addressing_mask, (uae_u32)opcode_memory,
|
|
user_stack_memory, super_stack_memory);
|
|
printf(" Low: %08x-%08x High: %08x-%08x\n",
|
|
test_low_memory_start, test_low_memory_end - 1,
|
|
test_high_memory_start, test_high_memory_end - 1);
|
|
printf("Test: %08x-%08x Safe: %08x-%08x\n",
|
|
test_memory_addr, test_memory_end - 1,
|
|
(uae_u32)safe_memory_start, (uae_u32)safe_memory_end - 1);
|
|
printf("%s (%s):\n", inst_name, group);
|
|
|
|
testcnt = 0;
|
|
fpu_approxcnt = 0;
|
|
memset(exceptioncount, 0, sizeof(exceptioncount));
|
|
supercnt = 0;
|
|
exitcnt = exitcnt2;
|
|
|
|
uae_u8 *test_data_prealloc = NULL;
|
|
|
|
if (prealloc) {
|
|
test_data_prealloc = malloc(prealloc_test_data_size);
|
|
if (!test_data_prealloc) {
|
|
printf("Couldn't preallocate test data memory, %d bytes.\n", prealloc_test_data_size);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
blt_data = allocate_blttemp(BLT_TEMP_SIZE);
|
|
if (!blt_data) {
|
|
printf("blt_temp failed to allocated.\n");
|
|
exit(0);
|
|
}
|
|
|
|
int otestcnt = -1;
|
|
for (;;) {
|
|
if (otestcnt != testcnt) {
|
|
printf("%s (%s). %u...\n", tfname, group, testcnt);
|
|
otestcnt = testcnt;
|
|
}
|
|
|
|
sprintf(tfname, "%s/%04d.dat", opcode, filecnt);
|
|
|
|
test_data_size = 0;
|
|
test_data = load_file(path, tfname, test_data_prealloc, &test_data_size, &foffset, 0, 1);
|
|
|
|
if (!test_data) {
|
|
if (foffset < 0) {
|
|
filecnt++;
|
|
foffset = 0;
|
|
continue;
|
|
}
|
|
if (askifmissing) {
|
|
printf("Couldn't open '%s%s'. Type new path and press enter.\n", path, tfname);
|
|
path[0] = 0;
|
|
fgets(path, sizeof(path), stdin);
|
|
if (path[0]) {
|
|
path[strlen(path) - 1] = 0;
|
|
if (path[0]) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
quit = 1;
|
|
break;
|
|
}
|
|
if (gl(test_data) != DATA_VERSION) {
|
|
printf("Invalid test data file (header, %08x<>%08x)\n", gl(test_data), DATA_VERSION);
|
|
exit(0);
|
|
}
|
|
if (gl(test_data + 4) != starttimeid) {
|
|
printf("Test data file header mismatch (old test data file?)\n");
|
|
break;
|
|
}
|
|
if (test_data[test_data_size - 2] != CT_END_FINISH) {
|
|
printf("Invalid test data file (footer)\n");
|
|
free(test_data);
|
|
exit(0);
|
|
}
|
|
uae_u32 flags = gl(test_data + 8);
|
|
instructionsize = flags & 3;
|
|
|
|
// last file?
|
|
int last = test_data[test_data_size - 1] == CT_END_FINISH;
|
|
|
|
test_data_size -= 16;
|
|
if (test_data_size <= 0)
|
|
break;
|
|
|
|
test_data += 16;
|
|
process_test(test_data);
|
|
test_data -= 16;
|
|
|
|
if (!prealloc) {
|
|
free(test_data);
|
|
}
|
|
test_data = NULL;
|
|
|
|
if (errors || quit || last || (!exitcnt && exitmode)) {
|
|
break;
|
|
}
|
|
|
|
if (foffset <= 0) {
|
|
filecnt++;
|
|
}
|
|
}
|
|
|
|
if (blt_data) {
|
|
free_blttemp(blt_data, BLT_TEMP_SIZE);
|
|
blt_data = 0;
|
|
}
|
|
|
|
if (errorcnt == 0) {
|
|
outbp = outbuffer;
|
|
out_endinfo();
|
|
printf("%s", outbuffer);
|
|
}
|
|
if (exitcnt == 0 && !exitmode) {
|
|
printf("Exit count expired\n");
|
|
}
|
|
if (!errors && !quit) {
|
|
printf("All tests complete (total %u).\n", testcnt);
|
|
}
|
|
|
|
return errors || quit;
|
|
}
|
|
|
|
static int getparamval(const char *p)
|
|
{
|
|
uae_u32 inv = 0;
|
|
if (p[0] == '~') {
|
|
inv = 0xffffffff;
|
|
p++;
|
|
}
|
|
if (strlen(p) > 2 && p[0] == '0' && toupper(p[1]) == 'X') {
|
|
char *endptr;
|
|
return strtol(p + 2, &endptr, 16) ^ inv;
|
|
} else {
|
|
return atol(p) ^ inv;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int stop_on_error = 1;
|
|
|
|
atexit(freestuff);
|
|
|
|
#ifndef M68K
|
|
|
|
char *params[] = { "", "lslw.w", "", NULL };
|
|
argv = params;
|
|
argc = 3;
|
|
|
|
strcpy(path, "C:\\projects\\winuae\\src\\cputest\\data\\");
|
|
|
|
vbr_zero = calloc(1, 1024);
|
|
|
|
cpu_lvl = 0;
|
|
|
|
#else
|
|
|
|
#define _stricmp strcasecmp
|
|
|
|
strcpy(path, "data/");
|
|
|
|
low_memory = (uae_u8*)0;
|
|
high_memory = (uae_u8*)0xffff8000;
|
|
|
|
cpu_lvl = get_cpu_model();
|
|
|
|
if (cpu_lvl == 5) {
|
|
// Overwrite MOVEC to/from MSP
|
|
// with NOPs if 68060
|
|
extern void *msp_address1;
|
|
extern void *msp_address2;
|
|
extern void *msp_address3;
|
|
extern void *msp_address4;
|
|
*((uae_u32*)&msp_address1) = 0x4e714e71;
|
|
*((uae_u32*)&msp_address2) = 0x4e714e71;
|
|
*((uae_u32*)&msp_address3) = 0x4e714e71;
|
|
*((uae_u32*)&msp_address4) = 0x4e714e71;
|
|
}
|
|
|
|
#endif
|
|
|
|
if (argc < 2) {
|
|
printf("cputest (<group>)/<all/mnemonic> (<start mnemonic>) (other params)\n");
|
|
#ifdef M68K
|
|
printf("Designed and programmed by Toni Wilen. ("REVDATE " " REVTIME")");
|
|
#endif
|
|
printf("\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 (continue to next test, all mode only)\n");
|
|
printf("-errors n = store up to n errors before aborting test.\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("-skipexcccr = ignore CCR if DivByZero, CHK, Address/Bus error exception.\n");
|
|
printf("-skipmem = do not validate memory writes.\n");
|
|
printf("-skipreg = do not validate registers.\n");
|
|
printf("-askifmissing = ask for new path if dat file is missing.\n");
|
|
printf("-exit n = exit after n tests.\n");
|
|
printf("-exitok n = exit after n tests, continue normally.\n");
|
|
printf("-fpuadj <exp> 16-bit exponent range value. (16383 = 1.0)\n");
|
|
printf("-fpsrmask = ignore FPSR bits that are not set.\n");
|
|
printf("-cycles [range adjust] = check cycle counts.\n");
|
|
printf("-cyclecnt <address>. Use custom hardware cycle counter.\n");
|
|
#ifdef AMIGA
|
|
printf("-uae = running in UAE, automatic bus error enable/disable.\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
opcode[0] = 0;
|
|
strcpy(group, "default");
|
|
|
|
check_undefined_sr = 1;
|
|
ccr_mask = 0xff;
|
|
fpsr_ignore_mask = 0xffffffff;
|
|
disasm = 1;
|
|
exitcnt2 = -1;
|
|
exitmode = 0;
|
|
cyclecounter_addr = 0xffffffff;
|
|
cycles_range = 2;
|
|
fpu_adjust_exp = -1;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
char *s = argv[i];
|
|
char *next = i + 1 < argc && argv[i + 1][0] != '-' ? argv[i + 1] : NULL;
|
|
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, "-nostop")) {
|
|
continue_on_error = 1;
|
|
} else if (!_stricmp(s, "-noundefined")) {
|
|
check_undefined_sr = 0;
|
|
} else if (!_stricmp(s, "-ccrmask")) {
|
|
ccr_mask = 0;
|
|
if (next) {
|
|
ccr_mask = ~getparamval(next);
|
|
i++;
|
|
}
|
|
} else if (!_stricmp(s, "-fpsrmask")) {
|
|
fpsr_ignore_mask = 0;
|
|
if (next) {
|
|
fpsr_ignore_mask = ~getparamval(next);
|
|
i++;
|
|
}
|
|
} else if (!_stricmp(s, "-silent")) {
|
|
dooutput = 0;
|
|
} else if (!_stricmp(s, "-68000")) {
|
|
cpu_lvl = 0;
|
|
} else if (!_stricmp(s, "-68010")) {
|
|
cpu_lvl = 1;
|
|
} else if (!_stricmp(s, "-68020")) {
|
|
cpu_lvl = 2;
|
|
} else if (!_stricmp(s, "-68030")) {
|
|
cpu_lvl = 3;
|
|
} else if (!_stricmp(s, "-68040")) {
|
|
cpu_lvl = 4;
|
|
} else if (!_stricmp(s, "-68060")) {
|
|
cpu_lvl = 5;
|
|
} else if (!_stricmp(s, "-nodisasm")) {
|
|
disasm = 0;
|
|
} else if (!_stricmp(s, "-basicexc")) {
|
|
basicexcept = 1;
|
|
} else if (!_stricmp(s, "-skipexcccr")) {
|
|
excskipccr = 1;
|
|
} else if (!_stricmp(s, "-skipmem")) {
|
|
skipmemwrite = 1;
|
|
} else if (!_stricmp(s, "-skipreg")) {
|
|
skipregchange = 1;
|
|
} else if (!_stricmp(s, "-skipccr")) {
|
|
skipccrchange = 1;
|
|
} else if (!_stricmp(s, "-askifmissing")) {
|
|
askifmissing = 1;
|
|
} else if (!_stricmp(s, "-next")) {
|
|
nextall = 1;
|
|
} else if (!_stricmp(s, "-exit")) {
|
|
if (next) {
|
|
exitcnt2 = atoi(next);
|
|
exitmode = 0;
|
|
i++;
|
|
}
|
|
} else if (!_stricmp(s, "-exitok")) {
|
|
if (next) {
|
|
exitcnt2 = atoi(next);
|
|
exitmode = 1;
|
|
i++;
|
|
}
|
|
} else if (!_stricmp(s, "-irqcnt")) {
|
|
if (next) {
|
|
irqcnt = atoi(next);
|
|
i++;
|
|
}
|
|
} else if (!_stricmp(s, "-prealloc")) {
|
|
prealloc = 1;
|
|
} else if (!_stricmp(s, "-fpuadj")) {
|
|
if (next) {
|
|
fpu_adjust_exp = atol(next);
|
|
if (fpu_adjust_exp >= 0) {
|
|
is_fpu_adjust = 1;
|
|
}
|
|
|
|
}
|
|
} else if (!_stricmp(s, "-cycles")) {
|
|
cycles = 1;
|
|
if (i + 1 < argc && argv[i][0] != '-') {
|
|
i++;
|
|
cycles_range = atoi(argv[i]);
|
|
if (i + 1 < argc && argv[i][0] != '-') {
|
|
i++;
|
|
cycles_adjust = atoi(argv[i]);
|
|
}
|
|
}
|
|
} else if (!_stricmp(s, "-cyclecnt")) {
|
|
if (next) {
|
|
cyclecounter_addr = getparamval(next);
|
|
cycles = 1;
|
|
}
|
|
} else if (!_stricmp(s, "-uae")) {
|
|
uaemode = 1;
|
|
} else if (!_stricmp(s, "-errors")) {
|
|
if (i + 1 < argc) {
|
|
i++;
|
|
errorcnt = atoi(argv[i]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef M68K
|
|
if(cyclecounter_addr != 0xffffffff) {
|
|
// yes, this is far too ugly
|
|
extern uae_u32 cyclereg_address1;
|
|
extern uae_u32 cyclereg_address2;
|
|
extern uae_u32 cyclereg_address3;
|
|
extern uae_u32 cyclereg_address4;
|
|
extern uae_u32 cyclereg_address5;
|
|
extern uae_u32 cyclereg_address6;
|
|
*((uae_u32*)((uae_u32)(&cyclereg_address1) + 2)) = cyclecounter_addr;
|
|
*((uae_u32*)((uae_u32)(&cyclereg_address2) + 2)) = cyclecounter_addr;
|
|
*((uae_u32*)((uae_u32)(&cyclereg_address3) + 2)) = cyclecounter_addr;
|
|
*((uae_u32*)((uae_u32)(&cyclereg_address4) + 2)) = cyclecounter_addr;
|
|
*((uae_u32*)((uae_u32)(&cyclereg_address5) + 2)) = cyclecounter_addr;
|
|
*((uae_u32*)((uae_u32)(&cyclereg_address6) + 2)) = cyclecounter_addr;
|
|
}
|
|
#endif
|
|
|
|
if (errorcnt > 0) {
|
|
outbuffer_size = errorcnt * PAGE_OUTBUFFER_SIZE;
|
|
stored_outbuffer = calloc(outbuffer_size, 1);
|
|
if (!stored_outbuffer) {
|
|
printf("Out of memory when allocating temporary output buffer.\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void *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 = x_opendir(path);
|
|
}
|
|
|
|
int cpumodel = cpu_lvl == 5 ? 6 : cpu_lvl;
|
|
sprintf(cpustr, "%u_", cpumodel);
|
|
char *pathptr = path + strlen(path);
|
|
|
|
for (;;) {
|
|
|
|
if (groupd) {
|
|
pathptr[0] = 0;
|
|
char dname[256];
|
|
int groupdr = x_readdir(path, groupd, dname);
|
|
if (!groupdr)
|
|
break;
|
|
if (groupdr > 0)
|
|
continue;
|
|
if (dname[0] == '.')
|
|
continue;
|
|
if (strnicmp(cpustr, dname, strlen(cpustr)))
|
|
continue;
|
|
sprintf(pathptr, "%s/", dname);
|
|
strcpy(group, dname + strlen(cpustr));
|
|
} else {
|
|
sprintf(pathptr, "%u_%s/",cpumodel, group);
|
|
}
|
|
|
|
low_memory_size = -1;
|
|
low_memory_temp = load_file(path, "lmem.dat", NULL, &low_memory_size, NULL, 0, 1);
|
|
high_memory_size = -1;
|
|
high_memory_temp = load_file(path, "hmem.dat", NULL, &high_memory_size, NULL, 0, 1);
|
|
|
|
#ifndef M68K
|
|
low_memory = calloc(1, 32768);
|
|
if (high_memory_size > 0)
|
|
high_memory = calloc(1, high_memory_size);
|
|
#endif
|
|
|
|
low_memory_back = calloc(1, 32768);
|
|
if (!low_memory_back) {
|
|
printf("Couldn't allocate low_memory_back.\n");
|
|
return 0;
|
|
}
|
|
if (!low_memory_temp) {
|
|
low_memory_temp = calloc(1, 32768);
|
|
if (!low_memory_temp) {
|
|
printf("Couldn't allocate low_memory_temp.\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (high_memory_size > 0) {
|
|
high_memory_back = calloc(1, high_memory_size);
|
|
if (!high_memory_back) {
|
|
printf("Couldn't allocate high_memory_back.\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!_stricmp(opcode, "all") || (opcode[0] && opcode[strlen(opcode) - 1] == '*')) {
|
|
void *d = x_opendir(path);
|
|
if (!d) {
|
|
printf("Couldn't list directory '%s'\n", path);
|
|
return 0;
|
|
}
|
|
#define MAX_FILE_LEN 128
|
|
#define MAX_FILES 500
|
|
char *dirs = calloc(MAX_FILES, MAX_FILE_LEN);
|
|
int diroff = 0;
|
|
int dircnt = 0;
|
|
if (!dirs)
|
|
return 0;
|
|
|
|
// get directory
|
|
for (;;) {
|
|
int dr = x_readdir(path, d, dirs + diroff);
|
|
if (!dr)
|
|
break;
|
|
if (dr < 0 && dirs[diroff] != '.') {
|
|
dircnt++;
|
|
diroff += MAX_FILE_LEN;
|
|
if (dircnt >= MAX_FILES) {
|
|
printf("too many directories!? (%d)\n", dircnt);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
x_closedir(d);
|
|
|
|
// sort directory
|
|
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 && 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 (nextall) {
|
|
first += MAX_FILE_LEN;
|
|
}
|
|
|
|
int err = 0;
|
|
if (!_stricmp(opcode, "all")) {
|
|
for (int i = first; i < diroff; i += MAX_FILE_LEN) {
|
|
if (test_mnemo(dirs + i)) {
|
|
err = 1;
|
|
if (stop_on_error)
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (int i = first; i < diroff; i += MAX_FILE_LEN) {
|
|
char *name = dirs + i;
|
|
if (!strnicmp(name, opcode, strlen(opcode) - 1)) {
|
|
if (test_mnemo(name)) {
|
|
if (stop_on_error)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
free(dirs);
|
|
|
|
if (err && stop_on_error)
|
|
break;
|
|
|
|
} else {
|
|
if (test_mnemo(opcode)) {
|
|
if (stop_on_error)
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(low_memory_temp);
|
|
free(high_memory_temp);
|
|
free(low_memory_back);
|
|
free(high_memory_back);
|
|
|
|
if (!groupd)
|
|
break;
|
|
}
|
|
|
|
if (groupd)
|
|
x_closedir(groupd);
|
|
|
|
return 0;
|
|
}
|