File 0001-Add-VirtualXT-CPU-Validator.patch of Package 86box
From a9d609de60ec171fbfc23bdfe4085afe44b27a59 Mon Sep 17 00:00:00 2001
From: Arachnos
Date: Wed, 3 Aug 2022 00:00:00 +0000
Subject: [PATCH] Add VirtualXT CPU Validator
The validator saves the state of the registers (including IP and flags)
before and after the execution of the opcode pointed by IP.
At the same time, writes and reads are saved, with a maximum amount of
10 for each.
Usage:
On both VirtualXT and 86Box:
--validator flag to specify the output path
--vstart to specify the start address of the validator (e.g. --vstart
0x00007C01)
--vend to specify the end address of the validator
Flags are still the same for the comparison tool (i.e. -virtualxt and
-validation).
From https://github.com/andreas-jonsson/virtualxt
"tools/validator/patched-86Box-3.1.zip" (commit 0f38700)
See:
https://github.com/andreas-jonsson/virtualxt/issues/2
https://github.com/andreas-jonsson/virtualxt/pull/14
[Rebased by Arachnos]
---
src/86box.c | 55 +++++---
src/CMakeLists.txt | 7 +-
src/cpu/808x.c | 15 ++-
src/include/86box/event.h | 22 ++++
src/include/86box/validator.h | 53 ++++++++
src/io.c | 3 +
src/machine/machine_table.c | 4 +-
src/mem/mem.c | 3 +
src/unix/unix.c | 3 +
src/validator/CMakeLists.txt | 1 +
src/validator/validator.c | 236 ++++++++++++++++++++++++++++++++++
src/win/win.c | 4 +
vcpkg.json | 3 +-
13 files changed, 389 insertions(+), 20 deletions(-)
create mode 100644 src/include/86box/event.h
create mode 100644 src/include/86box/validator.h
create mode 100644 src/validator/CMakeLists.txt
create mode 100644 src/validator/validator.c
diff --git a/src/86box.c b/src/86box.c
index deded3382..583105cef 100644
--- a/src/86box.c
+++ b/src/86box.c
@@ -97,6 +97,7 @@
#include <86box/version.h>
#include <86box/gdbstub.h>
#include <86box/machine_status.h>
+#include <86box/validator.h>
// Disable c99-designator to avoid the warnings about int ng
#ifdef __clang__
@@ -139,6 +140,9 @@ rom_path_t rom_paths = { "", NULL }; /* (O) full paths to ROMs */
char log_path[1024] = { '\0'}; /* (O) full path of logfile */
char vm_name[1024] = { '\0'}; /* (O) display name of the VM */
+char vdt_output[1024] = { '\0' };
+uint32_t vdt_start_addr, vdt_end_addr = 0;
+
/* Configuration values. */
int window_remember;
int vid_resize; /* (C) allow resizing */
@@ -462,27 +466,30 @@ pc_init(int argc, char *argv[])
usage:
printf("\nUsage: 86box [options] [cfg-file]\n\n");
printf("Valid options are:\n\n");
- printf("-? or --help - show this information\n");
- printf("-C or --config path - set 'path' to be config file\n");
+ printf("-? or --help - show this information\n");
+ printf("-C or --config path - set 'path' to be config file\n");
#ifdef _WIN32
- printf("-D or --debug - force debug output logging\n");
+ printf("-D or --debug - force debug output logging\n");
#endif
#if 0
- printf("-E or --nographic - forces the old behavior\n");
+ printf("-E or --nographic - forces the old behavior\n");
#endif
- printf("-F or --fullscreen - start in fullscreen mode\n");
- printf("-G or --lang langid - start with specified language (e.g. en-US, or system)\n");
+ printf("-F or --fullscreen - start in fullscreen mode\n");
+ printf("-G or --lang langid - start with specified language (e.g. en-US, or system)\n");
#ifdef _WIN32
- printf("-H or --hwnd id,hwnd - sends back the main dialog's hwnd\n");
+ printf("-H or --hwnd id,hwnd - sends back the main dialog's hwnd\n");
#endif
- printf("-L or --logfile path - set 'path' to be the logfile\n");
- printf("-N or --noconfirm - do not ask for confirmation on quit\n");
- printf("-O or --dumpcfg - dump config file after loading\n");
- printf("-P or --vmpath path - set 'path' to be root for vm\n");
- printf("-R or --rompath path - set 'path' to be ROM path\n");
- printf("-S or --settings - show only the settings dialog\n");
- printf("-V or --vmname name - overrides the name of the running VM\n");
- printf("-Z or --lastvmpath - the last parameter is VM path rather than config\n");
+ printf("-L or --logfile path - set 'path' to be the logfile\n");
+ printf("-N or --noconfirm - do not ask for confirmation on quit\n");
+ printf("-O or --dumpcfg - dump config file after loading\n");
+ printf("-P or --vmpath path - set 'path' to be root for vm\n");
+ printf("-R or --rompath path - set 'path' to be ROM path\n");
+ printf("-S or --settings - show only the settings dialog\n");
+ printf("-V or --vmname name - overrides the name of the running VM\n");
+ printf("-W or --validator path - set 'path' to be the validator output\n");
+ printf("-X or --vstart addr - set 'addr' to be the start address of the validator\n");
+ printf("-Y or --vend addr - set 'addr' to be the end address of the validator\n");
+ printf("-Z or --lastvmpath - the last parameter is VM path rather than config\n");
printf("\nA config file can be specified. If none is, the default file will be used.\n");
return(0);
} else if (!strcasecmp(argv[c], "--lastvmpath") ||
@@ -567,6 +574,21 @@ usage:
/* .. and then exit. */
return(0);
+ } else if (!strcasecmp(argv[c], "--validator") ||
+ !strcasecmp(argv[c], "-W")) {
+ if ((c+1) == argc) goto usage;
+
+ strcpy(vdt_output, argv[++c]);
+ } else if (!strcasecmp(argv[c], "--vstart") ||
+ !strcasecmp(argv[c], "-X")) {
+ if ((c+1) == argc) goto usage;
+
+ sscanf(argv[++c], "0x%08X", &vdt_start_addr);
+ } else if (!strcasecmp(argv[c], "--vend") ||
+ !strcasecmp(argv[c], "-Y")) {
+ if ((c+1) == argc) goto usage;
+
+ sscanf(argv[++c], "0x%08X", &vdt_end_addr);
}
/* Uhm... out of options here.. */
@@ -738,6 +760,9 @@ usage:
cdrom_global_init();
zip_global_init();
mo_global_init();
+#ifdef ENABLE_VALIDATOR
+ validator_init(vdt_output, vdt_start_addr, vdt_end_addr);
+#endif
/* Load the configuration file. */
config_load();
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1420aaa89..eed8edd8e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -57,8 +57,12 @@ if(VNC)
endif()
endif()
+if(VALIDATOR)
+ add_compile_definitions(ENABLE_VALIDATOR)
+endif()
+
target_link_libraries(86Box cpu chipset mch dev mem fdd game cdrom zip mo hdd
- net print scsi sio snd vid voodoo plat ui)
+ net print scsi sio snd vid voodoo plat ui validator)
if(WIN32 AND ARCH STREQUAL "i386")
if(MINGW)
@@ -121,6 +125,7 @@ endif()
add_subdirectory(cdrom)
add_subdirectory(chipset)
+add_subdirectory(validator)
add_subdirectory(cpu)
if(NEW_DYNAREC)
diff --git a/src/cpu/808x.c b/src/cpu/808x.c
index 96b184bc4..f5ef6a267 100644
--- a/src/cpu/808x.c
+++ b/src/cpu/808x.c
@@ -35,6 +35,7 @@
#include <86box/ppi.h>
#include <86box/timer.h>
#include <86box/gdbstub.h>
+#include <86box/validator.h>
/* Is the CPU 8088 or 8086. */
int is8086 = 0;
@@ -949,6 +950,8 @@ access(int num, int bits)
static void
interrupt(uint16_t addr)
{
+ validator_discard();
+
uint16_t old_cs, old_ip;
uint16_t new_cs, new_ip;
uint16_t tempf;
@@ -1703,7 +1706,12 @@ execx86(int cycs)
}
completed = 1;
- // pclog("[%04X:%04X] Opcode: %02X\n", CS, cpu_state.pc, opcode);
+
+ #ifdef ENABLE_VALIDATOR
+ uint16_t regs[12] = { AX, CX, DX, BX, SP, BP, SI, DI, ES, CS, SS, DS };
+ validator_begin(opcode, cpu_reg, cpu_state.pc, cpu_state.flags, regs);
+ #endif
+
switch (opcode) {
case 0x06: case 0x0E: case 0x16: case 0x1E: /* PUSH seg */
access(29, 16);
@@ -2819,6 +2827,11 @@ execx86(int cycs)
break;
}
+#ifdef ENABLE_VALIDATOR
+ uint16_t regs_after[12] = { AX, CX, DX, BX, SP, BP, SI, DI, ES, CS, SS, DS };
+ validator_end(cpu_state.pc, cpu_state.flags, regs_after);
+#endif
+
if (completed) {
repeating = 0;
ovr_seg = NULL;
diff --git a/src/include/86box/event.h b/src/include/86box/event.h
new file mode 100644
index 000000000..12de3ddee
--- /dev/null
+++ b/src/include/86box/event.h
@@ -0,0 +1,22 @@
+#include <stdbool.h>
+
+#define EVENT_SIZE 158
+
+#define EMPTY_MEM_OP { UINT32_MAX, 0 }
+#define EMPTY_MEM_OPS { EMPTY_MEM_OP, EMPTY_MEM_OP, EMPTY_MEM_OP, EMPTY_MEM_OP, EMPTY_MEM_OP, EMPTY_MEM_OP, EMPTY_MEM_OP, EMPTY_MEM_OP, EMPTY_MEM_OP, EMPTY_MEM_OP };
+
+typedef struct {
+ uint16_t ip, flags;
+ uint16_t regs[12];
+} regs_info_t;
+
+typedef struct {
+ uint32_t addr;
+ uint8_t data;
+} mem_op_t;
+
+typedef struct {
+ uint8_t opcode, opcode_ext;
+ regs_info_t before, after;
+ mem_op_t reads[10], writes[10];
+} _event_t;
\ No newline at end of file
diff --git a/src/include/86box/validator.h b/src/include/86box/validator.h
new file mode 100644
index 000000000..d931f7049
--- /dev/null
+++ b/src/include/86box/validator.h
@@ -0,0 +1,53 @@
+#include <86box/event.h>
+
+#define DEFAULT_QUEUE_SIZE 1024
+#define DEFAULT_BUFFER_SIZE 1024 * 1024 * 1024
+
+enum State {
+ Waiting,
+ Running,
+ Finished
+};
+
+enum Register {
+ _AX,
+ _CX,
+ _DX,
+ _BX,
+ _SP,
+ _BP,
+ _SI,
+ _DI,
+ _ES,
+ _CS,
+ _SS,
+ _DS
+};
+
+typedef struct {
+ char* output;
+ enum State state;
+ bool in_scope;
+ uint32_t start_addr, end_addr;
+} validator_t;
+
+typedef struct {
+ uint8_t data[DEFAULT_BUFFER_SIZE];
+ uint32_t idx;
+} buffer_t;
+
+void validator_init(const char* output, uint32_t start_addr, uint32_t end_addr);
+void validator_begin(uint8_t opcode, uint8_t opcode_ext, uint16_t ip, uint16_t flags, uint16_t regs[12]);
+void validator_end(uint16_t ip, uint16_t flags, uint16_t regs[12]);
+void validator_shutdown(void);
+
+void encode_event(_event_t event);
+void append_to_buffer(uint8_t byte);
+void push_event(void);
+
+void validator_write(void);
+void validator_discard(void);
+
+uint32_t get_addr(uint16_t _cs, uint16_t ip);
+void validator_read_byte(uint32_t addr, uint8_t data);
+void validator_write_byte(uint32_t addr, uint8_t data);
\ No newline at end of file
diff --git a/src/io.c b/src/io.c
index 268305e1a..343c78db7 100644
--- a/src/io.c
+++ b/src/io.c
@@ -29,6 +29,7 @@
#include <86box/timer.h>
#include "cpu.h"
#include <86box/m_amstrad.h>
+#include <86box/validator.h>
#define NPORTS 65536 /* PC/AT supports 64K ports */
@@ -335,6 +336,8 @@ inb(uint16_t port)
void
outb(uint16_t port, uint8_t val)
{
+ validator_discard();
+
io_t *p, *q;
int found = 0;
int qfound = 0;
diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c
index 07034c805..21ce3c126 100644
--- a/src/machine/machine_table.c
+++ b/src/machine/machine_table.c
@@ -259,8 +259,8 @@ const machine_t machines[] = {
.bus_flags = MACHINE_PC,
.flags = MACHINE_FLAGS_NONE,
.ram = {
- .min = 64,
- .max = 256,
+ .min = 256,
+ .max = 640,
.step = 64
},
.nvrmask = 0,
diff --git a/src/mem/mem.c b/src/mem/mem.c
index bc4e34691..fb51e97eb 100644
--- a/src/mem/mem.c
+++ b/src/mem/mem.c
@@ -37,6 +37,7 @@
#include <86box/plat.h>
#include <86box/rom.h>
#include <86box/gdbstub.h>
+#include <86box/validator.h>
#ifdef USE_DYNAREC
# include "codegen_public.h"
#else
@@ -701,6 +702,7 @@ read_mem_b(uint32_t addr)
ret = map->read_b(addr, map->p);
resub_cycles(old_cycles);
+ validator_read_byte(addr, ret);
return ret;
}
@@ -747,6 +749,7 @@ write_mem_b(uint32_t addr, uint8_t val)
map->write_b(addr, val, map->p);
resub_cycles(old_cycles);
+ validator_write_byte(addr, val);
}
diff --git a/src/unix/unix.c b/src/unix/unix.c
index 598eb9acc..e1a8db1d8 100644
--- a/src/unix/unix.c
+++ b/src/unix/unix.c
@@ -1314,6 +1314,9 @@ int main(int argc, char** argv)
SDL_DestroyMutex(blitmtx);
SDL_DestroyMutex(mousemutex);
SDL_Quit();
+#ifdef ENABLE_VALIDATOR
+ validator_shutdown();
+#endif
if (f_rl_callback_handler_remove) f_rl_callback_handler_remove();
return 0;
}
diff --git a/src/validator/CMakeLists.txt b/src/validator/CMakeLists.txt
new file mode 100644
index 000000000..53b44d96c
--- /dev/null
+++ b/src/validator/CMakeLists.txt
@@ -0,0 +1 @@
+add_library(validator OBJECT validator.c)
\ No newline at end of file
diff --git a/src/validator/validator.c b/src/validator/validator.c
new file mode 100644
index 000000000..f7ca5bf70
--- /dev/null
+++ b/src/validator/validator.c
@@ -0,0 +1,236 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include <86box/86box.h>
+#include <86box/validator.h>
+
+#include <zlib.h>
+
+validator_t validator;
+_event_t curr_event;
+
+gzFile *fi;
+
+buffer_t encoded_events = {.data = {0}, .idx = 0};
+
+void
+validator_init(const char* output, uint32_t start_addr, uint32_t end_addr)
+{
+ if (output[0] == '\0')
+ {
+ return;
+ }
+ else if (start_addr == 0)
+ {
+ validator.state = Running;
+ }
+
+ validator.output = output;
+ validator.start_addr = start_addr;
+ validator.end_addr = end_addr;
+
+ fi = (gzFile *)gzopen(validator.output, "wb");
+ if (fi == NULL)
+ pclog("Failed to open validator output!");
+ return;
+
+ return;
+}
+
+void
+validator_begin(uint8_t opcode, uint8_t opcode_ext, uint16_t ip, uint16_t flags, uint16_t regs[12])
+{
+ if (validator.output == NULL || validator.state == Finished)
+ {
+ return;
+ }
+ else if (validator.state == Waiting)
+ {
+ if (get_addr(regs[_CS], ip) == validator.start_addr)
+ {
+ pclog("Validator started at 0x%04X.\n", validator.start_addr);
+ validator.state = Running;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ validator.in_scope = true;
+
+ mem_op_t tmp[10] = EMPTY_MEM_OPS;
+ memcpy(curr_event.reads, tmp, sizeof(mem_op_t) * 10);
+ memcpy(curr_event.writes, tmp, sizeof(mem_op_t) * 10);
+
+ curr_event.opcode = opcode;
+ curr_event.opcode_ext = opcode_ext;
+
+ curr_event.before.ip = ip;
+ curr_event.before.flags = flags;
+
+ memcpy(curr_event.before.regs, regs, sizeof(uint16_t) * 12);
+ return;
+}
+
+void
+validator_end(uint16_t ip, uint16_t flags, uint16_t regs[12])
+{
+ if (!validator.in_scope || validator.state != Running)
+ {
+ return;
+ }
+
+ curr_event.after.ip = ip;
+ curr_event.after.flags = flags;
+ memcpy(curr_event.after.regs, regs, sizeof(uint16_t) * 12);
+
+ validator.in_scope = false;
+ push_event();
+
+ if (get_addr(regs[_CS], ip) == validator.end_addr)
+ {
+ pclog("Validator stopped at 0x%04X.\n", validator.end_addr);
+ validator_shutdown();
+ }
+}
+
+void
+validator_shutdown(void)
+{
+ if (validator.state != Running)
+ {
+ return;
+ }
+ validator.state = Finished;
+
+ if (encoded_events.idx != 0)
+ {
+ validator_write();
+ }
+ gzclose(fi);
+}
+
+void
+encode_event(_event_t event)
+{
+ append_to_buffer(event.opcode);
+ append_to_buffer(event.opcode_ext);
+
+ regs_info_t infos[2] = {event.before, event.after};
+ for (int i = 0; i < 2; i++)
+ {
+ append_to_buffer(infos[i].ip >> 0x8);
+ append_to_buffer(infos[i].ip & 0xff);
+ append_to_buffer(infos[i].flags >> 0x8);
+ append_to_buffer(infos[i].flags & 0xff);
+
+ for (int j = 0; j < 12; j++)
+ {
+ append_to_buffer(infos[i].regs[j] >> 0x8);
+ append_to_buffer(infos[i].regs[j] & 0xff);
+ }
+ }
+
+ mem_op_t* events[2] = {event.reads, event.writes};
+ for (int i = 0; i < 2; i++)
+ {
+ mem_op_t* ops = events[i];
+
+ for (int j = 0; j < 10; j++)
+ {
+ append_to_buffer(ops[j].addr >> 0x18);
+ append_to_buffer(ops[j].addr >> 0x10 & 0xff);
+ append_to_buffer(ops[j].addr >> 0x8 & 0xff);
+ append_to_buffer(ops[j].addr & 0xff);
+ append_to_buffer(ops[j].data);
+ }
+ }
+}
+
+void
+append_to_buffer(uint8_t byte)
+{
+ encoded_events.data[encoded_events.idx] = byte;
+ encoded_events.idx++;
+}
+
+void
+push_event(void)
+{
+ encode_event(curr_event);
+
+ if (encoded_events.idx >= DEFAULT_BUFFER_SIZE)
+ {
+ pclog("Flushing events...");
+ validator_write();
+ }
+}
+
+void
+validator_write(void)
+{
+ gzwrite(fi, encoded_events.data, encoded_events.idx);
+
+ memset(encoded_events.data, 0, sizeof(encoded_events));
+ encoded_events.idx = 0;
+ return;
+}
+
+void
+validator_discard(void)
+{
+ validator.in_scope = false;
+}
+
+uint32_t
+get_addr(uint16_t cs, uint16_t ip)
+{
+ return cs << 16 | ip;
+}
+
+void
+validator_read_byte(uint32_t addr, uint8_t data)
+{
+#ifdef ENABLE_VALIDATOR
+ if (!validator.in_scope || validator.state != Running)
+ {
+ return;
+ }
+
+ for (int i = 0; i < 10; i++)
+ {
+ if (curr_event.reads[i].addr == UINT32_MAX)
+ {
+ mem_op_t op = {addr, data};
+ curr_event.reads[i] = op;
+ return;
+ }
+ }
+
+ pclog("Max reads reached!");
+#endif
+}
+
+void
+validator_write_byte(uint32_t addr, uint8_t data)
+{
+#ifdef ENABLE_VALIDATOR
+ if (!validator.in_scope || validator.state != Running)
+ {
+ return;
+ }
+
+ for (int i = 0; i < 10; i++)
+ {
+ if (curr_event.writes[i].addr == UINT32_MAX)
+ {
+ mem_op_t op = {addr, data};
+ curr_event.writes[i] = op;
+ return;
+ }
+ }
+
+ pclog("Max writes reached!");
+#endif
+}
\ No newline at end of file
diff --git a/src/win/win.c b/src/win/win.c
index 5515d8e78..16f617cf8 100644
--- a/src/win/win.c
+++ b/src/win/win.c
@@ -502,6 +502,10 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpszArg, int nCmdShow)
/* Uninitialize COM before exit. */
CoUninitialize();
+ #ifdef ENABLE_VALIDATOR
+ validator_shutdown();
+ #endif
+
free(argbuf);
free(argv);
return (i);
diff --git a/vcpkg.json b/vcpkg.json
index 2f70e4456..1f4c388a3 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -8,7 +8,8 @@
"freetype",
"libpng",
"sdl2",
- "rtmidi"
+ "rtmidi",
+ "zlib"
],
"features": {
"qt-ui": {
--
2.37.1