From 15a2f6435c4989a425dc841a0e2f0e3bb91fdffa Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 27 Nov 2024 16:29:26 +0000 Subject: [PATCH] Nvidia NV3 initial implementation from the old repo when it was a fork. --- CMakeLists.txt | 5 + CMakePresets.json | 5 +- src/include/86box/nv/vid_nv.h | 494 +++++++++++ src/include/86box/nv/vid_nv3.h | 486 ++++++++++ src/include/86box/video.h | 2 + src/video/CMakeLists.txt | 16 +- src/video/nv/nv3/nv3_core.c | 837 ++++++++++++++++++ src/video/nv/nv3/nv3_core_arbiter.c | 198 +++++ src/video/nv/nv3/nv3_interrupt.c | 0 src/video/nv/nv3/subsystems/nv3_pbus.c | 249 ++++++ src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 31 + src/video/nv/nv3/subsystems/nv3_pextdev.c | 149 ++++ src/video/nv/nv3/subsystems/nv3_pfb.c | 109 +++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 146 +++ src/video/nv/nv3/subsystems/nv3_pgraph.c | 151 ++++ src/video/nv/nv3/subsystems/nv3_pmc.c | 236 +++++ src/video/nv/nv3/subsystems/nv3_pme.c | 123 +++ src/video/nv/nv3/subsystems/nv3_pramdac.c | 307 +++++++ src/video/nv/nv3/subsystems/nv3_pramin.c | 149 ++++ .../nv/nv3/subsystems/nv3_pramin_ramfc.c | 31 + .../nv/nv3/subsystems/nv3_pramin_ramht.c | 31 + .../nv/nv3/subsystems/nv3_pramin_ramro.c | 31 + src/video/nv/nv3/subsystems/nv3_ptimer.c | 120 +++ src/video/nv/nv3/subsystems/nv3_pvideo.c | 121 +++ src/video/nv/nv_base.c | 47 + src/video/vid_table.c | 3 + 26 files changed, 4074 insertions(+), 3 deletions(-) create mode 100644 src/include/86box/nv/vid_nv.h create mode 100644 src/include/86box/nv/vid_nv3.h create mode 100644 src/video/nv/nv3/nv3_core.c create mode 100644 src/video/nv/nv3/nv3_core_arbiter.c create mode 100644 src/video/nv/nv3/nv3_interrupt.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pbus.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pbus_dma.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pextdev.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pfb.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pfifo.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pgraph.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pmc.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pme.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pramdac.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pramin.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pramin_ramht.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pramin_ramro.c create mode 100644 src/video/nv/nv3/subsystems/nv3_ptimer.c create mode 100644 src/video/nv/nv3/subsystems/nv3_pvideo.c create mode 100644 src/video/nv/nv_base.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d7b3f6f0..3e0527c60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,6 +138,11 @@ option(GDBSTUB "Enable GDB stub server for debugging" option(DEV_BRANCH "Development branch" OFF) option(DISCORD "Discord Rich Presence support" ON) option(DEBUGREGS486 "Enable debug register opeartion on 486+ CPUs" OFF) +option(NV_LOG "NVidia RIVA 128 debug logging" OFF) + +if (NV_LOG) + add_compile_definitions(ENABLE_NV_LOG) +endif() if(WIN32) set(QT ON) diff --git a/CMakePresets.json b/CMakePresets.json index c19a7abc0..8d4873624 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -35,7 +35,8 @@ { "name": "debug", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug" + "CMAKE_BUILD_TYPE": "Debug", + "NV_LOG": "ON" }, "inherits": "base" }, @@ -65,7 +66,7 @@ "NEW_DYNAREC": "ON", "QT": "ON", "USE_QT6": "OFF", - "Qt5_DIR": "/opt/homebrew/opt/qt@5/lib/cmake/Qt5", + "Qt5_DIR": "/opt/homebrew/opt/qt@5/lSib/cmake/Qt5", "MOLTENVK_DIR": "/opt/homebrew/opt/molten-vk", "Qt5LinguistTools_DIR": "/opt/homebrew/opt/qt@5/lib/cmake/Qt5LinguistTools", "OpenAL_ROOT": "/opt/homebrew/opt/openal-soft" diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h new file mode 100644 index 000000000..ca0922a44 --- /dev/null +++ b/src/include/86box/nv/vid_nv.h @@ -0,0 +1,494 @@ +/* + * 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. + * + * JENSEN HUANG APPROVED !!!! + * + * Credit to: + * + * - fuel (PCBox developer) + * - Marcelina Koƛcielnicka (envytools) + * - nouveau developers + * - Utah GLX developers + * - XFree86 developers + * - xemu developers + * + * Authors: Connor Hyde / starfrost + * + * Copyright 2024 Connor Hyde + */ +#ifdef EMU_DEVICE_H // what + +//TODO: split this all into nv1, nv3, nv4... + +#include <86box/timer.h> +#include <86box/vid_svga.h> +#include <86box/vid_svga_render.h> + +void nv_log(const char *fmt, ...); + +// Defines common to all NV chip architectural generations + +// PCI IDs +#define PCI_VENDOR_NV 0x10DE // NVidia PCI ID +#define PCI_VENDOR_SGS 0x104A // SGS-Thompson +#define PCI_VENDOR_SGS_NV 0x12D2 // SGS-Thompson/NVidia joint venture + +#define NV_PCI_NUM_CFG_REGS 256 // number of pci config registers + +// 0x0000 was probably the NV0 'Nvidia Hardware Simulator' +#define PCI_DEVICE_NV1 0x0008 // Nvidia NV1 +#define PCI_DEVICE_NV1_VGA 0x0009 // Nvidia NV1 VGA core +#define PCI_DEVICE_NV2 0x0010 // Nvidia NV2 / Mutara V08 (cancelled) +#define PCI_DEVICE_NV3 0x0018 // Nvidia NV3 (Riva 128) +#define PCI_DEVICE_NV3T 0x0019 // Nvidia NV3T (Riva 128 ZX) +#define PCI_DEVICE_NV4 0x0020 // Nvidia NV4 (RIVA TNT) + +#define CHIP_REVISION_NV1_A0 0x0000 +#define CHIP_REVISION_NV1_B0 0x0010 +#define CHIP_REVISION_NV1_C0 0x0020 + +#define CHIP_REVISION_NV3_A0 0x0000 // January 1997 +#define CHIP_REVISION_NV3_B0 0x0010 // October 1997 +#define CHIP_REVISION_NV3_C0 0x0020 // 1998 + +// Architecture IDs +#define NV_ARCHITECTURE_NV1 1 +#define NV_ARCHITECTURE_NV2 2 +#define NV_ARCHITECTURE_NV3 3 + + +typedef enum nv_bus_generation_e +{ + // NV1 + // NV3 + nv_bus_pci = 0, + + // NV3 + nv_bus_agp_1x = 1, + + // NV3T + // NV4 + nv_bus_agp_2x = 2, + +} nv_bus_generation; + +// NV Base +typedef struct nv_base_s +{ + rom_t vbios; // NVIDIA/OEm VBIOS + // move to nv3_cio_t? + svga_t svga; // SVGA core (separate to nv3) + // stuff that doesn't fit in the svga structure + uint32_t cio_read_bank; // SVGA read bank + uint32_t cio_write_bank; // SVGA write bank + + mem_mapping_t framebuffer_mapping; // Linear Framebuffer / NV_USER memory mapping + mem_mapping_t mmio_mapping; // mmio mapping (32MB unified MMIO) + mem_mapping_t framebuffer_mapping_mirror; // Mirror of LFB mapping + mem_mapping_t ramin_mapping; // RAM INput area mapping + mem_mapping_t ramin_mapping_mirror; // RAM INput area mapping (mirrored) + uint8_t pci_slot; // pci slot number + uint8_t pci_irq_state; // current PCI irq state + uint32_t bar0_mmio_base; // PCI Base Address Register 0 - MMIO Base + uint32_t bar1_lfb_base; // PCI Base Address Register 1 - Linear Framebuffer (NV_BASE) + nv_bus_generation bus_generation; // current bus (see nv_bus_generation documentation) +} nv_base_t; + +#define NV_REG_LIST_END 0xD15EA5E + +// The NV architectures are very complex. +// There are hundreds of registers at minimum, and implementing these in a standard way would lead to +// unbelievably large switch statements and horrifically unreadable code. +// So this is used to abstract it and allow for more readable code. +// This is mostly just used for logging and stuff. +// Optionally, you can provide a function that is run when you read to and write from the register. +// You can also implement this functionality in a traditional way such as a switch statement, for simpler registers. To do this, simply set both read and write functions to NULL. +// Typically, unless they are for a special purpose (and handled specially) e.g. vga all register reads and writes are also 32-bit aligned +typedef struct nv_register_s +{ + int32_t address; // MMIO Address + char* friendly_name; // Friendly name + // reg_ptr not needed as a parameter, because we implicitly know which register si being tiwddled + uint32_t (*on_read)(); // Optional on-read function + void (*on_write)(uint32_t value);// Optional on-write fucntion +} nv_register_t; + +nv_register_t* nv_get_register(uint32_t address, nv_register_t* register_list, uint32_t num_regs); + +#define NV3_BOOT_REG_DEFAULT 0x00300111 + +// Master Control +typedef struct nv3_pmc_s +{ + /* + Holds chip manufacturing information at bootup. + Current specification (may change later): pre-packed for convenience + + FIB Revision 1, Mask Revision B0, Implementation 1 [NV3], Architecture 3 [NV3], Manufacturer Nvidia, Foundry SGS (seems to have been the most common?) + 31:28=0000, 27:24=0000, 23:16=0003, 15:8=0001, 7:4=0001, 3:0=0001 + little endian 00000000 00000011 00000001 00010001 = 0x00300111 + */ + int32_t boot; + int32_t interrupt_status; // Determines if interrupts are pending for specific subsystems. + int32_t interrupt_enable; // Determines if interrupts are actually enabled. + int32_t enable; // Determines which subsystems are enabled. + +} nv3_pmc_t; + +typedef struct nv3_pci_config_s +{ + uint8_t pci_regs[NV_PCI_NUM_CFG_REGS]; // The actual pci register values (not really used, just so they can be stored - they aren't very good for code readability) + bool vbios_enabled; // is the vbios enabled? + uint8_t int_line; +} nv3_pci_config_t; + +// add enums for eac +// Chip configuration +typedef struct nv3_straps_s +{ + uint32_t straps; +} nv3_straps_t; + +// Framebuffer +typedef struct nv3_pfb_s +{ + uint32_t boot; +} nv3_pfb_t; + +#define NV3_RMA_NUM_REGS 4 +// Access the GPU from real-mode +typedef struct nv3_pbus_rma_s +{ + uint32_t addr; // Address to RMA to + uint32_t data; // Data to send to MMIO + uint8_t mode; // the current state of the rma shifting engin + uint8_t rma_regs[NV3_RMA_NUM_REGS]; // The rma registers (saved) +} nv3_pbus_rma_t; + +// Bus Configuration +typedef struct nv3_pbus_s +{ + uint32_t interrupt_status; // Interrupt status + uint32_t interrupt_enable; // Interrupt enable + nv3_pbus_rma_t rma; +} nv3_pbus_t; + +// Command submission to PGRAPH +typedef struct nv_pfifo_s +{ + uint32_t interrupt_status; // Interrupt status + uint32_t interrupt_enable; // Interrupt enable +} nv3_pfifo_t; + +// RAMDAC +typedef struct nv3_pramdac_s +{ + // these should be uint8_t but C math is a lot better with this + uint32_t memory_clock_m; // memory clock M-divider + uint32_t memory_clock_n; // memory clock N-divider + uint32_t memory_clock_p; // memory clock P-divider + uint32_t pixel_clock_m; // pixel clock M-divider + uint32_t pixel_clock_n; // pixel clock N-divider + uint32_t pixel_clock_p; // pixel clock P-divider + uint32_t coeff_select; // coefficient select + + uint32_t general_control; // general control register + + // this could duplicate SVGA state but I tihnk it's more readable, + // we'll just modify both + uint32_t vserr_width; // vertical sync error width + uint32_t vequ_end; // vequ end (not sure what this is) + uint32_t vbblank_end; // vbblank end (not sure what this is) + uint32_t vblank_end; // vblank end + uint32_t vblank_start; // vblank start + uint32_t vequ_start; // vequ start (not sure what this is) + uint32_t vtotal; // vertical total lines + uint32_t hsync_width; // horizontal sync width + uint32_t hburst_start; // horizontal burst signal start (in lines) + uint32_t hburst_end; // horizontal burst signal end (in lines) + uint32_t hblank_start; // horizontal blank start (in lines) + uint32_t hblank_end; // horizontal blank end (in lines) + uint32_t htotal; // horizontal total lines + uint32_t hequ_width; // horizontal equ width (not sure what this is) + uint32_t hserr_width; // horizontal sync error width +} nv3_pramdac_t; + +// Graphics Subsystem +typedef struct nv3_pgraph_s +{ + uint32_t interrupt_status_0; // Interrupt status 0 + uint32_t interrupt_enable_0; // Interrupt enable 0 + uint32_t interrupt_status_1; // Interrupt status 1 + uint32_t interrupt_enable_1; // Interrupt enable 1 +} nv3_pgraph_t; + +// GPU Manufacturing Configuration (again) +// In the future this will be configurable +typedef struct nv3_pextdev_s +{ + /* + // Disabled 33Mhz + // Enabled 66Mhz + bool bus_speed; + + // Disabled No BIOS + // Enabled BIOS + bool bios; + + // RAM Type #1 + // Disabled 16Mbit (2MB) module size + // Enabled 8Mbit (1MB) module size + bool ram_type_1; + + // NEC Mode + bool nec_mode; + + // Disabled 64-bit + // Enabled 128-bit + bool bus_width; + + // Disabled PCI + // Enabled AGP + bool bus_type; + + // Disabled 13500 + // Enabled 14318180 + bool crystal; + + // TV Mode + // 0 - SECAM, 1 - NTSC, 2 - PAL, 3 - none + uint8_t tv_mode; + + // AGP 2X mode + // Disabled AGP 1X + // Enabled AGP 2X + bool agp_is_2x; + + bool unused; + + // Overwrite enable + bool overwrite; + + See defines in vid_nv3.h + */ + uint32_t straps; + + // more ram type stuff here but not used? +} nv3_pextdev_t; + +typedef struct nv3_ptimer_s +{ + uint32_t interrupt_status; // Interrupt status + uint32_t interrupt_enable; // Interrupt enable +} nv3_ptimer_t; + +// Graphics object hashtable +typedef struct nv3_pramin_ramht_s +{ + +} nv3_pramin_ramht_t; + +// Anti-fuckup device +typedef struct nv3_pramin_ramro_s +{ + +} nv3_pramin_ramro_t; + +// context for unused channels +typedef struct nv3_pramin_ramfc_s +{ + +} nv3_pramin_ramfc_t; + +// ????? ram auxillary +typedef struct nv_pramin_ramau_s +{ + +} nv3_pramin_ramau_t; + +typedef struct nv3_pramin_s +{ + +} nv3_pramin_t; + +typedef struct nv3_pvideo_s +{ + uint32_t interrupt_status; // Interrupt status + uint32_t interrupt_enable; // Interrupt enable +} nv3_pvideo_t; + +typedef struct nv3_pme_s // Mediaport +{ + uint32_t interrupt_status; + uint32_t interrupt_enable; +} nv3_pme_t; + +typedef struct nv3_s +{ + nv_base_t nvbase; // Base Nvidia structure + + // Config + nv3_straps_t straps; + nv3_pci_config_t pci_config; + + // Subsystems + nv3_pmc_t pmc; // Master Control + nv3_pfb_t pfb; // Framebuffer/VRAM + nv3_pbus_t pbus; // Bus Control + nv3_pfifo_t pfifo; // FIFO for command submisison + + nv3_pramdac_t pramdac; // RAMDAC (CLUT etc) + nv3_pgraph_t pgraph; // 2D/3D Graphics + nv3_pextdev_t pextdev; // Chip configuration + nv3_ptimer_t ptimer; // programmable interval timer + nv3_pramin_ramht_t ramht; // hashtable for PGRAPH objects + nv3_pramin_ramro_t ramro; // anti-fuckup mechanism for idiots who fucked up the FIFO submission + nv3_pramin_ramfc_t ramfc; // context for unused channels + nv3_pramin_ramau_t ramau; // auxillary weirdnes + nv3_pramin_t pramin; // Ram for INput of DMA objects. Very important! + nv3_pvideo_t pvideo; // Video overlay + nv3_pme_t pme; // Mediaport - external MPEG decoder and video interface + //more here + +} nv3_t; + +// device objects +extern nv3_t* nv3; + +// Address of this returned by unimplemented registers to prevent a crash +extern uint32_t unimplemented_dummy; + +// NV3 stuff + +// Device Core +void* nv3_init(const device_t *info); +void nv3_close(void* priv); +void nv3_speed_changed(void *priv); +void nv3_force_redraw(void* priv); + +// Memory Mapping +void nv3_update_mappings(); +uint8_t nv3_mmio_read8(uint32_t addr, void* priv); // Read 8-bit MMIO +uint16_t nv3_mmio_read16(uint32_t addr, void* priv); // Read 16-bit MMIO +uint32_t nv3_mmio_read32(uint32_t addr, void* priv); // Read 32-bit MMIO +void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv); // Write 8-bit MMIO +void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv); // Write 16-bit MMIO +void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv); // Write 32-bit MMIO + +uint8_t nv3_svga_in(uint16_t addr, void* priv); // Read SVGA compatibility registers +void nv3_svga_out(uint16_t addr, uint8_t val, void* priv); // Write SVGA registers +uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv); // Read PCI configuration registers +void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv); // Write PCI configuration registers + +uint8_t nv3_ramin_read8(uint32_t addr, void* priv); // Read 8-bit RAMIN +uint16_t nv3_ramin_read16(uint32_t addr, void* priv); // Read 16-bit RAMIN +uint32_t nv3_ramin_read32(uint32_t addr, void* priv); // Read 32-bit RAMIN +void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv); // Write 8-bit RAMIN +void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv); // Write 16-bit RAMIN +void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv); // Write 32-bit RAMIN + +// MMIO Arbitration +// Determine where the hell in this mess our reads or writes are going +uint32_t nv3_mmio_arbitrate_read(uint32_t address); +void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value); + +// Read and Write functions for GPU subsystems +// Remove the ones that aren't used here eventually, have all of htem for now +uint32_t nv3_pmc_read(uint32_t address); +void nv3_pmc_write(uint32_t address, uint32_t value); +uint32_t nv3_cio_read(uint32_t address); +void nv3_cio_write(uint32_t address, uint32_t value); +uint32_t nv3_pbus_read(uint32_t address); +void nv3_pbus_write(uint32_t address, uint32_t value); +uint32_t nv3_pfifo_read(uint32_t address); +void nv3_pfifo_write(uint32_t address, uint32_t value); +uint32_t nv3_prm_read(uint32_t address); +void nv3_prm_write(uint32_t address, uint32_t value); +uint32_t nv3_prmio_read(uint32_t address); +void nv3_prmio_write(uint32_t address, uint32_t value); +uint32_t nv3_ptimer_read(uint32_t address); +void nv3_ptimer_write(uint32_t address, uint32_t value); +uint32_t nv3_pfb_read(uint32_t address); +void nv3_pfb_write(uint32_t address, uint32_t value); +uint32_t nv3_pextdev_read(uint32_t address); +void nv3_pextdev_write(uint32_t address, uint32_t value); + +// Special consideration for straps +#define nv3_pstraps_read nv3_pextdev_read(NV3_PSTRAPS) +#define nv3_pstraps_write(x) nv3_pextdev_write(NV3_PSTRAPS, x) + +uint32_t nv3_prom_read(uint32_t address); +void nv3_prom_write(uint32_t address, uint32_t value); +uint32_t nv3_palt_read(uint32_t address); +void nv3_palt_write(uint32_t address, uint32_t value); +uint32_t nv3_pme_read(uint32_t address); +void nv3_pme_write(uint32_t address, uint32_t value); +uint32_t nv3_pgraph_read(uint32_t address); +void nv3_pgraph_write(uint32_t address, uint32_t value); + +// TODO: PGRAPH class registers + +uint32_t nv3_prmcio_read(uint32_t address); +void nv3_prmcio_write(uint32_t address, uint32_t value); +uint32_t nv3_pvideo_read(uint32_t address); +void nv3_pvideo_write(uint32_t address, uint32_t value); +uint32_t nv3_pramdac_read(uint32_t address); +void nv3_pramdac_write(uint32_t address, uint32_t value); +uint32_t nv3_vram_read(uint32_t address); +void nv3_vram_write(uint32_t address, uint32_t value); +#define nv3_nvm_read nv3_vram_read +#define nv3_nvm_write nv3_vram_write +uint32_t nv3_user_read(uint32_t address); +void nv3_user_write(uint32_t address, uint32_t value); +#define nv3_object_submit_start nv3_user_read +#define nv3_object_submit_end nv3_user_write +uint32_t nv3_pramin_read(uint32_t address); +void nv3_pramin_write(uint32_t address, uint32_t value); +// TODO: RAMHT, RAMFC...or maybe handle it inside of nv3_pramin_* + +// GPU subsystems + +// NV3 PMC +void nv3_pmc_init(); +uint32_t nv3_pmc_clear_interrupts(); +uint32_t nv3_pmc_handle_interrupts(bool send_now); + +// NV3 PGRAPH +void nv3_pgraph_init(); + +// NV3 PFIFO +void nv3_pfifo_init(); + + +// NV3 PFB +void nv3_pfb_init(); + +// NV3 PEXTDEV/PSTRAPS +void nv3_pextdev_init(); + +// NV3 PBUS +void nv3_pbus_init(); + +// NV3 PBUS RMA - Real Mode Access for VBIOS +uint8_t nv3_pbus_rma_read(uint16_t addr); +void nv3_pbus_rma_write(uint16_t addr, uint8_t val); + +// NV3 PRAMDAC +void nv3_pramdac_init(); +void nv3_pramdac_set_vram_clock(); +void nv3_pramdac_set_pixel_clock(); + +// NV3 PTIMER +void nv3_ptimer_init(); + +// NV3 PVIDEO +void nv3_pvideo_init(); + +// NV3 PMEDIA +void nv3_pmedia_init(); +#endif \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h new file mode 100644 index 000000000..a9a4884d9 --- /dev/null +++ b/src/include/86box/nv/vid_nv3.h @@ -0,0 +1,486 @@ +/* + * 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. + * + * JENSEN HUANG APPROVED !!!! + * + * + * + * Authors: Connor Hyde + * + * Copyright 2024 Connor Hyde + */ + +// vid_nv3.h: NV3 Architecture Hardware Reference (open-source) +// Last updated 26 November 2024 + +// The GPU base structure +extern nv3_t* nv3; + +#define NV3_MMIO_SIZE 0x1000000 // Max MMIO size + +// various vbioses for testing +// Coming soon: MIROmagic Premium BIOS (when I get mine dumped) +//todo: move to hash system + +// Oldest one of these - September 6, 1997 +#define NV3_VBIOS_ERAZOR_V14700 "roms/video/nvidia/nv3/OLD_0000.BIN" // ELSA VICTORY Erazor VBE 3.0 DDC2B DPMS Video BIOS Ver. 1.47.01 (ZZ/ A/00) +#define NV3_VBIOS_ERAZOR_V15403 "roms/video/nvidia/nv3/VCERAZOR.BIN" // ELSA VICTORY Erazor Ver. 1.54.03 [WD/VBE30/DDC2B/DPMS] +#define NV3_VBIOS_ERAZOR_V15500 "roms/video/nvidia/nv3/Ver15500.rv_" // ELSA VICTORY Erazor Ver. 1.55.00 [WD/VBE30/DDC2B/DPMS] +#define NV3_VBIOS_DIAMOND_V330_V162 "roms/video/nvidia/nv3/diamond_v330_rev-e.vbi" // Diamond Multimedia Systems, Inc. Viper V330 Version 1.62-CO +#define NV3_VBIOS_ASUS_V3000_V151 "roms/video/nvidia/nv3/riva128_asus.vbi" // ASUS AGP/3DP-V3000 BIOS 1.51B +#define NV3_VBIOS_STB_V128_V182 "roms/video/nvidia/nv3/riva128_stb.vbi" // STB Velocity 128 (RIVA 128) Ver.1.82 + +// Temporary, will be loaded from settings +#define VRAM_SIZE_2MB 0x200000 // 2MB +#define VRAM_SIZE_4MB 0x400000 // 4MB +#define VRAM_SIZE_8MB 0x800000 // NV3T only + + +// PCI config +#define NV3_PCI_CFG_VENDOR_ID 0x0 +#define NV3_PCI_CFG_DEVICE_ID 0x2 +#define NV3_PCI_CFG_CAPABILITIES 0x4 + +#define NV3_PCI_COMMAND_L_IO 1 +#define NV3_PCI_COMMAND_L_IO_ENABLED 0x1 +#define NV3_PCI_COMMAND_L_MEMORY 2 +#define NV3_PCI_COMMAND_L_MEMORY_ENABLED 0x1 + +#define NV3_PCI_COMMAND_H_FAST_BACK2BACK 0x01 + +#define NV3_PCI_STATUS_L_66MHZ_CAPABLE 0x20 +#define NV3_PCI_STATUS_H_DEVSEL_TIMING 5 +#define NV3_PCI_STATUS_H_FAST_DEVSEL_TIMING 0x00 + +#define NV3_PCI_CFG_REVISION 0x8 + +#define NV3_PCI_CFG_REVISION_A00 0x00 // nv3a January 1997 - engineering sample, had NV1 PAUDIO and other minor incompatibilities +#define NV3_PCI_CFG_REVISION_B00 0x10 // nv3b September 1997 +#define NV3_PCI_CFG_REVISION_C00 0x20 // todo: verify this - nv3c (nv3t?) / RIVA 128 ZX + +#define NV3_PCI_CFG_PROGRAMMING_INTERFACE 0x9 +#define NV3_PCI_CFG_SUBCLASS_CODE 0x0A +#define NV3_PCI_CFG_CLASS_CODE 0x0B +#define NV3_PCI_CFG_CLASS_CODE_VGA 0x03 + +#define NV3_PCI_CFG_CACHE_LINE_SIZE 0x0C +#define NV3_PCI_CFG_CACHE_LINE_SIZE_DEFAULT_FROM_VBIOS 0x40 + +#define NV3_PCI_CFG_LATENCY_TIMER 0x0D +#define NV3_PCI_CFG_HEADER_TYPE 0x0E +#define NV3_PCI_CFG_BIST 0x0F + +// PCI Bars +#define NV3_PCI_CFG_BAR_PREFETCHABLE 3 +#define NV3_PCI_CFG_BAR_PREFETCHABLE_ENABLED 0x1 + +#define NV3_PCI_CFG_BAR0_L 0x10 +#define NV3_PCI_CFG_BAR0_BYTE1 0x11 +#define NV3_PCI_CFG_BAR0_BYTE2 0x12 +#define NV3_PCI_CFG_BAR0_BASE_ADDRESS 0x13 +#define NV3_PCI_CFG_BAR1_L 0x14 +#define NV3_PCI_CFG_BAR1_BYTE1 0x15 +#define NV3_PCI_CFG_BAR1_BYTE2 0x16 +#define NV3_PCI_CFG_BAR1_BASE_ADDRESS 0x17 +#define NV3_PCI_CFG_BAR_INVALID_START 0x18 +#define NV3_PCI_CFG_BAR_INVALID_END 0x27 +#define NV3_PCI_CFG_SUBSYSTEM_ID 0x2C + +#define NV3_PCI_CFG_ENABLE_VBIOS 0x30 +#define NV3_PCI_CFG_VBIOS_BASE 0x32 ... 0x33 +#define NV3_PCI_CFG_VBIOS_BASE_L 0x32 +#define NV3_PCI_CFG_VBIOS_BASE_H 0x33 + +#define NV3_PCI_CFG_INT_LINE 0x3C +#define NV3_PCI_CFG_INT_PIN 0x3D + +#define NV3_PCI_CFG_SUBSYSTEM_ID_MIRROR_START 0x40 +#define NV3_PCI_CFG_SUBSYSTEM_ID_MIRROR_END 0x43 + +#define NV3_PCI_CFG_MIN_GRANT 0x3E +#define NV3_PCI_CFG_MIN_GRANT_DEFAULT 0x03 +#define NV3_PCI_CFG_MAX_LATENCY 0x3F +#define NV3_PCI_CFG_MAX_LATENCY_DEFAULT 0x01 + +// GPU Subsystems +// These most likely correspond to functional blocks in the original design + +#define NV3_PMC_START 0x0 // Chip Master Control Subsystem + +#define NV3_PMC_BOOT 0x0 // Boot Configuration +#define NV3_PMC_INTERRUPT_STATUS 0x100 // Interrupt Control +#define NV3_PMC_INTERRUPT_PAUDIO 0 // Unused, NV3A only +#define NV3_PMC_INTERRUPT_PAUDIO_PENDING 0x1 // Unused, NV3A only +#define NV3_PMC_INTERRUPT_PMEDIA 4 +#define NV3_PMC_INTERRUPT_PMEDIA_PENDING 0x1 +#define NV3_PMC_INTERRUPT_PFIFO 8 +#define NV3_PMC_INTERRUPT_PFIFO_PENDING 0x1 +#define NV3_PMC_INTERRUPT_PGRAPH0 12 +#define NV3_PMC_INTERRUPT_PGRAPH0_PENDING 0x1 +#define NV3_PMC_INTERRUPT_PGRAPH1 13 +#define NV3_PMC_INTERRUPT_PGRAPH1_PENDING 0x1 +#define NV3_PMC_INTERRUPT_PVIDEO 16 +#define NV3_PMC_INTERRUPT_PVIDEO_PENDING 0x1 +#define NV3_PMC_INTERRUPT_PTIMER 20 +#define NV3_PMC_INTERRUPT_PTIMER_PENDING 0x1 +#define NV3_PMC_INTERRUPT_PFB 24 +#define NV3_PMC_INTERRUPT_PFB_PENDING 0x1 +#define NV3_PMC_INTERRUPT_PBUS 28 +#define NV3_PMC_INTERRUPT_PBUS_PENDING 0x1 +#define NV3_PMC_INTERRUPT_SOFTWARE 31 +#define NV3_PMC_INTERRUPT_SOFTWARE_PENDING 0x1 +#define NV3_PMC_INTERRUPT_ENABLE 0x140 // Controls global interrupt enable state +#define NV3_PMC_INTERRUPT_ENABLE_HARDWARE 0x1 // Determines if hardware interrupts are enabled +#define NV3_PMC_INTERRUPT_ENABLE_SOFTWARE 0x2 // Determinse if software interrupts were enabled +#define NV3_PMC_ENABLE 0x200 // Determines which gpu subsystems were enabled + +#define NV3_PMC_END 0xfff // overlaps with CIO +#define NV3_CIO_START 0x3b0 // Legacy SVGA Emulation Subsystem +#define NV3_CIO_END 0x3df +#define NV3_PBUS_START 0x1000 // Bus Control Subsystem +#define NV3_PBUS_INTR 0x1100 // Bus Control - Interrupt Status +#define NV3_PBUS_INTR_EN 0x1140 // Bus Control - Interrupt Enable +#define NV3_PBUS_PCI_START 0x1800 // PCI mirror start +#define NV3_PBUS_PCI_END 0x18FF // PCI mirror end +#define NV3_PBUS_END 0x1FFF +#define NV3_PFIFO_START 0x2000 // FIFO for DMA Object Submission (uses hashtable to store the objects) +#define NV3_PFIFO_INTR 0x2100 // FIFO - Interrupt Status +#define NV3_PFIFO_INTR_EN 0x2140 // FIFO - Interrupt Enable +#define NV3_PFIFO_END 0x3FFF +#define NV3_PRM_START 0x4000 // Real-Mode Device Support Subsystem +#define NV3_PRM_INTR 0x4100 +#define NV3_PRM_INTR_EN 0x4140 +#define NV3_PRM_END 0x4FFF +#define NV3_PRAM_START 0x6000 // Local ram/cache? +#define NV3_PRAM_END 0x6FFF +#define NV3_PRMIO_START 0x7000 // Real-Mode I/O Subsystem +#define NV3_PRMIO_END 0x7FFF +#define NV3_PTIMER_START 0x9000 // Programmable Interval Timer +#define NV3_PTIMER_INTR 0x9100 +#define NV3_PTIMER_INTR_EN 0x9140 +#define NV3_PTIMER_END 0x9FFF +#define NV3_VGA_VRAM_START 0xA0000 // VGA Emulation VRAM +#define NV3_VGA_VRAM_END 0xBFFFF +#define NV3_VGA_START 0xC0000 // VGA Emulation Registers +#define NV3_VGA_END 0xC7FFF +#define NV3_PRMVIO_START NV3_VGA_START +#define NV3_PRMVIO_END NV3_VGA_END +#define NV3_PFB_START 0x100000 // GPU Interface to VRAM +#define NV3_PFB_BOOT 0x100000 // Boot registration +#define NV3_PFB_BOOT_RAM_AMOUNT 0 // The amount of ram +#define NV3_PFB_BOOT_RAM_AMOUNT_8MB 0x0 // 1mb in NV3A +#define NV3_PFB_BOOT_RAM_AMOUNT_2MB 0x1 +#define NV3_PFB_BOOT_RAM_AMOUNT_4MB 0x2 +#define NV3_PFB_BOOT_RAM_AMOUNT_UNDEFINED 0x3 // i assume this is used for debug +#define NV3_PFB_BOOT_RAM_WIDTH 2 // the bus width of the gpu's vram +#define NV3_PFB_BOOT_RAM_WIDTH_64 0x0 // 64bit +#define NV3_PFB_BOOT_RAM_WIDTH_128 0x1 // 128bit +#define NV3_PFB_BOOT_RAM_BANKS 3 // the number of banks +#define NV3_PFB_BOOT_RAM_BANKS_2 0x0 // 2 banks (seems to be used for 2mb) +#define NV3_PFB_BOOT_RAM_BANKS_4 0x1 // 4 banks (seems to be used for 4mb) +#define NV3_PFB_BOOT_RAM_DATA_TWIDDLE 4 +#define NV3_PFB_BOOT_RAM_DATA_TWIDDLE_OFF 0x0 +#define NV3_PFB_BOOT_RAM_DATA_TWIDDLE_ON 0x1 +#define NV3_PFB_BOOT_RAM_EXTENSION 5 +#define NV3_PFB_BOOT_RAM_EXTENSION_NONE 0x0 +#define NV3_PFB_BOOT_RAM_EXTENSION_8MB 0x1 +#define NV3_PFB_END 0x100FFF +#define NV3_PEXTDEV_START 0x101000 // External Devices +#define NV3_PSTRAPS 0x101000 // Straps Bits +#define NV3_PSTRAPS_BUS_SPEED 0 // Configured bus speed +#define NV3_PSTRAPS_BUS_SPEED_33MHZ 0x0 +#define NV3_PSTRAPS_BUS_SPEED_66MHZ 0x1 +#define NV3_PSTRAPS_BIOS 1 // Is a VBIOS present? +#define NV3_PSTRAPS_BIOS_NOT_PRESENT 0x0 +#define NV3_PSTRAPS_BIOS_PRESENT 1 +#define NV3_PSTRAPS_RAM_TYPE 2 // Type of RAM module +#define NV3_PSTRAPS_RAM_TYPE_16MBIT 0x0 +#define NV3_PSTRAPS_RAM_TYPE_8MBIT 0x1 +#define NV3_PSTRAPS_NEC_MODE 3 // PC98? +#define NV3_PSTRAPS_NEC_MODE_DISABLED 0x0 +#define NV3_PSTRAPS_NEC_MODE_ENABLED 0x1 +#define NV3_PSTRAPS_BUS_WIDTH 4 // Bus width +#define NV3_PSTRAPS_BUS_WIDTH_64BIT 0x0 +#define NV3_PSTRAPS_BUS_WIDTH_128BIT 0x0 +#define NV3_PSTRAPS_BUS_TYPE 5 // Determines if this is a PCI or AGP card +#define NV3_PSTRAPS_BUS_TYPE_PCI 0x0 +#define NV3_PSTRAPS_BUS_TYPE_AGP 0x1 +#define NV3_PSTRAPS_CRYSTAL 6 // type of clock crystal +#define NV3_PSTRAPS_CRYSTAL_13500K 0x0 // 13.5 Mhz +#define NV3_PSTRAPS_CRYSTAL_14318180 0x1 // 14.318180 Mhz clock crystal +#define NV3_PSTRAPS_TVMODE 7 // Type of TV signal to put out +#define NV3_PSTRAPS_TVMODE_SECAM 0x0 +#define NV3_PSTRAPS_TVMODE_NTSC 0x1 +#define NV3_PSTRAPS_TVMODE_PAL 0x2 +#define NV3_PSTRAPS_TVMODE_NONE 0x3 +#define NV3_PSTRAPS_AGP2X 9 +#define NV3_PSTRAPS_AGP2X_ENABLED 0x0 +#define NV3_PSTRAPS_AGP2X_DISABLED 0x1 +#define NV3_PSTRAPS_UNUSED 10 +#define NV3_PSTRAPS_OVERWRITE 11 +#define NV3_PSTRAPS_OVERWRITE_DISABLED 0x0 +#define NV3_PSTRAPS_OVERWRITE_ENABLED 0x1 +#define NV3_PEXTDEV_END 0x101FFF +#define NV3_PROM_START 0x110000 // VBIOS? +#define NV3_PROM_END 0x110FFF +#define NV3_PALT_START 0x120000 // ??? but it exists +#define NV3_PALT_END 0x120FFF +#define NV3_PME_START 0x200000 // Mediaport +#define NV3_PME_INTR 0x200100 // Mediaport: Interrupt Pending? +#define NV3_PME_INTR_EN 0x200140 // Mediaport: Interrupt Enable +#define NV3_PME_END 0x200FFF +#define NV3_PGRAPH_START 0x400000 // Scene graph for 2d/3d rendering...the most important part +#define NV3_PGRAPH_INTR_0 0x400100 +#define NV3_PGRAPH_INTR_1 0x400104 +#define NV3_PGRAPH_INTR_EN_0 0x400140 // Interrupt Control for PGRAPH #1 +//todo: add what this does +#define NV3_PGRAPH_INTR_EN_1 0x400180 // Interrupt Control for PGRAPH #2 (it can receive two at onc) + +// not sure about the class ids +// these are NOT what each class is, just uSed to manipulate it (there isn't a one to one class->reg mapping anyway) +#define NV3_PGRAPH_CLASS18_BETA_START 0x410000 // Beta blending factor +#define NV3_PGRAPH_CLASS18_BETA_END 0x411FFF +#define NV3_PGRAPH_CLASS20_ROP_START 0x420000 // Blending render operation used at final pixel/fragment generation stage +#define NV3_PGRAPH_CLASS20_ROP_END 0x421FFF +#define NV3_PGRAPH_CLASS21_COLORKEY_START 0x430000 // Color key for image +#define NV3_PGRAPH_CLASS21_COLORKEY_END 0x431FFF +#define NV3_PGRAPH_CLASS22_PLANEMASK_START 0x440000 // Plane mask (for clipping?) +#define NV3_PGRAPH_CLASS22_PLANEMASK_END 0x441FFF +#define NV3_PGRAPH_CLASSXX_CLIP_START 0x450000 // clipping, probably class 23 +#define NV3_PGRAPH_CLASSXX_CLIP_END 0x451FFF +#define NV3_PGRAPH_CLASS24_PATTERN_START 0x460000 // presumably a blend pattern +#define NV3_PGRAPH_CLASS24_PATTERN_END 0x461FFF +#define NV3_PGRAPH_CLASS30_RECTANGLE_START 0x470000 // also class 25 - that's black [NV1] +#define NV3_PGRAPH_CLASS30_RECTANGLE_END 0x471FFF // also class 25 - that's black [NV1] +#define NV3_PGRAPH_CLASS26_POINT_START 0x480000 // A single point +#define NV3_PGRAPH_CLASS26_POINT_END 0x481FFF +#define NV3_PGRAPH_CLASS27_LINE_START 0x490000 // A line +#define NV3_PGRAPH_CLASS27_LINE_END 0x491FFF +#define NV3_PGRAPH_CLASS28_LIN_START 0x4A0000 // A lin - a line without its starting or ending pixels +#define NV3_PGRAPH_CLASS28_LIN_END 0x4A1FFF +#define NV3_PGRAPH_CLASS29_TRIANGLE_START 0x4B0000 // A triangle [NV1 variant] - in NV1 this was converted to a quad patch +#define NV3_PGRAPH_CLASS29_TRIANGLE_END 0x4B1FFF +#define NV3_PGRAPH_CLASS75_GDITEXT_START 0x4C0000 // Windows 95/NT GDI text acceleration +#define NV3_PGRAPH_CLASS75_GDITEXT_END 0x4C1FFF + +#define NV3_PGRAPH_CLASS61_MEM2MEM_XFER_START 0x4D0000 // memory to memory transfer (not sure about which class this is) +#define NV3_PGRAPH_CLASS61_MEM2MEM_XFER_END 0x4D1FFF +#define NV3_PGRAPH_CLASSXX_IMAGE2MEM_XFER_SCALED_START 0x4E0000 // class 55, 56 +#define NV3_PGRAPH_CLASSXX_IMAGE2MEM_XFER_SCALED_END 0x4E1FFF + +#define NV3_PGRAPH_CLASS31_BLIT_START 0x500000 // Blit 2d image from memory +#define NV3_PGRAPH_CLASS31_BLIT_END 0x501FFF + +#define NV3_PGRAPH_CLASSXX_CPU2MEM_IMAGE_START 0x510000 // Used for class 33, 34, 54 +#define NV3_PGRAPH_CLASSXX_CPU2MEM_IMAGE_END 0x511FFF +#define NV3_PGRAPH_CLASSXX_CPU2MEM_BITMAP_START 0x520000 // not sure, might depend on format +#define NV3_PGRAPH_CLASSXX_CPU2MEM_BITMAP_END 0x521FFF + +#define NV3_PGRAPH_CLASSXX_IMAGE2MEM_XFER_START 0x540000 // send image to vram, not sure what class +#define NV3_PGRAPH_CLASSXX_IMAGE2MEM_XFER_END 0x541FFF +#define NV3_PGRAPH_CLASS54_CPU2MEM_STRETCHED_START 0x550000 // stretched cpu->vram transfer, 54 +#define NV3_PGRAPH_CLASS54_CPU2MEM_STRETCHED_END 0x551FFF + +#define NV3_PGRAPH_CLASS72_D3D5TRI_ZETA_START 0x570000 // [NV3] Copy a direct3d 5.0 accelerated triangle to the zeta buffer +#define NV3_PGRAPH_CLASS72_D3D5TRI_ZETA_END 0x571FFF +#define NV3_PGRAPH_CLASSXX_POINTZETA_START 0x580000 // possibly class 69 +#define NV3_PGRAPH_CLASSXX_POINTZETA_END 0x581FFF + +#define NV3_PGRAPH_CLASS62_MEM2IMAGE_START 0x5C0000 // class 55, 56, 62, 63? +#define NV3_PGRAPH_CLASS62_MEM2IMAGE_END 0x5C1FFF + +#define NV3_PGRAPH_REGISTER_END 0x401FFF // end of pgraph registers +#define NV3_PGRAPH_REAL_END 0x5C1FFF + +#define NV3_PRMCIO_START 0x601000 +#define NV3_PRMCIO_END 0x601FFF + +#define NV3_PDAC_START 0x680000 // OPTIONAL external DAC +#define NV3_PVIDEO_START 0x680000 // Video Generation / overlay configuration +#define NV3_PVIDEO_INTR 0x680100 +#define NV3_PVIDEO_INTR_EN 0x680140 +#define NV3_PVIDEO_END 0x6802FF +#define NV3_PRAMDAC_START 0x680300 + +#define NV3_PRAMDAC_CLOCK_MEMORY 0x680504 +#define NV3_PRAMDAC_CLOCK_MEMORY_VDIV 7:0 +#define NV3_PRAMDAC_CLOCK_MEMORY_NDIV 15:8 +#define NV3_PRAMDAC_CLOCK_MEMORY_PDIV 18:16 +#define NV3_PRAMDAC_CLOCK_PIXEL 0x680508 +#define NV3_PRAMDAC_COEFF_SELECT 0x68050C + +#define NV3_PRAMDAC_GENERAL_CONTROL 0x680600 + +// These are all 10-bit values, but aligned to 32bits +// so treating them as 32bit should be fine +#define NV3_PRAMDAC_VSERR_WIDTH 0x680700 +#define NV3_PRAMDAC_VEQU_END 0x680704 +#define NV3_PRAMDAC_VBBLANK_END 0x680708 +#define NV3_PRAMDAC_VBLANK_END 0x68070C +#define NV3_PRAMDAC_VBLANK_START 0x680710 +#define NV3_PRAMDAC_VBBLANK_START 0x680714 +#define NV3_PRAMDAC_VEQU_START 0x680718 +#define NV3_PRAMDAC_VTOTAL 0x68071C +#define NV3_PRAMDAC_HSYNC_WIDTH 0x680720 +#define NV3_PRAMDAC_HBURST_START 0x680724 +#define NV3_PRAMDAC_HBURST_END 0x680728 +#define NV3_PRAMDAC_HBLANK_START 0x68072C +#define NV3_PRAMDAC_HBLANK_END 0x680730 +#define NV3_PRAMDAC_HTOTAL 0x680734 +#define NV3_PRAMDAC_HEQU_WIDTH 0x680738 +#define NV3_PRAMDAC_HSERR_WIDTH 0x68073C + +#define NV3_PRAMDAC_END 0x680FFF +#define NV3_PDAC_END 0x680FFF // OPTIONAL external DAC + + +#define NV3_USER_START 0x800000 // Mapping for the area where objects are submitted into the FIFO +#define NV3_USER_END 0xFFFFFF + +// easier name +#define NV3_OBJECT_SUBMIT_START NV3_USER_START +#define NV3_OBJECT_SUBMIT_END NV3_USER_END + +// also PDFB (Debug Framebuffer?) +#define NV3_PNVM_START 0x1000000 // VRAM access (max 8MB) +#define NV3_PNVM_END 0x17FFFFF + +// to be less confusing - NVM = "NV Memory" +#define NV3_VRAM_START NV3_PNVM_START +#define NV3_VRAM_END NV3_PNVM_END + +// control structures for dma'd in graphics objects from pfifo +// these all have configurable sizes, define them here +#define NV3_PRAMIN_START 0x1C00000 + +#define NV3_PRAMIN_RAMHT_START 0x1C00000 // Hashtable for storing submitted objects +#define NV3_PRAMIN_RAMHT_END 0x1C00FFF +#define NV3_PRAMIN_RAMHT_SIZE_0 0xFFF +#define NV3_PRAMIN_RAMHT_SIZE_1 0x1FFF +#define NV3_PRAMIN_RAMHT_SIZE_2 0x3FFF +#define NV3_PRAMIN_RAMHT_SIZE_3 0x7FFF + +#define NV3_PRAMIN_RAMAU_START 0x1C01000 // Auxillary area +#define NV3_PRAMIN_RAMAU_END 0x1C01BFF +#define NV3_PRAMIN_RAMFC_START 0x1C01C00 // context for unused PFIFO DMA channels +#define NV3_PRAMIN_RAMFC_END 0x1C01DFF +#define NV3_PRAMIN_RAMFC_SIZE_0 0x1FF +#define NV3_PRAMIN_RAMFC_SIZE_1 0xFFF +#define NV3_PRAMIN_RAMRO_START 0x1C01E00 // Runout area for invalid submissions +#define NV3_PRAMIN_RAMRO_SIZE_0 0x1FF +#define NV3_PRAMIN_RAMRO_SIZE_1 0x1FFF +#define NV3_PRAMIN_RAMRO_END 0x1C01FFF +#define NV3_PRAMIN_RAMRM_START 0x1C02000 +#define NV3_PRAMIN_RAMRM_END 0x1C02FFF + +#define NV3_PRAMIN_END 0x1FFFFFF + +// not done + +// Master Control + +#define NV3_PMC_BOOT 0x0 +#define NV3_PMC_INTERRUPT 0x100 +#define NV3_PMC_INTERRUPT_ENABLE 0x140 +#define NV3_PMC_ENABLE 0x200 +#define NV3_PMC_ENABLE_PAUDIO 0 // UNUSED - PAudio removed in NV3 Stepping B0 +#define NV3_PMC_ENABLE_PAUDIO_ENABLED 0x1 // UNUSED - PAudio removed in NV3 Stepping B0 +#define NV3_PMC_ENABLE_PMEDIA 4 +#define NV3_PMC_ENABLE_PMEDIA_ENABLED 0x1 +#define NV3_PMC_ENABLE_PFIFO 8 +#define NV3_PMC_ENABLE_PFIFO_ENABLED 0x1 +#define NV3_PMC_ENABLE_PGRAPH 12 +#define NV3_PMC_ENABLE_PGRAPH_ENABLED 0x1 +#define NV3_PMC_ENABLE_PPMI 16 +#define NV3_PMC_ENABLE_PPMI_ENABLED 0x1 +#define NV3_PMC_ENABLE_PFB 20 +#define NV3_PMC_ENABLE_PFB_ENABLED 0x1 +#define NV3_PMC_ENABLE_PCRTC 24 +#define NV3_PMC_ENABLE_PCRTC_ENABLED 0x1 +#define NV3_PMC_ENABLE_PVIDEO 28 +#define NV3_PMC_ENABLE_PVIDEO_ENABLED 0x1 + +// CRTC/CIO (0x3b0-0x3df) + +#define NV3_CRTC_DATA_OUT 0x3C0 +#define NV3_CRTC_MISCOUT 0x3C2 + +// These are standard (0-18h) +#define NV3_CRTC_REGISTER_HTOTAL 0x00 +#define NV3_CRTC_REGISTER_HDISPEND 0x01 +#define NV3_CRTC_REGISTER_HBLANKSTART 0x02 +#define NV3_CRTC_REGISTER_HBLANKEND 0x03 +#define NV3_CRTC_REGISTER_HRETRACESTART 0x04 +#define NV3_CRTC_REGISTER_HRETRACEEND 0x05 +#define NV3_CRTC_REGISTER_VTOTAL 0x06 +#define NV3_CRTC_REGISTER_OVERFLOW 0x07 +#define NV3_CRTC_REGISTER_PRESETROWSCAN 0x08 +#define NV3_CRTC_REGISTER_MAXSCAN 0x09 +#define NV3_CRTC_REGISTER_CURSOR_START 0x0A +#define NV3_CRTC_REGISTER_CURSOR_END 0x0B +#define NV3_CRTC_REGISTER_STARTADDR_HIGH 0x0C +#define NV3_CRTC_REGISTER_STARTADDR_LOW 0x0D +#define NV3_CRTC_REGISTER_CURSORLOCATION_HIGH 0x0E +#define NV3_CRTC_REGISTER_CURSORLOCATION_LOW 0x0F +#define NV3_CRTC_REGISTER_VRETRACESTART 0x10 +#define NV3_CRTC_REGISTER_VRETRACEEND 0x11 +#define NV3_CRTC_REGISTER_VDISPEND 0x12 +#define NV3_CRTC_REGISTER_OFFSET 0x13 +#define NV3_CRTC_REGISTER_UNDERLINELOCATION 0x14 +#define NV3_CRTC_REGISTER_STARTVBLANK 0x15 +#define NV3_CRTC_REGISTER_ENDVBLANK 0x16 +#define NV3_CRTC_REGISTER_CRTCCONTROL 0x17 +#define NV3_CRTC_REGISTER_LINECOMP 0x18 +#define NV3_CRTC_REGISTER_STANDARDVGA_END 0x18 + + +// These are nvidia (25-63) +#define NV3_CRTC_REGISTER_RPC0 0x19 // What does this mean? +#define NV3_CRTC_REGISTER_RPC1 0x1A // What does this mean? +#define NV3_CRTC_REGISTER_READ_BANK 0x1D +#define NV3_CRTC_REGISTER_WRITE_BANK 0x1E +#define NV3_CRTC_REGISTER_FORMAT 0x25 +#define NV3_CRTC_REGISTER_FORMAT_VDT10 0 // Use 10 bit vtotal value instead of 8 bit +#define NV3_CRTC_REGISTER_FORMAT_VDE10 1 // Use 10 bit dispend value instead of 8 bit +#define NV3_CRTC_REGISTER_FORMAT_VRS10 2 // Use 10 bit vblank start value instead of 8 bit +#define NV3_CRTC_REGISTER_FORMAT_VBS10 3 // Use 10 bit vsync start value instead of 8 bit +#define NV3_CRTC_REGISTER_FORMAT_HBE6 4 // Use 6 bit hsync start value +#define NV3_CRTC_REGISTER_PIXELMODE 0x28 + +#define NV3_CRTC_REGISTER_HEB 0x2D // HRS most significant bit + +#define NV3_CRTC_REGISTER_PIXELMODE_VGA 0x00 // vga textmode +#define NV3_CRTC_REGISTER_PIXELMODE_8BPP 0x01 +#define NV3_CRTC_REGISTER_PIXELMODE_16BPP 0x02 +#define NV3_CRTC_REGISTER_PIXELMODE_32BPP 0x03 + +#define NV3_CRTC_REGISTER_RMA 0x38 // REAL MODE ACCESS! + +// where the fuck is GDC? +#define NV3_CRTC_BANKED_128K_A0000 0x00 +#define NV3_CRTC_BANKED_64K_A0000 0x04 +#define NV3_CRTC_BANKED_32K_B0000 0x08 +#define NV3_CRTC_BANKED_32K_B8000 0x0C + + +#define NV3_RMA_REGISTER_START 0x3D0 +#define NV3_RMA_REGISTER_END 0x3D3 + +#define NV3_CRTC_REGISTER_NVIDIA_END 0x3F +// for 86box 8bit addressing +// get rid of this asap, replace with 32->8 macros +#define NV3_RMA_SIGNATURE_MSB 0x65 +#define NV3_RMA_SIGNATURE_BYTE2 0xD0 +#define NV3_RMA_SIGNATURE_BYTE1 0x16 +#define NV3_RMA_SIGNATURE_LSB 0x2B + +#define NV3_CRTC_REGISTER_RMA_MODE_MAX 0x0F + + +//todo: pixel format + diff --git a/src/include/86box/video.h b/src/include/86box/video.h index 9ee710592..f9cf9cfec 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -589,6 +589,8 @@ extern const device_t voodoo_3_3500_se_agp_device; extern const device_t voodoo_3_3500_si_agp_device; extern const device_t velocity_100_agp_device; extern const device_t velocity_200_agp_device; +extern const device_t nv3_device_pci; +extern const device_t nv3_device_agp; /* Wyse 700 */ extern const device_t wy700_device; diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index bbd329b7e..1aec0e98d 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -27,7 +27,21 @@ 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_base.c + nv/nv3/nv3_core.c nv/nv3/nv3_core_arbiter.c nv/nv3/nv3_interrupt.c + nv/nv3/subsystems/nv3_pramdac.c + nv/nv3/subsystems/nv3_pfifo.c + nv/nv3/subsystems/nv3_pgraph.c + nv/nv3/subsystems/nv3_pmc.c + nv/nv3/subsystems/nv3_pme.c + nv/nv3/subsystems/nv3_pextdev.c + nv/nv3/subsystems/nv3_pfb.c + nv/nv3/subsystems/nv3_pbus.c nv/nv3/subsystems/nv3_pbus_dma.c + nv/nv3/subsystems/nv3_ptimer.c + nv/nv3/subsystems/nv3_pramin.c nv/nv3/subsystems/nv3_pramin_ramht.c nv/nv3/subsystems/nv3_pramin_ramfc.c nv/nv3/subsystems/nv3_pramin_ramro.c + nv/nv3/subsystems/nv3_pvideo.c + ) if(G100) target_compile_definitions(vid PRIVATE USE_G100) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c new file mode 100644 index 000000000..3b4394561 --- /dev/null +++ b/src/video/nv/nv3/nv3_core.c @@ -0,0 +1,837 @@ +/* + * 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. + * + * NV3 bringup and device emulation. + * + * Notes: + * xfree86 ref has INVERTED bit numbering? What? + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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> // DEPENDENT!!! +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> + +nv3_t* nv3; + +// Prototypes for functions only used in this translation unit +void nv3_init_mappings_mmio(); +void nv3_init_mappings_svga(); +void nv3_shutdown_mappings_mmio(); +void nv3_shutdown_mappings_svga(); + +uint8_t nv3_svga_in(uint16_t addr, void* priv); +void nv3_svga_out(uint16_t addr, uint8_t val, void* priv); + +// All MMIO regs are 32-bit i believe internally +// so we have to do some munging to get this to read + +// Read 8-bit MMIO +uint8_t nv3_mmio_read8(uint32_t addr, void* priv) +{ + // see if unaligned reads are a problem + uint32_t ret = nv3_mmio_read32(addr, priv); + return (uint8_t)(ret >> ((addr & 3) << 3) & 0xFF); +} + +// Read 16-bit MMIO +uint16_t nv3_mmio_read16(uint32_t addr, void* priv) +{ + uint32_t ret = nv3_mmio_read32(addr, priv); + return (uint8_t)(ret >> ((addr & 3) << 3) & 0xFFFF); +} + +// Read 32-bit MMIO +uint32_t nv3_mmio_read32(uint32_t addr, void* priv) +{ + return nv3_mmio_arbitrate_read(addr); +} + +// Write 8-bit MMIO +void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) +{ + // overwrite first 8bits of a 32 bit value + uint32_t new_val = nv3_mmio_read32(addr, NULL); + + new_val &= (~0xFF << (addr & 3) << 3); + new_val |= (val << ((addr & 3) << 3)); + + nv3_mmio_write32(addr, new_val, priv); +} + +// Write 16-bit MMIO +void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) +{ + // overwrite first 16bits of a 32 bit value + uint32_t new_val = nv3_mmio_read32(addr, NULL); + + new_val &= (~0xFFFF << (addr & 3) << 3); + new_val |= (val << ((addr & 3) << 3)); + + nv3_mmio_write32(addr, new_val, priv); +} + +// Write 32-bit MMIO +void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) +{ + nv3_mmio_arbitrate_write(addr, val); +} + +// PCI stuff +// BAR0 Pointer to MMIO space +// BAR1 Pointer to Linear Framebuffer (NV_USER) + +uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) +{ + uint8_t ret = 0x00; + + // figure out what size this gets read as first + // seems func does not matter at least here? + switch (addr) + { + // Get the pci vendor id.. + + case NV3_PCI_CFG_VENDOR_ID: + ret = (PCI_VENDOR_SGS_NV & 0xFF); + break; + + case NV3_PCI_CFG_VENDOR_ID + 1: // all access 8bit + ret = (PCI_VENDOR_SGS_NV >> 8); + break; + + // device id + + case NV3_PCI_CFG_DEVICE_ID: + ret = (PCI_DEVICE_NV3 & 0xFF); + break; + + case NV3_PCI_CFG_DEVICE_ID+1: + ret = (PCI_DEVICE_NV3 >> 8); + break; + + // various capabilities + // IO space enabled + // Memory space enabled + // Bus master enabled + // Write/inval enabled + // Pal snoop enabled + // Capabiliies list enabled + // 66Mhz FSB capable + + case PCI_REG_COMMAND_L: + ret = nv3->pci_config.pci_regs[PCI_REG_COMMAND_L]; // we actually respond to the fucking + break; + + case PCI_REG_COMMAND_H: + ret = nv3->pci_config.pci_regs[PCI_REG_COMMAND_H] | NV3_PCI_COMMAND_H_FAST_BACK2BACK; // always enable fast back2back + break; + + // pci status register + case PCI_REG_STATUS_L: + if (nv3->pextdev.straps + & NV3_PSTRAPS_BUS_SPEED_66MHZ) + ret = (nv3->pci_config.pci_regs[PCI_REG_STATUS_L] | NV3_PCI_STATUS_L_66MHZ_CAPABLE); + else + ret = nv3->pci_config.pci_regs[PCI_REG_STATUS_L]; + + break; + + case PCI_REG_STATUS_H: + ret = (nv3->pci_config.pci_regs[PCI_REG_STATUS_H]) & (NV3_PCI_STATUS_H_FAST_DEVSEL_TIMING << NV3_PCI_STATUS_H_DEVSEL_TIMING); + break; + + case NV3_PCI_CFG_REVISION: + ret = NV3_PCI_CFG_REVISION_B00; // Commercial release + break; + + case PCI_REG_PROG_IF: + ret = 0x00; + break; + + case NV3_PCI_CFG_SUBCLASS_CODE: + ret = 0x00; // nothing + break; + + case NV3_PCI_CFG_CLASS_CODE: + ret = NV3_PCI_CFG_CLASS_CODE_VGA; // CLASS_CODE_VGA + break; + + case NV3_PCI_CFG_CACHE_LINE_SIZE: + ret = NV3_PCI_CFG_CACHE_LINE_SIZE_DEFAULT_FROM_VBIOS; + break; + + case NV3_PCI_CFG_LATENCY_TIMER: + case NV3_PCI_CFG_HEADER_TYPE: + case NV3_PCI_CFG_BIST: + ret = 0x00; + break; + + // BARs are marked as prefetchable per the datasheet + case NV3_PCI_CFG_BAR0_L: + case NV3_PCI_CFG_BAR1_L: + // only bit that matters is bit 3 (prefetch bit) + ret =(NV3_PCI_CFG_BAR_PREFETCHABLE_ENABLED << NV3_PCI_CFG_BAR_PREFETCHABLE); + break; + + // These registers are hardwired to zero per the datasheet + // Writes have no effect, we can just handle it here though + case NV3_PCI_CFG_BAR0_BYTE1 ... NV3_PCI_CFG_BAR0_BYTE2: + case NV3_PCI_CFG_BAR1_BYTE1 ... NV3_PCI_CFG_BAR1_BYTE2: + ret = 0x00; + break; + + // MMIO base address + case NV3_PCI_CFG_BAR0_BASE_ADDRESS: + ret = nv3->nvbase.bar0_mmio_base >> 24;//8bit value + break; + + case NV3_PCI_CFG_BAR1_BASE_ADDRESS: + ret = nv3->nvbase.bar1_lfb_base >> 24; //8bit value + break; + + case NV3_PCI_CFG_ENABLE_VBIOS: + ret = nv3->pci_config.vbios_enabled; + break; + + case NV3_PCI_CFG_INT_LINE: + ret = nv3->pci_config.int_line; + break; + + case NV3_PCI_CFG_INT_PIN: + ret = PCI_INTA; + break; + + case NV3_PCI_CFG_MIN_GRANT: + ret = NV3_PCI_CFG_MIN_GRANT_DEFAULT; + break; + + case NV3_PCI_CFG_MAX_LATENCY: + ret = NV3_PCI_CFG_MAX_LATENCY_DEFAULT; + break; + + //bar2-5 are not used and hardwired to 0 + case NV3_PCI_CFG_BAR_INVALID_START ... NV3_PCI_CFG_BAR_INVALID_END: + ret = 0x00; + break; + + case NV3_PCI_CFG_SUBSYSTEM_ID_MIRROR_START: + case NV3_PCI_CFG_SUBSYSTEM_ID_MIRROR_END: + ret = nv3->pci_config.pci_regs[NV3_PCI_CFG_SUBSYSTEM_ID + (addr & 0x03)]; + break; + + default: // by default just return pci_config.pci_regs + ret = nv3->pci_config.pci_regs[addr]; + break; + + } + + nv_log("NV3: nv3_pci_read func=0x%04x addr=0x%04x ret=0x%04x\n", func, addr, ret); + return ret; +} + +void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) +{ + + // TOTAL IRRELEVANCY + + // some addresses are not writable so can't have any effect and can't be allowed to be modified using this code + // only the most significant byte of the PCI BARs can be modified + if (addr >= NV3_PCI_CFG_BAR0_L && addr <= NV3_PCI_CFG_BAR0_BYTE2 + && addr >= NV3_PCI_CFG_BAR1_L && addr <= NV3_PCI_CFG_BAR1_BYTE2) + return; + + nv_log("NV3: nv3_pci_write func=0x%04x addr=0x%04x val=0x%04x\n", func, addr, val); + + nv3->pci_config.pci_regs[addr] = val; + + switch (addr) + { + // standard pci command stuff + case PCI_REG_COMMAND_L: + nv3->pci_config.pci_regs[PCI_REG_COMMAND_L] = val; + // actually update the mappings + nv3_update_mappings(); + break; + // pci status register + case PCI_REG_STATUS_L: + nv3->pci_config.pci_regs[PCI_REG_STATUS_L] = val | (NV3_PCI_STATUS_L_66MHZ_CAPABLE); + break; + case PCI_REG_STATUS_H: + nv3->pci_config.pci_regs[PCI_REG_STATUS_H] = val | (NV3_PCI_STATUS_H_FAST_DEVSEL_TIMING << NV3_PCI_STATUS_H_DEVSEL_TIMING); + break; + //TODO: ACTUALLY REMAP THE MMIO AND NV_USER + case NV3_PCI_CFG_BAR0_BASE_ADDRESS: + nv3->nvbase.bar0_mmio_base = val << 24; + nv3_update_mappings(); + break; + case NV3_PCI_CFG_BAR1_BASE_ADDRESS: + nv3->nvbase.bar1_lfb_base = val << 24; + nv3_update_mappings(); + break; + case NV3_PCI_CFG_ENABLE_VBIOS: + case NV3_PCI_CFG_VBIOS_BASE: + + // make sure we are actually toggling the vbios, not the rom base + if (addr == NV3_PCI_CFG_ENABLE_VBIOS) + nv3->pci_config.vbios_enabled = (val & 0x01); + + if (nv3->pci_config.vbios_enabled) + { + // First see if we simply wanted to change the VBIOS location + + // Enable it in case it was disabled before + mem_mapping_enable(&nv3->nvbase.vbios.mapping); + + if (addr != NV3_PCI_CFG_ENABLE_VBIOS) + { + uint32_t old_addr = nv3->nvbase.vbios.mapping.base; + // 9bit register + uint32_t new_addr = nv3->pci_config.pci_regs[NV3_PCI_CFG_VBIOS_BASE_H] << 24 | + nv3->pci_config.pci_regs[NV3_PCI_CFG_VBIOS_BASE_L] << 16; + + // move it + mem_mapping_set_addr(&nv3->nvbase.vbios.mapping, new_addr, 0x8000); + + nv_log("...i like to move it move it (VBIOS Relocation) 0x%04x -> 0x%04x\n", old_addr, new_addr); + + } + else + { + nv_log("...VBIOS Enable\n"); + } + } + else + { + nv_log("...VBIOS Disable\n"); + mem_mapping_disable(&nv3->nvbase.vbios.mapping); + + } + break; + case NV3_PCI_CFG_INT_LINE: + nv3->pci_config.int_line = val; + break; + //bar2-5 are not used and can't be written to + case NV3_PCI_CFG_BAR_INVALID_START ... NV3_PCI_CFG_BAR_INVALID_END: + break; + + // these are mirrored to the subsystem id and also stored in the ROMBIOS + case NV3_PCI_CFG_SUBSYSTEM_ID_MIRROR_START: + case NV3_PCI_CFG_SUBSYSTEM_ID_MIRROR_END: + nv3->pci_config.pci_regs[NV3_PCI_CFG_SUBSYSTEM_ID + (addr & 0x03)] = val; + break; + + default: + + } +} + +void nv3_close(void* priv) +{ + svga_close(&nv3->nvbase.svga); + free(nv3); +} + + +// +// SVGA functions +// +void nv3_recalc_timings(svga_t* svga) +{ + nv3_t* nv3 = (nv3_t*)svga->priv; + + svga->ma_latch += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16; + svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 3; + + // should these actually use separate values? + // i don't we should force the top 2 bits to 1... + + // required for VESA resolutions, force parameters higher + if (svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 1 << (NV3_CRTC_REGISTER_FORMAT_VDT10)) svga->vtotal += 0x400; + if (svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 1 << (NV3_CRTC_REGISTER_FORMAT_VDE10)) svga->dispend += 0x400; + if (svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 1 << (NV3_CRTC_REGISTER_FORMAT_VRS10)) svga->vblankstart += 0x400; + if (svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 1 << (NV3_CRTC_REGISTER_FORMAT_VBS10)) svga->vsyncstart += 0x400; + if (svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 1 << (NV3_CRTC_REGISTER_FORMAT_HBE6)) svga->hdisp += 0x400; + + if (svga->crtc[NV3_CRTC_REGISTER_HEB] & 0x01) + svga->hdisp += 0x100; // large screen bit + + // Set the pixel mode + switch (svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 0x03) + { + ///0x0 is VGA textmode + case NV3_CRTC_REGISTER_PIXELMODE_8BPP: + svga->bpp = 8; + svga->lowres = 0; + svga->render = svga_render_8bpp_highres; + break; + case NV3_CRTC_REGISTER_PIXELMODE_16BPP: + svga->bpp = 16; + svga->lowres = 0; + svga->render = svga_render_16bpp_highres; + break; + case NV3_CRTC_REGISTER_PIXELMODE_32BPP: + svga->bpp = 32; + svga->lowres = 0; + svga->render = svga_render_32bpp_highres; + break; + } + + // from nv_riva128 + if (((svga->miscout >> 2) & 2) == 2) + { + // set clocks + nv3_pramdac_set_pixel_clock(); + nv3_pramdac_set_vram_clock(); + } +} + +void nv3_speed_changed(void* priv) +{ + nv3_recalc_timings(&nv3->nvbase.svga); +} + +// Force Redraw +// Reset etc. +void nv3_force_redraw(void* priv) +{ + nv3->nvbase.svga.fullchange = changeframecount; +} + +// Read from SVGA core memory +uint8_t nv3_svga_in(uint16_t addr, void* priv) +{ + nv3_t* nv3 = (nv3_t*)priv; + + uint8_t ret = 0x00; + + // If we need to RMA from GPU MMIO, go do that + if (addr >= NV3_RMA_REGISTER_START + && addr <= NV3_RMA_REGISTER_END) + { + if (!(nv3->pbus.rma.mode & 0x01)) + return ret; + + // must be dword aligned + uint32_t real_rma_read_addr = ((nv3->pbus.rma.mode & NV3_CRTC_REGISTER_RMA_MODE_MAX - 1) << 1) + (addr & 0x03); + ret = nv3_pbus_rma_read(real_rma_read_addr); + return ret; + } + + // mask off b0/d0 registers + if ((((addr & 0xFFF0) == 0x3D0 + || (addr & 0xFFF0) == 0x3B0) && addr < 0x3de) + && !(nv3->nvbase.svga.miscout & 1)) + addr ^= 0x60; + + switch (addr) + { + // Alias for "get current SVGA CRTC register ID" + case 0x3D4: + ret = nv3->nvbase.svga.crtcreg; + break; + case 0x3D5: + // Support the extended NVIDIA CRTC register range + switch (nv3->nvbase.svga.crtcreg) + { + default: + ret = nv3->nvbase.svga.crtc[nv3->nvbase.svga.crtcreg]; + } + break; + default: + ret = svga_in(addr, &nv3->nvbase.svga); + break; + } + + return ret; //TEMP +} + +// Write to SVGA core memory +void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) +{ + + // If we need to RMA to GPU MMIO, go do that + if (addr >= NV3_RMA_REGISTER_START + && addr <= NV3_RMA_REGISTER_END) + { + // we don't need to store these registers... + nv3->pbus.rma.rma_regs[addr & 3] = val; + + if (!(nv3->pbus.rma.mode & 0x01)) // we are halfway through sending something + return; + + uint32_t real_rma_write_addr = ((nv3->pbus.rma.mode & (NV3_CRTC_REGISTER_RMA_MODE_MAX - 1)) << 1) + (addr & 0x03); + + nv3_pbus_rma_write(real_rma_write_addr, nv3->pbus.rma.rma_regs[addr & 3]); + return; + } + + // mask off b0/d0 registers + if ((((addr & 0xFFF0) == 0x3D0 || (addr & 0xFFF0) == 0x3B0) + && addr < 0x3de) + && !(nv3->nvbase.svga.miscout & 1))//miscout bit 7 controls mappping + addr ^= 0x60; + + uint8_t crtcreg = nv3->nvbase.svga.crtcreg; + uint8_t old_value; + + // todo: + // RMA + // Pixel formats (8bit vs 555 vs 565) + // VBE 3.0? + + switch (addr) + { + case 0x3D4: + // real mode access to GPU MMIO space... + nv3->nvbase.svga.crtcreg = val; + break; + // support the extended crtc regs and debug this out + case 0x3D5: + + // Implements the VGA Protect register + if ((nv3->nvbase.svga.crtcreg < NV3_CRTC_REGISTER_OVERFLOW) && (nv3->nvbase.svga.crtc[0x11] & 0x80)) + return; + + // Ignore certain bits when VGA Protect register set and we are writing to CRTC register=07h + if ((nv3->nvbase.svga.crtcreg == NV3_CRTC_REGISTER_OVERFLOW) && (nv3->nvbase.svga.crtc[0x11] & 0x80)) + val = (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_OVERFLOW] & ~0x10) | (val & 0x10); + + // set the register value... + nv3->nvbase.svga.crtc[crtcreg] = val; + // ...now act on it + + // Handle nvidia extended Bank0/Bank1 IDs + switch (crtcreg) + { + case NV3_CRTC_REGISTER_READ_BANK: + nv3->nvbase.cio_read_bank = val; + if (nv3->nvbase.svga.chain4) // chain4 addressing (planar?) + nv3->nvbase.svga.read_bank = nv3->nvbase.cio_read_bank << 15; + else + nv3->nvbase.svga.read_bank = nv3->nvbase.cio_read_bank << 13; // extended bank numbers + break; + case NV3_CRTC_REGISTER_WRITE_BANK: + nv3->nvbase.cio_write_bank = val; + if (nv3->nvbase.svga.chain4) + nv3->nvbase.svga.write_bank = nv3->nvbase.cio_write_bank << 15; + else + nv3->nvbase.svga.write_bank = nv3->nvbase.cio_write_bank << 13; + break; + case NV3_CRTC_REGISTER_RMA: + nv3->pbus.rma.mode = val & NV3_CRTC_REGISTER_RMA_MODE_MAX; + break; + } + + break; + default: + svga_out(addr, val, &nv3->nvbase.svga); + break; + } + +} + +void nv3_draw_cursor(svga_t* svga, int32_t drawline) +{ + nv_log("nv3_draw_cursor drawline=0x%04x", drawline); +} + +// Initialise the MMIO mappings +void nv3_init_mappings_mmio() +{ + nv_log("NV3: Initialising 32MB MMIO area\n"); + + // 0x0 - 1000000: regs + // 0x1000000-2000000 + + // initialize the mmio mapping + mem_mapping_add(&nv3->nvbase.mmio_mapping, 0, 0, + nv3_mmio_read8, + nv3_mmio_read16, + nv3_mmio_read32, + nv3_mmio_write8, + nv3_mmio_write16, + nv3_mmio_write32, + NULL, MEM_MAPPING_EXTERNAL, nv3); + + // initialize the mmio mapping + mem_mapping_add(&nv3->nvbase.ramin_mapping, 0, 0, + nv3_ramin_read8, + nv3_ramin_read16, + nv3_ramin_read32, + nv3_ramin_write8, + nv3_ramin_write16, + nv3_ramin_write32, + NULL, MEM_MAPPING_EXTERNAL, nv3); + + + mem_mapping_add(&nv3->nvbase.ramin_mapping_mirror, 0, 0, + nv3_ramin_read8, + nv3_ramin_read16, + nv3_ramin_read32, + nv3_ramin_write8, + nv3_ramin_write16, + nv3_ramin_write32, + NULL, MEM_MAPPING_EXTERNAL, nv3); + + +} + +void nv3_init_mappings_svga() +{ + nv_log("NV3: Initialising SVGA core memory mapping\n"); + + // setup the svga mappings + mem_mapping_set(&nv3->nvbase.framebuffer_mapping, 0, 0, + svga_read_linear, + svga_readw_linear, + svga_readl_linear, + svga_write_linear, + svga_writew_linear, + svga_writel_linear, + NULL, 0, &nv3->nvbase.svga); + + // the SVGA/LFB mapping is also mirrored + mem_mapping_set(&nv3->nvbase.framebuffer_mapping_mirror, 0, 0, + svga_read_linear, + svga_readw_linear, + svga_readl_linear, + svga_write_linear, + svga_writew_linear, + svga_writel_linear, + NULL, 0, &nv3->nvbase.svga); + + io_sethandler(0x03c0, 0x0020, + nv3_svga_in, NULL, NULL, + nv3_svga_out, NULL, NULL, + nv3); +} + +void nv3_init_mappings() +{ + nv3_init_mappings_mmio(); + nv3_init_mappings_svga(); +} + +// Updates the mappings after initialisation. +void nv3_update_mappings() +{ + + // setting this to 0 doesn't seem to disable it, based on the datasheet + + nv_log("\nMemory Mapping Config Change:\n"); + + (nv3->pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) ? nv_log("Enable I/O\n") : nv_log("Disable I/O\n"); + + io_removehandler(0x03c0, 0x0020, + nv3_svga_in, NULL, NULL, + nv3_svga_out, NULL, NULL, + nv3); + + if (nv3->pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) + io_sethandler(0x03c0, 0x0020, + nv3_svga_in, NULL, NULL, + nv3_svga_out, NULL, NULL, + nv3); + + // turn off bar0 and bar1 by defualt + mem_mapping_disable(&nv3->nvbase.mmio_mapping); + mem_mapping_disable(&nv3->nvbase.framebuffer_mapping); + mem_mapping_disable(&nv3->nvbase.framebuffer_mapping_mirror); + mem_mapping_disable(&nv3->nvbase.ramin_mapping); + mem_mapping_disable(&nv3->nvbase.ramin_mapping_mirror); + + if (!(nv3->pci_config.pci_regs[PCI_REG_COMMAND]) & PCI_COMMAND_MEM) + { + nv_log("NV3: The memory was turned off, not much is going to happen.\n"); + return; + } + + mem_mapping_enable(&nv3->nvbase.mmio_mapping); + mem_mapping_enable(&nv3->nvbase.framebuffer_mapping); + mem_mapping_enable(&nv3->nvbase.framebuffer_mapping_mirror); + mem_mapping_enable(&nv3->nvbase.ramin_mapping); + mem_mapping_enable(&nv3->nvbase.ramin_mapping_mirror); + + // first map bar0 + + nv_log("NV3: BAR0 (MMIO Base) = 0x%08x\n", nv3->nvbase.bar0_mmio_base); + + //mem_mapping_enable(&nv3->nvbase.mmio_mapping); // should have no effect if already enabled + + mem_mapping_set_addr(&nv3->nvbase.mmio_mapping, nv3->nvbase.bar0_mmio_base, NV3_MMIO_SIZE); + + + // if this breaks anything, remove it + // skeptical that 0 is used to disable... + nv_log("NV3: BAR1 (Linear Framebuffer / NV_USER Base & RAMIN) = 0x%08x\n", nv3->nvbase.bar1_lfb_base); + + // this is likely mirrored + // 4x on 2mb cards + // 2x on 4mb cards + // and not at all on 8mb + mem_mapping_enable(&nv3->nvbase.framebuffer_mapping); + mem_mapping_enable(&nv3->nvbase.framebuffer_mapping_mirror); + mem_mapping_enable(&nv3->nvbase.ramin_mapping); + mem_mapping_enable(&nv3->nvbase.ramin_mapping_mirror); + + mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping, nv3->nvbase.bar1_lfb_base, NV3_MMIO_SIZE); + // 4MB VRAM memory map: + // LFB_BASE+VRAM_SIZE=RAMIN Mirror(?) 0x1400000 (VERIFY PCBOX) + // LFB_BASE+VRAM_SIZE*2=LFB Mirror(?) 0x1800000 + // LFB_BASE+VRAM_SIZE*3=Definitely RAMIN (then it ends, the total ram space is 16mb) 0x1C00000 + mem_mapping_set_addr(&nv3->nvbase.ramin_mapping_mirror, nv3->nvbase.bar1_lfb_base + VRAM_SIZE_4MB, VRAM_SIZE_4MB); + mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping_mirror, nv3->nvbase.bar1_lfb_base + (VRAM_SIZE_4MB * 2), VRAM_SIZE_4MB); + mem_mapping_set_addr(&nv3->nvbase.ramin_mapping, nv3->nvbase.bar1_lfb_base + (VRAM_SIZE_4MB * 3), VRAM_SIZE_4MB); + // TODO: RAMIN and its mirror + + // Did we change the banked SVGA mode? + switch (nv3->nvbase.svga.gdcreg[0x06] & 0x0c) + { + case NV3_CRTC_BANKED_128K_A0000: + nv_log("NV3: SVGA Banked Mode = 128K @ A0000h\n"); + mem_mapping_set_addr(&nv3->nvbase.svga.mapping, 0xA0000, 0x20000); // 128kb @ 0xA0000 + nv3->nvbase.svga.banked_mask = 0x1FFFF; + break; + case NV3_CRTC_BANKED_64K_A0000: + nv_log("NV3: SVGA Banked Mode = 64K @ A0000h\n"); + mem_mapping_set_addr(&nv3->nvbase.svga.mapping, 0xA0000, 0x10000); // 64kb @ 0xA0000 + nv3->nvbase.svga.banked_mask = 0xFFFF; + break; + case NV3_CRTC_BANKED_32K_B0000: + nv_log("NV3: SVGA Banked Mode = 32K @ B0000h\n"); + mem_mapping_set_addr(&nv3->nvbase.svga.mapping, 0xB0000, 0x8000); // 32kb @ 0xB0000 + nv3->nvbase.svga.banked_mask = 0x7FFF; + break; + case NV3_CRTC_BANKED_32K_B8000: + nv_log("NV3: SVGA Banked Mode = 32K @ B8000h\n"); + mem_mapping_set_addr(&nv3->nvbase.svga.mapping, 0xB8000, 0x8000); // 32kb @ 0xB8000 + nv3->nvbase.svga.banked_mask = 0x7FFF; + break; + } +} + +// +// Init code +// +void* nv3_init(const device_t *info) +{ + nv_log("NV3: initialising core\n"); + + // currently using ELSA VICTORY Erazor Ver. 1.54.03 [WD/VBE30/DDC2B/DPMS] + // ELSA VICTORY Erazor Ver. 1.55.00 [WD/VBE30/DDC2B/DPMS] seems to be broken :( + + int32_t err = rom_init(&nv3->nvbase.vbios, NV3_VBIOS_ERAZOR_V15403, 0xC0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); + + if (err) + nv_log("NV3: failed to load VBIOS err=%d\n", err); + else + nv_log("NV3: Successfully loaded VBIOS %s\n", NV3_VBIOS_ERAZOR_V15403); + + // set up the bus and start setting up SVGA core + if (nv3->nvbase.bus_generation == nv_bus_pci) + { + nv_log("NV3: using PCI bus\n"); + + pci_add_card(PCI_ADD_NORMAL, nv3_pci_read, nv3_pci_write, NULL, &nv3->nvbase.pci_slot); + + svga_init(&nv3_device_pci, &nv3->nvbase.svga, nv3, VRAM_SIZE_4MB, + nv3_recalc_timings, nv3_svga_in, nv3_svga_out, nv3_draw_cursor, NULL); + } + else if (nv3->nvbase.bus_generation == nv_bus_agp_1x) + { + nv_log("NV3: using AGP 1X bus\n"); + + pci_add_card(PCI_ADD_AGP, nv3_pci_read, nv3_pci_write, NULL, &nv3->nvbase.pci_slot); + + svga_init(&nv3_device_agp, &nv3->nvbase.svga, nv3, VRAM_SIZE_4MB, + nv3_recalc_timings, nv3_svga_in, nv3_svga_out, nv3_draw_cursor, NULL); + } + + // set vram + nv_log("NV3: VRAM=%d bytes\n", nv3->nvbase.svga.vram_max); + + // init memory mappings + nv3_init_mappings(); + + // make us actually exist + nv3->pci_config.int_line = 0xFF; // per datasheet + nv3->pci_config.pci_regs[PCI_REG_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEM; + + // svga is done, so now initialise the real gpu + nv_log("NV3: Initialising GPU core...\n"); + + nv3_pextdev_init(); // Initialise Straps + nv3_pmc_init(); // Initialise Master Control + nv3_pbus_init(); // Initialise Bus (the 128 part of riva) + nv3_pfb_init(); // Initialise Framebuffer Interface + nv3_pramdac_init(); // Initialise RAMDAC (CLUT, final pixel presentation etc) + nv3_pfifo_init(); // Initialise FIFO for graphics object submission + nv3_pgraph_init(); // Initialise accelerated graphics engine + nv3_ptimer_init(); // Initialise programmable interval timer + nv3_pvideo_init(); // Initialise video overlay engines + + return nv3; +} + +// This function simply allocates ram and sets the bus to pci before initialising. +void* nv3_init_pci(const device_t* info) +{ + nv3 = (nv3_t*)calloc(1, sizeof(nv3_t)); + nv3->nvbase.bus_generation = nv_bus_pci; + nv3_init(info); +} + +// This function simply allocates ram and sets the bus to agp before initialising. +void* nv3_init_agp(const device_t* info) +{ + nv3 = (nv3_t*)calloc(1, sizeof(nv3_t)); + nv3->nvbase.bus_generation = nv_bus_agp_1x; + nv3_init(info); +} + +// NV3 (RIVA 128) +// PCI +// 2MB or 4MB VRAM +const device_t nv3_device_pci = +{ + .name = "NVidia RIVA 128 (NV3) PCI", + .internal_name = "nv3", + .flags = DEVICE_PCI, + .local = 0, + .init = nv3_init_pci, + .close = nv3_close, + .speed_changed = nv3_speed_changed, + .force_redraw = nv3_force_redraw +}; + +// NV3 (RIVA 128) +// AGP +// 2MB or 4MB VRAM +const device_t nv3_device_agp = +{ + .name = "NVidia RIVA 128 (NV3) AGP", + .internal_name = "nv3_agp", + .flags = DEVICE_AGP, + .local = 0, + .init = nv3_init_agp, + .close = nv3_close, + .speed_changed = nv3_speed_changed, + .force_redraw = nv3_force_redraw +}; \ No newline at end of file diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c new file mode 100644 index 000000000..0b307e84a --- /dev/null +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -0,0 +1,198 @@ +/* + * 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. + * + * The insane NV3 MMIO arbiter. + * Writes to ALL sections of the GPU based on the write position + * All writes are internally considered to be 32-bit! Be careful... + * + * Also handles interrupt dispatch + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 starfrost + */ + +// STANDARD NV3 includes +#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_nv3.h> + +// Gets a register... +// move this somewhere else when we have more models +nv_register_t* nv_get_register(uint32_t address, nv_register_t* register_list, uint32_t num_regs) +{ + for (int32_t reg_num = 0; reg_num < num_regs; reg_num++) + { + if (register_list[reg_num].address == NV_REG_LIST_END) + break; //unimplemented + + if (register_list[reg_num].address == address) + return ®ister_list[reg_num]; + } + + return NULL; +} + +// Arbitrates an MMIO read +uint32_t nv3_mmio_arbitrate_read(uint32_t address) +{ + uint32_t ret = 0x00; + + // note: some registers are byte aligned not dword aligned + // only very few are though, so they can be handled specially, using the register list most likely + address &= 0xFFFFFC; + + // gigantic set of if statements to send the write to the right subsystem + if (address >= NV3_PMC_START && address <= NV3_PMC_END) + ret = nv3_pmc_read(address); + else if (address >= NV3_CIO_START && address <= NV3_CIO_END) + ret = nv3_cio_read(address); + else if (address >= NV3_PBUS_PCI_START && address <= NV3_PBUS_PCI_END) + ret = nv3_pci_read(0x00, address & 0xFF, NULL); + else if (address >= NV3_PBUS_START && address <= NV3_PBUS_END) + ret = nv3_pbus_read(address); + else if (address >= NV3_PFB_START && address <= NV3_PFB_END) + ret = nv3_pfb_read(address); + else if (address >= NV3_PRM_START && address <= NV3_PRM_END) + ret = nv3_prm_read(address); + else if (address >= NV3_PRMIO_START && address <= NV3_PRMIO_END) + ret = nv3_prmio_read(address); + else if (address >= NV3_PTIMER_START && address <= NV3_PTIMER_END) + ret = nv3_ptimer_read(address); + else if (address >= NV3_PFB_START && address <= NV3_PFB_END) + ret = nv3_pfb_read(address); + else if (address >= NV3_PEXTDEV_START && address <= NV3_PEXTDEV_END) + ret = nv3_pextdev_read(address); + else if (address >= NV3_PROM_START && address <= NV3_PROM_END) + ret = nv3_prom_read(address); + else if (address >= NV3_PALT_START && address <= NV3_PALT_END) + ret = nv3_palt_read(address); + else if (address >= NV3_PME_START && address <= NV3_PME_END) + ret = nv3_pme_read(address); + else if (address >= NV3_PGRAPH_START && address <= NV3_PGRAPH_REAL_END) // what we're actually doing here determined by nv3_pgraph_* func + ret = nv3_pgraph_read(address); + else if (address >= NV3_PRMCIO_START && address <= NV3_PRMCIO_END) + ret = nv3_prmcio_read(address); + else if (address >= NV3_PVIDEO_START && address <= NV3_PVIDEO_END) + ret = nv3_pvideo_read(address); + else if (address >= NV3_PRAMDAC_START && address <= NV3_PRAMDAC_END) + ret = nv3_pramdac_read(address); + else if (address >= NV3_VRAM_START && address <= NV3_VRAM_END) + ret = nv3_vram_read(address); + else if (address >= NV3_USER_START && address <= NV3_USER_END) + ret = nv3_user_read(address); + else if (address >= NV3_PRAMIN_START && address <= NV3_PRAMIN_END) + ret = nv3_pramin_read(address); // RAMHT, RAMFC, RAMRO etc dettermined by nv3_ramin_* function + else + { + nv_log("NV3: MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); + return 0x00; + } + + nv_log("NV3: MMIO read, 0x%08x <- 0x%08x\n", ret, address); + return ret; +} + +void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) +{ + // note: some registers are byte aligned not dword aligned + // only very few are though, so they can be handled specially, using the register list most likely + address &= 0xFFFFFC; + + // gigantic set of if statements to send the write to the right subsystem + if (address >= NV3_PMC_START && address <= NV3_PMC_END) + nv3_pmc_write(address, value); + else if (address >= NV3_CIO_START && address <= NV3_CIO_END) + nv3_cio_write(address, value); + else if (address >= NV3_PBUS_PCI_START && address <= NV3_PBUS_PCI_END) // PCI mirrored at 0x1800 in MMIO + nv3_pci_write(0x00, address & 0xFF, value, NULL); // priv does not matter + else if (address >= NV3_PBUS_START && address <= NV3_PBUS_END) + nv3_pbus_write(address, value); + else if (address >= NV3_PFIFO_START && address <= NV3_PFIFO_END) + nv3_pfifo_write(address, value); + else if (address >= NV3_PRM_START && address <= NV3_PRM_END) + nv3_prm_write(address, value); + else if (address >= NV3_PRMIO_START && address <= NV3_PRMIO_END) + nv3_prmio_write(address, value); + else if (address >= NV3_PTIMER_START && address <= NV3_PTIMER_END) + nv3_ptimer_write(address, value); + else if (address >= NV3_PFB_START && address <= NV3_PFB_END) + nv3_pfb_write(address, value); + else if (address >= NV3_PEXTDEV_START && address <= NV3_PEXTDEV_END) + nv3_pextdev_write(address, value); + else if (address >= NV3_PROM_START && address <= NV3_PROM_END) + nv3_prom_write(address, value); + else if (address >= NV3_PALT_START && address <= NV3_PALT_END) + nv3_palt_write(address, value); + else if (address >= NV3_PME_START && address <= NV3_PME_END) + nv3_pme_write(address, value); + else if (address >= NV3_PGRAPH_START && address <= NV3_PGRAPH_REAL_END) // what we're actually doing here is determined by the nv3_pgraph_* functions + nv3_pgraph_write(address, value); + else if (address >= NV3_PRMCIO_START && address <= NV3_PRMCIO_END) + nv3_prmcio_write(address, value); + else if (address >= NV3_PVIDEO_START && address <= NV3_PVIDEO_END) + nv3_pvideo_write(address, value); + else if (address >= NV3_PRAMDAC_START && address <= NV3_PRAMDAC_END) + nv3_pramdac_write(address, value); + else if (address >= NV3_VRAM_START && address <= NV3_VRAM_END) + nv3_vram_write(address, value); + else if (address >= NV3_USER_START && address <= NV3_USER_END) + nv3_user_write(address, value); + else if (address >= NV3_PRAMIN_START && address <= NV3_PRAMIN_END) + nv3_pramin_write(address, value); // RAMHT, RAMFC, RAMRO etc is determined by the nv3_ramin_* functions + else + { + nv_log("NV3: MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x\n", address); + return; + } + + nv_log("NV3: MMIO write, 0x%08x -> 0x%08x\n", value, address); +} + + +// // +// ******* DUMMY FUNCTIONS FOR UNIMPLEMENTED SUBSYSTEMS ******* // +// // + +// Read and Write functions for GPU subsystems +// Remove the ones that aren't used here eventually, have all of htem for now +uint32_t nv3_cio_read(uint32_t address) { return 0; }; +void nv3_cio_write(uint32_t address, uint32_t value) {}; +uint32_t nv3_prm_read(uint32_t address) { return 0; }; +void nv3_prm_write(uint32_t address, uint32_t value) {}; +uint32_t nv3_prmio_read(uint32_t address) { return 0; }; +void nv3_prmio_write(uint32_t address, uint32_t value) {}; +uint32_t nv3_ptimer_read(uint32_t address) { return 0; }; +void nv3_ptimer_write(uint32_t address, uint32_t value) {}; +uint32_t nv3_prom_read(uint32_t address) { return 0; }; +void nv3_prom_write(uint32_t address, uint32_t value) {}; +uint32_t nv3_palt_read(uint32_t address) { return 0; }; +void nv3_palt_write(uint32_t address, uint32_t value) {}; + +// TODO: PGRAPH class registers +uint32_t nv3_prmcio_read(uint32_t address) { return 0; }; +void nv3_prmcio_write(uint32_t address, uint32_t value) {}; + +uint32_t nv3_vram_read(uint32_t address) { return 0; }; +void nv3_vram_write(uint32_t address, uint32_t value) {}; + +uint32_t nv3_user_read(uint32_t address) { return 0; }; +void nv3_user_write(uint32_t address, uint32_t value) {}; +uint32_t nv3_pramin_read(uint32_t address) { return 0; }; +void nv3_pramin_write(uint32_t address, uint32_t value) {}; \ No newline at end of file diff --git a/src/video/nv/nv3/nv3_interrupt.c b/src/video/nv/nv3/nv3_interrupt.c new file mode 100644 index 000000000..e69de29bb diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c new file mode 100644 index 000000000..a344d8627 --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -0,0 +1,249 @@ +/* + * 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. + * + * NV3 PBUS: 128-bit unified bus + * + * + * + * Authors: Connor Hyde, I need a better email dataess ;^) + * + * Copyright 2024 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_nv3.h> + +// NV3 PBUS RMA - Real Mode Access for VBIOS +// This basically works like a shifter, you write one byte at a time from [0x3d0...0x3d3] and it shifts it in to build a 32-bit MMIO address... +// Putting this in pbus because imo it makes the most sense (related to memory access/memory interface) + +nv_register_t pbus_registers[] = { + { NV3_PBUS_INTR, "PBUS - Interrupt Status", NULL, NULL}, + { NV3_PBUS_INTR_EN, "PBUS - Interrupt Enable", NULL, NULL,}, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +void nv3_pbus_init() +{ + nv_log("NV3: Initialising PBUS..."); + + nv_log("Done\n"); +} + +uint32_t nv3_pbus_read(uint32_t address) +{ + nv_register_t* reg = nv_get_register(address, pbus_registers, sizeof(pbus_registers)/sizeof(pbus_registers[0])); + + // todo: friendly logging + + nv_log("NV3: PBUS Read from 0x%08x", address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_read) + return reg->on_read(); + else + { + switch (reg->address) + { + case NV3_PBUS_INTR: + return nv3->pbus.interrupt_status; + break; + case NV3_PBUS_INTR_EN: + return nv3->pbus.interrupt_enable; + break; + } + + } + } + + return 0x0; +} + +void nv3_pbus_write(uint32_t address, uint32_t value) +{ + nv_register_t* reg = nv_get_register(address, pbus_registers, sizeof(pbus_registers)/sizeof(pbus_registers[0])); + + nv_log("NV3: PBUS Write 0x%08x -> 0x%08x\n", value, address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + else + { + switch (reg->address) + { + // Interrupt registers + // Interrupt state: + // Bit 0 - PCI Bus Error + case NV3_PBUS_INTR: + nv3->pbus.interrupt_status &= ~value; + nv3_pmc_clear_interrupts(); + break; + case NV3_PBUS_INTR_EN: + nv3->pbus.interrupt_enable = value & 0x00000001; + break; + } + + } + } +} + +uint8_t nv3_pbus_rma_read(uint16_t addr) +{ + addr &= 0xFF; + uint32_t real_final_address; + uint8_t ret; + + switch (addr) + { + // signature so you know reads work + case 0x00: + ret = NV3_RMA_SIGNATURE_MSB; + break; + case 0x01: + ret = NV3_RMA_SIGNATURE_BYTE2; + break; + case 0x02: + ret = NV3_RMA_SIGNATURE_BYTE1; + break; + case 0x03: + ret = NV3_RMA_SIGNATURE_LSB; + break; + case 0x08 ... 0x0B: + // reads must be dword aligned + real_final_address = (nv3->pbus.rma.addr + (addr & 0x03)); + + if (nv3->pbus.rma.addr < NV3_MMIO_SIZE) + ret = nv3_mmio_read8(real_final_address, NULL); + else + { + // ABOVE CODE IS TEMPORARY UNTIL PNVM EXISTS!!!!! + // would svga->fast work? + nv3->nvbase.svga.chain4 = true; + nv3->nvbase.svga.packed_chain4 = true; + ret = svga_read_linear((real_final_address - NV3_MMIO_SIZE) & (VRAM_SIZE_4MB - 1), &nv3->nvbase.svga); + nv3->nvbase.svga.chain4 = false; + nv3->nvbase.svga.packed_chain4 = false; + } + + // log current location for vbios RE + nv_log("NV3: MMIO Real Mode Access read, initial address=0x%04x final RMA MMIO address=0x%08x data=0x%08x\n", + addr, real_final_address, ret); + + break; + } + + return ret; +} + +// Implements a 32-bit write using 16 bit port number +void nv3_pbus_rma_write(uint16_t addr, uint8_t val) +{ + uint8_t ret = 0x00; + + // addresses are in reality 8bit so just mask it to be safe + addr &= 0xFF; + + // format: + // 0x00 ID + // 0x04 Pointer to data + // 0x08 Data port(?) + // 0x0B Data - 32bit. SENT IN THE RIGHT ORDER FOR ONCE WAHOO! + // 0x10 Increment (?) data - implemented the same as data for now + + if (addr < 0x08) + { + switch (addr % 0x04) + { + case 0x00: // lowest byte + nv3->pbus.rma.addr &= ~0xff; + nv3->pbus.rma.addr |= val; + break; + case 0x01: // 2nd highest byte + nv3->pbus.rma.addr &= ~0xff00; + nv3->pbus.rma.addr |= (val << 8); + break; + case 0x02: // 3rd highest byte + nv3->pbus.rma.addr &= ~0xff0000; + nv3->pbus.rma.addr |= (val << 16); + break; + case 0x03: // 4th highest byte + nv3->pbus.rma.addr &= ~0xff000000; + nv3->pbus.rma.addr |= (val << 24); + break; + } + } + // Data to send to MMIO + else + { + switch (addr % 0x04) + { + case 0x00: // lowest byte + nv3->pbus.rma.data &= ~0xff; + nv3->pbus.rma.data |= val; + break; + case 0x01: // 2nd highest byte + nv3->pbus.rma.data &= ~0xff00; + nv3->pbus.rma.data |= (val << 8); + break; + case 0x02: // 3rd highest byte + nv3->pbus.rma.data &= ~0xff0000; + nv3->pbus.rma.data |= (val << 16); + break; + case 0x03: // 4th highest byte + nv3->pbus.rma.data &= ~0xff000000; + nv3->pbus.rma.data |= (val << 24); + + nv_log("NV3: MMIO Real Mode Access write transaction complete, initial address=0x%04x final RMA MMIO address=0x%08x data=0x%08x\n", + addr, nv3->pbus.rma.addr, nv3->pbus.rma.data); + + if (nv3->pbus.rma.addr < NV3_MMIO_SIZE) + nv3_mmio_write32(nv3->pbus.rma.addr, nv3->pbus.rma.data, NULL); + else // failsafe code, i don't think you will ever write outside of VRAM? + { + nv3->nvbase.svga.chain4 = true; + nv3->nvbase.svga.packed_chain4 = true; + svga_writel_linear((nv3->pbus.rma.addr - NV3_MMIO_SIZE) & (VRAM_SIZE_4MB - 1), nv3->pbus.rma.data, &nv3->nvbase.svga); + nv3->nvbase.svga.chain4 = false; + nv3->nvbase.svga.packed_chain4 = false; + } + + + break; + } + } + + if (addr & 0x10) + nv3->pbus.rma.addr += 0x04; // Alignment +} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c new file mode 100644 index 000000000..825f326f2 --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c @@ -0,0 +1,31 @@ +/* + * 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. + * + * NV3 PBUS DMA: DMA Engine + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +// Single unified write function... \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pextdev.c b/src/video/nv/nv3/subsystems/nv3_pextdev.c new file mode 100644 index 000000000..761bacdb3 --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -0,0 +1,149 @@ +/* + * 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. + * + * NV3 PEXTDEV - External Devices + * Including straps + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +void nv3_pextdev_init() +{ + nv_log("NV3: Initialising PEXTDEV....\n"); + + // Set the chip straps + // Make these configurable in the future... + + // Current settings + // AGP2X Disabled + // TV Mode NTSC + // Crystal 13.5 Mhz + // Bus width 128-Bit (some gpus were sold as 64bit for cost reduction) + // + + nv_log("NV3: Initialising straps...\n"); + + nv3->pextdev.straps = + (NV3_PSTRAPS_AGP2X_DISABLED << NV3_PSTRAPS_AGP2X) | + (NV3_PSTRAPS_TVMODE_NTSC << NV3_PSTRAPS_TVMODE) | + (NV3_PSTRAPS_CRYSTAL_13500K << NV3_PSTRAPS_CRYSTAL); + + // figure out the bus + if (nv3->nvbase.bus_generation == nv_bus_pci) + nv3->pextdev.straps |= (NV3_PSTRAPS_BUS_TYPE_PCI << NV3_PSTRAPS_BUS_TYPE); + else + nv3->pextdev.straps |= (NV3_PSTRAPS_BUS_TYPE_AGP << NV3_PSTRAPS_BUS_TYPE); + + // now the lower bits + nv3->pextdev.straps |= + (NV3_PSTRAPS_BUS_WIDTH_128BIT << NV3_PSTRAPS_BUS_WIDTH) | + (NV3_PSTRAPS_BIOS_PRESENT << NV3_PSTRAPS_BIOS) | + (NV3_PSTRAPS_BUS_SPEED_66MHZ << NV3_PSTRAPS_BUS_SPEED); + + nv_log("NV3: Straps = 0x%04x\n", nv3->pextdev.straps); + nv_log("NV3: Initialising PEXTDEV: Done\n"); +} + +// +// ****** PEXTDEV register list START ****** +// + +nv_register_t pextdev_registers[] = { + { NV3_PSTRAPS, "Straps - Chip Configuration", NULL, NULL }, + { NV_REG_LIST_END, NULL, NULL, NULL }, // sentinel value +}; + + +// +// ****** Read/Write functions start ****** +// + +uint32_t nv3_pextdev_read(uint32_t address) +{ + nv_register_t* reg = nv_get_register(address, pextdev_registers, sizeof(pextdev_registers)/sizeof(pextdev_registers[0])); + + // todo: friendly logging + + // special consideration for straps + if (address == NV3_PSTRAPS) + { + nv_log("NV3: Straps Read (current value=0x%08x)\n", nv3->pextdev.straps); + } + else + { + nv_log("NV3: PEXTDEV Read from 0x%08x", address); + + } + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_read) + return reg->on_read(); + else + { + switch (reg->address) + { + case NV3_PSTRAPS: + return nv3->pextdev.straps; + } + } + } + + return 0x0; +} + +void nv3_pextdev_write(uint32_t address, uint32_t value) +{ + nv_register_t* reg = nv_get_register(address, pextdev_registers, sizeof(pextdev_registers)/sizeof(pextdev_registers[0])); + + nv_log("NV3: PEXTDEV Write 0x%08x -> 0x%08x\n", value, address); + + // special consideration for straps + if (address == NV3_PSTRAPS) + { + nv_log("NV3: Huh? Tried to write to the straps. Something is wrong...\n", nv3->pextdev.straps); + return; + } + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + } +} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c new file mode 100644 index 000000000..d644185a2 --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -0,0 +1,109 @@ +/* + * 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. + * + * NV3 PFB: Framebuffer + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +nv_register_t pfb_registers[] = { + { NV3_PFB_BOOT, "PFB Boot Config", NULL, NULL}, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +void nv3_pfb_init() +{ + nv_log("NV3: Initialising PFB..."); + + // initial configuration: + // ram 4mb + // bus width 128bit + // extension ram none (was this ever used?) + // ram banks 4 (based on observation of physical RIVA 128 with 4mb, does 1 bank = 1chip?) + // twiddle off (check this on a real card once it's actually installed) + nv3->pfb.boot = (NV3_PFB_BOOT_RAM_EXTENSION_NONE << (NV3_PFB_BOOT_RAM_EXTENSION) + | (NV3_PFB_BOOT_RAM_DATA_TWIDDLE_OFF << NV3_PFB_BOOT_RAM_DATA_TWIDDLE) + | (NV3_PFB_BOOT_RAM_BANKS_4 << NV3_PFB_BOOT_RAM_BANKS) + | (NV3_PFB_BOOT_RAM_WIDTH_128 << NV3_PFB_BOOT_RAM_WIDTH) + | (NV3_PFB_BOOT_RAM_AMOUNT_4MB << NV3_PFB_BOOT_RAM_AMOUNT) + ); + + nv_log("Done\n"); +} + +uint32_t nv3_pfb_read(uint32_t address) +{ + nv_register_t* reg = nv_get_register(address, pfb_registers, sizeof(pfb_registers)/sizeof(pfb_registers[0])); + + // todo: friendly logging + + nv_log("NV3: PFB Read from 0x%08x", address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_read) + return reg->on_read(); + else + { + switch (reg->address) + { + case NV3_PFB_BOOT: + return nv3->pfb.boot; + break; + } + } + } + + return 0x0; +} + +void nv3_pfb_write(uint32_t address, uint32_t value) +{ + nv_register_t* reg = nv_get_register(address, pfb_registers, sizeof(pfb_registers)/sizeof(pfb_registers[0])); + + nv_log("NV3: PFB Write 0x%08x -> 0x%08x\n", value, address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + + } + +} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c new file mode 100644 index 000000000..8f8abbac4 --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -0,0 +1,146 @@ +/* + * 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. + * + * NV3 pfifo (FIFO for graphics object submission) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +// Single unified write function... + +// +// ****** pfifo register list START ****** +// + +nv_register_t pfifo_registers[] = { + { NV3_PFIFO_INTR, "PFIFO - Interrupt Status", NULL, NULL}, + { NV3_PFIFO_INTR_EN, "PFIFO - Interrupt Enable", NULL, NULL,}, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +// PFIFO init code +void nv3_pfifo_init() +{ + nv_log("NV3: Initialising PFIFO..."); + + nv_log("Done!\n"); +} + +uint32_t nv3_pfifo_read(uint32_t address) +{ + // before doing anything, check the subsystem enablement state + + if (!(nv3->pmc.enable >> NV3_PMC_ENABLE_PFIFO) + & NV3_PMC_ENABLE_PFIFO_ENABLED) + { + nv_log("NV3: Repressing PFIFO read. The subsystem is disabled according to pmc_enable, returning 0\n"); + return 0x00; + } + + nv_register_t* reg = nv_get_register(address, pfifo_registers, sizeof(pfifo_registers)/sizeof(pfifo_registers[0])); + + // todo: friendly logging + + nv_log("NV3: PFIFO Read from 0x%08x", address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_read) + return reg->on_read(); + else + { + // Interrupt state: + // Bit 0 - Cache Error + // Bit 4 - RAMRO Triggered + // Bit 8 - RAMRO Overflow (too many invalid dma objects) + // Bit 12 - DMA Pusher + // Bit 16 - DMA Page Table Entry (pagefault?) + switch (reg->address) + { + case NV3_PFIFO_INTR: + return nv3->pfifo.interrupt_status; + case NV3_PFIFO_INTR_EN: + return nv3->pfifo.interrupt_enable; + } + } + } + return 0x0; +} + +void nv3_pfifo_write(uint32_t address, uint32_t value) +{ + // before doing anything, check the subsystem enablement + + if (!(nv3->pmc.enable >> NV3_PMC_ENABLE_PFIFO) + & NV3_PMC_ENABLE_PFIFO_ENABLED) + { + nv_log("NV3: Repressing PFIFO write. The subsystem is disabled according to pmc_enable\n"); + return; + } + + nv_register_t* reg = nv_get_register(address, pfifo_registers, sizeof(pfifo_registers)/sizeof(pfifo_registers[0])); + + nv_log("NV3: pfifo Write 0x%08x -> 0x%08x\n", value, address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + else + { + switch (reg->address) + { + // Interrupt state: + // Bit 0 - Cache Error + // Bit 4 - RAMRO Triggered + // Bit 8 - RAMRO Overflow (too many invalid dma objects) + // Bit 12 - DMA Pusher + // Bit 16 - DMA Page Table Entry (pagefault?) + case NV3_PFIFO_INTR: + nv3->pfifo.interrupt_status &= ~value; + nv3_pmc_clear_interrupts(); + break; + case NV3_PFIFO_INTR_EN: + nv3->pbus.interrupt_enable = value & 0x00001111; + break; + } + } + } + +} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c new file mode 100644 index 000000000..d37ccdf9f --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -0,0 +1,151 @@ +/* + * 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. + * + * NV3 PGRAPH (Scene Graph for 2D/3D Accelerated Graphics) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +// Single unified write function... + +// +// ****** PGRAPH register list START ****** +// + +void nv3_pgraph_init() +{ + nv_log("NV3: Initialising PGRAPH..."); + + nv_log("Done!\n"); +} + +nv_register_t pgraph_registers[] = { + { NV3_PGRAPH_INTR_0, "PGRAPH Interrupt Status 0", NULL, NULL }, + { NV3_PGRAPH_INTR_EN_0, "PGRAPH Interrupt Enable 0", NULL, NULL }, + { NV3_PGRAPH_INTR_1, "PGRAPH Interrupt Status 1", NULL, NULL }, + { NV3_PGRAPH_INTR_EN_1, "PGRAPH Interrupt Enable 1", NULL, NULL }, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +uint32_t nv3_pgraph_read(uint32_t address) +{ + // before doing anything, check that this is even enabled.. + + if (!(nv3->pmc.enable >> NV3_PMC_ENABLE_PGRAPH) + & NV3_PMC_ENABLE_PGRAPH_ENABLED) + { + nv_log("NV3: Repressing PGRAPH read. The subsystem is disabled according to pmc_enable, returning 0\n"); + return 0x00; + } + + nv_register_t* reg = nv_get_register(address, pgraph_registers, sizeof(pgraph_registers)/sizeof(pgraph_registers[0])); + + // todo: friendly logging + + nv_log("NV3: PGRAPH Read from 0x%08x", address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_read) + return reg->on_read(); + else + { + switch (reg->address) + { + //interrupt status and enable regs + case NV3_PGRAPH_INTR_0: + return nv3->pgraph.interrupt_status_0; + case NV3_PGRAPH_INTR_1: + return nv3->pgraph.interrupt_status_1; + case NV3_PGRAPH_INTR_EN_0: + return nv3->pgraph.interrupt_enable_0; + case NV3_PGRAPH_INTR_EN_1: + return nv3->pgraph.interrupt_enable_1; + } + } + + } + + return 0x0; +} + +void nv3_pgraph_write(uint32_t address, uint32_t value) +{ + if (!(nv3->pmc.enable >> NV3_PMC_ENABLE_PGRAPH) + & NV3_PMC_ENABLE_PGRAPH_ENABLED) + { + nv_log("NV3: Repressing PGRAPH write. The subsystem is disabled according to pmc_enable\n"); + return; + } + + nv_register_t* reg = nv_get_register(address, pgraph_registers, sizeof(pgraph_registers)/sizeof(pgraph_registers[0])); + + nv_log("NV3: pgraph Write 0x%08x -> 0x%08x\n", value, address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + else + { + switch (reg->address) + { + //interrupt status and enable regs + case NV3_PGRAPH_INTR_0: + nv3->pgraph.interrupt_status_0 &= ~value; + //we changed interrupt state + nv3_pmc_clear_interrupts(); + break; + case NV3_PGRAPH_INTR_1: + nv3->pgraph.interrupt_status_1 &= ~value; + //we changed interrupt state + nv3_pmc_clear_interrupts(); + break; + // Only bits divisible by 4 matter + // and only bit0-16 is defined in intr_1 + case NV3_PGRAPH_INTR_EN_0: + nv3->pgraph.interrupt_enable_0 = value & 0x11111111; + break; + case NV3_PGRAPH_INTR_EN_1: + nv3->pgraph.interrupt_enable_1 = value & 0x00011111; + break; + } + } + } +} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c new file mode 100644 index 000000000..10717f7bc --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -0,0 +1,236 @@ +/* + * 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. + * + * NV3 PMC - Master control for the chip + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +// Single unified write function... + +void nv3_pmc_init() +{ + nv_log("NV3: Initialising PMC....\n"); + + nv3->pmc.boot = NV3_BOOT_REG_DEFAULT; + + nv_log("NV3: Initialising PMC: Done\n"); +} + +// +// ****** PMC register list START ****** +// + +nv_register_t pmc_registers[] = { + { NV3_PMC_BOOT, "PMC: Boot Manufacturing Information", NULL, NULL }, + { NV3_PMC_INTERRUPT_STATUS, "PMC: Current Pending Subsystem Interrupts", NULL, NULL}, + { NV3_PMC_INTERRUPT_ENABLE, "PMC: Global Interrupt Enable", NULL, NULL,}, + { NV3_PMC_ENABLE, "PMC: Global Subsystem Enable", NULL, NULL }, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +uint32_t nv3_pmc_clear_interrupts() +{ + nv_log("NV3: Clearing IRQs\n"); + pci_set_irq(nv3->nvbase.pci_slot, PCI_INTA, &nv3->nvbase.pci_irq_state); +} + +// Handle hardware interrupts +// We only clear when we need to, in other functions... +uint32_t nv3_pmc_handle_interrupts(bool send_now) +{ + // TODO: + // PGRAPH DMA INTR_EN (there is no DMA engine yet) + // PRM Real-Mode Compatibility Interrupts + + uint32_t new_intr_value; + + // set the new interrupt value + // PAUDIO not used + // IF NV3 REV A EMULATION IS ADDED, ADD THIS COMPONENT! + + // the registers are designed to line up so you can enable specific interrupts + + // Check Mediaport interrupts + if (nv3->pme.interrupt_status & nv3->pme.interrupt_enable) + new_intr_value |= (NV3_PMC_INTERRUPT_PMEDIA_PENDING << NV3_PMC_INTERRUPT_PMEDIA); + + // Check FIFO interrupts + if (nv3->pfifo.interrupt_status & nv3->pfifo.interrupt_enable) + new_intr_value |= (NV3_PMC_INTERRUPT_PFIFO_PENDING << NV3_PMC_INTERRUPT_PFIFO); + + // PFB interrupt is VBLANK PGRAPH interrupt...what nvidia...clean this up once we verify it + if (nv3->pgraph.interrupt_status_0 & (1 << 8)) + new_intr_value |= (NV3_PMC_INTERRUPT_PFB_PENDING << NV3_PMC_INTERRUPT_PFB); + else if (nv3->pgraph.interrupt_status_0 & ~(1 << 8)) // otherwise PGRAPH-0 interurpt + new_intr_value |= (NV3_PMC_INTERRUPT_PGRAPH0_PENDING << NV3_PMC_INTERRUPT_PGRAPH0); + + // Check second pgraph interrupt register + if (nv3->pgraph.interrupt_status_1 & nv3->pgraph.interrupt_enable_1) + new_intr_value |= (NV3_PMC_INTERRUPT_PGRAPH1_PENDING << NV3_PMC_INTERRUPT_PGRAPH1); + + // check video overlay interrupts + if (nv3->pvideo.interrupt_status & nv3->pvideo.interrupt_enable) + new_intr_value |= (NV3_PMC_INTERRUPT_PVIDEO_PENDING << NV3_PMC_INTERRUPT_PVIDEO); + + // check PIT interrupts + if (nv3->ptimer.interrupt_status & nv3->ptimer.interrupt_enable) + new_intr_value |= (NV3_PMC_INTERRUPT_PTIMER_PENDING << NV3_PMC_INTERRUPT_PTIMER); + + // check bus interrupts + if (nv3->pbus.interrupt_status & nv3->pbus.interrupt_enable) + new_intr_value |= (NV3_PMC_INTERRUPT_PBUS_PENDING << NV3_PMC_INTERRUPT_PBUS); + + // check SW interrupts + if (nv3->pmc.interrupt_status & (1 << NV3_PMC_INTERRUPT_SOFTWARE)) + new_intr_value |= (NV3_PMC_INTERRUPT_SOFTWARE_PENDING << NV3_PMC_INTERRUPT_SOFTWARE); + + nv3->pmc.interrupt_status = new_intr_value; + + // ***TODO: DOes INTR still change if INTR_EN=0???*** + // If interrupts are disabled don't bother + + if (!nv3->pmc.interrupt_enable) + nv3_pmc_clear_interrupts(); + + // if we actually need to send the interrupt (i.e. this is a write) send it now + if (send_now) + { + if (!(nv3->pmc.interrupt_status & 0x7FFFFFFF)) + { + if (nv3->pmc.interrupt_enable & NV3_PMC_INTERRUPT_ENABLE_HARDWARE) + { + nv_log("NV3: Firing hardware-originated interrupt NV3_PMC_INTR_0=0x%08x\n", nv3->pmc.interrupt_status); + pci_set_irq(nv3->nvbase.pci_slot, PCI_INTA, &nv3->nvbase.pci_irq_state); + } + else + nv_log("NV3: NOT firing hardware-originated interrupt NV3_PMC_INTR_0=0x%08x, BECAUSE HARDWARE INTERRUPTS ARE DISABLED\n", nv3->pmc.interrupt_status); + } + else + { + if (nv3->pmc.interrupt_enable & NV3_PMC_INTERRUPT_ENABLE_SOFTWARE) + { + nv_log("NV3: Firing software-originated interrupt NV3_PMC_INTR_0=0x%08x\n", nv3->pmc.interrupt_status); + pci_set_irq(nv3->nvbase.pci_slot, PCI_INTA, &nv3->nvbase.pci_irq_state); + } + else + nv_log("NV3: NOT firing software-originated interrupt NV3_PMC_INTR_0=0x%08x, BECAUSE SOFTWARE INTERRUPTS ARE DISABLED\n", nv3->pmc.interrupt_status); + } + } + + return nv3->pmc.interrupt_status; +} + + + +// +// ****** Read/Write functions start ****** +// + +uint32_t nv3_pmc_read(uint32_t address) +{ + nv_register_t* reg = nv_get_register(address, pmc_registers, sizeof(pmc_registers)/sizeof(pmc_registers[0])); + + // todo: friendly logging + nv_log("NV3: PMC Read from 0x%08x", address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_read) + return reg->on_read(); + else + { + switch (reg->address) + { + case NV3_PMC_BOOT: + return nv3->pmc.boot; + case NV3_PMC_INTERRUPT_STATUS: + nv3_pmc_clear_interrupts(); + + return nv3_pmc_handle_interrupts(false); + case NV3_PMC_INTERRUPT_ENABLE: + //TODO: ACTUALLY CHANGE THE INTERRUPT STATE + return nv3->pmc.interrupt_enable; + case NV3_PMC_ENABLE: + return nv3->pmc.enable; + + } + } + } + + return 0x0; +} + +void nv3_pmc_write(uint32_t address, uint32_t value) +{ + nv_register_t* reg = nv_get_register(address, pmc_registers, sizeof(pmc_registers)/sizeof(pmc_registers[0])); + + nv_log("NV3: PMC Write 0x%08x -> 0x%08x", value, address); + + // if the register actually exists... + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // ... call its on-write function + if (reg->on_write) + reg->on_write(value); + else + { + // if it doesn't have one fallback to a switch statement + switch (reg->address) + { + case NV3_PMC_INTERRUPT_STATUS: + // this + if (!(nv3->pmc.interrupt_status & (NV3_PMC_INTERRUPT_SOFTWARE - 1))) + { + nv_log("Huh? This is a hardware interrupt...Please use the INTR_EN registers of the GPU subsystem you want to trigger " + " an interrupt on, rather than writing to NV3_PMC_INTERRUPT_STATUS (Or this is a bug)...NV3_PMC_INTERRUPT_STATUS=0x%08x)\n", nv3->pmc.interrupt_enable); + return; + } + + nv3_pmc_handle_interrupts(true); + nv3->pmc.interrupt_status = value; + break; + case NV3_PMC_INTERRUPT_ENABLE: + nv3->pmc.interrupt_enable = value; + break; + case NV3_PMC_ENABLE: + nv3->pmc.enable = value; + break; + } + } + } +} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pme.c b/src/video/nv/nv3/subsystems/nv3_pme.c new file mode 100644 index 000000000..9683ff5ab --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -0,0 +1,123 @@ +/* + * 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. + * + * NV3 pme: Nvidia Mediaport - External MPEG Decode Interface + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +nv_register_t pme_registers[] = { + { NV3_PME_INTR, "PME - Interrupt Status", NULL, NULL}, + { NV3_PME_INTR_EN, "PME - Interrupt Enable", NULL, NULL,}, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +void nv3_pme_init() +{ + nv_log("NV3: Initialising PME..."); + + nv_log("Done\n"); +} + +uint32_t nv3_pme_read(uint32_t address) +{ + nv_register_t* reg = nv_get_register(address, pme_registers, sizeof(pme_registers)/sizeof(pme_registers[0])); + + // todo: friendly logging + + nv_log("NV3: PME Read from 0x%08x", address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_read) + return reg->on_read(); + else + { + // Interrupt state: + // Bit 0 - Image Notifier + // Bit 4 - Vertical Blank Interfal Notifier + // Bit 8 - Video Notifier + // Bit 12 - Audio Notifier + // Bit 16 - VMI Notifer + switch (reg->address) + { + case NV3_PME_INTR: + return nv3->pme.interrupt_status; + case NV3_PME_INTR_EN: + return nv3->pme.interrupt_enable; + } + } + } + + return 0x0; +} + +void nv3_pme_write(uint32_t address, uint32_t value) +{ + nv_register_t* reg = nv_get_register(address, pme_registers, sizeof(pme_registers)/sizeof(pme_registers[0])); + + nv_log("NV3: PME Write 0x%08x -> 0x%08x\n", value, address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + else + { + switch (reg->address) + { + // Interrupt state: + // Bit 0 - Image Notifier + // Bit 4 - Vertical Blank Interfal Notifier + // Bit 8 - Video Notifier + // Bit 12 - Audio Notifier + // Bit 16 - VMI Notifer + + case NV3_PME_INTR: + nv3->pme.interrupt_status &= ~value; + nv3_pmc_clear_interrupts(); + break; + case NV3_PME_INTR_EN: + nv3->pme.interrupt_enable = value & 0x00001111; + break; + } + } + + } + } \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c new file mode 100644 index 000000000..a2a39b6cb --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -0,0 +1,307 @@ +/* + * 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. + * + * NV3 bringup and device emulation. + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 starfrost + */ + +// nv3_pramdac.c: NV3 RAMDAC +// Todo: Allow overridability using 68050C register... + +#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_nv3.h> + +void nv3_pramdac_init() +{ + nv_log("NV3: Initialising PRAMDAC\n"); + + // defaults, these come from vbios in reality + // driver defaults are nonsensical(?), or the algorithm is wrong + // munged this to 100mhz for now + nv3->pramdac.memory_clock_m = nv3->pramdac.pixel_clock_m = 0x07; + nv3->pramdac.memory_clock_n = nv3->pramdac.pixel_clock_n = 0xc8; + nv3->pramdac.memory_clock_p = nv3->pramdac.pixel_clock_p = 0x0c; + + nv3_pramdac_set_pixel_clock(); + nv3_pramdac_set_vram_clock(); + + nv_log("NV3: Initialising PRAMDAC: Done\n"); +} + +uint32_t nv3_pramdac_get_vram_clock_register() +{ + // pack into 19 bits + // M divisor [7-0] + // N divisor [16-8] + // P divisor [18-16] + return (nv3->pramdac.memory_clock_m << 16) & 0xFF; + + (nv3->pramdac.memory_clock_n << 8) & 0xFF; + + (nv3->pramdac.memory_clock_p) & 0x07; // 0-3 +} + +uint32_t nv3_pramdac_get_pixel_clock_register() +{ + return (nv3->pramdac.pixel_clock_m << 16) & 0xFF; + + (nv3->pramdac.pixel_clock_n << 8) & 0xFF; + + (nv3->pramdac.pixel_clock_p) & 0x07; // 0-3 +} + +void nv3_pramdac_set_vram_clock_register(uint32_t value) +{ + nv3->pramdac.memory_clock_m = value & 0xFF; + nv3->pramdac.memory_clock_n = (value >> 8) & 0xFF; + nv3->pramdac.memory_clock_p = (value >> 16) & 0x07; + + nv3_pramdac_set_vram_clock(); +} + +void nv3_pramdac_set_pixel_clock_register(uint32_t value) +{ + nv3->pramdac.pixel_clock_m = value & 0xFF; + nv3->pramdac.pixel_clock_n = (value >> 8) & 0xFF; + nv3->pramdac.pixel_clock_p = (value >> 16) & 0x07; + + nv3_pramdac_set_pixel_clock(); +} + +void nv3_pramdac_set_vram_clock() +{ + // from driver and vbios source + float frequency = 13500000.0f; + + // prevent division by 0 + if (nv3->pramdac.memory_clock_m == 0) + nv3->pramdac.memory_clock_m == 1; + else + frequency = (frequency * nv3->pramdac.memory_clock_n) / (nv3->pramdac.memory_clock_m << nv3->pramdac.memory_clock_p); + + nv_log("NV3: Memory clock = %.2f MHz\n", frequency / 1000000.0f); +} + +void nv3_pramdac_set_pixel_clock() +{ + // frequency divider algorithm from old varcem/86box/pcbox riva driver, + // verified by reversing NT drivers v1.50e CalcMNP [symbols] function + + // todo: actually implement it + + // missing section + // not really needed. + // if (nv3->pfb.boot.clock_crystal == CLOCK_CRYSTAL_13500) + // { + // freq = 13500000.0f; + // } + // else + // + // { + // freq = 14318000.0f; + // } + + float frequency = 13500000.0f; + + // prevent division by 0 + if (nv3->pramdac.pixel_clock_m == 0) + nv3->pramdac.pixel_clock_m == 1; + else + frequency = (frequency * nv3->pramdac.pixel_clock_n) / (nv3->pramdac.pixel_clock_m << nv3->pramdac.pixel_clock_p); + + nv3->nvbase.svga.clock = cpuclock / frequency; + + nv_log("NV3: Pixel clock = %.2f MHz\n", frequency / 1000000.0f); +} + +// +// ****** PRAMDAC register list START ****** +// + +// NULL means handle in read functions +nv_register_t pramdac_registers[] = +{ + { NV3_PRAMDAC_CLOCK_PIXEL, "PRAMDAC - NV3 GPU Core - Pixel clock", nv3_pramdac_get_pixel_clock_register, nv3_pramdac_set_pixel_clock_register }, + { NV3_PRAMDAC_CLOCK_MEMORY, "PRAMDAC - NV3 GPU Core - Memory clock", nv3_pramdac_get_vram_clock_register, nv3_pramdac_set_vram_clock_register }, + { NV3_PRAMDAC_COEFF_SELECT, "PRAMDAC - PLL Clock Coefficient Select", NULL, NULL}, + { NV3_PRAMDAC_GENERAL_CONTROL, "PRAMDAC - General Control", NULL, NULL }, + { NV3_PRAMDAC_VSERR_WIDTH, "PRAMDAC - Vertical Sync Error Width", NULL, NULL}, + { NV3_PRAMDAC_VEQU_END, "PRAMDAC - VEqu End", NULL, NULL}, + { NV3_PRAMDAC_VBBLANK_END, "PRAMDAC - VBBlank End", NULL, NULL}, + { NV3_PRAMDAC_VBLANK_END, "PRAMDAC - Vertical Blanking Interval End", NULL, NULL}, + { NV3_PRAMDAC_VBLANK_START, "PRAMDAC - Vertical Blanking Interval Start", NULL, NULL}, + { NV3_PRAMDAC_VEQU_START, "PRAMDAC - VEqu Start", NULL, NULL}, + { NV3_PRAMDAC_VTOTAL, "PRAMDAC - Total Vertical Lines", NULL, NULL}, + { NV3_PRAMDAC_HSYNC_WIDTH, "PRAMDAC - Horizontal Sync Pulse Width", NULL, NULL}, + { NV3_PRAMDAC_HBURST_START, "PRAMDAC - Horizontal Burst Signal Start", NULL, NULL}, + { NV3_PRAMDAC_HBURST_END, "PRAMDAC - Horizontal Burst Signal Start", NULL, NULL}, + { NV3_PRAMDAC_HTOTAL, "PRAMDAC - Total Horizontal Lines", NULL, NULL}, + { NV3_PRAMDAC_HEQU_WIDTH, "PRAMDAC - HEqu End", NULL, NULL}, + { NV3_PRAMDAC_HSERR_WIDTH, "PRAMDAC - Horizontal Sync Error", NULL, NULL}, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +// +// ****** Read/Write functions start ****** +// + +uint32_t nv3_pramdac_read(uint32_t address) +{ + nv_register_t* reg = nv_get_register(address, pramdac_registers, sizeof(pramdac_registers)/sizeof(pramdac_registers[0])); + + // todo: friendly logging + + nv_log("NV3: PRAMDAC Read from 0x%08x", address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_read) + return reg->on_read(); + else + { + //s hould be pretty easy to understand + switch (reg->address) + { + case NV3_PRAMDAC_COEFF_SELECT: + return nv3->pramdac.coeff_select; + case NV3_PRAMDAC_GENERAL_CONTROL: + return nv3->pramdac.general_control; + case NV3_PRAMDAC_VSERR_WIDTH: + return nv3->pramdac.vserr_width; + case NV3_PRAMDAC_VBBLANK_END: + return nv3->pramdac.vbblank_end; + case NV3_PRAMDAC_VBLANK_END: + return nv3->pramdac.vblank_end; + case NV3_PRAMDAC_VBLANK_START: + return nv3->pramdac.vblank_start; + case NV3_PRAMDAC_VEQU_START: + return nv3->pramdac.vequ_start; + case NV3_PRAMDAC_VTOTAL: + return nv3->pramdac.vtotal; + case NV3_PRAMDAC_HSYNC_WIDTH: + return nv3->pramdac.hsync_width; + case NV3_PRAMDAC_HBURST_START: + return nv3->pramdac.hburst_start; + case NV3_PRAMDAC_HBURST_END: + return nv3->pramdac.hburst_end; + case NV3_PRAMDAC_HBLANK_START: + return nv3->pramdac.hblank_start; + case NV3_PRAMDAC_HBLANK_END: + return nv3->pramdac.hblank_end; + case NV3_PRAMDAC_HTOTAL: + return nv3->pramdac.htotal; + case NV3_PRAMDAC_HEQU_WIDTH: + return nv3->pramdac.hequ_width; + case NV3_PRAMDAC_HSERR_WIDTH: + return nv3->pramdac.hserr_width; + } + } + } + + return 0x0; +} + +void nv3_pramdac_write(uint32_t address, uint32_t value) +{ + nv_register_t* reg = nv_get_register(address, pramdac_registers, sizeof(pramdac_registers)/sizeof(pramdac_registers[0])); + + nv_log("NV3: PRAMDAC Write 0x%08x -> 0x%08x", value, address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + else + { + //s hould be pretty easy to understand + // we also update the SVGA state here + switch (reg->address) + { + case NV3_PRAMDAC_COEFF_SELECT: + nv3->pramdac.coeff_select = value; + break; + case NV3_PRAMDAC_GENERAL_CONTROL: + nv3->pramdac.general_control = value; + break; + case NV3_PRAMDAC_VSERR_WIDTH: + //vslines? + nv3->pramdac.vserr_width = value; + break; + case NV3_PRAMDAC_VBBLANK_END: + nv3->pramdac.vbblank_end = value; + break; + case NV3_PRAMDAC_VBLANK_END: + nv3->pramdac.vblank_end = value; + break; + case NV3_PRAMDAC_VBLANK_START: + nv3->nvbase.svga.vblankstart = value; + nv3->pramdac.vblank_start = value; + break; + case NV3_PRAMDAC_VEQU_START: + nv3->pramdac.vequ_start = value; + break; + case NV3_PRAMDAC_VTOTAL: + nv3->pramdac.vtotal = value; + nv3->nvbase.svga.vtotal = value; + break; + case NV3_PRAMDAC_HSYNC_WIDTH: + nv3->pramdac.hsync_width = value; + break; + case NV3_PRAMDAC_HBURST_START: + nv3->pramdac.hburst_start = value; + break; + case NV3_PRAMDAC_HBURST_END: + nv3->pramdac.hburst_end = value; + break; + case NV3_PRAMDAC_HBLANK_START: + nv3->nvbase.svga.hblankstart = value; + nv3->pramdac.hblank_start = value; + break; + case NV3_PRAMDAC_HBLANK_END: + nv3->nvbase.svga.hblank_end_val = value; + nv3->pramdac.hblank_end = value; + break; + case NV3_PRAMDAC_HTOTAL: + nv3->pramdac.htotal = value; + nv3->nvbase.svga.htotal = value; + break; + case NV3_PRAMDAC_HEQU_WIDTH: + nv3->pramdac.hequ_width = value; + break; + case NV3_PRAMDAC_HSERR_WIDTH: + nv3->pramdac.hserr_width = value; + break; + } + } + } +} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c new file mode 100644 index 000000000..cef9597c9 --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -0,0 +1,149 @@ +/* + * 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. + * + * NV3 PRAMIN - Basically, this is how we know what to render. + * Has a giant hashtable of all the submitted DMA objects using a pseudo-C++ class system + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +// i believe the main loop is to walk the hashtable in RAMIN (last 0.5 MB of VRAM), +// find the objects that were submitted from DMA +// (going from software -> nvidia d3d / ogl implementation -> resource manager client -> nvapi -> nvrm -> GPU PFIFO -> GPU PBUS -> GPU PFB RAMIN -> PGRAPH) +// and then rendering each of those using PGRAPH + +// Notes for all of these functions: +// Structures in RAMIN are stored from the bottom of vram up in reverse order +// this can be explained without bitwise math like so: +// real VRAM address = VRAM_size - (ramin_address - (ramin_address % reversal_unit_size)) - reversal_unit_size + (ramin_address % reversal_unit_size) +// reversal unit size in this case is 16 bytes, vram size is 2-8mb (but 8mb is zx/nv3t only and 2mb...i haven't found a 22mb card) + +// Read 8-bit ramin +uint8_t nv3_ramin_read8(uint32_t addr, void* priv) +{ + addr &= (VRAM_SIZE_4MB - 1); + uint32_t raw_addr = addr; // saved after and + + addr ^= (VRAM_SIZE_4MB - 0x10); + + uint8_t val = nv3->nvbase.svga.vram[addr]; + + nv_log("NV3: Read byte from ramin addr=0x%08x (raw address=0x%08x)", addr, raw_addr); + + return val; +} + +// Read 16-bit ramin +uint16_t nv3_ramin_read16(uint32_t addr, void* priv) +{ + addr &= (VRAM_SIZE_4MB - 1); + + // why does this not work in one line + svga_t* svga = &nv3->nvbase.svga; + uint16_t* vram_16bit = (uint16_t*)svga->vram; + uint32_t raw_addr = addr; // saved after and + + addr ^= (VRAM_SIZE_4MB - 0x10); + addr >>= 1; // what + + uint16_t val = vram_16bit[addr]; // what + + nv_log("NV3: Read word from ramin addr=0x%08x (raw address=0x%08x)", addr, raw_addr); + + return val; +} + +// Read 32-bit ramin +uint32_t nv3_ramin_read32(uint32_t addr, void* priv) +{ + addr &= (VRAM_SIZE_4MB - 1); + + // why does this not work in one line + svga_t* svga = &nv3->nvbase.svga; + uint32_t* vram_32bit = (uint32_t*)svga->vram; + uint32_t raw_addr = addr; // saved after and + + addr ^= (VRAM_SIZE_4MB - 0x10); + addr >>= 2; // what + + uint32_t val = vram_32bit[addr]; + + nv_log("NV3: Read dword from ramin addr=0x%08x (raw address=0x%08x)", addr, raw_addr); + + return val; +} + +// Write 8-bit ramin +void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) +{ + addr &= (VRAM_SIZE_4MB - 1); + uint32_t raw_addr = addr; // saved after and + + // Structures in RAMIN are stored from the bottom of vram up in reverse order + // this can be explained without bitwise math like so: + // real VRAM address = VRAM_size - (ramin_address - (ramin_address % reversal_unit_size)) - reversal_unit_size + (ramin_address % reversal_unit_size) + // reversal unit size in this case is 16 bytes, vram size is 2-8mb (but 8mb is zx/nv3t only and 2mb...i haven't found a 22mb card) + addr ^= (VRAM_SIZE_4MB - 0x10); + + nv3->nvbase.svga.vram[addr] = val; + + nv_log("NV3: Write byte to ramin addr=0x%08x val=0x%08x (raw address=0x%08x)", addr, val, raw_addr); +} + +// Write 16-bit ramin +void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) +{ + addr &= (VRAM_SIZE_4MB - 1); + + // why does this not work in one line + svga_t* svga = &nv3->nvbase.svga; + uint16_t* vram_16bit = (uint16_t*)svga->vram; + uint32_t raw_addr = addr; // saved after and + + addr ^= (VRAM_SIZE_4MB - 0x10); + addr >>= 1; // what + + vram_16bit[addr] = val; + + nv_log("NV3: Write word to ramin addr=0x%08x (raw address=0x%08x)", addr, raw_addr); +} + +// Write 32-bit ramin +void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) +{ + addr &= (VRAM_SIZE_4MB - 1); + + // why does this not work in one line + svga_t* svga = &nv3->nvbase.svga; + uint32_t* vram_32bit = (uint32_t*)svga->vram; + uint32_t raw_addr = addr; // saved after and + + addr ^= (VRAM_SIZE_4MB - 0x10); + addr >>= 2; // what + + vram_32bit[addr] = val; + + nv_log("NV3: Write dword to ramin addr=0x%08x (raw address=0x%08x)", addr, raw_addr); +} diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c new file mode 100644 index 000000000..9a397c8d7 --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c @@ -0,0 +1,31 @@ +/* + * 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. + * + * NV3 PFIFO RAMFC area: Stores context unused DMA channels + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +// Single unified write function... \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c new file mode 100644 index 000000000..baadb9666 --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -0,0 +1,31 @@ +/* + * 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. + * + * NV3 PFIFO hashtable (Quickly access submitted DMA objects) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +// Single unified write function... \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c new file mode 100644 index 000000000..46c95deac --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -0,0 +1,31 @@ +/* + * 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. + * + * NV3 PFIFO ram runout area (you fucked up) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + +// Single unified write function... \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c new file mode 100644 index 000000000..9e15a1035 --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -0,0 +1,120 @@ +/* + * 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. + * + * NV3 PTIMER - PIT emulation + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + + +nv_register_t ptimer_registers[] = { + { NV3_PTIMER_INTR, "PTIMER - Interrupt Status", NULL, NULL}, + { NV3_PTIMER_INTR_EN, "PTIMER - Interrupt Enable", NULL, NULL,}, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +// ptimer init code +void nv3_ptimer_init() +{ + nv_log("NV3: Initialising PTIMER..."); + + nv_log("Done!\n"); +} + +uint32_t nv3_ptimer_read(uint32_t address) +{ + // before doing anything, check the subsystem enablement + + nv_register_t* reg = nv_get_register(address, ptimer_registers, sizeof(ptimer_registers)/sizeof(ptimer_registers[0])); + + // todo: friendly logging + + nv_log("NV3: PTIMER Read from 0x%08x", address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_read) + return reg->on_read(); + else + { + // Interrupt state: + // Bit 0: Alarm + + switch (reg->address) + { + case NV3_PTIMER_INTR: + return nv3->ptimer.interrupt_status; + case NV3_PTIMER_INTR_EN: + return nv3->ptimer.interrupt_enable; + } + } + } + + return 0x00; +} + +void nv3_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("NV3: PTIMER Write 0x%08x -> 0x%08x\n", value, address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + else + { + switch (reg->address) + { + // Interrupt state: + // Bit 0 - Alarm + + case NV3_PTIMER_INTR: + nv3->ptimer.interrupt_status &= ~value; + nv3_pmc_clear_interrupts(); + break; + case NV3_PTIMER_INTR_EN: + nv3->ptimer.interrupt_enable = value & 0x00000001; + break; + } + } + } +} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c new file mode 100644 index 000000000..1aede7b0c --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -0,0 +1,121 @@ +/* + * 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. + * + * NV3 PVIDEO - Video Overlay + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 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_nv3.h> + + +nv_register_t pvideo_registers[] = { + { NV3_PVIDEO_INTR, "PVIDEO - Interrupt Status", NULL, NULL}, + { NV3_PVIDEO_INTR_EN, "PVIDEO - Interrupt Enable", NULL, NULL,}, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +// ptimer init code +void nv3_pvideo_init() +{ + nv_log("Initialising PVIDEO..."); + + nv_log("Done!\n"); +} + +uint32_t nv3_pvideo_read(uint32_t address) +{ + // before doing anything, check the subsystem enablement + + nv_register_t* reg = nv_get_register(address, pvideo_registers, sizeof(pvideo_registers)/sizeof(pvideo_registers[0])); + + // todo: friendly logging + + nv_log("NV3: PVIDEO Read from 0x%08x", address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_read) + return reg->on_read(); + else + { + // Interrupt state: + // Bit 0 - Notifier + + switch (reg->address) + { + case NV3_PVIDEO_INTR: + return nv3->pvideo.interrupt_status; + case NV3_PVIDEO_INTR_EN: + return nv3->pvideo.interrupt_enable; + } + } + } + + return 0x0; +} + +void nv3_pvideo_write(uint32_t address, uint32_t value) +{ + // before doing anything, check the subsystem enablement + nv_register_t* reg = nv_get_register(address, pvideo_registers, sizeof(pvideo_registers)/sizeof(pvideo_registers[0])); + + nv_log("NV3: PVIDEO Write 0x%08x -> 0x%08x\n", value, address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + else + { + switch (reg->address) + { + // Interrupt state: + // Bit 0 - Notifier + + case NV3_PVIDEO_INTR: + nv3->pvideo.interrupt_status &= ~value; + nv3_pmc_clear_interrupts(); + break; + case NV3_PVIDEO_INTR_EN: + nv3->pvideo.interrupt_enable = value & 0x00000001; + break; + } + } + } + +} \ No newline at end of file diff --git a/src/video/nv/nv_base.c b/src/video/nv/nv_base.c new file mode 100644 index 000000000..b67c65dc2 --- /dev/null +++ b/src/video/nv/nv_base.c @@ -0,0 +1,47 @@ +/* + * 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. + * + * Base file for emulation of NVidia video cards. + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024 starfrost + */ + +// Common NV1/3/4... init +#define HAVE_STDARG_H // wtf is this crap +#include +#include +#include +#include <86box/86box.h> +#include <86box/nv/vid_nv.h> + + +// Common logging +#ifdef ENABLE_NV_LOG +int nv_do_log = ENABLE_NV_LOG; + +void nv_log(const char *fmt, ...) +{ + va_list ap; + + if (nv_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +void +nv_log(const char *fmt, ...) +{ + +} +#endif \ No newline at end of file diff --git a/src/video/vid_table.c b/src/video/vid_table.c index 859712357..d10727d04 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -249,6 +249,9 @@ video_cards[] = { { &compaq_voodoo_3_3500_agp_device }, { &voodoo_3_3500_se_agp_device }, { &voodoo_3_3500_si_agp_device }, + { &nv3_device_agp }, + { &nv3_device_pci }, + { NULL } // clang-format on };