From 599b3910951c85220ef26a319fc8fdbf1acb16f8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 21 Sep 2025 18:45:32 +0100 Subject: [PATCH] Add register framework --- src/include/86box/nv/vid_nv.h | 4 ++ src/include/86box/nv/vid_nv4.h | 27 ++++++++- src/video/CMakeLists.txt | 2 + src/video/nv/nv4/nv4_core.c | 27 ++++++++- src/video/nv/nv4/nv4_core_io.c | 39 ++++++++++++- src/video/nv/nv4/nv4_debug_register_list.c | 46 +++++++++++++++ src/video/nv/nv4/subsystems/nv4_pramdac.c | 68 ++++++++++++++++++++++ 7 files changed, 206 insertions(+), 7 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 3d69f75c7..83f94c167 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -136,6 +136,10 @@ typedef struct nv_base_s bool pixel_clock_enabled; // Pixel Clock Enabled - stupid crap used to prevent us enabling the timer multiple times double memory_clock_frequency; // Source Frequency for PTIMER rivatimer_t* memory_clock_timer; // Timer for measuring memory/gpu clock + + // VCLK / NVCLK do not have timers set here. + pc_timer_t* nv4_vclk_timer; // NV4+ MCLK (Video RAM) timer + bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times void* i2c; // I2C for monitor EDID void* ddc; // Display Data Channel for EDID diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h index bcdfb4dd0..0f2aad285 100644 --- a/src/include/86box/nv/vid_nv4.h +++ b/src/include/86box/nv/vid_nv4.h @@ -62,6 +62,7 @@ typedef struct nv4_pramdac_s uint32_t mclk; uint32_t vclk; uint32_t nvclk; + uint32_t clk_coeff_select; // Clock coefficient selection uint32_t cursor_address; } nv4_pramdac_t; @@ -83,6 +84,13 @@ extern const device_config_t nv4_config[]; extern nv4_t* nv4; // Allocated at device startup +#ifdef NV_LOG + +// Debug register list +extern nv_register_t nv4_registers[]; + +#endif + // // Functions // @@ -120,9 +128,22 @@ void nv4_ramin_write32(uint32_t addr, uint32_t val, void* priv); uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv); void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv); -// PRAMDAC - +// SVGA uint8_t nv4_svga_read(uint16_t addr, void* priv); void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); -void nv4_update_mappings(); \ No newline at end of file +// Memory +void nv4_update_mappings(); + +// PRAMDAC +uint32_t nv4_pramdac_read(uint32_t address); +void nv4_pramdac_write(uint32_t address, uint32_t data); + +// We don't implement NVCLK/VCLK because they are too fast +void nv4_pramdac_set_vclk(); + +void nv4_vclk_tick(); + +// PTIMER +uint32_t nv4_ptimer_read(uint32_t address); +void nv4_ptimer_write(uint32_t address, uint32_t data); \ No newline at end of file diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index b9c735be8..f1d755dba 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -201,6 +201,8 @@ add_library(vid OBJECT nv/nv4/nv4_core.c nv/nv4/nv4_core_io.c nv/nv4/nv4_core_config.c + nv/nv4/nv4_debug_register_list.c + nv/nv4/subsystems/nv4_pramdac.c nv/nv4/subsystems/nv4_ptimer.c # Generic diff --git a/src/video/nv/nv4/nv4_core.c b/src/video/nv/nv4/nv4_core.c index 13f9df321..5712cf7d1 100644 --- a/src/video/nv/nv4/nv4_core.c +++ b/src/video/nv/nv4/nv4_core.c @@ -199,7 +199,6 @@ bool nv4_init() /* Set log device name based on card model */ const char* log_device_name = "NV4"; - /* Just hardcode full logging */ if (device_get_config_int("nv_debug_fulllog")) @@ -234,6 +233,15 @@ bool nv4_init() video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv4_agp); + // Setup clock information. These values don't matter, I pulled them out of an STB BIOS, we don't need to macro them + nv4->pramdac.nvclk = 0x17809; + nv4->pramdac.mclk = 0x1a30a; + nv4->pramdac.vclk = 0x1400c; + + //timer_add(nv4->pramdac.nvclk, ) + + + nv4_init_mappings(); //nv4_update_mappings(); @@ -250,6 +258,21 @@ void* nv4_init_stb4400(const device_t *info) return NULL; } +void nv4_nvclk_tick() +{ + +} + +void nv4_mclk_tick() +{ + +} + +void nv4_vclk_tick() +{ + +} + void nv4_close(void* priv) { free(nv4); @@ -278,7 +301,7 @@ void nv4_recalc_timings(svga_t* svga) nv4_t* nv4 = (nv4_t*)svga->priv; - // TODO: Everything, this code sucks, incl. NV4_PRAMDAC_GENERAL_CONTROL_BPC and the OFFSET register + // TODO: Everything, this code sucks, incl. NV4_PRAMDAC_GENERAL_CONTROL_BPC and the offset register uint32_t pixel_mode = svga->crtc[NV4_CIO_CRE_PIXEL_INDEX] & 0x03; svga->memaddr_latch += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0x1F) << 16; diff --git a/src/video/nv/nv4/nv4_core_io.c b/src/video/nv/nv4/nv4_core_io.c index d9fa6f58a..4c5aa5a40 100644 --- a/src/video/nv/nv4/nv4_core_io.c +++ b/src/video/nv/nv4/nv4_core_io.c @@ -44,12 +44,47 @@ void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); uint32_t nv4_mmio_arbitrate_read(uint32_t addr) { - nv_log_verbose_only("MMIO read from address=0x%08x\n", addr); + uint32_t ret = 0x00; + + if (addr >= NV4_PTIMER_START && addr <= NV4_PTIMER_END) + ret = nv4_ptimer_read(addr); + else if (addr >= NV4_PRAMDAC_START && addr <= NV4_PRAMDAC_END) + ret = nv4_pramdac_read(addr); + + #ifdef NV_LOG + nv_register_t reg = nv_get_register(addr, &nv4_registers, sizeof(nv4_registers)/sizeof(nv4_registers[0])); + + if (reg) + { + if (reg->on_read) + ret = reg->on_read(); + + nv_log_verbose_only("Register read from 0x%08x value=%08x", addr, ret); + + } + #endif + } void nv4_mmio_arbitrate_write(uint32_t addr, uint32_t val) { - nv_log_verbose_only("MMIO write to address=0x%08x value %08x\n", addr, val); + if (addr >= NV4_PTIMER_START && addr <= NV4_PTIMER_END) + nv4_ptimer_write(addr, val); + else if (addr >= NV4_PRAMDAC_START && addr <= NV4_PRAMDAC_END) + nv4_pramdac_write(addr, val); + + #ifdef NV_LOG + nv_register_t reg = nv_get_register(addr, &nv4_registers, sizeof(nv4_registers)/sizeof(nv4_registers[0])); + + if (reg) + { + if (reg->on_write) + reg->on_write(val); + + nv_log_verbose_only("Register write from 0x%08x value=%08x", addr, val); + + } + #endif } // Determine if this address needs to be redirected to the SVGA subsystem. diff --git a/src/video/nv/nv4/nv4_debug_register_list.c b/src/video/nv/nv4/nv4_debug_register_list.c index e69de29bb..32516be0c 100644 --- a/src/video/nv/nv4/nv4_debug_register_list.c +++ b/src/video/nv/nv4/nv4_debug_register_list.c @@ -0,0 +1,46 @@ +/* + * 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. + * + * NV4 debug register list + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/io.h> +#include <86box/pci.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv4.h> + +#ifdef NV_LOG + +nv_register_t nv4_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, }, + { NV4_PRAMDAC_VPLL_COEFF, "NV4 PRAMDAC - Pixel Clock Coefficient", NULL, NULL, }, + { NV4_PRAMDAC_NVPLL_COEFF, "NV4 PRAMDAC - GPU Core Clock Coefficient", NULL, NULL, }, + { NV4_PRAMDAC_MPLL_COEFF, "NV4 PRAMDAC - VRAM Clock Coefficient", NULL, NULL, }, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +#endif \ No newline at end of file diff --git a/src/video/nv/nv4/subsystems/nv4_pramdac.c b/src/video/nv/nv4/subsystems/nv4_pramdac.c index d8d4883ec..8460f6df4 100644 --- a/src/video/nv/nv4/subsystems/nv4_pramdac.c +++ b/src/video/nv/nv4/subsystems/nv4_pramdac.c @@ -14,3 +14,71 @@ * 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> + + +uint64_t nv4_pramdac_get_hz(uint32_t coeff, bool apply_divider) +{ + uint32_t m = coeff & 0xFF; + uint32_t n = (coeff >> 8) & 0xFF; + uint32_t p = (coeff >> 16) & 0x07; + + // Check clock base + uint32_t hz_base = (nv4->straps & (1 << NV4_STRAP_CRYSTAL)) ? 14318180 : 13500000; + uint32_t final_hz = (hz_base * n) / (m << p); + + // Check VCLK divider + + if (apply_divider) + { + if (nv4->pramdac.clk_coeff_select & (1 << NV4_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO)) + final_hz >>= 1; + } + + return final_hz; +} + +void nv4_pramdac_set_vclk() +{ + uint64_t final_hz = nv4_pramdac_get_hz(nv4->pramdac.nvclk, false); + + //TODO: Everything + if (!nv4->nvbase.nv4_vclk_timer) + timer_set_delay_u64(nv4->nvbase.nv4_vclk_timer, final_hz / TIMER_USEC); + +} + +uint32_t nv4_pramdac_read(uint32_t address) +{ + uint32_t ret = 0x00; + + switch (address) + { + case NV4_PRAMDAC_VPLL_COEFF: // Pixel clock + ret = nv4->pramdac.vclk; + break; + case NV4_PRAMDAC_NVPLL_COEFF: // System clock + ret = nv4->pramdac.nvclk; + break; + case NV4_PRAMDAC_MPLL_COEFF: // Memory clock + ret = nv4->pramdac.mclk; + break; + + } +} + +void nv4_pramdac_write(uint32_t address, uint32_t data) +{ + +}