diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h index 7fef843d4..6e94024a6 100644 --- a/src/include/86box/nv/vid_nv4.h +++ b/src/include/86box/nv/vid_nv4.h @@ -20,9 +20,10 @@ #include #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv4_defines.h> -extern const device_config_t nv4_config[]; // Config for RIVA 128 (revision A/B) +extern const device_config_t nv4_config[]; +extern nv4_t* nv4; // Allocated at device startup // Structures typedef struct nv4_s @@ -30,6 +31,25 @@ typedef struct nv4_s nv_base_t nvbase; // Base Nvidia structure } nv4_t; +// +// PTIMER +// + +typedef struct nv4_ptimer_s +{ + uint32_t interrupt_status; // PTIMER Interrupt status + uint32_t interrupt_enable; // PTIMER Interrupt enable + uint32_t clock_numerator; // PTIMER (tick?) numerator + uint32_t clock_denominator; // PTIMER (tick?) denominator + uint64_t time; // time + uint32_t alarm; // The value of time when there should be an alarm +} nv4_ptimer_t; + + +// +// Functions +// + // Device Core void nv4_init(); void nv4_close(void* priv); diff --git a/src/include/86box/nv/vid_nv4_defines.h b/src/include/86box/nv/vid_nv4_defines.h index f7499e3a8..e27ff091e 100644 --- a/src/include/86box/nv/vid_nv4_defines.h +++ b/src/include/86box/nv/vid_nv4_defines.h @@ -2745,7 +2745,7 @@ #define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0 1 #define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0_COLOR 0x0 #define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0_ALPHA 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0 4:2 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0 2 #define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_ZERO 0x1 #define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_FACTOR 0x2 #define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_DIFFUSE 0x3 @@ -2807,7 +2807,7 @@ #define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0 0 #define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0_NORMAL 0x0 #define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0 4:2 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0 2 #define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_ZERO 0x1 #define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_FACTOR 0x2 #define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_DIFFUSE 0x3 @@ -4177,14 +4177,14 @@ #define NV4_PTIMER_START 0x9000 #define NV4_PTIMER_END 0x9FFF -#define NV4_PTIMER_INTR_0 0x9100 -#define NV4_PTIMER_INTR_0_ALARM 0 -#define NV4_PTIMER_INTR_0_ALARM_NOT_PENDING 0x0 -#define NV4_PTIMER_INTR_0_ALARM_PENDING 0x1 -#define NV4_PTIMER_INTR_0_ALARM_RESET 0x1 -#define NV4_PTIMER_INTR_EN_0 0x9140 -#define NV4_PTIMER_INTR_EN_0_ALARM 0 -#define NV4_PTIMER_INTR_EN_0_ALARM_ENABLED 0x1 // 0 = disabled +#define NV4_PTIMER_INTR 0x9100 +#define NV4_PTIMER_INTR_ALARM 0 +#define NV4_PTIMER_INTR_ALARM_NOT_PENDING 0x0 +#define NV4_PTIMER_INTR_ALARM_PENDING 0x1 +#define NV4_PTIMER_INTR_ALARM_RESET 0x1 +#define NV4_PTIMER_INTR_EN 0x9140 +#define NV4_PTIMER_INTR_EN_ALARM 0 +#define NV4_PTIMER_INTR_EN_ALARM_ENABLED 0x1 // 0 = disabled #define NV4_PTIMER_NUMERATOR 0x9200 #define NV4_PTIMER_NUMERATOR_VALUE 0 #define NV4_PTIMER_NUMERATOR_VALUE_0 0x0 @@ -4192,11 +4192,11 @@ #define NV4_PTIMER_DENOMINATOR_VALUE 0 #define NV4_PTIMER_DENOMINATOR_VALUE_0 0x0 #define NV4_PTIMER_TIME_0 0x9400 -#define NV4_PTIMER_TIME_0_NSEC 31:5 +#define NV4_PTIMER_TIME_0_NSEC 5 #define NV4_PTIMER_TIME_1 0x9410 #define NV4_PTIMER_TIME_1_NSEC 0 -#define NV4_PTIMER_ALARM_0 0x9420 -#define NV4_PTIMER_ALARM_0_NSEC 31:5 +#define NV4_PTIMER_ALARM 0x9420 +#define NV4_PTIMER_ALARM_NSEC 5 #define NV4_TRACE 0xFFFF: 0x0 #define NV4_TRACE_DATA ( 0*32+ 7):( 0*32+ 0) diff --git a/src/video/nv/nv4/nv4_core.c b/src/video/nv/nv4/nv4_core.c index 2af47f22a..d020e44e6 100644 --- a/src/video/nv/nv4/nv4_core.c +++ b/src/video/nv/nv4/nv4_core.c @@ -27,19 +27,22 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv4.h> +nv4_t* nv4; + void nv4_init() { - + nv4 = calloc(1, sizeof(nv4_t)); } void* nv4_init_stb4400(const device_t *info) { - + nv4_init(); + return nv4; } void nv4_close(void* priv) { - + free(nv4); } void nv4_speed_changed(void *priv) diff --git a/src/video/nv/nv4/nv4_debug_register_list.c b/src/video/nv/nv4/nv4_debug_register_list.c new file mode 100644 index 000000000..e69de29bb diff --git a/src/video/nv/nv4/subsystems/nv4_ptimer.c b/src/video/nv/nv4/subsystems/nv4_ptimer.c new file mode 100644 index 000000000..b52f79f17 --- /dev/null +++ b/src/video/nv/nv4/subsystems/nv4_ptimer.c @@ -0,0 +1,226 @@ +/* + * 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. + * + * Nvidia RIVA TNT (NV4 architecture) - Timer emulation + * + * + * + * Authors: Connor Hyde, + * + * Copyright 2024-2025 starfrost + */ + +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/pci.h> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv4.h> + +nv_register_t ptimer_registers[] = { + { NV4_PTIMER_INTR, "NV4 PTIMER - Interrupt Status", NULL, NULL}, + { NV4_PTIMER_INTR_EN, "NV4 PTIMER - Interrupt Enable", NULL, NULL,}, + { NV4_PTIMER_NUMERATOR, "NV4 PTIMER - Numerator", NULL, NULL, }, + { NV4_PTIMER_DENOMINATOR, "NV4 PTIMER - Denominator", NULL, NULL, }, + { NV4_PTIMER_TIME_0_NSEC, "NV4 PTIMER - Time0", NULL, NULL, }, + { NV4_PTIMER_TIME_1_NSEC, "NV4 PTIMER - Time1", NULL, NULL, }, + { NV4_PTIMER_ALARM_NSEC, "NV4 PTIMER - Alarm", NULL, NULL, }, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +// ptimer init code +void nv4_ptimer_init(void) +{ + nv_log("Initialising PTIMER..."); + + nv_log("Done!\n"); +} + +// Handles the PTIMER alarm interrupt +void nv4_ptimer_interrupt(uint32_t num) +{ + nv4->ptimer.interrupt_status |= (1 << num); + + nv4_pmc_handle_interrupts(true); +} + +// Ticks the timer. +void nv4_ptimer_tick(double real_time) +{ + // prevent a divide by zero + if (nv4->ptimer.clock_numerator == 0 + || nv4->ptimer.clock_denominator == 0) + return; + + // get the current time + + // See Envytools. We need to use the frequency as a source. + // We need to figure out how many cycles actually occurred because this counts up every cycle... + // However it seems that their formula is wrong. I can't be bothered to figure out what's going on and, based on documentation from NVIDIA, + // timer_0 is meant to roll over every 4 seconds. Multiplying by 10 basically does the job. + + // Convert to microseconds + double freq_base = (real_time / 1000000.0f) / ((double)1.0 / nv4->nvbase.memory_clock_frequency) * 10.0f; + double current_time = freq_base * ((double)nv4->ptimer.clock_numerator) / (double)nv4->ptimer.clock_denominator; // *10.0? + + // truncate it + nv4->ptimer.time += (uint64_t)current_time; + + // Check if the alarm has actually triggered.. + // Only log on ptimer alarm. Otherwise, it's too much spam. + if (nv4->ptimer.time >= nv4->ptimer.alarm) + { + nv_log_verbose_only("PTIMER alarm interrupt fired (if interrupts enabled) because we reached TIME value 0x%08x\n", nv4->ptimer.alarm); + nv4_ptimer_interrupt(NV4_PTIMER_INTR_ALARM); + } +} + +uint32_t nv4_ptimer_read(uint32_t address) +{ + // always enabled + + nv_register_t* reg = nv_get_register(address, ptimer_registers, sizeof(ptimer_registers)/sizeof(ptimer_registers[0])); + + // Only log these when tehy actually tick + if (address != NV4_PTIMER_TIME_0_NSEC + && address != NV4_PTIMER_TIME_1_NSEC) + { + nv_log_verbose_only("PTIMER Read from 0x%08x", address); + } + + uint32_t ret = 0x00; + + // if the register actually exists + if (reg) + { + // on-read function + if (reg->on_read) + ret = reg->on_read(); + else + { + // Interrupt state: + // Bit 0: Alarm + + switch (reg->address) + { + case NV4_PTIMER_INTR: + ret = nv4->ptimer.interrupt_status; + break; + case NV4_PTIMER_INTR_EN: + ret = nv4->ptimer.interrupt_enable; + break; + case NV4_PTIMER_NUMERATOR: + ret = nv4->ptimer.clock_numerator; // 15:0 + break; + case NV4_PTIMER_DENOMINATOR: + ret = nv4->ptimer.clock_denominator ; //15:0 + break; + // 64-bit value + // High part + case NV4_PTIMER_TIME_0_NSEC: + ret = nv4->ptimer.time & 0xFFFFFFFF; //28:0 + break; + // Low part + case NV4_PTIMER_TIME_1_NSEC: + ret = nv4->ptimer.time >> 32; // 31:5 + break; + case NV4_PTIMER_ALARM_NSEC: + ret = nv4->ptimer.alarm; // 31:5 + break; + } + + } + //TIME0 and TIME1 produce too much log spam that slows everything down + if (reg->address != NV4_PTIMER_TIME_0_NSEC + && reg->address != NV4_PTIMER_TIME_1_NSEC) + { + if (reg->friendly_name) + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); + else + nv_log_verbose_only("\n"); + } + } + else + { + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); + } + + return ret; +} + +void nv4_ptimer_write(uint32_t address, uint32_t value) +{ + // before doing anything, check the subsystem enablement + nv_register_t* reg = nv_get_register(address, ptimer_registers, sizeof(ptimer_registers)/sizeof(ptimer_registers[0])); + + nv_log_verbose_only("PTIMER Write 0x%08x -> 0x%08x", value, address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log_verbose_only(": %s\n", reg->friendly_name); + else + nv_log_verbose_only("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + else + { + switch (reg->address) + { + // Interrupt state: + // Bit 0 - Alarm + + case NV4_PTIMER_INTR: + nv4->ptimer.interrupt_status &= ~value; + nv4_pmc_clear_interrupts(); + break; + + // Interrupt enablement state + case NV4_PTIMER_INTR_EN: + nv4->ptimer.interrupt_enable = value & 0x1; + break; + // nUMERATOR + case NV4_PTIMER_NUMERATOR: + nv4->ptimer.clock_numerator = value & 0xFFFF; // 15:0 + break; + case NV4_PTIMER_DENOMINATOR: + // prevent Div0 + if (!value) + value = 1; + + nv4->ptimer.clock_denominator = value & 0xFFFF; //15:0 + break; + // 64-bit value + // High part + case NV4_PTIMER_TIME_0_NSEC: + nv4->ptimer.time |= (value) & 0xFFFFFFE0; //28:0 + break; + // Low part + case NV4_PTIMER_TIME_1_NSEC: + nv4->ptimer.time |= ((uint64_t)(value & 0xFFFFFFE0) << 32); // 31:5 + break; + case NV4_PTIMER_ALARM: + nv4->ptimer.alarm = value & 0xFFFFFFE0; // 31:5 + break; + } + } + } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } +} \ No newline at end of file