From 204b24022f291ce7b4a5f3af9fee8d644f4f4c00 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 15:21:31 +0000 Subject: [PATCH 01/22] Implement the rivatimer and cyclical logging from my main NV3 branch so that we can get it reviewed and merged. --- src/86box.c | 152 ++++++++++++- src/include/86box/86box.h | 5 +- src/include/86box/nv/vid_nv_rivatimer.h | 84 ++++++++ src/video/nv/nv_rivatimer.c | 274 ++++++++++++++++++++++++ 4 files changed, 502 insertions(+), 13 deletions(-) create mode 100644 src/include/86box/nv/vid_nv_rivatimer.h create mode 100644 src/video/nv/nv_rivatimer.c diff --git a/src/86box.c b/src/86box.c index 8fa74fd4b..8450c8b6b 100644 --- a/src/86box.c +++ b/src/86box.c @@ -103,6 +103,7 @@ #include <86box/machine_status.h> #include <86box/apm.h> #include <86box/acpi.h> +#include <86box/nv/vid_nv_rivatimer.h> // Disable c99-designator to avoid the warnings about int ng #ifdef __clang__ @@ -252,12 +253,37 @@ static volatile atomic_int do_pause_ack = 0; static volatile atomic_int pause_ack = 0; #ifndef RELEASE_BUILD -static char buff[1024]; -static int seen = 0; + +#define LOG_SIZE_BUFFER 1024 /* Log size buffer */ +#define LOG_SIZE_BUFFER_CYCLIC_LINES 32 /* Cyclic log size buffer (number of lines that should be cehcked) */ +#define LOG_MINIMUM_REPEAT_ORDER 4 /* Minimum repeat size */ + +static char buff[LOG_SIZE_BUFFER]; +static char cyclic_buff[LOG_SIZE_BUFFER_CYCLIC_LINES][LOG_SIZE_BUFFER]; +static int32_t cyclic_last_line = 0; +static int32_t log_cycles = 0; + +static int seen = 0; static int suppr_seen = 1; #endif +/* + Ensures STDLOG is open for pclog_ex and pclog_ex_cyclic +*/ +void +pclog_ensure_stdlog_open() +{ + if (stdlog == NULL) { + if (log_path[0] != '\0') { + stdlog = plat_fopen(log_path, "w"); + if (stdlog == NULL) + stdlog = stdout; + } else + stdlog = stdout; + } +} + /* * Log something to the logfile or stdout. * @@ -269,19 +295,12 @@ void pclog_ex(const char *fmt, va_list ap) { #ifndef RELEASE_BUILD - char temp[1024]; + char temp[LOG_SIZE_BUFFER]; if (strcmp(fmt, "") == 0) return; - if (stdlog == NULL) { - if (log_path[0] != '\0') { - stdlog = plat_fopen(log_path, "w"); - if (stdlog == NULL) - stdlog = stdout; - } else - stdlog = stdout; - } + pclog_ensure_stdlog_open(); vsprintf(temp, fmt, ap); if (suppr_seen && !strcmp(buff, temp)) @@ -298,6 +317,114 @@ pclog_ex(const char *fmt, va_list ap) #endif } + +/* +Starfrost, 7-8 January 2025: + +For RIVA 128 emulation I needed a way to suppress logging if a repeated pattern of the same set of lines were found. + +Implements a version of the Rabin-Karp algorithm https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm +*/ +void +pclog_ex_cyclic(const char* fmt, va_list ap) +{ +#ifndef RELEASE_BUILD + char temp[LOG_SIZE_BUFFER]; + + cyclic_last_line %= LOG_SIZE_BUFFER_CYCLIC_LINES; + + vsprintf(temp, fmt, ap); + + pclog_ensure_stdlog_open(); + + strncpy(cyclic_buff[cyclic_last_line], temp, LOG_SIZE_BUFFER); + + uint32_t hashes[LOG_SIZE_BUFFER_CYCLIC_LINES] = {0}; + + // Random numbers + uint32_t base = 257; + uint32_t mod = 1000000007; + + uint32_t repeat_order = 0; + bool is_cycle = false; + + // compute the set of hashes for the current log buffer + for (int32_t log_line = 0; log_line < LOG_SIZE_BUFFER_CYCLIC_LINES; log_line++) + { + if (cyclic_buff[log_line][0] == '\0') + continue; // skip + + for (int32_t log_line_char = 0; log_line_char < LOG_SIZE_BUFFER; log_line_char++) + { + hashes[log_line] = hashes[log_line] * base + cyclic_buff[log_line][log_line_char] % mod; + } + } + + + // Now see if there are real cycles... + // We implement a minimum repeat size. + for (int32_t check_size = LOG_MINIMUM_REPEAT_ORDER; check_size < LOG_SIZE_BUFFER_CYCLIC_LINES / 2; check_size++) + { + //TODO: Log what we need for cycle 1. + //TODO: Command line option that lets us turn off this behaviour. + for (int32_t log_line_to_check = 0; log_line_to_check < check_size; log_line_to_check++) + { + if (hashes[log_line_to_check] == hashes[(log_line_to_check + check_size) % LOG_SIZE_BUFFER_CYCLIC_LINES]) + { + repeat_order = check_size; + break; + } + } + + is_cycle = (repeat_order != 0); + + // if there still is a cycle.. + if (is_cycle) + break; + + } + + if (is_cycle) + { + if (cyclic_last_line % repeat_order == 0) + { + log_cycles++; + + if (log_cycles == 1) + { + // 'Replay' the last few log entries so they actually show up + // Todo: is this right? + + for (uint32_t index = cyclic_last_line - 1; index > (cyclic_last_line - repeat_order); index--) + { + // *very important* to prevent out of bounds index + uint32_t real_index = index % LOG_SIZE_BUFFER_CYCLIC_LINES; + fprintf(stdlog, "%s", temp); + + } + + fprintf(stdlog, "%s", temp); // allow normal logging + } + + + if (log_cycles > 1 && log_cycles < 100) + fprintf(stdlog, "***** Cyclical Log Repeat of Order %d #%d *****\n", repeat_order, log_cycles); + else if (log_cycles == 100) + fprintf(stdlog, "Logged the same cycle 100 times...shutting up until something interesting happens\n"); + } + } + else + { + log_cycles = 0; + fprintf(stdlog, "%s", temp); + } + + cyclic_last_line++; + +#endif + +} + void pclog_toggle_suppr(void) { @@ -1427,6 +1554,9 @@ pc_run(void) pc_reset_hard_init(); } + /* Update the guest-CPU independent timer for devices with independent clock speed */ + rivatimer_update_all(); + /* Run a block of code. */ startblit(); cpu_exec((int32_t) cpu_s->rspeed / 100); diff --git a/src/include/86box/86box.h b/src/include/86box/86box.h index 503518a2b..05c2a901e 100644 --- a/src/include/86box/86box.h +++ b/src/include/86box/86box.h @@ -187,8 +187,9 @@ extern int config_changed; /* config has changed */ /* Function prototypes. */ #ifdef HAVE_STDARG_H -extern void pclog_ex(const char *fmt, va_list); -extern void fatal_ex(const char *fmt, va_list); +extern void pclog_ex(const char *fmt, va_list ap); +extern void fatal_ex(const char *fmt, va_list ap); +extern void pclog_ex_cyclic(const char* fmt, va_list ap); #endif extern void pclog_toggle_suppr(void); #ifdef _MSC_VER diff --git a/src/include/86box/nv/vid_nv_rivatimer.h b/src/include/86box/nv/vid_nv_rivatimer.h new file mode 100644 index 000000000..d5bb86b90 --- /dev/null +++ b/src/include/86box/nv/vid_nv_rivatimer.h @@ -0,0 +1,84 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Fast, high-frequency, guest CPU-independent timer for Riva emulation. + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ + +/* +RivaTimer + +This is a fast, high-frequency, guest CPU-independent timer. + +The main 86box timer is dependent on the TSC (time-stamp counter) register of the emulated CPU core. +This is fine for most purposes and has advantages in the fields of synchronisation and integrates neatly with +the clock dividers of the PC architecture, but in the case of the RIVA 128 it does not particularly suffice +(although it can be made to work with various techniques) since the clock source on the RIVA 128 is on the board itself +and the GPU has several different clocks that control different parts of the GPU (e.g., PTIMER runs on the memory clock but the core gpu is using the pixel clock). + +As faster graphics cards that offload more and more of the 3D graphics pipeline are emulated in the future, more and more work needs to be done by the emulator and +issues of synchronisation with a host CPU will simply make that work harder. Some features that are required for + +Architecture Brand Name 3D Features +NV1 (1995) NV1 Some weird URBS rectangle crap but feature set generally similar to nv3 but a bit worse +NV3 (1997) RIVA 128 (ZX) Triangle setup, edge-slope calculations, edge interpolation, span-slope calculations, span interpolation (Color-buffer, z-buffer, texture mapping, filtering) +NV4 (1998) RIVA TNT NV3 + 2x1 pixel pipelines + 32-bit colour + larger textures + trilinear + more ram (16mb) +NV5 (1999) RIVA TNT2 NV4 + higher clock speed +NV10 (1999) GeForce 256 NV5 + initial geometry transformation + lighting (8x lights) + MPEG-2 motion compensation + 4x1 pixel pipelines +NV15 (2000) GeForce 2 NV10 + First attempt at programmability + 4x2 pixel pipelines +NV20 (2001) GeForce 3 Programmable shaders! + +As you can see, the performance basically exponentially increases over a period of only 4 years. + +So I decided to create this timer that is completely separate from the CPU Core. +*/ + +#pragma once +#include +#include +#include +#include +#include <86Box\86box.h> + +#ifdef _WIN32 +#include +// Linux & MacOS should have the same API since OSX 10.12 +#else +#include +#endif + +typedef struct rivatimer_s +{ + struct rivatimer_s* prev; // Previous Rivatimer + double period; // Period in uS before firing + double value; // The current value of the rivatimer + bool running; // Is this RivaTimer running? + struct rivatimer_s* next; // Next RivaTimer + void (*callback)(); // Callback to call on fire + #ifdef _WIN32 + LARGE_INTEGER starting_time; // Starting time. + #else + struct timespec starting_time; // Starting time. + #endif + double time; // Accumulated time in uS. +} rivatimer_t; + +void rivatimer_init(); // Initialise the Rivatimer. +rivatimer_t* rivatimer_create(double period, void (*callback)(double real_time)); +void rivatimer_destroy(rivatimer_t* rivatimer_ptr); + +void rivatimer_update_all(); +void rivatimer_start(rivatimer_t* rivatimer_ptr); +void rivatimer_stop(rivatimer_t* rivatimer_ptr); +double rivatimer_get_time(rivatimer_t* rivatimer_ptr); +void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)(double real_time)); +void rivatimer_set_period(rivatimer_t* rivatimer_ptr, double period); diff --git a/src/video/nv/nv_rivatimer.c b/src/video/nv/nv_rivatimer.c new file mode 100644 index 000000000..343ec02ff --- /dev/null +++ b/src/video/nv/nv_rivatimer.c @@ -0,0 +1,274 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Fast, high-frequency, CPU-independent timer. + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ + +/* See vid_nv_rivatimer.h comments for rationale behind not using the regular timer system + +Notes applicable to this file: +Since Windows XP, QueryPerformanceCounter and QueryPerformanceFrequency cannot fail so they are not checked. + +*/ + +#include <86Box/nv/vid_nv_rivatimer.h> + +#ifdef _WIN32 +LARGE_INTEGER performance_frequency; +#endif + +rivatimer_t* rivatimer_head; // The head of the rivatimer list. +rivatimer_t* rivatimer_tail; // The tail of the rivatimer list. + +/* Functions only used in this translation unit */ +bool rivatimer_really_exists(rivatimer_t* rivatimer); // Determine if a rivatimer really exists in the linked list. + +void rivatimer_init() +{ + // Destroy all the rivatimers. + rivatimer_t* rivatimer_ptr = rivatimer_head; + + if (!rivatimer_ptr) + return; + + while (rivatimer_ptr) + { + // since we are destroing it + rivatimer_t* old_next = rivatimer_ptr->next; + rivatimer_destroy(rivatimer_ptr); + + rivatimer_ptr = old_next; + } + + + #ifdef _WIN32 + // Query the performance frequency. + QueryPerformanceFrequency(&performance_frequency); + #endif +} + +// Creates a rivatimer. +rivatimer_t* rivatimer_create(double period, void (*callback)(double real_time)) +{ + rivatimer_t* new_rivatimer = NULL; + + // See i + if (period <= 0 + || !callback) + { + fatal("Invalid rivatimer_create call: period <= 0 or no callback"); + } + + // If there are no rivatimers, create one + if (!rivatimer_head) + { + rivatimer_head = calloc(1, sizeof(rivatimer_t)); + rivatimer_head->prev = NULL; // indicate this is the first in the list even if we don't strictly need to + rivatimer_tail = rivatimer_head; + new_rivatimer = rivatimer_head; + } + else // Otherwise add a new one to the list + { + rivatimer_tail->next = calloc(1, sizeof(rivatimer_t)); + rivatimer_tail = rivatimer_tail->next; + new_rivatimer = rivatimer_tail; + } + + // sanity check + if (new_rivatimer) + { + new_rivatimer->running = false; + new_rivatimer->period = period; + new_rivatimer->next = NULL; // indicate this is the last in the list + new_rivatimer->callback = callback; + } + + return new_rivatimer; +} + +// Determines if a rivatimer really exists. +bool rivatimer_really_exists(rivatimer_t* rivatimer) +{ + rivatimer_t* current = rivatimer_head; + + if (!current) + return false; + + while (current) + { + if (current == rivatimer) + return true; + + current = current->next; + } + + return false; +} + +// Destroy a rivatimer. +void rivatimer_destroy(rivatimer_t* rivatimer_ptr) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_destroy: The timer was already destroyed, or never existed in the first place. Punch starfrost in the face"); + + // Case: We are destroying the head + if (rivatimer_ptr == rivatimer_head) + { + // This is the only rivatimer + if (rivatimer_ptr->next == NULL) + { + rivatimer_head = NULL; + rivatimer_tail = NULL; + } + // This is not the only rivatimer + else + { + rivatimer_head = rivatimer_ptr->next; + rivatimer_head->prev = NULL; + // This is the only rivatimer and now there is only one + if (!rivatimer_head->next) + rivatimer_tail = rivatimer_head; + } + } + // Case: We are destroying the tail + else if (rivatimer_ptr == rivatimer_tail) + { + // We already covered the case where there is only one item above + rivatimer_tail = rivatimer_ptr->prev; + rivatimer_tail->next = NULL; + } + // Case: This is not the first or last rivatimer, so we don't need to set the head or tail + else + { + // Fix the break in the chain that this + if (rivatimer_ptr->next) + rivatimer_ptr->prev->next = rivatimer_ptr->next; + if (rivatimer_ptr->prev) + rivatimer_ptr->next->prev = rivatimer_ptr->prev; + } + + free(rivatimer_ptr); + rivatimer_ptr = NULL; //explicitly set to null +} + +void rivatimer_update_all() +{ + rivatimer_t* rivatimer_ptr = rivatimer_head; + + if (!rivatimer_ptr) + return; + + while (rivatimer_ptr) + { + // if it's not running skip it + if (!rivatimer_ptr->running) + { + rivatimer_ptr = rivatimer_ptr->next; + continue; + } + + #ifdef _WIN32 + LARGE_INTEGER current_time; + + QueryPerformanceCounter(¤t_time); + + double microseconds = ((double)current_time.QuadPart / 1000000.0) - (rivatimer_ptr->starting_time.QuadPart / 1000000.0); + #else + struct timespec current_time; + + clock_gettime(CLOCK_REALTIME, ¤t_time); + + double microseconds = ((double)current_time.tv_sec * 1000000.0) + ((double)current_time.tv_nsec / 1000.0); + #endif + + rivatimer_ptr->time += microseconds; + + // Reset the current time so we can actually restart + #ifdef _WIN32 + QueryPerformanceCounter(&rivatimer_ptr->starting_time); + #else + clock_gettime(CLOCK_REALTIME, &rivatimer_ptr->starting_time); + #endif + + // Time to fire + if (microseconds > rivatimer_ptr->period) + { + if (!rivatimer_ptr->callback) + { + pclog("Eh? No callback in RivaTimer?"); + continue; + } + + rivatimer_ptr->callback(microseconds); + } + + rivatimer_ptr = rivatimer_ptr->next; + } + +} + +void rivatimer_start(rivatimer_t* rivatimer_ptr) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_start: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + + if (rivatimer_ptr->period <= 0) + fatal("rivatimer_start: Zero period!"); + + rivatimer_ptr->running = true; + + // Start off so rivatimer_update_all can actually update. + #ifdef _WIN32 + QueryPerformanceCounter(&rivatimer_ptr->starting_time); + #else + clock_gettime(CLOCK_REALTIME, &rivatimer_ptr->starting_time); + #endif +} + +void rivatimer_stop(rivatimer_t* rivatimer_ptr) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_stop: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + + rivatimer_ptr->running = false; + rivatimer_ptr->time = 0; +} + +// Get the current time value of a rivatimer +double rivatimer_get_time(rivatimer_t* rivatimer_ptr) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_get_time: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + + return rivatimer_ptr->time; +} + +void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)(double real_time)) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_set_callback: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + + if (!callback) + fatal("rivatimer_set_callback: No callback!"); + + rivatimer_ptr->callback = callback; +} + +void rivatimer_set_period(rivatimer_t* rivatimer_ptr, double period) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_set_period: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + + rivatimer_ptr->period = period; +} \ No newline at end of file From 6eaec5b756fab78c94fcb861155acb7ed2c576fe Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 15:31:54 +0000 Subject: [PATCH 02/22] forgot to port over some parts --- src/timer.c | 4 ++++ src/video/CMakeLists.txt | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/timer.c b/src/timer.c index 6ddf8ebb5..2b92a1958 100644 --- a/src/timer.c +++ b/src/timer.c @@ -4,6 +4,7 @@ #include #include <86box/86box.h> #include <86box/timer.h> +#include <86Box/nv/vid_nv_rivatimer.h> uint64_t TIMER_USEC; uint32_t timer_target; @@ -168,6 +169,9 @@ timer_init(void) timer_target = 0ULL; tsc = 0; + /* Initialise the CPU-independent timer */ + rivatimer_init(); + timer_inited = 1; } diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index bbd329b7e..3d8e14c5e 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -27,7 +27,9 @@ add_library(vid OBJECT agpgart.c video.c vid_table.c vid_cga.c vid_cga_comp.c vid_tkd8001_ramdac.c vid_att20c49x_ramdac.c vid_s3.c vid_s3_virge.c vid_ibm_rgb528_ramdac.c vid_sdac_ramdac.c vid_ogc.c vid_mga.c vid_nga.c vid_tvp3026_ramdac.c vid_att2xc498_ramdac.c vid_xga.c - vid_bochs_vbe.c) + vid_bochs_vbe.c + nv/nv_rivatimer.c + ) if(G100) target_compile_definitions(vid PRIVATE USE_G100) From 55f476617d67cc476f48c42cfe04554019afe82b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 19:07:27 +0000 Subject: [PATCH 03/22] Don't punch me in the face, and also fix the compilation, and also fix a stupid bug in log replay. --- src/86box.c | 5 ++--- src/include/86box/nv/vid_nv_rivatimer.h | 6 +++--- src/video/nv/nv_rivatimer.c | 12 ++++++------ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/86box.c b/src/86box.c index 8450c8b6b..b5a4de15e 100644 --- a/src/86box.c +++ b/src/86box.c @@ -271,8 +271,7 @@ static int suppr_seen = 1; /* Ensures STDLOG is open for pclog_ex and pclog_ex_cyclic */ -void -pclog_ensure_stdlog_open() +void pclog_ensure_stdlog_open() { if (stdlog == NULL) { if (log_path[0] != '\0') { @@ -399,7 +398,7 @@ pclog_ex_cyclic(const char* fmt, va_list ap) { // *very important* to prevent out of bounds index uint32_t real_index = index % LOG_SIZE_BUFFER_CYCLIC_LINES; - fprintf(stdlog, "%s", temp); + fprintf(stdlog, "%s", cyclic_buff[real_index]); } diff --git a/src/include/86box/nv/vid_nv_rivatimer.h b/src/include/86box/nv/vid_nv_rivatimer.h index d5bb86b90..627bbcdcf 100644 --- a/src/include/86box/nv/vid_nv_rivatimer.h +++ b/src/include/86box/nv/vid_nv_rivatimer.h @@ -63,7 +63,7 @@ typedef struct rivatimer_s double value; // The current value of the rivatimer bool running; // Is this RivaTimer running? struct rivatimer_s* next; // Next RivaTimer - void (*callback)(); // Callback to call on fire + void (*callback)(void); // Callback to call on fire #ifdef _WIN32 LARGE_INTEGER starting_time; // Starting time. #else @@ -72,11 +72,11 @@ typedef struct rivatimer_s double time; // Accumulated time in uS. } rivatimer_t; -void rivatimer_init(); // Initialise the Rivatimer. +void rivatimer_init(void); // Initialise the Rivatimer. rivatimer_t* rivatimer_create(double period, void (*callback)(double real_time)); void rivatimer_destroy(rivatimer_t* rivatimer_ptr); -void rivatimer_update_all(); +void rivatimer_update_all(void); void rivatimer_start(rivatimer_t* rivatimer_ptr); void rivatimer_stop(rivatimer_t* rivatimer_ptr); double rivatimer_get_time(rivatimer_t* rivatimer_ptr); diff --git a/src/video/nv/nv_rivatimer.c b/src/video/nv/nv_rivatimer.c index 343ec02ff..ee8c13916 100644 --- a/src/video/nv/nv_rivatimer.c +++ b/src/video/nv/nv_rivatimer.c @@ -120,7 +120,7 @@ bool rivatimer_really_exists(rivatimer_t* rivatimer) void rivatimer_destroy(rivatimer_t* rivatimer_ptr) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_destroy: The timer was already destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_destroy: The timer was already destroyed, or never existed in the first place."); // Case: We are destroying the head if (rivatimer_ptr == rivatimer_head) @@ -221,7 +221,7 @@ void rivatimer_update_all() void rivatimer_start(rivatimer_t* rivatimer_ptr) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_start: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_start: The timer has been destroyed, or never existed in the first place."); if (rivatimer_ptr->period <= 0) fatal("rivatimer_start: Zero period!"); @@ -239,7 +239,7 @@ void rivatimer_start(rivatimer_t* rivatimer_ptr) void rivatimer_stop(rivatimer_t* rivatimer_ptr) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_stop: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_stop: The timer has been destroyed, or never existed in the first place."); rivatimer_ptr->running = false; rivatimer_ptr->time = 0; @@ -249,7 +249,7 @@ void rivatimer_stop(rivatimer_t* rivatimer_ptr) double rivatimer_get_time(rivatimer_t* rivatimer_ptr) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_get_time: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_get_time: The timer has been destroyed, or never existed in the first place."); return rivatimer_ptr->time; } @@ -257,7 +257,7 @@ double rivatimer_get_time(rivatimer_t* rivatimer_ptr) void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)(double real_time)) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_set_callback: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_set_callback: The timer has been destroyed, or never existed in the first place."); if (!callback) fatal("rivatimer_set_callback: No callback!"); @@ -268,7 +268,7 @@ void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)(double void rivatimer_set_period(rivatimer_t* rivatimer_ptr, double period) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_set_period: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_set_period: The timer has been destroyed, or never existed in the first place."); rivatimer_ptr->period = period; } \ No newline at end of file From cbfeed7ea46acbc6d2481798b1dc920b8bcbc50d Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 19:09:35 +0000 Subject: [PATCH 04/22] Fix incorrect include --- src/include/86box/nv/vid_nv_rivatimer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/86box/nv/vid_nv_rivatimer.h b/src/include/86box/nv/vid_nv_rivatimer.h index 627bbcdcf..8ccc0f0df 100644 --- a/src/include/86box/nv/vid_nv_rivatimer.h +++ b/src/include/86box/nv/vid_nv_rivatimer.h @@ -47,7 +47,7 @@ So I decided to create this timer that is completely separate from the CPU Core. #include #include #include -#include <86Box\86box.h> +#include <86Box/86box.h> #ifdef _WIN32 #include From 8c48478706d8b7c53fc6afe0ddc5498fcab8f1f1 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 19:11:20 +0000 Subject: [PATCH 05/22] fix the screwed up callbacks --- src/include/86box/nv/vid_nv_rivatimer.h | 2 +- src/video/nv/nv_rivatimer.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/include/86box/nv/vid_nv_rivatimer.h b/src/include/86box/nv/vid_nv_rivatimer.h index 8ccc0f0df..a4fe85e92 100644 --- a/src/include/86box/nv/vid_nv_rivatimer.h +++ b/src/include/86box/nv/vid_nv_rivatimer.h @@ -63,7 +63,7 @@ typedef struct rivatimer_s double value; // The current value of the rivatimer bool running; // Is this RivaTimer running? struct rivatimer_s* next; // Next RivaTimer - void (*callback)(void); // Callback to call on fire + void (*callback)(double real_time); // Callback to call on fire #ifdef _WIN32 LARGE_INTEGER starting_time; // Starting time. #else diff --git a/src/video/nv/nv_rivatimer.c b/src/video/nv/nv_rivatimer.c index ee8c13916..44e5901d1 100644 --- a/src/video/nv/nv_rivatimer.c +++ b/src/video/nv/nv_rivatimer.c @@ -34,7 +34,7 @@ rivatimer_t* rivatimer_tail; // The tail of the rivatimer list. /* Functions only used in this translation unit */ bool rivatimer_really_exists(rivatimer_t* rivatimer); // Determine if a rivatimer really exists in the linked list. -void rivatimer_init() +void rivatimer_init(void) { // Destroy all the rivatimers. rivatimer_t* rivatimer_ptr = rivatimer_head; @@ -162,7 +162,7 @@ void rivatimer_destroy(rivatimer_t* rivatimer_ptr) rivatimer_ptr = NULL; //explicitly set to null } -void rivatimer_update_all() +void rivatimer_update_all(void) { rivatimer_t* rivatimer_ptr = rivatimer_head; From 8dc456cce5c2eb87e744ec1d82858a98f14ffec3 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Sun, 12 Jan 2025 20:37:50 +0100 Subject: [PATCH 06/22] NCR 5380-based changes of the day (January 12, 2025) 1. Sanity check for the SCSI temp_buffer if it's allocated or not. 2. Data reads and writes in non-DMA mode should be accessible only when DMA mode is Idle (as in, no DMA at all, whereas DMA mode will go to the SCSI controllers' callbacks). --- src/scsi/scsi_ncr5380.c | 47 ++++++++++++++++++++++++++------------- src/scsi/scsi_ncr53c400.c | 38 +++++++++++++++++++++++-------- src/scsi/scsi_t128.c | 2 ++ 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/scsi/scsi_ncr5380.c b/src/scsi/scsi_ncr5380.c index 527ff373f..092cc08e9 100644 --- a/src/scsi/scsi_ncr5380.c +++ b/src/scsi/scsi_ncr5380.c @@ -197,20 +197,29 @@ ncr5380_bus_read(ncr_t *ncr) phase = (ncr->cur_bus & SCSI_PHASE_MESSAGE_IN); if (phase == SCSI_PHASE_DATA_IN) { - ncr->tx_data = dev->sc->temp_buffer[ncr->data_pos++]; - ncr->state = STATE_DATAIN; - ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(ncr->tx_data) | BUS_DBP; + if (ncr->dma_mode == DMA_IDLE) { + ncr5380_log("Phase Data In.\n"); + if ((dev->sc != NULL) && (dev->sc->temp_buffer != NULL)) + ncr->tx_data = dev->sc->temp_buffer[ncr->data_pos++]; + + ncr->state = STATE_DATAIN; + ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(ncr->tx_data) | BUS_DBP; + } } else if (phase == SCSI_PHASE_DATA_OUT) { if (ncr->new_phase & BUS_IDLE) { ncr->state = STATE_IDLE; ncr->cur_bus &= ~BUS_BSY; - } else - ncr->state = STATE_DATAOUT; + } else { + if (ncr->dma_mode == DMA_IDLE) + ncr->state = STATE_DATAOUT; + } } else if (phase == SCSI_PHASE_STATUS) { + ncr5380_log("Phase Status.\n"); ncr->cur_bus |= BUS_REQ; ncr->state = STATE_STATUS; ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(dev->status) | BUS_DBP; } else if (phase == SCSI_PHASE_MESSAGE_IN) { + ncr5380_log("Phase Message In.\n"); ncr->state = STATE_MESSAGEIN; ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(0) | BUS_DBP; } else if (phase == SCSI_PHASE_MESSAGE_OUT) { @@ -335,19 +344,22 @@ ncr5380_bus_update(ncr_t *ncr, int bus) if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) { if (ncr->data_pos >= dev->buffer_length) { ncr->cur_bus &= ~BUS_REQ; + ncr5380_log("CMD Phase1 DataIn.\n"); scsi_device_command_phase1(dev); ncr->new_phase = SCSI_PHASE_STATUS; ncr->wait_data = 4; ncr->wait_complete = 8; } else { - ncr->tx_data = dev->sc->temp_buffer[ncr->data_pos++]; + if ((dev->sc != NULL) && (dev->sc->temp_buffer != NULL)) + ncr->tx_data = dev->sc->temp_buffer[ncr->data_pos++]; + ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(ncr->tx_data) | BUS_DBP | BUS_REQ; if (ncr->dma_mode == DMA_IDLE) { /*If a data in command that is not read 6/10 has been issued*/ ncr->data_wait |= 1; - ncr5380_log("DMA mode idle in\n"); + ncr5380_log("DMA mode idle IN=%d.\n", ncr->data_pos); ncr->timer(ncr->priv, ncr->period); } else { - ncr5380_log("DMA mode IN.\n"); + ncr5380_log("DMA mode IN=%d.\n", ncr->data_pos); ncr->clear_req = 3; } @@ -359,7 +371,8 @@ ncr5380_bus_update(ncr_t *ncr, int bus) case STATE_DATAOUT: dev = &scsi_devices[ncr->bus][ncr->target_id]; if ((bus & BUS_ACK) && !(ncr->bus_in & BUS_ACK)) { - dev->sc->temp_buffer[ncr->data_pos++] = BUS_GETDATA(bus); + if ((dev->sc != NULL) && (dev->sc->temp_buffer != NULL)) + dev->sc->temp_buffer[ncr->data_pos++] = BUS_GETDATA(bus); if (ncr->data_pos >= dev->buffer_length) { ncr->cur_bus &= ~BUS_REQ; @@ -439,12 +452,12 @@ ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr) switch (port & 7) { case 0: /* Output data register */ - ncr5380_log("Write: Output data register, val = %02x\n", val); + ncr5380_log("[%04X:%08X]: Write: Output data register, val=%02x\n", CS, cpu_state.pc, val); ncr->output_data = val; break; case 1: /* Initiator Command Register */ - ncr5380_log("Write: Initiator command register\n"); + ncr5380_log("[%04X:%08X]: Write: Initiator command register, val=%02x.\n", CS, cpu_state.pc, val); if ((val & 0x80) && !(ncr->icr & 0x80)) { ncr5380_log("Resetting the 5380\n"); ncr5380_reset(ncr); @@ -465,7 +478,7 @@ ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr) break; case 3: /* Target Command Register */ - ncr5380_log("Write: Target Command register\n"); + ncr5380_log("Write: Target Command register, val=%02x.\n", val); ncr->tcr = val; break; @@ -482,7 +495,7 @@ ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr) break; case 7: /* start DMA Initiator Receive */ - ncr5380_log("Write: start DMA initiator receive register, dma? = %02x\n", ncr->mode & MODE_DMA); + ncr5380_log("[%04X:%08X]: Write: start DMA initiator receive register, dma? = %02x\n", CS, cpu_state.pc, ncr->mode & MODE_DMA); /*a Read 6/10 has occurred, start the timer when the block count is loaded*/ ncr->dma_mode = DMA_INITIATOR_RECEIVE; if (ncr->dma_initiator_receive_ext) @@ -510,13 +523,13 @@ ncr5380_read(uint16_t port, ncr_t *ncr) ncr5380_log("Read: Current SCSI data register\n"); if (ncr->icr & ICR_DBP) { /*Return the data from the output register if on data bus phase from ICR*/ - ncr5380_log("Data Bus Phase, ret = %02x\n", ncr->output_data); ret = ncr->output_data; + ncr5380_log("[%04X:%08X]: Data Bus Phase, ret=%02x, clearreq=%d, waitdata=%x.\n", CS, cpu_state.pc, ret, ncr->clear_req, ncr->wait_data); } else { /*Return the data from the SCSI bus*/ ncr5380_bus_read(ncr); - ncr5380_log("NCR GetData=%02x\n", BUS_GETDATA(ncr->cur_bus)); ret = BUS_GETDATA(ncr->cur_bus); + ncr5380_log("[%04X:%08X]: NCR Get SCSI bus data=%02x.\n", CS, cpu_state.pc, ret); } break; @@ -595,7 +608,9 @@ ncr5380_read(uint16_t port, ncr_t *ncr) break; case 6: - ret = ncr->tx_data; + ncr5380_log("Read: Input Data.\n"); + ncr5380_bus_read(ncr); + ret = BUS_GETDATA(ncr->cur_bus); break; case 7: /* reset Parity/Interrupt */ diff --git a/src/scsi/scsi_ncr53c400.c b/src/scsi/scsi_ncr53c400.c index 6622ec59a..997e8f152 100644 --- a/src/scsi/scsi_ncr53c400.c +++ b/src/scsi/scsi_ncr53c400.c @@ -148,6 +148,12 @@ ncr53c400_write(uint32_t addr, uint8_t val, void *priv) ncr400->busy = 1; if (!(ncr->mode & MODE_MONITOR_BUSY) && ((scsi_device_get_callback(dev) > 0.0))) timer_on_auto(&ncr400->timer, ncr->period / 250.0); + else if ((ncr->mode & MODE_MONITOR_BUSY) && !ncr->wait_data) { + if (scsi_device_get_callback(dev) > 0.0) + timer_on_auto(&ncr400->timer, 100.0); + else + timer_on_auto(&ncr400->timer, 40.0); + } } } break; @@ -181,13 +187,14 @@ ncr53c400_write(uint32_t addr, uint8_t val, void *priv) if ((ncr->mode & MODE_DMA) && (dev->buffer_length > 0)) { memset(ncr400->buffer, 0, MIN(128, dev->buffer_length)); if (ncr->mode & MODE_MONITOR_BUSY) - timer_on_auto(&ncr400->timer, ncr->period); + timer_on_auto(&ncr400->timer, (ncr->period / 4.0) * 3.0); else if (scsi_device_get_callback(dev) > 0.0) timer_on_auto(&ncr400->timer, 40.0); else timer_on_auto(&ncr400->timer, ncr->period); - ncr53c400_log("DMA timer on=%02x, callback=%lf, scsi buflen=%d, waitdata=%d, waitcomplete=%d, clearreq=%d, datawait=%d, enabled=%d.\n", ncr->mode & MODE_MONITOR_BUSY, scsi_device_get_callback(dev), dev->buffer_length, ncr->wait_complete, ncr->wait_data, ncr->wait_complete, ncr->clear_req, ncr->data_wait, timer_is_enabled(&ncr400->timer)); + ncr53c400_log("DMA timer on=%02x, callback=%lf, scsi buflen=%d, waitdata=%d, waitcomplete=%d, clearreq=%d, datawait=%d, enabled=%d.\n", + ncr->mode & MODE_MONITOR_BUSY, scsi_device_get_callback(dev), dev->buffer_length, ncr->wait_data, ncr->wait_complete, ncr->clear_req, ncr->data_wait, timer_is_enabled(&ncr400->timer)); } break; @@ -244,6 +251,12 @@ ncr53c400_read(uint32_t addr, void *priv) ncr53c400_log("Transfer busy read, status = %02x.\n", ncr400->status_ctrl); if (!(ncr->mode & MODE_MONITOR_BUSY) && (scsi_device_get_callback(dev) > 0.0)) timer_on_auto(&ncr400->timer, ncr->period / 250.0); + else if ((ncr->mode & MODE_MONITOR_BUSY) && !ncr->wait_data) { + if (scsi_device_get_callback(dev) > 0.0) + timer_on_auto(&ncr400->timer, 100.0); + else + timer_on_auto(&ncr400->timer, 40.0); + } } } break; @@ -397,16 +410,20 @@ t130b_in(uint16_t port, void *priv) } static void -ncr53c400_dma_mode_ext(void *priv, UNUSED(void *ext_priv)) +ncr53c400_dma_mode_ext(void *priv, void *ext_priv) { - ncr_t *ncr = (ncr_t *) priv; + ncr53c400_t *ncr400 = (ncr53c400_t *) ext_priv; + ncr_t *ncr = (ncr_t *) priv; /*When a pseudo-DMA transfer has completed (Send or Initiator Receive), mark it as complete and idle the status*/ - if (!(ncr->mode & MODE_DMA)) { - ncr53c400_log("No DMA mode\n"); - ncr->tcr &= ~TCR_LAST_BYTE_SENT; - ncr->isr &= ~STATUS_END_OF_DMA; - ncr->dma_mode = DMA_IDLE; + ncr53c400_log("BlockCountLoaded=%d.\n", ncr400->block_count_loaded); + if (!ncr400->block_count_loaded) { + if (!(ncr->mode & MODE_DMA)) { + ncr53c400_log("No DMA mode\n"); + ncr->tcr &= ~TCR_LAST_BYTE_SENT; + ncr->isr &= ~STATUS_END_OF_DMA; + ncr->dma_mode = DMA_IDLE; + } } } @@ -474,6 +491,7 @@ ncr53c400_callback(void *priv) ncr400->block_count = (ncr400->block_count - 1) & 0xff; ncr53c400_log("NCR 53c400 Remaining blocks to be written=%d\n", ncr400->block_count); if (!ncr400->block_count) { + ncr->dma_mode = DMA_IDLE; ncr400->block_count_loaded = 0; ncr53c400_log("IO End of write transfer\n"); ncr->tcr |= TCR_LAST_BYTE_SENT; @@ -527,6 +545,7 @@ ncr53c400_callback(void *priv) ncr400->block_count = (ncr400->block_count - 1) & 0xff; ncr53c400_log("NCR 53c400 Remaining blocks to be read=%d\n", ncr400->block_count); if (!ncr400->block_count) { + ncr->dma_mode = DMA_IDLE; ncr400->block_count_loaded = 0; ncr53c400_log("IO End of read transfer\n"); ncr->isr |= STATUS_END_OF_DMA; @@ -544,6 +563,7 @@ ncr53c400_callback(void *priv) break; } + ncr53c400_log("Bus Read.\n"); ncr5380_bus_read(ncr); if (!(ncr->cur_bus & BUS_BSY) && (ncr->mode & MODE_MONITOR_BUSY)) { diff --git a/src/scsi/scsi_t128.c b/src/scsi/scsi_t128.c index c878bbb91..faad87052 100644 --- a/src/scsi/scsi_t128.c +++ b/src/scsi/scsi_t128.c @@ -287,6 +287,7 @@ write_again: t128->block_count = (t128->block_count - 1) & 0xff; t128_log("T128 Remaining blocks to be written=%d\n", t128->block_count); if (!t128->block_count) { + ncr->dma_mode = DMA_IDLE; t128->block_loaded = 0; t128_log("IO End of write transfer\n"); ncr->tcr |= TCR_LAST_BYTE_SENT; @@ -343,6 +344,7 @@ read_again: t128_log("T128 Remaining blocks to be read=%d, status=%02x, len=%i, cdb[0] = %02x\n", t128->block_count, t128->status, dev->buffer_length, ncr->command[0]); if (!t128->block_count) { t128->block_loaded = 0; + ncr->dma_mode = DMA_IDLE; t128_log("IO End of read transfer\n"); ncr->isr |= STATUS_END_OF_DMA; timer_stop(&t128->timer); From 4f15889638cb38a51e790c49391535040e13d10a Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 23:09:03 +0000 Subject: [PATCH 07/22] fix compile by fixing capitalisation --- src/include/86box/nv/vid_nv_rivatimer.h | 2 +- src/timer.c | 2 +- src/video/nv/nv_rivatimer.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/include/86box/nv/vid_nv_rivatimer.h b/src/include/86box/nv/vid_nv_rivatimer.h index a4fe85e92..59f6cfebf 100644 --- a/src/include/86box/nv/vid_nv_rivatimer.h +++ b/src/include/86box/nv/vid_nv_rivatimer.h @@ -47,7 +47,7 @@ So I decided to create this timer that is completely separate from the CPU Core. #include #include #include -#include <86Box/86box.h> +#include <86box/86box.h> #ifdef _WIN32 #include diff --git a/src/timer.c b/src/timer.c index 2b92a1958..a3a7c9efb 100644 --- a/src/timer.c +++ b/src/timer.c @@ -4,7 +4,7 @@ #include #include <86box/86box.h> #include <86box/timer.h> -#include <86Box/nv/vid_nv_rivatimer.h> +#include <86box/nv/vid_nv_rivatimer.h> uint64_t TIMER_USEC; uint32_t timer_target; diff --git a/src/video/nv/nv_rivatimer.c b/src/video/nv/nv_rivatimer.c index 44e5901d1..12d40c026 100644 --- a/src/video/nv/nv_rivatimer.c +++ b/src/video/nv/nv_rivatimer.c @@ -22,7 +22,7 @@ Since Windows XP, QueryPerformanceCounter and QueryPerformanceFrequency cannot f */ -#include <86Box/nv/vid_nv_rivatimer.h> +#include <86box/nv/vid_nv_rivatimer.h> #ifdef _WIN32 LARGE_INTEGER performance_frequency; From 31207c98d656e2a68fd9c4b1d7d5c4774bdaa6a2 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 23:15:43 +0000 Subject: [PATCH 08/22] hopefully the final issue...fix missing prototype --- src/86box.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/86box.c b/src/86box.c index b5a4de15e..219535c84 100644 --- a/src/86box.c +++ b/src/86box.c @@ -266,6 +266,9 @@ static int32_t log_cycles = 0; static int seen = 0; static int suppr_seen = 1; + +// Functions only used in this translation unit +void pclog_ensure_stdlog_open(); #endif /* @@ -273,6 +276,7 @@ static int suppr_seen = 1; */ void pclog_ensure_stdlog_open() { +#ifndef RELEASE_BUILD if (stdlog == NULL) { if (log_path[0] != '\0') { stdlog = plat_fopen(log_path, "w"); @@ -281,6 +285,7 @@ void pclog_ensure_stdlog_open() } else stdlog = stdout; } +#endif } /* From fa3fb7ecccb0c73cb0c33f74865eb65621f911d8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 23:22:22 +0000 Subject: [PATCH 09/22] explicit void... --- src/86box.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/86box.c b/src/86box.c index 219535c84..85cbb9957 100644 --- a/src/86box.c +++ b/src/86box.c @@ -268,13 +268,13 @@ static int seen = 0; static int suppr_seen = 1; // Functions only used in this translation unit -void pclog_ensure_stdlog_open(); +void pclog_ensure_stdlog_open(void); #endif /* Ensures STDLOG is open for pclog_ex and pclog_ex_cyclic */ -void pclog_ensure_stdlog_open() +void pclog_ensure_stdlog_open(void) { #ifndef RELEASE_BUILD if (stdlog == NULL) { From be878ede582870a70532890b85fbd43e6f242e8d Mon Sep 17 00:00:00 2001 From: TC1995 Date: Mon, 13 Jan 2025 00:56:04 +0100 Subject: [PATCH 10/22] Okay, maybe for the 53c400 only so. Based on my tests, the former 5380 commit will apply to the 53c400 only, for now... --- src/scsi/scsi_ncr5380.c | 4 ++-- src/scsi/scsi_t128.c | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/scsi/scsi_ncr5380.c b/src/scsi/scsi_ncr5380.c index 092cc08e9..0c3af9282 100644 --- a/src/scsi/scsi_ncr5380.c +++ b/src/scsi/scsi_ncr5380.c @@ -197,7 +197,7 @@ ncr5380_bus_read(ncr_t *ncr) phase = (ncr->cur_bus & SCSI_PHASE_MESSAGE_IN); if (phase == SCSI_PHASE_DATA_IN) { - if (ncr->dma_mode == DMA_IDLE) { + if ((ncr->dma_mode == DMA_IDLE) || ncr->dma_initiator_receive_ext) { ncr5380_log("Phase Data In.\n"); if ((dev->sc != NULL) && (dev->sc->temp_buffer != NULL)) ncr->tx_data = dev->sc->temp_buffer[ncr->data_pos++]; @@ -210,7 +210,7 @@ ncr5380_bus_read(ncr_t *ncr) ncr->state = STATE_IDLE; ncr->cur_bus &= ~BUS_BSY; } else { - if (ncr->dma_mode == DMA_IDLE) + if ((ncr->dma_mode == DMA_IDLE) || ncr->dma_send_ext) ncr->state = STATE_DATAOUT; } } else if (phase == SCSI_PHASE_STATUS) { diff --git a/src/scsi/scsi_t128.c b/src/scsi/scsi_t128.c index faad87052..c878bbb91 100644 --- a/src/scsi/scsi_t128.c +++ b/src/scsi/scsi_t128.c @@ -287,7 +287,6 @@ write_again: t128->block_count = (t128->block_count - 1) & 0xff; t128_log("T128 Remaining blocks to be written=%d\n", t128->block_count); if (!t128->block_count) { - ncr->dma_mode = DMA_IDLE; t128->block_loaded = 0; t128_log("IO End of write transfer\n"); ncr->tcr |= TCR_LAST_BYTE_SENT; @@ -344,7 +343,6 @@ read_again: t128_log("T128 Remaining blocks to be read=%d, status=%02x, len=%i, cdb[0] = %02x\n", t128->block_count, t128->status, dev->buffer_length, ncr->command[0]); if (!t128->block_count) { t128->block_loaded = 0; - ncr->dma_mode = DMA_IDLE; t128_log("IO End of read transfer\n"); ncr->isr |= STATUS_END_OF_DMA; timer_stop(&t128->timer); From f039fce49749779cf70d4f83545fd6d5da761f72 Mon Sep 17 00:00:00 2001 From: OBattler Date: Mon, 13 Jan 2025 04:16:08 +0100 Subject: [PATCH 11/22] PIC: Signal to the CPU to end the block on IRR read, fixes stalls in waiting for IRQ, fixes #5108. --- src/pic.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pic.c b/src/pic.c index 82905261a..f8e83c40b 100644 --- a/src/pic.c +++ b/src/pic.c @@ -17,6 +17,7 @@ * Copyright 2015-2020 Andrew Jenner. * Copyright 2016-2020 Miran Grca. */ +#include #include #include #include @@ -491,12 +492,13 @@ pic_read(uint16_t addr, void *priv) else dev->data_bus = 0x00; #endif - } + } else + cpu_block_end = 1; /* If A0 = 0, VIA shadow is disabled, and poll mode is disabled, simply read whatever is currently on the data bus. */ } - pic_log("pic_read(%04X, %08X) = %02X\n", addr, priv, dev->data_bus); + pic_log("pic_read(%04X) = %02X\n", addr, dev->data_bus); return dev->data_bus; } From 123ff3b5e767864e8f1381ed18aeb3fa17966bbd Mon Sep 17 00:00:00 2001 From: OBattler Date: Mon, 13 Jan 2025 04:35:23 +0100 Subject: [PATCH 12/22] Fix NEC Vx0 INS*/OUS* timings calculation. --- src/cpu/808x.c | 53 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/cpu/808x.c b/src/cpu/808x.c index d815551ae..0c1fb0f4d 100644 --- a/src/cpu/808x.c +++ b/src/cpu/808x.c @@ -1634,28 +1634,55 @@ cpu_data_opff_rm(void) } } +uint8_t +cpu_inb(uint16_t port) +{ + uint8_t ret; + + wait(4, 0); + old_cycles = cycles; + + ret = inb(port); + + resub_cycles(); +} + uint16_t cpu_inw(uint16_t port) { - if (is8086 && !(port & 1)) { - wait(4, 0); - } else { - wait(8, 0); - } + uint16_t ret; - return inw(port); + if (is8086 && !(port & 1)) + wait(4, 0); + else + wait(8, 0); + + ret = inw(port); + + resub_cycles(); +} + +void +cpu_outb(uint16_t port, uint16_t val) +{ + wait(4, 0); + + outb(port, val); + + resub_cycles(); } void cpu_outw(uint16_t port, uint16_t val) { - if (is8086 && !(port & 1)) { + if (is8086 && !(port & 1)) wait(4, 0); - } else { + else wait(8, 0); - } - return outw(port, val); + outw(port, val); + + resub_cycles(); } /* Executes instructions up to the specified number of cycles. */ @@ -1804,8 +1831,7 @@ execx86(int cycs) writememw(es, DI, cpu_inw(DX)); DI += (cpu_state.flags & D_FLAG) ? -2 : 2; } else { - wait(4, 0); - writememb(es, DI, inb(DX)); + writememb(es, DI, cpu_inb(DX)); DI += (cpu_state.flags & D_FLAG) ? -1 : 1; } @@ -1833,8 +1859,7 @@ execx86(int cycs) cpu_outw(DX, readmemw(dest_seg, SI)); SI += (cpu_state.flags & D_FLAG) ? -2 : 2; } else { - wait(4, 0); - outb(DX, readmemb(dest_seg + SI)); + cpu_outb(DX, readmemb(dest_seg + SI)); SI += (cpu_state.flags & D_FLAG) ? -1 : 1; } if (in_rep == 0) From b1f94abc27131792c4b33be5588a736ac1f3c916 Mon Sep 17 00:00:00 2001 From: OBattler Date: Mon, 13 Jan 2025 05:01:00 +0100 Subject: [PATCH 13/22] Fixed the fix. --- src/cpu/808x.c | 52 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/cpu/808x.c b/src/cpu/808x.c index 0c1fb0f4d..f37672a5c 100644 --- a/src/cpu/808x.c +++ b/src/cpu/808x.c @@ -1637,52 +1637,72 @@ cpu_data_opff_rm(void) uint8_t cpu_inb(uint16_t port) { + int old_cycles = cycles; uint8_t ret; - wait(4, 0); + wait(is_mazovia ? 5 : 4, 1); old_cycles = cycles; ret = inb(port); - resub_cycles(); + resub_cycles(old_cycles); + + return ret; } uint16_t cpu_inw(uint16_t port) { + int old_cycles = cycles; uint16_t ret; - if (is8086 && !(port & 1)) - wait(4, 0); - else - wait(8, 0); + wait(is_mazovia ? 5 : 4, 1); + if (is8086 && !(port & 1)) { + old_cycles = cycles; + ret = inw(port); + } else { + wait(is_mazovia ? 5 : 4, 1); + old_cycles = cycles; + ret = inb(port++); + ret |= (inb(port) << 8); + } - ret = inw(port); + resub_cycles(old_cycles); - resub_cycles(); + return ret; } void cpu_outb(uint16_t port, uint16_t val) { - wait(4, 0); + int old_cycles = cycles; + + wait(is_mazovia ? 5 : 4, 1); + old_cycles = cycles; outb(port, val); - resub_cycles(); + resub_cycles(old_cycles); } void cpu_outw(uint16_t port, uint16_t val) { - if (is8086 && !(port & 1)) - wait(4, 0); - else - wait(8, 0); + int old_cycles = cycles; - outw(port, val); + wait(is_mazovia ? 5 : 4, 1); - resub_cycles(); + if (is8086 && !(port & 1)) { + old_cycles = cycles; + outw(port, val); + } else { + wait(is_mazovia ? 5 : 4, 1); + old_cycles = cycles; + outb(port++, val); + outb(port, val >> 8); + } + + resub_cycles(old_cycles); } /* Executes instructions up to the specified number of cycles. */ From ec175738ee48899770c7275179beb2746dc26312 Mon Sep 17 00:00:00 2001 From: OBattler Date: Mon, 13 Jan 2025 05:38:10 +0100 Subject: [PATCH 14/22] FIX timings of SYSCALL/SYSRET/SYSENTER/SYSEXIT/FXSAVE/FXSTOR. --- src/cpu/x86_ops_amd.h | 12 ++---------- src/cpu/x86_ops_i686.h | 28 ++++++++++------------------ 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/cpu/x86_ops_amd.h b/src/cpu/x86_ops_amd.h index 9e6bcce55..54da4a79d 100644 --- a/src/cpu/x86_ops_amd.h +++ b/src/cpu/x86_ops_amd.h @@ -22,12 +22,8 @@ opSYSCALL(uint32_t fetchdat) ret = syscall_op(fetchdat); - if (ret <= 1) { - CLOCK_CYCLES(20); - PREFETCH_RUN(20, 7, -1, 0, 0, 0, 0, 0); - PREFETCH_FLUSH(); + if (ret <= 1) CPU_BLOCK_END(); - } return ret; } @@ -41,12 +37,8 @@ opSYSRET(uint32_t fetchdat) ret = sysret(fetchdat); - if (ret <= 1) { - CLOCK_CYCLES(20); - PREFETCH_RUN(20, 7, -1, 0, 0, 0, 0, 0); - PREFETCH_FLUSH(); + if (ret <= 1) CPU_BLOCK_END(); - } return ret; } diff --git a/src/cpu/x86_ops_i686.h b/src/cpu/x86_ops_i686.h index 5e5dc3c7c..a67571875 100644 --- a/src/cpu/x86_ops_i686.h +++ b/src/cpu/x86_ops_i686.h @@ -18,12 +18,8 @@ opSYSENTER(uint32_t fetchdat) { int ret = sysenter(fetchdat); - if (ret <= 1) { - CLOCK_CYCLES(20); - PREFETCH_RUN(20, 7, -1, 0, 0, 0, 0, 0); - PREFETCH_FLUSH(); + if (ret <= 1) CPU_BLOCK_END(); - } return ret; } @@ -33,12 +29,8 @@ opSYSEXIT(uint32_t fetchdat) { int ret = sysexit(fetchdat); - if (ret <= 1) { - CLOCK_CYCLES(20); - PREFETCH_RUN(20, 7, -1, 0, 0, 0, 0, 0); - PREFETCH_FLUSH(); + if (ret <= 1) CPU_BLOCK_END(); - } return ret; } @@ -118,7 +110,8 @@ sf_fx_save_stor_common(uint32_t fetchdat, int bits) fpu_state.swd &= ~(FPU_SW_Summary | FPU_SW_Backward); } - CLOCK_CYCLES((cr0 & 1) ? 34 : 44); + // CLOCK_CYCLES((cr0 & 1) ? 34 : 44); + CLOCK_CYCLES(1); } else { /* FXSAVE */ writememw(easeg, cpu_state.eaaddr, i387_get_control_word()); @@ -163,7 +156,7 @@ sf_fx_save_stor_common(uint32_t fetchdat, int bits) writememw(easeg, cpu_state.eaaddr + (index * 16) + 40, fp.signExp); } - CLOCK_CYCLES((cr0 & 1) ? 56 : 67); + CLOCK_CYCLES(1); } return cpu_state.abrt; @@ -327,7 +320,8 @@ fx_save_stor_common(uint32_t fetchdat, int bits) } } - CLOCK_CYCLES((cr0 & 1) ? 34 : 44); + // CLOCK_CYCLES((cr0 & 1) ? 34 : 44); + CLOCK_CYCLES(1); } else { /* FXSAVE */ if ((twd & 0x0003) != 0x0003) @@ -372,7 +366,7 @@ fx_save_stor_common(uint32_t fetchdat, int bits) cpu_state.eaaddr = old_eaaddr; - CLOCK_CYCLES((cr0 & 1) ? 56 : 67); + CLOCK_CYCLES(1); } return cpu_state.abrt; @@ -400,8 +394,7 @@ static int opHINT_NOP_a16(uint32_t fetchdat) { fetch_ea_16(fetchdat); - CLOCK_CYCLES((is486) ? 1 : 3); - PREFETCH_RUN(3, 1, -1, 0, 0, 0, 0, 0); + CLOCK_CYCLES(1); return 0; } @@ -409,7 +402,6 @@ static int opHINT_NOP_a32(uint32_t fetchdat) { fetch_ea_32(fetchdat); - CLOCK_CYCLES((is486) ? 1 : 3); - PREFETCH_RUN(3, 1, -1, 0, 0, 0, 0, 0); + CLOCK_CYCLES(1); return 0; } From ad056b136c1413fdf371114d9524186312cde807 Mon Sep 17 00:00:00 2001 From: eddmanx Date: Mon, 13 Jan 2025 17:09:01 +0400 Subject: [PATCH 15/22] Update README.md - added the Avalonia86 manager --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ce38632e6..c01a852b5 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ It is also recommended to use a manager application with 86Box for easier handli * [86Box Manager](https://github.com/86Box/86BoxManager) by [Overdoze](https://github.com/daviunic) (Windows only) * [86Box Manager X](https://github.com/RetBox/86BoxManagerX) by [xafero](https://github.com/xafero) (Cross platform Port of 86Box Manager using Avalonia) +* [Avalonia 86](https://github.com/notBald/Avalonia86) by [notBald](https://github.com/notBald) (Windows and Linux) * [sl86](https://github.com/DDXofficial/sl86) by [DDX](https://github.com/DDXofficial) (Command-line 86Box machine manager written in Python) * [Linbox-qt5](https://github.com/Dungeonseeker/linbox-qt5) by [Dungeonseeker](https://github.com/Dungeonseeker/) (Linux focused, should work on Windows though untested) * [MacBox for 86Box](https://github.com/Moonif/MacBox) by [Moonif](https://github.com/Moonif) (MacOS only) From d39a8b9867cee2879c05cae606ff7715708223a2 Mon Sep 17 00:00:00 2001 From: OBattler Date: Mon, 13 Jan 2025 17:36:56 +0100 Subject: [PATCH 16/22] Reverted the PIC fix as it was apparently a red herring. --- src/pic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pic.c b/src/pic.c index f8e83c40b..fa7ff3662 100644 --- a/src/pic.c +++ b/src/pic.c @@ -492,8 +492,7 @@ pic_read(uint16_t addr, void *priv) else dev->data_bus = 0x00; #endif - } else - cpu_block_end = 1; + } /* If A0 = 0, VIA shadow is disabled, and poll mode is disabled, simply read whatever is currently on the data bus. */ } From 29ec9fa9ba5bb383dbda07716a9630e822442e55 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Mon, 13 Jan 2025 21:10:12 +0100 Subject: [PATCH 17/22] More NCR53c400 fixes (January 13th, 2025) Getting on my nerves, NCR 5380... --- src/include/86box/scsi_ncr5380.h | 1 + src/scsi/scsi_ncr5380.c | 9 +++++---- src/scsi/scsi_ncr53c400.c | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/include/86box/scsi_ncr5380.h b/src/include/86box/scsi_ncr5380.h index 55692075b..09307fed6 100644 --- a/src/include/86box/scsi_ncr5380.h +++ b/src/include/86box/scsi_ncr5380.h @@ -104,6 +104,7 @@ typedef struct ncr_t { int state; int clear_req; int wait_data; + int wait_data_back; int wait_complete; int command_pos; int data_pos; diff --git a/src/scsi/scsi_ncr5380.c b/src/scsi/scsi_ncr5380.c index 0c3af9282..c5fddc801 100644 --- a/src/scsi/scsi_ncr5380.c +++ b/src/scsi/scsi_ncr5380.c @@ -197,7 +197,7 @@ ncr5380_bus_read(ncr_t *ncr) phase = (ncr->cur_bus & SCSI_PHASE_MESSAGE_IN); if (phase == SCSI_PHASE_DATA_IN) { - if ((ncr->dma_mode == DMA_IDLE) || ncr->dma_initiator_receive_ext) { + if ((ncr->dma_mode == DMA_IDLE) || ncr->dma_initiator_receive_ext || (ncr->wait_data_back == 1)) { ncr5380_log("Phase Data In.\n"); if ((dev->sc != NULL) && (dev->sc->temp_buffer != NULL)) ncr->tx_data = dev->sc->temp_buffer[ncr->data_pos++]; @@ -210,11 +210,12 @@ ncr5380_bus_read(ncr_t *ncr) ncr->state = STATE_IDLE; ncr->cur_bus &= ~BUS_BSY; } else { - if ((ncr->dma_mode == DMA_IDLE) || ncr->dma_send_ext) + if ((ncr->dma_mode == DMA_IDLE) || ncr->dma_send_ext || (ncr->wait_data_back == 1)) ncr->state = STATE_DATAOUT; } } else if (phase == SCSI_PHASE_STATUS) { ncr5380_log("Phase Status.\n"); + ncr->wait_data_back = 0; ncr->cur_bus |= BUS_REQ; ncr->state = STATE_STATUS; ncr->cur_bus = (ncr->cur_bus & ~BUS_DATAMASK) | BUS_SETDATA(dev->status) | BUS_DBP; @@ -359,7 +360,7 @@ ncr5380_bus_update(ncr_t *ncr, int bus) ncr5380_log("DMA mode idle IN=%d.\n", ncr->data_pos); ncr->timer(ncr->priv, ncr->period); } else { - ncr5380_log("DMA mode IN=%d.\n", ncr->data_pos); + pclog("DMA mode IN=%d.\n", ncr->data_pos); ncr->clear_req = 3; } @@ -495,7 +496,7 @@ ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr) break; case 7: /* start DMA Initiator Receive */ - ncr5380_log("[%04X:%08X]: Write: start DMA initiator receive register, dma? = %02x\n", CS, cpu_state.pc, ncr->mode & MODE_DMA); + pclog("[%04X:%08X]: Write: start DMA initiator receive register, dma? = %02x\n", CS, cpu_state.pc, ncr->mode & MODE_DMA); /*a Read 6/10 has occurred, start the timer when the block count is loaded*/ ncr->dma_mode = DMA_INITIATOR_RECEIVE; if (ncr->dma_initiator_receive_ext) diff --git a/src/scsi/scsi_ncr53c400.c b/src/scsi/scsi_ncr53c400.c index 997e8f152..e028c7b42 100644 --- a/src/scsi/scsi_ncr53c400.c +++ b/src/scsi/scsi_ncr53c400.c @@ -193,6 +193,7 @@ ncr53c400_write(uint32_t addr, uint8_t val, void *priv) else timer_on_auto(&ncr400->timer, ncr->period); + ncr->wait_data_back = ncr->wait_data; ncr53c400_log("DMA timer on=%02x, callback=%lf, scsi buflen=%d, waitdata=%d, waitcomplete=%d, clearreq=%d, datawait=%d, enabled=%d.\n", ncr->mode & MODE_MONITOR_BUSY, scsi_device_get_callback(dev), dev->buffer_length, ncr->wait_data, ncr->wait_complete, ncr->clear_req, ncr->data_wait, timer_is_enabled(&ncr400->timer)); } From c9985798d3f785ef09c0ac053d25a554b95d15e1 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Mon, 13 Jan 2025 22:04:29 +0100 Subject: [PATCH 18/22] Remove excess logs. --- src/scsi/scsi_ncr5380.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scsi/scsi_ncr5380.c b/src/scsi/scsi_ncr5380.c index c5fddc801..e5dff88f8 100644 --- a/src/scsi/scsi_ncr5380.c +++ b/src/scsi/scsi_ncr5380.c @@ -360,7 +360,7 @@ ncr5380_bus_update(ncr_t *ncr, int bus) ncr5380_log("DMA mode idle IN=%d.\n", ncr->data_pos); ncr->timer(ncr->priv, ncr->period); } else { - pclog("DMA mode IN=%d.\n", ncr->data_pos); + ncr5380_log("DMA mode IN=%d.\n", ncr->data_pos); ncr->clear_req = 3; } @@ -496,7 +496,7 @@ ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr) break; case 7: /* start DMA Initiator Receive */ - pclog("[%04X:%08X]: Write: start DMA initiator receive register, dma? = %02x\n", CS, cpu_state.pc, ncr->mode & MODE_DMA); + ncr5380_log("[%04X:%08X]: Write: start DMA initiator receive register, dma? = %02x\n", CS, cpu_state.pc, ncr->mode & MODE_DMA); /*a Read 6/10 has occurred, start the timer when the block count is loaded*/ ncr->dma_mode = DMA_INITIATOR_RECEIVE; if (ncr->dma_initiator_receive_ext) From 40c52048a60057fcee0e31baa186ca7980881670 Mon Sep 17 00:00:00 2001 From: unreal9010 <84349460+unreal9010@users.noreply.github.com> Date: Mon, 13 Jan 2025 23:20:57 +0100 Subject: [PATCH 19/22] Added a couple of Quantum Fireball SE/EX drives --- src/disk/hdd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/disk/hdd.c b/src/disk/hdd.c index b861b5e50..5243f016b 100644 --- a/src/disk/hdd.c +++ b/src/disk/hdd.c @@ -448,9 +448,13 @@ static hdd_preset_t hdd_speed_presets[] = { { .name = "[ATA-2] Quantum Fireball 640AT", .internal_name = "FB64A341", .model = "QUANTUM FIREBALL 640AT", .zones = 2, .avg_spt = 120, .heads = 2, .rpm = 5400, .full_stroke_ms = 24, .track_seek_ms = 3.1, .rcache_num_seg = 4, .rcache_seg_size = 128, .max_multiple = 8 }, { .name = "[ATA-2] Quantum Fireball TM1080AT", .internal_name = "TM10A462", .model = "QUANTUM FIREBALL TM1.0A", .zones = 2, .avg_spt = 120, .heads = 2, .rpm = 4500, .full_stroke_ms = 21, .track_seek_ms = 3, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 8 }, { .name = "[ATA-2] Quantum Fireball TM1.2AT", .internal_name = "TM12A012", .model = "QUANTUM FIREBALL TM1.2A", .zones = 4, .avg_spt = 120, .heads = 2, .rpm = 4500, .full_stroke_ms = 21, .track_seek_ms = 3, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 8 }, + { .name = "[ATA-2] Quantum Fireball SE8.4A", .internal_name = "SE84A011", .model = "QUANTUM FIREBALL SE8.4A", .zones = 4, .avg_spt = 100, .heads = 8, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 16 }, + { .name = "[ATA-2] Quantum Fireball SE6.4A", .internal_name = "SE64A011", .model = "QUANTUM FIREBALL SE6.4A", .zones = 3, .avg_spt = 100, .heads = 6, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 16 }, { .name = "[ATA-2] Quantum Fireball ST3.2AT", .internal_name = "ST32A461", .model = "QUANTUM FIREBALL ST3.2A", .zones = 4, .avg_spt = 100, .heads = 4, .rpm = 5400, .full_stroke_ms = 21, .track_seek_ms = 2, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 16 }, - { .name = "[ATA-2] Quantum Fireball CR4.3AT", .internal_name = "CR43A013", .model = "QUANTUM FIREBALL CR4.3A", .zones = 2, .avg_spt = 110, .heads = 2, .rpm = 5400, .full_stroke_ms = 22, .track_seek_ms = 2.5, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 16 }, - { .name = "[ATA-2] Quantum Fireball EX5.1AT", .internal_name = "EX51A012", .model = "QUANTUM FIREBALL EX5.1A", .zones = 8, .avg_spt = 110, .heads = 4, .rpm = 5400, .full_stroke_ms = 22, .track_seek_ms = 2.5, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 16 }, + { .name = "[ATA-2] Quantum Fireball CR4.3AT", .internal_name = "CR43A013", .model = "QUANTUM FIREBALL CR4.3A", .zones = 2, .avg_spt = 110, .heads = 3, .rpm = 5400, .full_stroke_ms = 22, .track_seek_ms = 2.5, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 16 }, + { .name = "[ATA-2] Quantum Fireball EX5.1A", .internal_name = "EX51A012", .model = "QUANTUM FIREBALL EX5.1A", .zones = 8, .avg_spt = 110, .heads = 4, .rpm = 5400, .full_stroke_ms = 22, .track_seek_ms = 2.5, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 16 }, + { .name = "[ATA-2] Quantum Fireball EX10.2A", .internal_name = "EX10A011", .model = "QUANTUM FIREBALL EX10.2A", .zones = 3, .avg_spt = 110, .heads = 6, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 16 }, + { .name = "[ATA-2] Quantum Fireball EX12.7A", .internal_name = "EX12A011", .model = "QUANTUM FIREBALL EX12.7A", .zones = 4, .avg_spt = 110, .heads = 8, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 16 }, { .name = "[ATA-2] Samsung PLS-31274A", .internal_name = "PLS31274A", .model = "SAMSUNG PLS-31274A", .zones = 4, .avg_spt = 110, .heads = 4, .rpm = 4500, .full_stroke_ms = 45, .track_seek_ms = 4.5, .rcache_num_seg = 4, .rcache_seg_size = 256, .max_multiple = 8 }, { .name = "[ATA-2] Samsung Winner-1", .internal_name = "WNR31601A", .model = "SAMSUNG WNR-31601A", .zones = 8, .avg_spt = 110, .heads = 4, .rpm = 5400, .full_stroke_ms = 22, .track_seek_ms = 3, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 16 }, { .name = "[ATA-2] Seagate Medalist (ST3780A)", .internal_name = "ST3780A", .model = "ST3780A", .zones = 8, .avg_spt = 120, .heads = 4, .rpm = 4500, .full_stroke_ms = 25, .track_seek_ms = 3.5, .rcache_num_seg = 4, .rcache_seg_size = 256, .max_multiple = 16 }, From 5b101aad81915c64b677c2e92cd5c60be809bd97 Mon Sep 17 00:00:00 2001 From: OBattler Date: Mon, 13 Jan 2025 23:22:37 +0100 Subject: [PATCH 20/22] AT KBC: Fast track command AE (enable keyboard) because the LG MultiNet sends command A7 immediately after it, fixes keyboard lock-ups in its CMOS Setup. --- src/device/kbc_at.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/device/kbc_at.c b/src/device/kbc_at.c index 5c3c9ac6e..74be59017 100644 --- a/src/device/kbc_at.c +++ b/src/device/kbc_at.c @@ -2127,6 +2127,13 @@ kbc_at_write(uint16_t port, uint8_t val, void *priv) } else if (fast_reset && ((val & 0xf0) == 0xf0)) { pulse_output(dev, val & 0x0f); + dev->state = STATE_MAIN_IBF; + return; + } else if (val == 0xae) { + /* Fast track it because of the LG MultiNet. */ + kbc_at_log("ATkbc: enable keyboard\n"); + set_enable_kbd(dev, 1); + dev->state = STATE_MAIN_IBF; return; } From b1f54b9b84b0a653808fef789be987b34cfcbe03 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 14 Jan 2025 00:31:13 +0000 Subject: [PATCH 21/22] Move cyclical logging to the new logging system per obat feedback. --- src/86box.c | 113 +------------------------- src/include/86box/86box.h | 1 - src/include/86box/log.h | 7 ++ src/log.c | 167 +++++++++++++++++++++++++++++++++++--- 4 files changed, 162 insertions(+), 126 deletions(-) diff --git a/src/86box.c b/src/86box.c index 85cbb9957..97c211bee 100644 --- a/src/86box.c +++ b/src/86box.c @@ -254,14 +254,9 @@ static volatile atomic_int pause_ack = 0; #ifndef RELEASE_BUILD -#define LOG_SIZE_BUFFER 1024 /* Log size buffer */ -#define LOG_SIZE_BUFFER_CYCLIC_LINES 32 /* Cyclic log size buffer (number of lines that should be cehcked) */ -#define LOG_MINIMUM_REPEAT_ORDER 4 /* Minimum repeat size */ +#define LOG_SIZE_BUFFER 1024 /* Log size buffer */ static char buff[LOG_SIZE_BUFFER]; -static char cyclic_buff[LOG_SIZE_BUFFER_CYCLIC_LINES][LOG_SIZE_BUFFER]; -static int32_t cyclic_last_line = 0; -static int32_t log_cycles = 0; static int seen = 0; @@ -322,112 +317,6 @@ pclog_ex(const char *fmt, va_list ap) } -/* -Starfrost, 7-8 January 2025: - -For RIVA 128 emulation I needed a way to suppress logging if a repeated pattern of the same set of lines were found. - -Implements a version of the Rabin-Karp algorithm https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm -*/ -void -pclog_ex_cyclic(const char* fmt, va_list ap) -{ -#ifndef RELEASE_BUILD - char temp[LOG_SIZE_BUFFER]; - - cyclic_last_line %= LOG_SIZE_BUFFER_CYCLIC_LINES; - - vsprintf(temp, fmt, ap); - - pclog_ensure_stdlog_open(); - - strncpy(cyclic_buff[cyclic_last_line], temp, LOG_SIZE_BUFFER); - - uint32_t hashes[LOG_SIZE_BUFFER_CYCLIC_LINES] = {0}; - - // Random numbers - uint32_t base = 257; - uint32_t mod = 1000000007; - - uint32_t repeat_order = 0; - bool is_cycle = false; - - // compute the set of hashes for the current log buffer - for (int32_t log_line = 0; log_line < LOG_SIZE_BUFFER_CYCLIC_LINES; log_line++) - { - if (cyclic_buff[log_line][0] == '\0') - continue; // skip - - for (int32_t log_line_char = 0; log_line_char < LOG_SIZE_BUFFER; log_line_char++) - { - hashes[log_line] = hashes[log_line] * base + cyclic_buff[log_line][log_line_char] % mod; - } - } - - - // Now see if there are real cycles... - // We implement a minimum repeat size. - for (int32_t check_size = LOG_MINIMUM_REPEAT_ORDER; check_size < LOG_SIZE_BUFFER_CYCLIC_LINES / 2; check_size++) - { - //TODO: Log what we need for cycle 1. - //TODO: Command line option that lets us turn off this behaviour. - for (int32_t log_line_to_check = 0; log_line_to_check < check_size; log_line_to_check++) - { - if (hashes[log_line_to_check] == hashes[(log_line_to_check + check_size) % LOG_SIZE_BUFFER_CYCLIC_LINES]) - { - repeat_order = check_size; - break; - } - } - - is_cycle = (repeat_order != 0); - - // if there still is a cycle.. - if (is_cycle) - break; - - } - - if (is_cycle) - { - if (cyclic_last_line % repeat_order == 0) - { - log_cycles++; - - if (log_cycles == 1) - { - // 'Replay' the last few log entries so they actually show up - // Todo: is this right? - - for (uint32_t index = cyclic_last_line - 1; index > (cyclic_last_line - repeat_order); index--) - { - // *very important* to prevent out of bounds index - uint32_t real_index = index % LOG_SIZE_BUFFER_CYCLIC_LINES; - fprintf(stdlog, "%s", cyclic_buff[real_index]); - - } - - fprintf(stdlog, "%s", temp); // allow normal logging - } - - - if (log_cycles > 1 && log_cycles < 100) - fprintf(stdlog, "***** Cyclical Log Repeat of Order %d #%d *****\n", repeat_order, log_cycles); - else if (log_cycles == 100) - fprintf(stdlog, "Logged the same cycle 100 times...shutting up until something interesting happens\n"); - } - } - else - { - log_cycles = 0; - fprintf(stdlog, "%s", temp); - } - - cyclic_last_line++; - -#endif - -} void pclog_toggle_suppr(void) diff --git a/src/include/86box/86box.h b/src/include/86box/86box.h index 05c2a901e..96aeb645c 100644 --- a/src/include/86box/86box.h +++ b/src/include/86box/86box.h @@ -189,7 +189,6 @@ extern int config_changed; /* config has changed */ #ifdef HAVE_STDARG_H extern void pclog_ex(const char *fmt, va_list ap); extern void fatal_ex(const char *fmt, va_list ap); -extern void pclog_ex_cyclic(const char* fmt, va_list ap); #endif extern void pclog_toggle_suppr(void); #ifdef _MSC_VER diff --git a/src/include/86box/log.h b/src/include/86box/log.h index 9d3568069..7f0b96d60 100644 --- a/src/include/86box/log.h +++ b/src/include/86box/log.h @@ -12,9 +12,11 @@ * * Authors: Miran Grca, * Fred N. van Kempen, + * Connor Hyde * * Copyright 2021 Miran Grca. * Copyright 2021 Fred N. van Kempen. + * Copyright 2025 Connor Hyde. */ #ifndef EMU_LOG_H @@ -26,11 +28,16 @@ extern "C" { # endif +#define LOG_SIZE_BUFFER 1024 /* Log size buffer */ +#define LOG_SIZE_BUFFER_CYCLIC_LINES 32 /* Cyclic log size buffer (number of lines that should be cehcked) */ +#define LOG_MINIMUM_REPEAT_ORDER 4 /* Minimum repeat size */ + /* Function prototypes. */ extern void log_set_suppr_seen(void *priv, int suppr_seen); extern void log_set_dev_name(void *priv, char *dev_name); # ifdef HAVE_STDARG_H extern void log_out(void *priv, const char *fmt, va_list); +extern void log_out_cyclic(void* priv, const char *fmt, va_list); extern void log_fatal(void *priv, const char *fmt, ...); # endif extern void *log_open(char *dev_name); diff --git a/src/log.c b/src/log.c index b5267d70b..a4d84e616 100644 --- a/src/log.c +++ b/src/log.c @@ -12,12 +12,15 @@ * * Authors: Miran Grca, * Fred N. van Kempen, - * + * Connor Hyde + * * Copyright 2021 Miran Grca. * Copyright 2021 Fred N. van Kempen. + * Copyright 2025 Connor Hyde. */ #include #include +#include #include #include #include @@ -36,13 +39,31 @@ #ifndef RELEASE_BUILD typedef struct log_t { - char buff[1024]; - char *dev_name; - int seen; - int suppr_seen; + char buff[1024]; + char *dev_name; + int seen; + int suppr_seen; + char cyclic_buff[LOG_SIZE_BUFFER_CYCLIC_LINES][LOG_SIZE_BUFFER]; // Cyclical log buffer. This is 32kb, might calloc? + int32_t cyclic_last_line; + int32_t log_cycles; } log_t; extern FILE *stdlog; /* file to log output to */ +// Functions only used in this translation unit +void log_ensure_stdlog_open(void); + +void +log_ensure_stdlog_open(void) +{ + if (stdlog == NULL) { + if (log_path[0] != '\0') { + stdlog = plat_fopen(log_path, "w"); + if (stdlog == NULL) + stdlog = stdout; + } else + stdlog = stdout; + } +} void log_set_suppr_seen(void *priv, int suppr_seen) @@ -91,14 +112,7 @@ log_out(void *priv, const char *fmt, va_list ap) if (strcmp(fmt, "") == 0) return; - if (stdlog == NULL) { - if (log_path[0] != '\0') { - stdlog = plat_fopen(log_path, "w"); - if (stdlog == NULL) - stdlog = stdout; - } else - stdlog = stdout; - } + log_ensure_stdlog_open(); vsprintf(temp, fmt, ap); if (log->suppr_seen && !strcmp(log->buff, temp)) @@ -117,6 +131,131 @@ log_out(void *priv, const char *fmt, va_list ap) fflush(stdlog); } + +/* +Starfrost, 7-8 January 2025: + +For RIVA 128 emulation I needed a way to suppress logging if a repeated pattern of the same set of lines were found. + +Implements a version of the Rabin-Karp algorithm https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm +*/ +void +log_out_cyclic(void* priv, const char* fmt, va_list ap) +{ +#ifndef RELEASE_BUILD + // get our new logging system instance. + log_t* log = (log_t*)priv; + + // does the log actually exist? + if (!log) + return; + + // is the string empty? + if (fmt[0] == '\0') + return; + + // ensure stdlog is open + log_ensure_stdlog_open(); + + char temp[LOG_SIZE_BUFFER] = {0}; + + log->cyclic_last_line %= LOG_SIZE_BUFFER_CYCLIC_LINES; + + vsprintf(temp, fmt, ap); + + log_copy(log, log->cyclic_buff[log->cyclic_last_line], temp, LOG_SIZE_BUFFER); + + uint32_t hashes[LOG_SIZE_BUFFER_CYCLIC_LINES] = {0}; + + // Random numbers + uint32_t base = 257; + uint32_t mod = 1000000007; + + uint32_t repeat_order = 0; + bool is_cycle = false; + + // compute the set of hashes for the current log buffer + for (int32_t log_line = 0; log_line < LOG_SIZE_BUFFER_CYCLIC_LINES; log_line++) + { + if (log->cyclic_buff[log_line][0] == '\0') + continue; // skip + + for (int32_t log_line_char = 0; log_line_char < LOG_SIZE_BUFFER; log_line_char++) + { + hashes[log_line] = hashes[log_line] * base + log->cyclic_buff[log_line][log_line_char] % mod; + } + } + + + // Now see if there are real cycles... + // We implement a minimum repeat size. + for (int32_t check_size = LOG_MINIMUM_REPEAT_ORDER; check_size < LOG_SIZE_BUFFER_CYCLIC_LINES / 2; check_size++) + { + //TODO: Log what we need for cycle 1. + //TODO: Command line option that lets us turn off this behaviour. + for (int32_t log_line_to_check = 0; log_line_to_check < check_size; log_line_to_check++) + { + if (hashes[log_line_to_check] == hashes[(log_line_to_check + check_size) % LOG_SIZE_BUFFER_CYCLIC_LINES]) + { + repeat_order = check_size; + break; + } + } + + is_cycle = (repeat_order != 0); + + // if there still is a cycle.. + if (is_cycle) + break; + + } + + if (is_cycle) + { + if (log->cyclic_last_line % repeat_order == 0) + { + log->log_cycles++; + + if (log->log_cycles == 1) + { + // 'Replay' the last few log entries so they actually show up + // Todo: is this right? + + for (uint32_t index = log->cyclic_last_line - 1; index > (log->cyclic_last_line - repeat_order); index--) + { + // *very important* to prevent out of bounds index + uint32_t real_index = index % LOG_SIZE_BUFFER_CYCLIC_LINES; + log_copy(log, temp, log->cyclic_buff[real_index], LOG_SIZE_BUFFER); + + fprintf(stdlog, "%s", log->cyclic_buff[real_index]); + + } + + // restore the original line + log_copy(log, temp, log->cyclic_buff[log->cyclic_last_line], LOG_SIZE_BUFFER); + + fprintf(stdlog, "%s", temp); // allow normal logging + } + + + if (log->log_cycles > 1 && log->log_cycles < 100) + fprintf(stdlog, "***** Cyclical Log Repeat of Order %d #%d *****\n", repeat_order, log->log_cycles); + else if (log->log_cycles == 100) + fprintf(stdlog, "Logged the same cycle 100 times...shutting up until something interesting happens\n"); + } + } + else + { + log->log_cycles = 0; + fprintf(stdlog, "%s", temp); + } + + log->cyclic_last_line++; + +#endif + +} + void log_fatal(void *priv, const char *fmt, ...) { @@ -145,6 +284,8 @@ log_open(char *dev_name) log->dev_name = dev_name; log->suppr_seen = 1; + log->cyclic_last_line = 0; + log->log_cycles = 0; return (void *) log; } From 8fda1aca83241f04d02cea5efe2db78d76a539a8 Mon Sep 17 00:00:00 2001 From: OBattler Date: Tue, 14 Jan 2025 01:35:22 +0100 Subject: [PATCH 22/22] Acer V35N: Do not add the NVR - it is already added by the Super I/O chip. --- src/machine/m_at_socket7.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/machine/m_at_socket7.c b/src/machine/m_at_socket7.c index f80d2d421..2dbb83684 100644 --- a/src/machine/m_at_socket7.c +++ b/src/machine/m_at_socket7.c @@ -58,10 +58,6 @@ machine_at_acerv35n_init(const machine_t *model) return ret; machine_at_common_init_ex(model, 2); - /* Yes, it's called amstrad_mega_pc_nvr_device, but it's basically the - standard AT NVR, just initialized to 0x00's (perhaps that should be the - default behavior?). */ - device_add(&amstrad_megapc_nvr_device); pci_init(PCI_CONFIG_TYPE_1); pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0);